예제 #1
0
def raw_bcrypt(password, ident, salt, log_rounds):
    """perform central password hashing step in bcrypt scheme.

    :param password: the password to hash
    :param ident: identifier w/ minor version (e.g. 2, 2a)
    :param salt: the binary salt to use (encoded in bcrypt-base64)
    :param rounds: the log2 of the number of rounds (as int)
    :returns: bcrypt-base64 encoded checksum
    """
    #===================================================================
    # parse inputs
    #===================================================================

    # parse ident
    assert isinstance(ident, native_string_types)
    add_null_padding = True
    if ident == u('2a') or ident == u('2y') or ident == u('2b'):
        pass
    elif ident == u('2'):
        add_null_padding = False
    elif ident == u('2x'):
        raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
                         "currently supported")
    else:
        raise ValueError("unknown ident: %r" % (ident,))

    # decode & validate salt
    assert isinstance(salt, bytes)
    salt = bcrypt64.decode_bytes(salt)
    if len(salt) < 16:
        raise ValueError("Missing salt bytes")
    elif len(salt) > 16:
        salt = salt[:16]

    # prepare password
    assert isinstance(password, bytes)
    if add_null_padding:
        password += BNULL

    # validate rounds
    if log_rounds < 4 or log_rounds > 31:
        raise ValueError("Bad number of rounds")

    #===================================================================
    #
    # run EKS-Blowfish algorithm
    #
    # This uses the "enhanced key schedule" step described by
    # Provos and Mazieres in "A Future-Adaptable Password Scheme"
    # http://www.openbsd.org/papers/bcrypt-paper.ps
    #
    #===================================================================

    engine = BlowfishEngine()

    # convert password & salt into list of 18 32-bit integers (72 bytes total).
    pass_words = engine.key_to_words(password)
    salt_words = engine.key_to_words(salt)

    # truncate salt_words to original 16 byte salt, or loop won't wrap
    # correctly when passed to .eks_salted_expand()
    salt_words16 = salt_words[:4]

    # do EKS key schedule setup
    engine.eks_salted_expand(pass_words, salt_words16)

    # apply password & salt keys to key schedule a bunch more times.
    rounds = 1<<log_rounds
    engine.eks_repeated_expand(pass_words, salt_words, rounds)

    # encipher constant data, and encode to bytes as digest.
    data = list(BCRYPT_CDATA)
    i = 0
    while i < 6:
        data[i], data[i+1] = engine.repeat_encipher(data[i], data[i+1], 64)
        i += 2
    raw = digest_struct.pack(*data)[:-1]
    return bcrypt64.encode_bytes(raw)
예제 #2
0
def raw_bcrypt(password, ident, salt, log_rounds):
    """perform central password hashing step in bcrypt scheme.

    :param password: the password to hash
    :param ident: identifier w/ minor version (e.g. 2, 2a)
    :param salt: the binary salt to use (encoded in bcrypt-base64)
    :param rounds: the log2 of the number of rounds (as int)
    :returns: bcrypt-base64 encoded checksum
    """
    #===================================================================
    # parse inputs
    #===================================================================

    # parse ident
    assert isinstance(ident, unicode)
    if ident == u('2'):
        minor = 0
    elif ident == u('2a'):
        minor = 1
        # XXX: how to indicate caller wants to use crypt_blowfish's
        # workaround variant of 2a?
    elif ident == u('2x'):
        raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
                         "currently supported")
    elif ident == u('2y'):
        # crypt_blowfish compatibility ident which guarantees compat w/ 2a
        minor = 1
    else:
        raise ValueError("unknown ident: %r" % (ident, ))

    # decode & validate salt
    assert isinstance(salt, bytes)
    salt = bcrypt64.decode_bytes(salt)
    if len(salt) < 16:
        raise ValueError("Missing salt bytes")
    elif len(salt) > 16:
        salt = salt[:16]

    # prepare password
    assert isinstance(password, bytes)
    if minor > 0:
        password += BNULL

    # validate rounds
    if log_rounds < 4 or log_rounds > 31:
        raise ValueError("Bad number of rounds")

    #===================================================================
    #
    # run EKS-Blowfish algorithm
    #
    # This uses the "enhanced key schedule" step described by
    # Provos and Mazieres in "A Future-Adaptable Password Scheme"
    # http://www.openbsd.org/papers/bcrypt-paper.ps
    #
    #===================================================================

    engine = BlowfishEngine()

    # convert password & salt into list of 18 32-bit integers (72 bytes total).
    pass_words = engine.key_to_words(password)
    salt_words = engine.key_to_words(salt)

    # truncate salt_words to original 16 byte salt, or loop won't wrap
    # correctly when passed to .eks_salted_expand()
    salt_words16 = salt_words[:4]

    # do EKS key schedule setup
    engine.eks_salted_expand(pass_words, salt_words16)

    # apply password & salt keys to key schedule a bunch more times.
    rounds = 1 << log_rounds
    engine.eks_repeated_expand(pass_words, salt_words, rounds)

    # encipher constant data, and encode to bytes as digest.
    data = list(BCRYPT_CDATA)
    i = 0
    while i < 6:
        data[i], data[i + 1] = engine.repeat_encipher(data[i], data[i + 1], 64)
        i += 2
    raw = digest_struct.pack(*data)[:-1]
    return bcrypt64.encode_bytes(raw)
예제 #3
0
def raw_bcrypt(password, ident, salt, log_rounds):
    """perform central password hashing step in bcrypt scheme.

    :param password: the password to hash
    :param ident: identifier w/ minor version (eg 2, 2a)
    :param salt: the binary salt to use (encoded in bcrypt-base64)
    :param rounds: the log2 of the number of rounds (as int)
    :returns: bcrypt-base64 encoded checksum
    """
    #===========================================================
    # parse inputs
    #===========================================================

    # parse ident
    assert isinstance(ident, unicode)
    if ident == u('2'):
        minor = 0
    elif ident == u('2a'):
        minor = 1
        # XXX: how to indicate caller wants to use crypt_blowfish's
        # workaround variant of 2a?
    elif ident == u('2x'):
        raise ValueError("crypt_blowfish's buggy '2x' hashes are not "
                         "currently supported")
    elif ident == u('2y'):
        # crypt_blowfish compatibility ident which guarantees compat w/ 2a
        minor = 1
    else:
        raise ValueError("unknown ident: %r" % (ident,))

    # decode & validate salt
    assert isinstance(salt, bytes)
    salt = bcrypt64.decode_bytes(salt)
    if len(salt) < 16:
        raise ValueError("Missing salt bytes")
    elif len(salt) > 16:
        salt = salt[:16]

    # prepare password
    assert isinstance(password, bytes)
    if minor > 0:
        password += BNULL

    # validate rounds
    if log_rounds < 4 or log_rounds > 31:
        raise ValueError("Bad number of rounds")

    #===========================================================
    #
    # run EKS-Blowfish algorithm
    #
    # This uses the "enhanced key schedule" step described by
    # Provos and Mazieres in "A Future-Adaptable Password Scheme"
    # http:#www.openbsd.org/papers/bcrypt-paper.ps
    #
    #===========================================================

    engine = BlowfishEngine()

    # convert password & salt into list of 18 32-bit integers.
    pass_words = engine.key_to_words(password)
    salt_words = engine.key_to_words(salt)

    # do EKS key schedule setup
    # NOTE: [:4] is due to salt being 16 bytes originally,
    #       and the list needs to wrap properly
    engine.eks_expand(pass_words, salt_words[:4])

    # apply password & salt keys to key schedule a bunch more times.
    rounds = 1<<log_rounds
    engine.eks_rounds_expand0(pass_words, salt_words, rounds)

    #encipher constant data, and encode to bytes as digest.
    data = list(BCRYPT_CDATA)
    i = 0
    while i < 6:
        data[i], data[i+1] = engine.repeat_encipher(data[i], data[i+1], 64)
        i += 2
    raw = digest_struct.pack(*data)[:-1]
    return bcrypt64.encode_bytes(raw)