Exemple #1
0
 def test_hash_fast(self, func, secret):
     """
     Hash various secrets as cheaply as possible.
     """
     hash_secret(
         secret,
         salt=b"12345678",
         time_cost=1,
         memory_cost=8,
         parallelism=1,
         hash_len=8,
         type=Type.I,
     )
 def test_hash_fast(self, func, secret):
     """
     Hash various secrets as cheaply as possible.
     """
     hash_secret(
         secret,
         salt=b"12345678",
         time_cost=1,
         memory_cost=8,
         parallelism=1,
         hash_len=8,
         type=Type.I,
     )
Exemple #3
0
    def hash(self, string):
        """Hash a provided string using argon2id
        
        Arguments:
            string {str} -- string you want to hash
        
        Returns:
            str -- argon2 hash of the given string
        """

        encoded_str = string.encode(self.string_encoding)

        # random salt, 16 bytes long
        salt = os.urandom(self.salt_len)

        # hash the given string using argon2id algorithm
        result = hash_secret(
            secret=encoded_str,
            salt=salt,
            time_cost=self.time_cost,
            memory_cost=self.memory_cost,
            parallelism=self.parallelism,
            hash_len=self.hash_len,
            type=Type.ID,
        )

        return result.decode(self.hash_encoding)
    def test_hash_secret(self, type, hash):
        """
        Creates the same encoded hash as the Argon2 CLI client.
        """
        rv = hash_secret(
            TEST_PASSWORD,
            TEST_SALT,
            TEST_TIME,
            TEST_MEMORY,
            TEST_PARALLELISM,
            TEST_HASH_LEN,
            type,
        )

        assert hash == rv
        assert isinstance(rv, bytes)
Exemple #5
0
    def test_hash_secret(self, type, hash):
        """
        Creates the same encoded hash as the Argon2 CLI client.
        """
        rv = hash_secret(
            TEST_PASSWORD,
            TEST_SALT,
            TEST_TIME,
            TEST_MEMORY,
            TEST_PARALLELISM,
            TEST_HASH_LEN,
            type,
        )

        assert hash == rv
        assert isinstance(rv, bytes)
Exemple #6
0
def test_argument_ranges(password, time_cost, parallelism, memory_cost,
                         hash_len, salt_len):
    """
    Ensure that both hashing and verifying works for most combinations of legal
    values.

    Limits are intentionally chosen to be *not* on 2^x boundaries.

    This test is rather slow.
    """
    assume(parallelism * 8 <= memory_cost)
    hash = hash_secret(
        secret=password, salt=os.urandom(salt_len),
        time_cost=time_cost, parallelism=parallelism,
        memory_cost=memory_cost,
        hash_len=hash_len,
        type=Type.I,
    )
    assert verify_secret(hash, password, Type.I)
Exemple #7
0
def _hash_argon2id13_secret(password, salt, iterations, memory):
    """
    Internal helper. Returns the salted/hashed password using the
    argon2id-13 algorithm. The return value is base64-encoded.
    """
    rawhash = hash_secret(
        secret=password,
        salt=base64.b64decode(salt),
        time_cost=iterations,
        memory_cost=memory,
        parallelism=1,  # hard-coded by WAMP-SCRAM spec
        hash_len=32,
        type=Type.ID,
        version=0x13,  # note this is decimal "19" which appears in places
    )
    # spits out stuff like:
    # '$argon2i$v=19$m=512,t=2,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ'

    _, tag, ver, options, salt_data, hash_data = rawhash.split(b'$')
    return hash_data
Exemple #8
0
    def hash(self, password):
        """
        Hash *password* and return an encoded hash.

        :param password: Password to hash.
        :type password: ``bytes`` or ``unicode``

        :raises argon2.exceptions.HashingError: If hashing fails.

        :rtype: unicode
        """
        return hash_secret(
            secret=_ensure_bytes(password, self.encoding),
            salt=_ensure_bytes(self.salt, self.encoding),
            time_cost=self.time_cost,
            memory_cost=self.memory_cost,
            parallelism=self.parallelism,
            hash_len=self.hash_len,
            type=Type.ID,
        ).decode("ascii")
def _hash_argon2id13_secret(password, salt, iterations, memory):
    """
    Internal helper. Returns the salted/hashed password using the
    argon2id-13 algorithm. The return value is base64-encoded.
    """
    rawhash = hash_secret(
        secret=password,
        salt=base64.b64decode(salt),
        time_cost=iterations,
        memory_cost=memory,
        parallelism=1,  # hard-coded by WAMP-SCRAM spec
        hash_len=32,
        type=Type.ID,
        version=0x13,  # note this is decimal "19" which appears in places
    )
    # spits out stuff like:
    # '$argon2i$v=19$m=512,t=2,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ'

    _, tag, ver, options, salt_data, hash_data = rawhash.split(b'$')
    return hash_data
def test_argument_ranges(password, time_cost, parallelism, memory_cost,
                         hash_len, salt_len):
    """
    Ensure that both hashing and verifying works for most combinations of legal
    values.

    Limits are intentionally chosen to be *not* on 2^x boundaries.

    This test is rather slow.
    """
    assume(parallelism * 8 <= memory_cost)
    hash = hash_secret(
        secret=password,
        salt=os.urandom(salt_len),
        time_cost=time_cost,
        parallelism=parallelism,
        memory_cost=memory_cost,
        hash_len=hash_len,
        type=Type.I,
    )
    assert verify_secret(hash, password, Type.I)
Exemple #11
0
    def hashWithFixedSalt(self, password, salt):
        """
        Hash *password* and return an encoded hash.

        :param password: Password to hash.
        :param salt: Password salt, should be array of bytes (generate using os.urandom)
        :type password: ``bytes`` or ``unicode``

        :raises argon2.exceptions.HashingError: If hashing fails.

        :rtype: unicode
        """
        return low_level.hash_secret(
            secret=_ensure_bytes(password, self.encoding),
            salt=salt,
            time_cost=self.time_cost,
            memory_cost=self.memory_cost,
            parallelism=self.parallelism,
            hash_len=self.hash_len,
            type=Type.I,
        ).decode("ascii")
Exemple #12
0
def derive_scram_credential(email: str,
                            password: str,
                            salt: Optional[bytes] = None) -> Dict:
    """
    Derive WAMP-SCRAM credentials from user email and password. The SCRAM parameters used
    are the following (these are also contained in the returned credentials):

    * kdf ``argon2id-13``
    * time cost ``4096``
    * memory cost ``512``
    * parallelism ``1``

    See `draft-irtf-cfrg-argon2 <https://datatracker.ietf.org/doc/draft-irtf-cfrg-argon2/>`__ and
    `argon2-cffi <https://argon2-cffi.readthedocs.io/en/stable/>`__.

    :param email: User email.
    :param password: User password.
    :param salt: Optional salt to use (must be 16 bytes long). If none is given, compute salt
        from email as ``salt = SHA256(email)[:16]``.
    :return: WAMP-SCRAM credentials. When serialized, the returned credentials can be copy-pasted
        into the ``config.json`` node configuration for a Crossbar.io node.
    """
    assert HAS_ARGON, 'missing dependency argon2'
    from argon2.low_level import hash_secret
    from argon2.low_level import Type

    # derive salt from email
    if not salt:
        m = hashlib.sha256()
        m.update(email.encode('utf8'))
        salt = m.digest()[:16]
    assert len(salt) == 16

    hash_data = hash_secret(
        secret=password.encode('utf8'),
        salt=salt,
        time_cost=4096,
        memory_cost=512,
        parallelism=1,
        hash_len=32,
        type=Type.ID,
        version=19,
    )
    _, tag, v, params, _, salted_password = hash_data.decode('ascii').split(
        '$')
    assert tag == 'argon2id'
    assert v == 'v=19'  # argon's version 1.3 is represented as 0x13, which is 19 decimal...
    params = {k: v for k, v in [x.split('=') for x in params.split(',')]}

    salted_password = salted_password.encode('ascii')
    client_key = hmac.new(salted_password, b"Client Key",
                          hashlib.sha256).digest()
    stored_key = hashlib.new('sha256', client_key).digest()
    server_key = hmac.new(salted_password, b"Server Key",
                          hashlib.sha256).digest()

    credential = {
        "kdf": "argon2id-13",
        "memory": int(params['m']),
        "iterations": int(params['t']),
        "salt": binascii.b2a_hex(salt).decode('ascii'),
        "stored-key": binascii.b2a_hex(stored_key).decode('ascii'),
        "server-key": binascii.b2a_hex(server_key).decode('ascii'),
    }
    return credential
Exemple #13
0
from pprint import pprint
from argon2.low_level import hash_secret
from argon2.low_level import Type

if len(sys.argv) != 2:
    print("usage: {} password".format(sys.argv[0]))
    sys.exit(2)

password = sys.argv[1].encode('ascii')
salt = os.urandom(16)

hash_data = hash_secret(
    secret=password,
    salt=salt,
    time_cost=4096,
    memory_cost=512,
    parallelism=2,
    hash_len=16,
    type=Type.ID,
    version=19,
)

_, tag, v, params, othersalt, salted_password = hash_data.decode(
    'ascii').split('$')
assert tag == 'argon2id'
assert v == 'v=19'
params = {k: v for k, v in [x.split('=') for x in params.split(',')]}

salted_password = salted_password.encode('ascii')
client_key = hmac.new(salted_password, b"Client Key", hashlib.sha256).digest()
stored_key = hashlib.new('sha256', client_key).digest()
server_key = hmac.new(salted_password, b"Server Key", hashlib.sha256).digest()
if len(sys.argv) not in (2, 3):
    print("usage: {} password".format(sys.argv[0]))
    sys.exit(2)

password = sys.argv[1].encode('ascii')
if len(sys.argv) == 3:
    salt = binascii.a2b_hex(sys.argv[2].encode('ascii'))
    assert len(salt) == 16
else:
    salt = os.urandom(16)

hash_data = hash_secret(
    secret=password,
    salt=salt,
    time_cost=4096,
    memory_cost=512,
    parallelism=1,
    hash_len=32,
    type=Type.ID,
    version=19,
)

_, tag, v, params, othersalt, salted_password = hash_data.decode('ascii').split('$')
assert tag == 'argon2id'
assert v == 'v=19'  # argon's version 1.3 is represented as 0x13, which is 19 decimal...
params = {
    k: v
    for k, v in
    [x.split('=') for x in params.split(',')]
}

salted_password = salted_password.encode('ascii')
Exemple #15
0
    def on_challenge(self, session, challenge):
        assert challenge.method == u"scram"
        assert self._client_nonce is not None
        required_args = ['nonce', 'salt', 'cost']
        optional_args = ['memory', 'parallel', 'channel_binding']
        # probably want "algorithm" too, with either "argon2id-19" or
        # "pbkdf2" as values
        for k in required_args:
            if k not in challenge.extra:
                raise RuntimeError(
                    "WAMP-SCRAM challenge option '{}' is "
                    " required but not specified".format(k)
                )
        for k in challenge.extra:
            if k not in optional_args + required_args:
                raise RuntimeError(
                    "WAMP-SCRAM challenge has unknown attribute '{}'".format(k)
                )

        channel_binding = challenge.extra.get(u'channel_binding', u'')
        server_nonce = challenge.extra[u'nonce']  # base64
        salt = challenge.extra[u'salt']  # base64
        cost = int(challenge.extra[u'cost'])
        memory = int(challenge.extra.get(u'memory', 512))
        parallel = int(challenge.extra.get(u'parallel', 2))
        password = self._args['password'].encode('utf8')  # supplied by user
        authid = saslprep(self._args['authid'])
        client_nonce = self._client_nonce

        auth_message = (
            "{client_first_bare},{server_first},{client_final_no_proof}".format(
                client_first_bare="n={},r={}".format(authid, client_nonce),
                server_first="r={},s={},i={}".format(server_nonce, salt, cost),
                client_final_no_proof="c={},r={}".format(channel_binding, server_nonce),
            )
        )

        rawhash = hash_secret(
            secret=password,
            salt=base64.b64decode(salt),
            time_cost=cost,
            memory_cost=memory,
            parallelism=parallel,
            hash_len=16,  # another knob?
            type=Type.ID,
            version=19,
        )
        # spits out stuff like:
        # '$argon2i$v=19$m=512,t=2,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ'

        _, tag, ver, options, salt_data, hash_data = rawhash.split(b'$')
        salted_password = hash_data
        client_key = hmac.new(salted_password, b"Client Key", hashlib.sha256).digest()
        stored_key = hashlib.new('sha256', client_key).digest()

        client_signature = hmac.new(stored_key, auth_message.encode('ascii'), hashlib.sha256).digest()
        client_proof = xor_array(client_key, client_signature)

        def confirm_server_signature(session, details):
            """
            When the server is satisfied, it sends a 'WELCOME' message.
            This will cause the session to be set up and 'join' gets
            notified. Here, we check the server-signature thus
            authorizing the server -- if it fails we drop the
            connection.
            """
            alleged_server_sig = base64.b64decode(details.authextra['scram_server_signature'])
            server_key = hmac.new(salted_password, b"Server Key", hashlib.sha256).digest()
            server_signature = hmac.new(server_key, auth_message.encode('ascii'), hashlib.sha256).digest()
            if not hmac.compare_digest(server_signature, alleged_server_sig):
                session.log.error("Verification of server SCRAM signature failed")
                session.leave(
                    u"wamp.error.cannot_authenticate",
                    u"Verification of server signature failed",
                )
            else:
                session.log.info(
                    "Verification of server SCRAM signature successful"
                )
        session.on('join', confirm_server_signature)

        return base64.b64encode(client_proof)