def generate_hash_and_salt(password: bytes) -> (bytes, bytes): """ Generates a hash and salt that will match for a given input string. Input is anticipated to be any arbitrary string.""" salt = encode_base64(urandom(16)) password_hashed = encode_base64( pbkdf2('sha1', password, salt, iterations=ITERATIONS, dklen=32)) return password_hashed, salt
def generate_user_hash_and_salt(password: bytes) -> (bytes, bytes): """ Generates a hash and salt that will match a given input string, and also matches the hashing that is done on a user's device. Input is anticipated to be any arbitrary string.""" salt = encode_base64(urandom(16)) password = device_hash(password) password_hashed = encode_base64( pbkdf2('sha1', password, salt, iterations=ITERATIONS, dklen=32)) return password_hashed, salt
def compare_password(proposed_password, salt, real_password_hash): """ Compares a proposed password with a salt and a real password, returns True if the hash results are identical. Expects the proposed password to be a base64 encoded string. Expects the real password to be a base64 encoded string. """ proposed_hash = encode_base64( pbkdf2('sha1', proposed_password, salt, iterations=ITERATIONS, dklen=32)) return proposed_hash == real_password_hash
def authenticate(self, sm, token): mechanism, hashfun_name, = token logger.info("attempting %s mechanism (using %s hashfun)", mechanism, hashfun_name) # this is pretty much a verbatim implementation of RFC 5802. hashfun_factory = functools.partial(hashlib.new, hashfun_name) digest_size = hashfun_factory().digest_size # we don’t support channel binding gs2_header = b"n,," username, password = yield from self._credential_provider() username = saslprep(username).encode("utf8") password = saslprep(password).encode("utf8") our_nonce = base64.b64encode( _system_random.getrandbits(self.nonce_length * 8).to_bytes( self.nonce_length, "little")) auth_message = b"n=" + username + b",r=" + our_nonce _, payload = yield from sm.initiate(mechanism, gs2_header + auth_message) auth_message += b"," + payload payload = dict(self.parse_message(payload)) try: iteration_count = int(payload[b"i"]) nonce = payload[b"r"] salt = base64.b64decode(payload[b"s"]) except (ValueError, KeyError): yield from sm.abort() raise SASLFailure( None, text="malformed server message: {!r}".format(payload)) if not nonce.startswith(our_nonce): yield from sm.abort() raise SASLFailure(None, text="server nonce doesn't fit our nonce") t0 = time.time() salted_password = pbkdf2(hashfun_name, password, salt, iteration_count) logger.debug("pbkdf2 timing: %f seconds", time.time() - t0) client_key = hmac.new(salted_password, b"Client Key", hashfun_factory).digest() stored_key = hashfun_factory(client_key).digest() reply = b"c=" + base64.b64encode(b"n,,") + b",r=" + nonce auth_message += b"," + reply client_proof = (int.from_bytes( hmac.new(stored_key, auth_message, hashfun_factory).digest(), "big") ^ int.from_bytes(client_key, "big")).to_bytes( digest_size, "big") logger.debug("response generation time: %f seconds", time.time() - t0) try: state, payload = yield from sm.response( reply + b",p=" + base64.b64encode(client_proof)) except SASLFailure as err: raise err.promote_to_authentication_failure() from None if state != "success": raise SASLFailure("malformed-request", text="SCRAM protocol violation") server_signature = hmac.new( hmac.new(salted_password, b"Server Key", hashfun_factory).digest(), auth_message, hashfun_factory).digest() payload = dict(self.parse_message(payload)) if base64.b64decode(payload[b"v"]) != server_signature: raise SASLFailure( None, "authentication successful, but server signature invalid") return True
def compute(self, val): return pbkdf2(hash_name=self.func, password=val, salt=self.salt, iterations=self.iterations, dklen=self.dklen)
def generate_user_hash_and_salt(password): salt = encode_base64(urandom(16)) password = device_hash(password) password_hashed = encode_base64(pbkdf2('sha1', password, salt, iterations=1000, dklen=32)) return password_hashed, salt
def authenticate(self, sm, token): mechanism, hashfun_name, = token logger.info("attempting %s mechanism (using %s hashfun)", mechanism, hashfun_name) # this is pretty much a verbatim implementation of RFC 5802. hashfun_factory = functools.partial(hashlib.new, hashfun_name) digest_size = hashfun_factory().digest_size # we don’t support channel binding gs2_header = b"n,," username, password = yield from self._credential_provider() username = saslprep(username).encode("utf8") password = saslprep(password).encode("utf8") our_nonce = base64.b64encode(_system_random.getrandbits( self.nonce_length * 8 ).to_bytes( self.nonce_length, "little" )) auth_message = b"n=" + username + b",r=" + our_nonce _, payload = yield from sm.initiate( mechanism, gs2_header + auth_message) auth_message += b"," + payload payload = dict(self.parse_message(payload)) try: iteration_count = int(payload[b"i"]) nonce = payload[b"r"] salt = base64.b64decode(payload[b"s"]) except (ValueError, KeyError): yield from sm.abort() raise SASLFailure( None, text="malformed server message: {!r}".format(payload)) if not nonce.startswith(our_nonce): yield from sm.abort() raise SASLFailure( None, text="server nonce doesn't fit our nonce") t0 = time.time() salted_password = pbkdf2( hashfun_name, password, salt, iteration_count) logger.debug("pbkdf2 timing: %f seconds", time.time() - t0) client_key = hmac.new( salted_password, b"Client Key", hashfun_factory).digest() stored_key = hashfun_factory(client_key).digest() reply = b"c=" + base64.b64encode(b"n,,") + b",r=" + nonce auth_message += b"," + reply client_proof = ( int.from_bytes( hmac.new( stored_key, auth_message, hashfun_factory).digest(), "big") ^ int.from_bytes(client_key, "big")).to_bytes(digest_size, "big") logger.debug("response generation time: %f seconds", time.time() - t0) try: state, payload = yield from sm.response( reply + b",p=" + base64.b64encode(client_proof) ) except SASLFailure as err: raise err.promote_to_authentication_failure() from None if state != "success": raise SASLFailure( "malformed-request", text="SCRAM protocol violation") server_signature = hmac.new( hmac.new( salted_password, b"Server Key", hashfun_factory).digest(), auth_message, hashfun_factory).digest() payload = dict(self.parse_message(payload)) if base64.b64decode(payload[b"v"]) != server_signature: raise SASLFailure( None, "authentication successful, but server signature invalid") return True
def authenticate(self, sm, token): mechanism, info, = token logger.info("attempting %s mechanism (using %s hashfun)", mechanism, info) # this is pretty much a verbatim implementation of RFC 5802. hashfun_factory = functools.partial(hashlib.new, info.hashfun_name) gs2_header = self._get_gs2_header() username, password = yield from self._credential_provider() username = saslprep(username).encode("utf8") password = saslprep(password).encode("utf8") our_nonce = base64.b64encode(_system_random.getrandbits( self.nonce_length * 8 ).to_bytes( self.nonce_length, "little" )) auth_message = b"n=" + username + b",r=" + our_nonce state, payload = yield from sm.initiate( mechanism, gs2_header + auth_message) if state != SASLState.CHALLENGE or payload is None: yield from sm.abort() raise SASLFailure( None, text="protocol violation: expected challenge with payload") auth_message += b"," + payload payload = dict(self.parse_message(payload)) try: iteration_count = int(payload[b"i"]) nonce = payload[b"r"] salt = base64.b64decode(payload[b"s"]) except (ValueError, KeyError): yield from sm.abort() raise SASLFailure( None, text="malformed server message: {!r}".format(payload)) if not nonce.startswith(our_nonce): yield from sm.abort() raise SASLFailure( None, text="server nonce doesn't fit our nonce") if (self.enforce_minimum_iteration_count and iteration_count < info.minimum_iteration_count): raise SASLFailure( None, text="minimum iteration count for {} violated " "({} is less than {})".format( mechanism, iteration_count, info.minimum_iteration_count, ) ) t0 = time.time() salted_password = pbkdf2( info.hashfun_name, password, salt, iteration_count) logger.debug("pbkdf2 timing: %f seconds", time.time() - t0) client_key = hmac.new( salted_password, b"Client Key", hashfun_factory).digest() stored_key = hashfun_factory(client_key).digest() reply = b"c=" + base64.b64encode(self._get_cb_data()) + b",r=" + nonce auth_message += b"," + reply client_proof = xor_bytes( hmac.new( stored_key, auth_message, hashfun_factory).digest(), client_key) logger.debug("response generation time: %f seconds", time.time() - t0) try: state, payload = yield from sm.response( reply + b",p=" + base64.b64encode(client_proof) ) except SASLFailure as err: raise err.promote_to_authentication_failure() from None # this is the pseudo-challenge for the server signature # we have to reply with the empty string! if state != SASLState.CHALLENGE: raise SASLFailure( "malformed-request", text="SCRAM protocol violation") state, dummy_payload = yield from sm.response(b"") if state != SASLState.SUCCESS or dummy_payload is not None: raise SASLFailure( None, "SASL protocol violation") server_signature = hmac.new( hmac.new( salted_password, b"Server Key", hashfun_factory).digest(), auth_message, hashfun_factory).digest() payload = dict(self.parse_message(payload)) if base64.b64decode(payload[b"v"]) != server_signature: raise SASLFailure( None, "authentication successful, but server signature invalid") return True
def derive_key_from_password(salt, password=None, hash_name="sha512", iterations=50000, prompt="Please supply the password for the key: ", dklen=None): return hashlib.pbkdf2(hash_name, getpass.getpass(prompt) if password is None else password, salt, iterations, dklen)