def test_invalid_verify(self, backend): prk = binascii.unhexlify(b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) with pytest.raises(InvalidKey): hkdf.verify(prk, b"wrong key")
def test_already_finalized(self, backend): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) hkdf.derive(b"first") with pytest.raises(AlreadyFinalized): hkdf.derive(b"second")
def aeskeygen(shared_key): backend = default_backend() info = b"hkdf-example" hkdf = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=info, backend=backend) key = hkdf.derive(shared_key) return key
def test_verify(self, backend): prk = binascii.unhexlify(b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5") okm = b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" b"5bf34007208d5b887185865" info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) assert hkdf.verify(prk, binascii.unhexlify(okm)) is None
def generate_session_keys(self, shared_secret): hkdf = HKDFExpand(algorithm=hashes.SHA256(), backend=default_backend(), length=72, info=b"key_generation") key = hkdf.derive(shared_secret) kf = key[:32] kb = key[32:64] sf = key[64:68] sb = key[68:72] return [kf, kb, sf, sb, 1, 1]
def generate_session_keys(self, shared_secret): hkdf = HKDFExpand(algorithm=hashes.SHA256(), backend=default_backend(), length=40, info=b"key_generation") key = hkdf.derive(shared_secret) kf = key[:16] kb = key[16:32] sf = key[32:36] sb = key[36:40] return [kf, kb, sf, sb, 1, 1]
def hkdf_expand_test(backend, algorithm, params): hkdf = HKDFExpand(algorithm, int(params["l"]), info=binascii.unhexlify(params["info"]) or None, backend=backend) okm = hkdf.derive(binascii.unhexlify(params["prk"])) assert okm == binascii.unhexlify(params["okm"])
def test_random_source_verify_default_backend(self, backend): key_material = urandom(16) identifier = b'region123' derived_key = derive_key(key_material, identifier, strong=True) kdf = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=identifier, backend=backend) kdf.verify(key_material, derived_key)
def test_invalid_verify(self, backend): prk = binascii.unhexlify( b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5" ) info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) with pytest.raises(InvalidKey): hkdf.verify(prk, b"wrong key")
def hkdf_expand_test(backend, algorithm, params): hkdf = HKDFExpand( algorithm, int(params["l"]), info=binascii.unhexlify(params["info"]) or None, backend=backend ) okm = hkdf.derive(binascii.unhexlify(params["prk"])) assert okm == binascii.unhexlify(params["okm"])
def aeskeygen(shared_key): backend = default_backend() info = b"hkdf-example" hkdf = HKDFExpand( algorithm=hashes.SHA256(), length=32, info=info, backend=backend ) key = hkdf.derive(shared_key) return key
def test_verify(self, backend): prk = binascii.unhexlify( b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5" ) okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) hkdf.verify(prk, binascii.unhexlify(okm))
def test_buffer_protocol(self, backend): prk = bytearray( binascii.unhexlify( b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2" b"b3e5")) okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) assert binascii.hexlify(hkdf.derive(prk)) == okm
async def ingameRegistration(request: Request) -> Union[dict, bytes]: start = time.time() mpargs = request.args name = mpargs["user[username]"].strip() # what is this setup osu lol email = mpargs["user[user_email]"].strip() pw = mpargs["user[password]"].strip() if not mpargs.get("check") or not all((name, email, pw)): return b"missing required paramaters" errors = defaultdict(list) if " " in name and "_" in name: errors["username"].append('Username cannot contain both "_" and " "') if await glob.db.fetchval("SELECT 1 FROM users WHERE name = %s", [name]): errors["username"].append("Username already taken!") if await glob.db.fetchval("SELECT 1 FROM users WHERE email = %s", [email]): errors["user_email"].append("Email already in use!") if not len(pw) >= 8: errors["password"].append("Password must be 8+ characters!") if errors: return {"form_error": {"user": errors}} if int(mpargs["check"]) == 0: md5 = hashlib.md5(pw.encode()).hexdigest().encode() k = HKDFExpand( algorithm=hashes.SHA256(), length=32, info=b"", backend=backend(), ) bc = k.derive(md5).decode("unicode-escape") glob.cache["pw"][bc] = md5 uid = await glob.db.execute( "INSERT INTO users (name, email, pw, safe_name, registered_at) VALUES (%s, %s, %s, %s, %s)", [name, email, bc, name.lower().replace(" ", "_"), time.time()], ) await glob.db.execute("INSERT INTO stats (id) VALUES (%s)", [uid]) info( f"{name} successfully registered. | Time Elapsed: {(time.time() - start) * 1000:.2f}ms", ) return b"ok"
async def ingameRegistration(request: Request) -> Union[dict, bytes]: start = time.time() mpargs = request.args name = mpargs['user[username]'].strip() # what is this setup osu lol email = mpargs['user[user_email]'].strip() pw = mpargs['user[password]'].strip() if not mpargs.get('check') or not all((name, email, pw)): return b'missing required paramaters' errors = defaultdict(list) if ' ' in name and '_' in name: errors['username'].append('Username cannot contain both "_" and " "') if await glob.db.fetchval("SELECT 1 FROM users WHERE name = %s", [name]): errors['username'].append('Username already taken!') if await glob.db.fetchval("SELECT 1 FROM users WHERE name = %s", [email]): errors['user_email'].append('Email already in use!') if not len(pw) >= 8: errors['password'].append('Password must be 8+ characters!') if errors: return {'form_error': {'user': errors}} if int(mpargs['check']) == 0: md5 = hashlib.md5(pw.encode()).hexdigest().encode() k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) bc = k.derive(md5).decode('unicode-escape') glob.cache['pw'][bc] = md5 uid = await glob.db.execute( "INSERT INTO users (name, email, pw, safe_name, registered_at) VALUES (%s, %s, %s, %s, %s)", [name, email, bc, name.lower().replace(' ', '_'), time.time()]) await glob.db.execute('INSERT INTO stats (id) VALUES (%s)', [uid]) log( f'{name} successfully registered. | Time Elapsed: {(time.time() - start) * 1000:.2f}ms', Ansi.LBLUE) return b'ok'
def hkdf_expand(algorithm="SHA256", length=256, info='', backend=BACKEND): """ Returns an hmac based key derivation function (expand only) from cryptography.hazmat.primitives.hkdf. """ return HKDFExpand(algorithm=getattr(hashes, algorithm.upper())(), length=length, info=info, backend=BACKEND)
def initate_connection(self, target_host, no_exchange=False): """Tries the primary and alternate ports.""" global connected, fernet # establish an initial connection try: self.outgoing.connect((target_host, REMOTE_PORT)) except Exception as e: logging.error(str(e)) logging.info("Trying alternate remote") try: self.outgoing.connect((target_host, REMOTE_ALT_PORT)) except Exception as e: logging.error(str(e)) return # setup connection from server thread connected = target_host if no_exchange: return # exchange the ec public key try: self.outgoing.sendall(private_key.public_key().public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)) peer_public_key = load_pem_public_key(self.outgoing.recv(4096)) shared_key = private_key.exchange(peer_public_key) derived_key = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=None).derive(shared_key) fernet = Fernet(base64.urlsafe_b64encode(derived_key)) except Exception as e: logging.error(str(e)) connected = None
def test_invalid_backend(): pretend_backend = object() with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): HKDF(hashes.SHA256(), 16, None, None, pretend_backend) with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): HKDFExpand(hashes.SHA256(), 16, None, pretend_backend)
def hkdf_expand_label( algorithm: hashes.HashAlgorithm, secret: bytes, label: bytes, hash_value: bytes, length: int, ) -> bytes: return HKDFExpand( algorithm=algorithm, length=length, info=hkdf_label(label, hash_value, length), backend=default_backend(), ).derive(secret)
def derive_reply_keys(message_id): """Derive the lookup key and encrytion key from an aliased message id.""" algorithm = hashes.SHA256() hkdf = HKDFExpand(algorithm=algorithm, length=16, info=b"replay replies lookup key") lookup_key = hkdf.derive(message_id) hkdf = HKDFExpand(algorithm=algorithm, length=32, info=b"replay replies encryption key") encryption_key = hkdf.derive(message_id) return (lookup_key, encryption_key)
def HKDF_Expand_Label(key, label, context, length, backend=default_backend(), algorithm=hashes.SHA256()): tmp_label = b"tls13 " + label.encode() hkdf_label = (struct.pack(">h", length) + struct.pack("b", len(tmp_label)) + tmp_label + struct.pack("b", len(context)) + context) return HKDFExpand(algorithm=algorithm, length=length, info=hkdf_label, backend=backend).derive(key)
def derive_key(key_material, identifier, length=32, strong=False, backend=DEFAULT_BACKEND): if not isinstance(key_material, bytes): raise TypeError('key_material must be bytes.') if not isinstance(identifier, bytes): raise TypeError('identifier must be bytes.') if strong: kdf = HKDFExpand(algorithm=DEFAULT_HASH, length=length, info=identifier, backend=backend) else: kdf = PBKDF2HMAC(algorithm=DEFAULT_HASH, length=length, salt=identifier, iterations=131072, backend=backend) return kdf.derive(key_material)
def accept_connection(self): """Accepts connection and derives a shared key.""" global connected, fernet try: # exchange the ec public key peer_public_key = load_pem_public_key(self.peer.recv(4096)) self.peer.sendall(private_key.public_key().public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)) shared_key = private_key.exchange(peer_public_key) derived_key = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=None).derive(shared_key) fernet = Fernet(base64.urlsafe_b64encode(derived_key)) except Exception as e: logging.error(str(e))
def hkdf_expand_label(secret: bytes, label: bytes, context: GroupContext, cipher_suite: CipherSuite) -> bytes: """ Generates a secret from given secret, label and GroupContext object :param secret: secret argument for HKDFExpand :param label: label used in HKDFExpand :param context: GroupContext object used in HKDFExpand :param cipher_suite: CipherSuite which should be used :return: secret derived from given parameters """ context_hash = cipher_suite.get_hash() context_hash.update(bytes(context)) label_part = bytes(HkdfLabel(context_hash.finalize(), cipher_suite.get_hash_length(), label, context)) hkdf_label: bytes = b'mls10 ' + label_part # todo: support hash from cipher suite return HKDFExpand(length=cipher_suite.get_hash_length(), info=hkdf_label, algorithm=hashes.SHA256(), backend=default_backend()).derive(secret)
def _hkdf_expand(self, length: int, label: str, prk: bytes, transcript: bytes) -> bytes: """ Derive the encryption key and the IV to protect the COSE_Encrypt0 message in the EDHOC message 2. :return: """ hash_func = self.cipher_suite.hash.hash_cls info = EdhocKDFInfo(edhoc_aead_id=self.cipher_suite.aead.identifier, transcript_hash=self.transcript( hash_func, transcript), label=label, length=length) derived_bytes = HKDFExpand(algorithm=hash_func(), length=info.length, info=info.encode()).derive(prk) return derived_bytes
def hkdf_sha256(key, length=16, info=''): hkdf = HKDFExpand(algorithm=hashes.SHA256(), length=length, info=info, backend=bend) return hkdf.derive(key)
def test_unicode_error(self, backend): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) with pytest.raises(TypeError): hkdf.derive("first") # type: ignore[arg-type]
def getBitwardenSecrets(email, password, kdfIterations, encKey, encPrivateKey): BitwardenSecrets = {} BitwardenSecrets['email'] = email BitwardenSecrets['kdfIterations'] = kdfIterations BitwardenSecrets['MasterPassword'] = password BitwardenSecrets['ProtectedSymmetricKey'] = encKey BitwardenSecrets['ProtectedRSAPrivateKey'] = encPrivateKey kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=bytes(BitwardenSecrets['email'], 'utf-8'), iterations=BitwardenSecrets['kdfIterations'], backend=default_backend() ) BitwardenSecrets['MasterKey'] = kdf.derive(BitwardenSecrets['MasterPassword']) BitwardenSecrets['MasterKey_b64'] = base64.b64encode(BitwardenSecrets['MasterKey']).decode('utf-8') kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=bytes(BitwardenSecrets['MasterPassword']), iterations=1, backend=default_backend() ) BitwardenSecrets['MasterPasswordHash'] = base64.b64encode(kdf.derive(BitwardenSecrets['MasterKey'])).decode('utf-8') hkdf = HKDFExpand( algorithm=hashes.SHA256(), length=32, info=b"enc", backend=default_backend() ) BitwardenSecrets['StretchedEncryptionKey'] = hkdf.derive(BitwardenSecrets['MasterKey']) BitwardenSecrets['StretchedEncryptionKey_b64'] = base64.b64encode(BitwardenSecrets['StretchedEncryptionKey']).decode('utf-8') hkdf = HKDFExpand( algorithm=hashes.SHA256(), length=32, info=b"mac", backend=default_backend() ) BitwardenSecrets['StretchedMacKey'] = hkdf.derive(BitwardenSecrets['MasterKey']) BitwardenSecrets['StretchedMacKey_b64'] = base64.b64encode(BitwardenSecrets['StretchedMacKey']).decode('utf-8') BitwardenSecrets['StretchedMasterKey'] = BitwardenSecrets['StretchedEncryptionKey'] + BitwardenSecrets['StretchedMacKey'] BitwardenSecrets['StretchedMasterKey_b64'] = base64.b64encode(BitwardenSecrets['StretchedMasterKey']).decode('utf-8') BitwardenSecrets['GeneratedSymmetricKey'], \ BitwardenSecrets['GeneratedEncryptionKey'], \ BitwardenSecrets['GeneratedMACKey'] = decryptMasterEncryptionKey(BitwardenSecrets['ProtectedSymmetricKey'], BitwardenSecrets['StretchedEncryptionKey'], BitwardenSecrets['StretchedMacKey']) BitwardenSecrets['GeneratedSymmetricKey_b64'] = base64.b64encode(BitwardenSecrets['GeneratedSymmetricKey']).decode('utf-8') BitwardenSecrets['GeneratedEncryptionKey_b64'] = base64.b64encode(BitwardenSecrets['GeneratedEncryptionKey']).decode('utf-8') BitwardenSecrets['GeneratedMACKey_b64'] = base64.b64encode(BitwardenSecrets['GeneratedMACKey']).decode('utf-8') BitwardenSecrets['RSAPrivateKey'] = decryptRSAPrivateKey(BitwardenSecrets['ProtectedRSAPrivateKey'], \ BitwardenSecrets['GeneratedEncryptionKey'], \ BitwardenSecrets['GeneratedMACKey']) return(BitwardenSecrets)
def hkdf(key, length=16, info=''): hkdf = HKDFExpand(algorithm=SHA256(), length=length, info=info, backend=bend) return hkdf.derive(key)
def expand(self, prk, info, L): h = self.hash hkdf = HKDFExpand(h, L, info, default_backend()) return hkdf.derive(prk)
def test_unicode_error(self, backend): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) with pytest.raises(TypeError): hkdf.derive(u"first")
async def register_post(): if 'authenticated' in session: return await flash('error', "You're already logged in.", 'home') if not glob.config.registration: return await flash('error', 'Registrations are currently disabled.', 'home') form = await request.form username = form.get('username', type=str) email = form.get('email', type=str) passwd_txt = form.get('password', type=str) if username is None or email is None or passwd_txt is None: return await flash('error', 'Invalid parameters.', 'home') if glob.config.hCaptcha_sitekey != 'changeme': captcha_data = form.get('h-captcha-response', type=str) if ( captcha_data is None or not await utils.validate_captcha(captcha_data) ): return await flash('error', 'Captcha failed.', 'register') # Usernames must: # - be within 2-15 characters in length # - not contain both ' ' and '_', one is fine # - not be in the config's `disallowed_names` list # - not already be taken by another player # check if username exists if not regexes.username.match(username): return await flash('error', 'Invalid username syntax.', 'register') if '_' in username and ' ' in username: return await flash('error', 'Username may contain "_" or " ", but not both.', 'register') if username in glob.config.disallowed_names: return await flash('error', 'Disallowed username; pick another.', 'register') if await glob.db.fetch('SELECT 1 FROM users WHERE name = %s', username): return await flash('error', 'Username already taken by another user.', 'register') # Emails must: # - match the regex `^[^@\s]{1,200}@[^@\s\.]{1,30}\.[^@\.\s]{1,24}$` # - not already be taken by another player if not regexes.email.match(email): return await flash('error', 'Invalid email syntax.', 'register') if await glob.db.fetch('SELECT 1 FROM users WHERE email = %s', email): return await flash('error', 'Email already taken by another user.', 'register') # Passwords must: # - be within 8-32 characters in length # - have more than 3 unique characters # - not be in the config's `disallowed_passwords` list if not 8 <= len(passwd_txt) <= 32: return await flash('error', 'Password must be 8-32 characters in length.', 'register') if len(set(passwd_txt)) <= 3: return await flash('error', 'Password must have more than 3 unique characters.', 'register') if passwd_txt.lower() in glob.config.disallowed_passwords: return await flash('error', 'That password was deemed too simple.', 'register') # TODO: add correct locking # (start of lock) pw_md5 = hashlib.md5(passwd_txt.encode()).hexdigest().encode() k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) pw_hash = k.derive(pw_md5).decode('unicode-escape') glob.cache['pw'][pw_hash] = pw_md5 # cache pw safe_name = utils.get_safe_name(username) # fetch the users' country if 'CF-Connecting-IP' in request.headers: ip = request.headers['CF-Connecting-IP'] else: ip = request.headers['X-Forwarded-For'].split(',')[0] try: country = await utils.fetch_geoloc(ip) except: country = 'xx' user = await glob.db.execute( 'INSERT INTO users ' '(name, safe_name, email, pw, country, registered_at) ' 'VALUES (%s, %s, %s, %s, %s, UNIX_TIMESTAMP())', [username, safe_name, email, pw_hash, country] ) user_id = user # add to `stats` table. await glob.db.execute( 'INSERT INTO stats ' '(id) VALUES (%s)', user_id ) # (end of lock) if glob.config.debug: log(f'{username} has registered - awaiting verification.', Ansi.LGREEN) # user has successfully registered return await render_template('verify.html')
async def login_post(): if 'authenticated' in session: return await flash('error', "You're already logged in!", 'home') if glob.config.debug: login_time = time.time_ns() form = await request.form username = form.get('username', type=str) passwd_txt = form.get('password', type=str) if username is None or passwd_txt is None: return await flash('error', 'Invalid parameters.', 'home') # check if account exists user_info = await glob.db.fetchrow( 'SELECT id, name, email, priv, ' 'pw, silence_end ' 'FROM users ' 'WHERE safe_name = %s', [utils.get_safe_name(username)] ) # user doesn't exist; deny post # NOTE: Bot isn't a user. if not user_info or user_info['id'] == 1: if glob.config.debug: log(f"{username}'s login failed - account doesn't exist.", Ansi.LYELLOW) return await flash('error', 'Account does not exist.', 'login') # cache and other related password information pw_cache = glob.cache['pw'] pw_hash = user_info['pw'].encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') pw_md5 = hashlib.md5(passwd_txt.encode()).hexdigest().encode() # check credentials (password) against db # intentionally slow, will cache to speed up if pw_hash in pw_cache: if pw_md5 != pw_cache[pw_hash]: # ~0.1ms if glob.config.debug: log(f"{username}'s login failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Password is incorrect.', 'login') else: # ~200ms k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) try: k.verify(pw_md5, pw_hash) except: if glob.config.debug: log(f"{username}'s login failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Password is incorrect.', 'login') # login successful; cache password for next login pw_cache[pw_hash] = pw_md5 # user not verified; render verify if not user_info['priv'] & Privileges.Verified: if glob.config.debug: log(f"{username}'s login failed - not verified.", Ansi.LYELLOW) return await render_template('verify.html') # user banned; deny post if user_info['priv'] & Privileges.Disallowed: if glob.config.debug: log(f"{username}'s login failed - banned.", Ansi.RED) return await flash('error', 'Your account is restricted. You are not allowed to log in.', 'login') # login successful; store session data if glob.config.debug: log(f"{username}'s login succeeded.", Ansi.LGREEN) session['authenticated'] = True session['user_data'] = { 'id': user_info['id'], 'name': user_info['name'], 'email': user_info['email'], 'priv': user_info['priv'], 'silence_end': user_info['silence_end'], 'is_staff': user_info['priv'] & Privileges.Staff, 'is_donator': user_info['priv'] & Privileges.Supporter } if glob.config.debug: login_time = (time.time_ns() - login_time) / 1e6 log(f'Login took {login_time:.2f}ms!', Ansi.LYELLOW) return await flash('success', f'Hey, welcome back {username}!', 'home')
async def settings_password_post(): form = await request.form old_password = form.get('old_password') new_password = form.get('new_password') repeat_password = form.get('repeat_password') # new password and repeat password don't match; deny post if new_password != repeat_password: return await flash('error', "Your new password doesn't match your repeated password!", 'settings/password') # new password and old password match; deny post if old_password == new_password: return await flash('error', 'Your new password cannot be the same as your old password!', 'settings/password') # Passwords must: # - be within 8-32 characters in length # - have more than 3 unique characters # - not be in the config's `disallowed_passwords` list if not 8 < len(new_password) <= 32: return await flash('error', 'Your new password must be 8-32 characters in length.', 'settings/password') if len(set(new_password)) <= 3: return await flash('error', 'Your new password must have more than 3 unique characters.', 'settings/password') if new_password.lower() in glob.config.disallowed_passwords: return await flash('error', 'Your new password was deemed too simple.', 'settings/password') # cache and other password related information pw_cache = glob.cache['pw'] pw_hash = (await glob.db.fetchval( 'SELECT pw ' 'FROM users ' 'WHERE id = %s', [session['user_data']['id']]) ).encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') pw_md5 = hashlib.md5(old_password.encode()).hexdigest().encode() # check old password against db # intentionally slow, will cache to speed up if pw_hash in pw_cache: if pw_md5 != pw_cache[pw_hash]: # ~0.1ms if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') else: # ~200ms k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) try: k.verify(pw_hash, pw_md5) except: if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') # remove old password from cache if pw_hash in pw_cache: del pw_cache[pw_hash] # calculate new md5 & bcrypt pw pw_md5 = hashlib.md5(new_password.encode()).hexdigest().encode() k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) pw_hash_new = k.derive(pw_md5).decode('unicode-escape') # update password in cache and db pw_cache[pw_hash_new] = pw_md5 await glob.db.execute( 'UPDATE users ' 'SET pw = %s ' 'WHERE safe_name = %s', [pw_hash_new, utils.get_safe_name(session['user_data']['name'])] ) # logout session.pop('authenticated', None) session.pop('user_data', None) return await flash('success', 'Your password has been changed! Please log in again.', 'login')
def derive_new_key(key): hkdf = HKDFExpand(algorithm=hashes.SHA256(), length=BLOCK_SIZE, info=None, backend=BACKEND) return hkdf.derive(key)
request.resp_headers['cho-token'] = 'no' # client knows there is something up if we set token to 'no' return writer.userID(-1) # if server is migrated then passwords are previously stored as bcrypt # lets check if we need to convert and do so if needed if glob.config.server_migration and ('$' in user['pw'] and len(user['pw']) == 60): user_pw = user['pw'].encode() if not bcrypt.checkpw(pw, user_pw): if glob.config.debug: log(f"{username}'s login attempt failed: provided an incorrect password", Ansi.LRED) request.resp_headers['cho-token'] = 'no' # client knows there is something up if we set token to 'no' return writer.userID(-1) else: # correct password, we allow the user to continue but lets convert the password to our new format first k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) new_pw = k.derive(pw).decode('unicode-escape') await glob.db.execute('UPDATE users SET pw = %s WHERE id = %s', [new_pw, user['id']]) # add to cache for the future glob.cache['pw'][new_pw] = pw else: # password is already converted or db already has correct formats bcache = glob.cache['pw'] # get our cached pws to potentially enhance speed user_pw = user['pw'].encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') # this is cursed SHUT UP if user_pw in bcache: if pw != bcache[user_pw]: # compare provided md5 with the stored (cached) pw to ensure they have provided the correct password if glob.config.debug: log(f"{username}'s login attempt failed: provided an incorrect password", Ansi.LRED)
def derivekey(key): hkdf = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=None, backend=BACKEND)