예제 #1
0
def verify(message, signature, pub_key, hasher='SHA-1', salt_len=None):
    # type: (bytes, bytes, PublicKey, str, int) -> bool
    # Determine the size of the hash output (hLen)
    try:
        h_len = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.
            format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))))
    # Determine the size of the public key in bytes (k)
    k = common.byte_size(pub_key.n)
    mod_bits = k * 8 - 1
    em_len = math.ceil(mod_bits / 8)
    s = transform.bytes2int(signature)
    m = core.decrypt_int(
        s, pub_key.e,
        pub_key.n)  # Use encrypt_int because of the additional range-checking
    em = transform.int2bytes(m, em_len)
    # EMSA-PSS-VERIFY (m, em, mod_bits)
    if len(message) > 2**61 - 1:
        raise VerificationError('Incorrect signature')
    m_hash = pkcs1.compute_hash(message, hasher)
    s_len = salt_len if salt_len is not None else h_len
    if em_len < h_len + s_len + 2:
        raise VerificationError('Incorrect signature')
    if em[-1] != 0xbc:
        raise VerificationError('Incorrect signature')

    masked_db, h = em[:em_len - h_len - 1], em[em_len - h_len - 1:-1]

    for i in range(8 * em_len - mod_bits):
        if masked_db[0] & (1 << (7 - i)) != 0:
            raise VerificationError('Incorrect signature')
    db_mask = mgf1(h, em_len - h_len - 1, hasher)
    db = bytearray(common.xor(masked_db, db_mask))
    a = 0xff
    for _ in range(8 * em_len - mod_bits):
        a = a >> 1
    db[0] &= a
    for i in range(em_len - h_len - s_len - 2):
        if db[i] != 0:
            raise VerificationError('Incorrect signature')
    if db[em_len - h_len - s_len - 2] != 0x01:
        raise VerificationError('Incorrect signature')
    salt = db[-s_len:] if s_len > 0 else b''
    m2 = b''.join((b'\x00' * 8, m_hash, salt))
    h2 = pkcs1.compute_hash(m2, hasher)
    return h == h2
예제 #2
0
    def test_hash_sign_verify(self):
        """Test happy flow of hash, sign, and verify"""

        message = b'je moeder'
        msg_hash = pkcs1.compute_hash(message, 'SHA-256')
        signature = pkcs1.sign_hash(msg_hash, self.priv, 'SHA-256')

        self.assertTrue(pkcs1.verify(message, signature, self.pub))
예제 #3
0
    def test_split_hash_sign(self):
        """Hashing and then signing should match with directly signing the message. """

        message = b'je moeder'
        msg_hash = pkcs1.compute_hash(message, 'SHA-256')
        signature1 = pkcs1.sign_hash(msg_hash, self.priv, 'SHA-256')

        # Calculate the signature using the unified method
        signature2 = pkcs1.sign(message, self.priv, 'SHA-256')

        self.assertEqual(signature1, signature2)
예제 #4
0
파일: oaep.py 프로젝트: hRun/TA-cryptosuite
def mgf1(seed, length, hasher='SHA-1'):
    """
    MGF1 is a Mask Generation Function based on a hash function.

    A mask generation function takes an octet string of variable length and a
    desired output length as input, and outputs an octet string of the desired
    length. The plaintext-awareness of RSAES-OAEP relies on the random nature of
    the output of the mask generation function, which in turn relies on the
    random nature of the underlying hash.

    :param bytes seed: seed from which mask is generated, an octet string
    :param int length: intended length in octets of the mask, at most 2^32(hLen)
    :param str hasher: hash function (hLen denotes the length in octets of the hash
        function output)

    :return: mask, an octet string of length `length`
    :rtype: bytes

    :raise OverflowError: when `length` is too large for the specified `hasher`
    :raise ValueError: when specified `hasher` is invalid
    """

    try:
        hash_length = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.format(
                hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))
            )
        )

    # If l > 2^32(hLen), output "mask too long" and stop.
    if length > (2**32 * hash_length):
        raise OverflowError(
            "Desired length should be at most 2**32 times the hasher's output "
            "length ({hash_length} for {hasher} function)".format(
                hash_length=hash_length,
                hasher=hasher,
            )
        )

    # Looping `counter` from 0 to ceil(l / hLen)-1, build `output` based on the
    # hashes formed by (`seed` + C), being `C` an octet string of length 4
    # generated by converting `counter` with the primitive I2OSP
    output = b''.join(
        pkcs1.compute_hash(
            seed + transform.int2bytes(counter, fill_size=4),
            method_name=hasher,
        )
        for counter in range(common.ceil_div(length, hash_length) + 1)
    )

    # Output the leading `length` octets of `output` as the octet string mask.
    return output[:length]
예제 #5
0
def mgf1(seed, length, hasher='SHA-1'):
    """
    MGF1 is a Mask Generation Function based on a hash function.

    A mask generation function takes an octet string of variable length and a
    desired output length as input, and outputs an octet string of the desired
    length. The plaintext-awareness of RSAES-OAEP relies on the random nature of
    the output of the mask generation function, which in turn relies on the
    random nature of the underlying hash.

    :param bytes seed: seed from which mask is generated, an octet string
    :param int length: intended length in octets of the mask, at most 2^32(hLen)
    :param str hasher: hash function (hLen denotes the length in octets of the hash
        function output)

    :return: mask, an octet string of length `length`
    :rtype: bytes

    :raise OverflowError: when `length` is too large for the specified `hasher`
    :raise ValueError: when specified `hasher` is invalid
    """

    try:
        hash_length = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.format(
                hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))
            )
        )

    # If l > 2^32(hLen), output "mask too long" and stop.
    if length > (2**32 * hash_length):
        raise OverflowError(
            "Desired length should be at most 2**32 times the hasher's output "
            "length ({hash_length} for {hasher} function)".format(
                hash_length=hash_length,
                hasher=hasher,
            )
        )

    # Looping `counter` from 0 to ceil(l / hLen)-1, build `output` based on the
    # hashes formed by (`seed` + C), being `C` an octet string of length 4
    # generated by converting `counter` with the primitive I2OSP
    output = b''.join(
        pkcs1.compute_hash(
            seed + transform.int2bytes(counter, fill_size=4),
            method_name=hasher,
        )
        for counter in range(common.ceil_div(length, hash_length) + 1)
    )

    # Output the leading `length` octets of `output` as the octet string mask.
    return output[:length]
예제 #6
0
def sign(message, priv_key, hasher='SHA-1', salt_len=None):
    # type: (bytes, PrivateKey, str, int) -> bytes
    # Determine the size of the hash output (hLen)
    try:
        h_len = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.
            format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))))

    # Determine the size of the public key in bytes (k)
    k = common.byte_size(priv_key.n)
    mod_bits = k * 8 - 1
    em_len = math.ceil(mod_bits / 8)
    if len(message) > 2**61 - 1:
        raise OverflowError('message too long')
    m_hash = pkcs1.compute_hash(message, hasher)
    s_len = salt_len if salt_len is not None else h_len
    if em_len < h_len + s_len + 2:
        raise SigningError('Encoding error')
    salt = b'' if s_len == 0 else randnum.read_random_bits(s_len * 8)
    m2 = b''.join((b'\x00' * 8, m_hash, salt))
    h = pkcs1.compute_hash(m2, hasher)
    ps = b'\x00' * (em_len - s_len - h_len - 2)
    db = b'\x01'.join((ps, salt))
    db_mask = mgf1(h, em_len - h_len - 1, hasher)
    masked_db = bytearray(common.xor(db, db_mask))

    a = 0xff
    for _ in range(8 * em_len - mod_bits):
        a = a >> 1
    masked_db[0] &= a
    # for i in range(8*em_len - mod_bits + 1):
    #     masked_db[0] &= ~(1 << (7-i))
    em = b''.join((masked_db, h, b'\xbc'))
    m = transform.bytes2int(em)
    # s = priv_key.blinded_encrypt(m)
    s = core.encrypt_int(m, priv_key.d, priv_key.n)
    sig = transform.int2bytes(s, k)
    return sig
예제 #7
0
def encrypt(message, pub_key, label=b'', hasher='SHA-1', **kwargs):
    # type: (bytes, PublicKey, bytes, str, dict) -> bytes
    """
    Encrypts the given message using OAEP.
    For a complete documentation see https://tools.ietf.org/html/rfc8017#section-7.1.1

    :param message: Message to be encrypted. The length must be smaller than or equal to k - 2hLen - 2.
        Where hLen denotes the length in octets of the hash function output and k denotes the length on octets
        of the RSA modulus n.
    :param pub_key: Recipient's RSA public key
    :param label: Optional label to be associated with the message
    :param hasher: The hash function's name
    :param kwargs: Used internally for testing-purposes
    :return: The ciphertext of length k
    """

    # The following code implements RSAES-OAEP-ENCRYPT (rfc8017 7.1.1). The variable names of the specification are
    # put in parentheses. Also, the corresponding step# from the document is included in the comments.

    # Determine the size of the hash output (hLen)
    try:
        h_len = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.
            format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))))

    # Determine the length of the message in bytes (mLen)
    m_len = len(message)

    # Step 1: Length checking:
    # a.)   The label cannot be longer than the maximum input-length of the hash-function
    #       Hardcoded the maximum input-length to 2**61 - 1 (SHA-1)
    if len(label) > 2**61 - 1:
        raise OverflowError('Label too long')

    # Determine the size of the public key in bytes (k)
    k = common.byte_size(pub_key.n)

    # b.)   The message length can be at most k - 2hLen - 2
    if m_len > k - 2 * h_len - 2:
        raise OverflowError('Message too long')

    # Step 2: EME-OAEP encoding
    # a.)   l_hash = Hash(label)
    l_hash = pkcs1.compute_hash(label, hasher)

    # b.)   Generate a padding string (ps)
    ps = b'\x00' * (k - m_len - 2 * h_len - 2)

    # c.)   Construct the data block (db)
    #       db = l_hash || ps || 0x01 || message
    db = b''.join((l_hash, ps, b'\x01', message))

    # d.)   Generate a random hash
    # Used only for testing-purposes. This should NEVER be set in production as it might compromise security!
    if 'test_seed' in kwargs:
        seed = kwargs['test_seed']
    else:
        seed = randnum.read_random_bits(h_len * 8)

    # e-h.) Calculate the masks
    db_mask = mgf1(seed, k - h_len - 1, hasher)
    masked_db = common.xor(db, db_mask)
    seed_mask = mgf1(masked_db, h_len, hasher)
    masked_seed = common.xor(seed, seed_mask)

    # i.)   Construct the encoded message
    #       em = 0x00 || masked_seed || masked_db
    em = b''.join((b'\x00', masked_seed, masked_db))

    # Step 3: RSA encryption
    # a.)   Convert em to an integer
    #       m = OS2IP(em)
    m = transform.bytes2int(em)

    # b.)   Apply the RSAEP encryption primitive
    #       c = RSAEP(pub_key, m)
    c = core.encrypt_int(m, pub_key.e, pub_key.n)

    # c.)   Convert c to bytes (the ciphertext C)
    #       ct = I2OSP(c, k)
    ct = transform.int2bytes(c, k)
    return ct
예제 #8
0
def decrypt(ct, priv_key, label=b'', hasher='SHA-1'):
    # type: (bytes, PrivateKey, bytes, str) -> bytes
    def split_data_block(_db, _h_len):
        # type: (bytes, bytes) -> (bytes, bytes)
        _l_hash = _db[0:_h_len]
        i = _h_len
        for i in range(_h_len, len(_db)):
            d = _db[i]
            if d != 0:
                break
        if _db[i] != 0x01:
            raise DecryptionError('Decryption error')
        _m = _db[i + 1:]
        return _l_hash, _m

    # Determine the size of the hash output (hLen)
    try:
        h_len = pkcs1.HASH_METHODS[hasher]().digest_size
    except KeyError:
        raise ValueError(
            'Invalid `hasher` specified. Please select one of: {hash_list}'.
            format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys()))))

    # Determine the length of the message in bytes (mLen)
    c_len = len(ct)

    # Step 1: Length checking:
    # a.)   The label cannot be longer than the maximum input-length of the hash-function
    #       Hardcoded the maximum input-length to 2**61 - 1 (SHA-1)
    if len(label) > 2**61 - 1:
        raise OverflowError('Label too long')

    # Determine the size of the public key in bytes (k)
    k = common.byte_size(priv_key.n)

    # b.)   The length of the ciphertext must be exactly equal to k
    if c_len != k:
        raise DecryptionError('Decryption failed')

    # c.)   k must be larger or equal to 2hLen + 2
    if k < 2 * h_len + 2:
        raise DecryptionError('Decryption failed')

    # Step 2: RSA decryption
    # a.)   Convert the ciphertext to an integer c
    #       c = OS2IP(ct)
    c = transform.bytes2int(ct)

    # b.)   Apply the RSADP decryption primitive
    #       m = RSADP(priv_key, c)
    m = priv_key.blinded_decrypt(c)

    # c.)   Convert to bytes (encoded message em)
    #       em = I2OSP(m, k)
    em = transform.int2bytes(m, k)

    l_hash = pkcs1.compute_hash(label, hasher)
    y, masked_seed, masked_db = em[0], em[1:h_len + 1], em[h_len + 1:]

    seed_mask = mgf1(masked_db, h_len, hasher)
    seed = common.xor(masked_seed, seed_mask)
    db_mask = mgf1(seed, k - h_len - 1, hasher)
    db = common.xor(masked_db, db_mask)

    l_hash2, message = split_data_block(db, h_len)

    if l_hash != l_hash2 or y != 0x00:
        raise DecryptionError('Decryption error')

    return message