def check_password(login, password): """Checks provided credentials against database or system user. :param str login: login username :param str password: cleartext password :return: bool """ check_login_attempt(login) is_verified = False try: user = db.session.query(Principal).filter_by(login=login).one() if user.is_root: is_verified = check_system_login(user.login, password) else: salt, pass_hash = user.password_hash.split(':') computed = KDF.PBKDF2(password, salt, dkLen=DK_LEN, count=HASH_ROUNDS).encode('hex') if computed == pass_hash: is_verified = True except NoResultFound: LOG.error("User not found with login: %s", login) # simulate a password check anyway KDF.PBKDF2(password, 'salt', dkLen=DK_LEN, count=HASH_ROUNDS).encode('hex') if not is_verified: log_failed_login_attempt(login) return is_verified
def set_passphrase(self, passphrase): """Set a user defined passphrase to override the AES and HMAC keys""" self.passphrase = passphrase self.aes_key = KDF.PBKDF2(passphrase, dconfig.aes_salt.decode("hex"), 32) self.mac_key = KDF.PBKDF2(passphrase, dconfig.mac_salt.decode("hex"), 64)
def decode_url(self, url): """Takes a URL, and returns the cryptographic nonce. Use PBKDF2 with our nonce and our salts to return the file name, AES key, and MAC key. keyword arguments: url -- the url after the FQDN provided by the client""" self.url = url url = url + "==" # add the padding back try: self.nonce = base64.urlsafe_b64decode(url.encode("utf-8")) except: url = "errormessage==" self.nonce = base64.urlsafe_b64decode(url.encode("utf-8")) self.f_key = KDF.PBKDF2(self.nonce, dconfig.nonce_salt.decode("hex"), 16) self.aes_key = KDF.PBKDF2(self.nonce, dconfig.aes_salt.decode("hex"), 32) self.mac_key = KDF.PBKDF2(self.nonce, dconfig.mac_salt.decode("hex"), 64) duress = KDF.PBKDF2(self.nonce, dconfig.duress_salt.decode("hex"), 16) self.dkey = base64.urlsafe_b64encode(duress)[:22] self.fname = base64.urlsafe_b64encode(self.f_key)[:22]
def get_key(self): """ :return: """ key = KDF.PBKDF2(password=self.password, salt=self.salt, count=100000, dkLen=self.lenght_of_key) return key
def main_loop(): pw_hashed = None pw = '' print('If you forget the password, all shards that are stored on your') print('server will be lost forever.') while pw == '': try: pw = getpass.getpass('Enter the password for Natural ' \ + 'Message shard encryption: ') except KeyboardInterrupt: print() # move to a new output line return (None) # The salt is set to zero so that I get the same # output every time that I run this because I will # use the output here as the password later. # This routine is based on the one in RNCryptor (with # the salt modified): print('Please wait while the password is being hashed.') print('You will be prompted to verify the results...') pw_hashed = KDF.PBKDF2( pw, b'00', dkLen=32, count=iterations, prf=lambda p, s: hmac.new(p, s, hashlib.sha256).digest()) return pw_hashed
def __derive_key(self): """ Generates derived key using stored config """ return KDF.PBKDF2( self.app_uuid + self.config[self.name]['UUID'], self.config[self.name]['salt'].encode('utf-8'), dkLen=32, #32 bytes = 256 bits, max AES key length count=self.config[self.name]['iterations'])
def _pbkdf2(self, password, salt, iterations=10000, key_length=32): return KDF.PBKDF2( password, salt, dkLen=key_length, count=iterations, prf=lambda p, s: hmac.new(p, s, hashlib.sha1).digest())
def derive_keys(masterPassoword: str, salt: bytes, iterations: int) -> DerivedKeys: """ In OPVault format, the key derivation parameters, except Master Password, are all in 'profile'. In iCloud/Dropbox synced format, the profile is stored as `profile.js` as a json object. In MacOS local format, the profile is a table in a SQLite database called 'OnePassword.sqlite', usually located at ~/Library/Group Containers/2BUA8C4S2C.com.agilebits/Library/Application Support/1Password/Data. Because we don't use 2skd in OpVault, here it is fairly simple. The algorithm used here is PBKDF2-HMAC-SHA512, with the plain 'salt' in profile. See https://support.1password.com/opvault-design/#key-derivation for more information. The derived cryptoKey and hmacKey are used to encrypt/decrypt and hmac-authenticate masterKey and overviewKey in profile. input: masterpassword: the user entered master password string salt: the base64 encoded string directly from profile.js. Thus it need to be decoded here """ # encode master password as a utf-8 null terminated string. mp = (masterPassoword + '\0').encode('utf-8') # decode the salt into bytes dk = KDF.PBKDF2(password=mp, salt=salt, dkLen=64, count=iterations, prf=hmac_sha512) return DerivedKeys(cryptoKey=dk[0:32], hmacKey=dk[32:])
def encrypt(self, data): salt = get_random_bytes(8) #random salt to calculate the key key = KDF.PBKDF2(self.pwd, salt) #128bit key derivation function iv = get_random_bytes(16) #initialization vector of the chain blocks cipher = AES.new(key, AES.MODE_CFB, iv) #CFB mode does not need message padding return salt + iv + cipher.encrypt(data)
def __init__(self, password): ACypher.__init__(self, 'aes') if password == '': Error_Fatal("Error: The AES key is void. Check your ini file.") self.SALT = get_random_bytes(8) self.KEY = KDF.PBKDF2(password, self.SALT) self.password = password
def __generate_derived_key(self): """ generate derived key using initial keying material and salt """ return KDF.PBKDF2(self.ikm + self.config[self.name]['UUID'], self.config[self.name]['salt'], dkLen=16, count=self.config[self.name]['iterations'])
def aes_decrypt(self, input_byte, encrypt_key): pbk_key_instance = KDF.PBKDF2(encrypt_key, self.__salt, dkLen=self.__salt_size, count=self.__iteration_count) pbes_key_cipher = AES.new(pbk_key_instance, AES.MODE_CBC, self.__IV) desecret_res = pbes_key_cipher.decrypt(input_byte) unpad_byte = Padding.unpad(desecret_res, AES.block_size) return unpad_byte
def __call__(self, archive): print("Deciphering") nonce = archive[:16] salt = archive[16:16 + 8] ctr_aes = Counter.new(128, initial_value=int(binascii.hexlify(nonce), 16)) key = KDF.PBKDF2(self.password, salt) cipher = AES.new(key, AES.MODE_CTR, counter=ctr_aes) return cipher.decrypt(archive[16 + 8:])
def createAccount(self, request): """ Create new custom account """ status = StringMsg() # return status status.data = 'error' # default to error # Verify if user passed reCAPTCHA # POST request to Google reCAPTCHA API url = 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s' % (GRECAPTCHA_SECRET, request.recaptcha) try: result = urlfetch.Fetch(url, method=2) except: raise endpoints.BadRequestException('urlfetch error: Unable to POST to Google reCAPTCHA') return status data = json.loads(result.content) if not data['success']: status.data = 'recaptcha_fail' return status user_id = 'ca_' + request.email # Get profile from datastore -- if profile not found, then profile=None profile_key = ndb.Key(Profile, user_id) profile = profile_key.get() # If profile exists, return status if profile: status.data = 'user_exists' return status # Salt and hash the password salt = Crypto.Random.new().read(16) passkey = KDF.PBKDF2(request.password, salt).encode('hex') salt_passkey = salt.encode('hex') + '|' + passkey # Generate new session ID session_id = Crypto.Random.new().read(16).encode('hex') # Create new profile for user Profile( key = profile_key, userId = user_id, contactEmail = request.email, salt_passkey = salt_passkey, session_id = session_id, loggedIn = True, emailVerified = False, notifications = [False, True] ).put() # Generate user access token token = self._genToken({'userId': user_id, 'session_id': session_id}) # If we get here, means we suceeded status.data = 'success' status.accessToken = token return status
def decriptInterKey(self, enc): # Generate password derived key to encript AES-32 intermediary key password_key = KDF.PBKDF2(self.password, self.salt, 32, 1000, None) # Start the initializing vector iv = open('data/crypto/iv_password_16.txt', 'rb').read() # Create the cipher object and process the input stream cipher = AES.AESCipher(password_key, AES.MODE_CBC, iv) return self.unpad(cipher.decrypt(enc[len(password_key) / 2:]))
def bf_encrypt(self, input_byte, encrypt_key): pbk_key_instance = KDF.PBKDF2(encrypt_key, self.__salt, dkLen=self.__salt_size, count=self.__iteration_count) pbes_key_cipher = Blowfish.new(pbk_key_instance, Blowfish.MODE_CBC, self.__IV[0:8]) pad_byte = Padding.pad(input_byte, Blowfish.block_size) secret_res = pbes_key_cipher.encrypt(pad_byte) return secret_res
def changePassword(self, request): """ Change password """ status = StringMsg() status.data = 'error' # Get user profile user_id = self._getUserId(request.accessToken) profile_key = ndb.Key(Profile, user_id) profile = profile_key.get() # Not sure how this would happen, but it would be an error if not profile: return status # Check if provided old password matches user's current password db_salt, db_passkey = profile.salt_passkey.split('|') passkey = KDF.PBKDF2(request.oldPw, db_salt.decode('hex')).encode('hex') # Passwords don't match, return if passkey != db_passkey: status.data = 'old_pw_wrong' return status # If passwords match, salt & hash new password new_salt = Crypto.Random.new().read(16) new_passkey = KDF.PBKDF2(request.newPw, new_salt).encode('hex') new_salt_passkey = new_salt.encode('hex') + '|' + new_passkey profile.salt_passkey = new_salt_passkey # Also generate new session ID session_id = Crypto.Random.new().read(16).encode('hex') profile.session_id = session_id # Update DB profile.put() # Send user an email to notify password change self._emailPwChange(profile) # Return success status status.data = 'success' return status
def decrypt(masterpassword, cipher_dict): key = KDF.PBKDF2(masterpassword, b64decode(cipher_dict['mastersalt'].encode('utf-8')), dkLen=32, count=500000) cipher_obj = AES.new(key, AES.MODE_CBC, b64decode(cipher_dict['iv'].encode('utf-8'))) plaintext = cipher_obj.decrypt( b64decode(cipher_dict['ciphertext'].encode('utf-8'))) plaintext = unpad(plaintext, AES.block_size).decode('utf-8') return plaintext
def decrypt(pwd, msg): """ Decryption function to decrypt ciphertext and return original message :param pwd: decryption key (first 128 bits to be used if longer than 128) :param msg: string to be decrypted :return: plaintext message """ key = KDF.PBKDF2(pwd[:128], msg[:8]) # pwd[:128] truncates key to max 128 cipher = AES.new(key, AES.MODE_CFB, msg[8:24]) return cipher.decrypt(msg[24:])
def create_url(self): """Create a cryptographic nonce for our URL, and use PBKDF2 with our nonce and our salts to generate a file name, AES key, and MAC key. - 128-bits for the URL - 128-bits for file name - 256-bits for AES-256 key - 512-bits for HMAC-SHA512 key""" self.nonce = os.urandom(16) self.f_key = KDF.PBKDF2(self.nonce, dconfig.nonce_salt.decode("hex"), 16) self.aes_key = KDF.PBKDF2(self.nonce, dconfig.aes_salt.decode("hex"), 32) self.mac_key = KDF.PBKDF2(self.nonce, dconfig.mac_salt.decode("hex"), 64) self.url = base64.urlsafe_b64encode(self.nonce)[:22] self.fname = base64.urlsafe_b64encode(self.f_key)[:22] if self.exists(): return self.create_url()
def decode_url(self, url): """Takes a URL, and returns the cryptographic nonce. Use PBKDF2 with our nonce and our salts to return the file name, AES key, and MAC key. keyword arguments: url -- the url after the FQDN provided by the client""" self.url = url url = url + "==" # add the padding back self.nonce = base64.urlsafe_b64decode(url.encode("utf-8")) self.f_key = KDF.PBKDF2( self.nonce, dconfig.nonce_salt.decode("hex"), 16) self.aes_key = KDF.PBKDF2( self.nonce, dconfig.aes_salt.decode("hex"), 32) self.mac_key = KDF.PBKDF2( self.nonce, dconfig.mac_salt.decode("hex"), 64) self.fname = base64.urlsafe_b64encode(self.f_key)[:22] if os.path.exists(self.path('dkey')): with open(self.path('dkey'), 'r') as dkey: self.dkey = dkey.read()
def __init__(self, db="dojot_devm", dbhost="postgres", dbuser="******", dbpass=None, dbdriver="postgresql+psycopg2", kafka_host="kafka", kafka_port="9092", broker="http://data-broker", subject="dojot.device-manager.device", device_subject="device-data", status_timeout="5", create_db=True, log_level="INFO"): # Postgres configuration data self.dbname = os.environ.get('DBNAME', db) self.dbhost = os.environ.get('DBHOST', dbhost) self.dbuser = os.environ.get('DBUSER', dbuser) self.dbpass = os.environ.get('DBPASS', dbpass) self.dbdriver = os.environ.get('DBDRIVER', dbdriver) self.create_db = os.environ.get('CREATE_DB', create_db) # Kafka configuration self.kafka_host = os.environ.get('KAFKA_HOST', kafka_host) self.kafka_port = os.environ.get('KAFKA_PORT', kafka_port) # Log configuration self.log_level = os.environ.get('LOG_LEVEL', log_level) # Data broker configuration # Full baseurl of data-broker self.data_broker = os.environ.get('BROKER', broker) # Which subject to publish new device information to self.subject = os.environ.get('SUBJECT', subject) self.device_subject = os.environ.get('DEVICE_SUBJECT', device_subject) self.status_timeout = int( os.environ.get('STATUS_TIMEOUT', status_timeout)) # crypto configuration if not os.environ.get('DEV_MNGR_CRYPTO_PASS'): raise Exception( "environment variable 'DEV_MNGR_CRYPTO_PASS' not configured") if not os.environ.get('DEV_MNGR_CRYPTO_IV'): raise Exception( "environment variable 'DEV_MNGR_CRYPTO_IV' not configured") if not os.environ.get('DEV_MNGR_CRYPTO_SALT'): raise Exception( "environment variable 'DEV_MNGR_CRYPTO_SALT' not configured") salt = os.environ.get('DEV_MNGR_CRYPTO_SALT') salt = salt.encode('ASCII') password = os.environ.get('DEV_MNGR_CRYPTO_PASS') key = KDF.PBKDF2(password, salt, dkLen=16, count=1000, prf=None) self.crypto = {'key': key, 'iv': os.environ.get('DEV_MNGR_CRYPTO_IV')}
def encrypt(masterpassword, plaintext): mastersalt = os.urandom(32) key = KDF.PBKDF2(masterpassword, mastersalt, dkLen=32, count=500000) cipher_obj = AES.new(key, AES.MODE_CBC) ciphertext = cipher_obj.encrypt( pad(plaintext.encode('utf-8'), AES.block_size)) iv = cipher_obj.iv return { 'ciphertext': b64encode(ciphertext).decode('utf-8'), 'iv': b64encode(iv).decode('utf-8'), 'mastersalt': b64encode(mastersalt).decode('utf-8') }
def encrypt(pwd, data): """ Encryption function that encrypts a string using EAS encryption with 128 bit key :param pwd: encryption key (first 128 bits to be used if longer than 128) :param data: string to be encrypted :return: ciphertext """ salt = get_random_bytes(8) key = KDF.PBKDF2(pwd[:128], salt) # pwd[:128] truncates key to max 128 iv = get_random_bytes(16) cipher = AES.new(key, AES.MODE_CFB, iv) return salt + iv + cipher.encrypt(data)
def generate_password_hash(raw_password): """Generates a password for a user. The value will be stored in the database as a concatenation of a random salt and generated hash, separated by a full colon. :return: string """ rand = Random.new() salt = rand.read(64).encode('hex') pass_hash = KDF.PBKDF2(raw_password, salt, dkLen=DK_LEN, count=HASH_ROUNDS).encode('hex') return '%s:%s' % (salt, pass_hash)
def login(self, request): """ Check username/password to login """ status = StringMsg() # return status status.data = 'error' # default to error # Verify if user passed reCAPTCHA # POST request to Google reCAPTCHA API url = 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s' % (GRECAPTCHA_SECRET, request.recaptcha) try: result = urlfetch.Fetch(url, method=2) except: raise endpoints.BadRequestException('urlfetch error: Unable to POST to Google reCAPTCHA') return status data = json.loads(result.content) if not data['success']: status.data = 'recaptcha_fail' return status user_id = 'ca_' + request.email # Get profile from datastore -- if profile not found, then profile=None profile_key = ndb.Key(Profile, user_id) profile = profile_key.get() # If profile does not exist, return False if not profile: return status # Parse salt and passkey from DB, compare it to provided version db_salt, db_passkey = profile.salt_passkey.split('|') passkey = KDF.PBKDF2(request.password, db_salt.decode('hex')).encode('hex') # Passwords don't match, return False if passkey != db_passkey: return status # Generate new session ID session_id = Crypto.Random.new().read(16).encode('hex') profile.session_id = session_id # Update user's status to logged-in profile.loggedIn = True profile.put() # Generate user access token token = self._genToken({'userId': user_id, 'session_id': session_id}) # If we get here, means we suceeded status.data = 'success' status.accessToken = token return status
async def handle_registration_intent(self, req: web.Request) -> web.Response: params = await req.json() if "public_key" not in params: raise HTTPBadRequest(body={"error_msg": "'public_key' is obligatory"}) else: key = RSA.import_key(params["public_key"]) message = get_random_bytes(32) cipher_rsa = PKCS1_OAEP.new(key) secret_key = KDF.PBKDF2(self._context.get("SECRET_KEY"), "mysalt") cipher_aes = AES.new(secret_key, AES.MODE_EAX) return web.Response(body={ "server_string": cipher_rsa.encrypt(), "client_string": cipher_aes.encrypt(message+key.encode('utf-8')), })
def generate_dk(self, token): """Generate a defined key for a given token in hex >>> c = Credentials() >>> c.salt = 'abc' >>> c.iterations = 4 >>> dk = c.generate_dk("password") >>> len(dk) 64 """ return KDF.PBKDF2(token, self.salt, dkLen=self.DK_LEN, count=self.iterations + self.ITER_OFFSET).encode('hex')
def makeKey(password, salt): """ Makes a key using the inputed password and salt Args: :param password: Password to hash :param salt: Salt used for hash Returns: bytes: The hashed password. """ return KDF.PBKDF2( password, salt, dkLen=32, count=5000, hmac_hash_module=Hash.SHA256 )
def encriptInterKey(self, key): # Generate password derived key to encript AES-32 intermediary key password_key = KDF.PBKDF2(self.password, self.salt, len(key), 1000, None) # Insert padding to intermediary key key_padded = self.pad(key, len(password_key)) # Get password initializing vector iv = open('data/crypto/iv_password_16.txt', 'rb').read() # Create the cipher object and process the input stream cipher = AES.AESCipher(password_key, AES.MODE_CBC, iv) return (iv + cipher.encrypt(key_padded))