def testEncode1(self): der = DerObjectId('1.2.840.113549.1.1.1') self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) # der = DerObjectId() der.value = '1.2.840.113549.1.1.1' self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))
def _import_pkcs8(encoded, passphrase): from Cryptodome.IO import PKCS8 # From RFC5915, Section 1: # # Distributing an EC private key with PKCS#8 [RFC5208] involves including: # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the # namedCurve as the parameters in the privateKeyAlgorithm field; and # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING. algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any # distiction for now. unrestricted_oid = "1.2.840.10045.2.1" ecdh_oid = "1.3.132.1.12" ecmqv_oid = "1.3.132.1.13" if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) curve_oid = DerObjectId().decode(params).value return _import_private_der(private_key, passphrase, curve_oid)
def _export_subjectPublicKeyInfo(self): # Uncompressed form order_bytes = _curve.order.size_in_bytes() public_key = (bchr(4) + self.pointQ.x.to_bytes(order_bytes) + self.pointQ.y.to_bytes(order_bytes)) unrestricted_oid = "1.2.840.10045.2.1" return _create_subject_public_key_info(unrestricted_oid, public_key, DerObjectId(_curve.oid))
def _import_private_der(encoded, passphrase, curve_oid=None): # See RFC5915 https://tools.ietf.org/html/rfc5915 # # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: parameters = DerObjectId(explicit=0).decode(private_key[2]).value if curve_oid is not None and parameters != curve_oid: raise ValueError("Curve mismatch") curve_oid = parameters except ValueError: pass if curve_oid is None: raise ValueError("No curve found") for curve_name, curve in _curves.items(): if curve.oid == curve_oid: break else: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) scalar_bytes = DerOctetString().decode(private_key[1]).payload modulus_bytes = curve.p.size_in_bytes() if len(scalar_bytes) != modulus_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y)
def testDecode1(self): # Empty sequence der = DerObjectId() der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) self.assertEquals(der.value, '1.2.840.113549.1.1.1')
def testInit1(self): der = DerObjectId("1.1") self.assertEquals(der.encode(), b('\x06\x01)'))
def testDecode2(self): # Verify that decode returns the object der = DerObjectId() self.assertEquals(der, der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')))
def _export_subjectPublicKeyInfo(self, compress): public_key = self._export_SEC1(compress) unrestricted_oid = "1.2.840.10045.2.1" return _create_subject_public_key_info(unrestricted_oid, public_key, DerObjectId(self._curve.oid))
def _EMSA_PKCS1_V1_5_ENCODE(msg_hash, emLen, with_hash_parameters=True): """ Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.2). ``_EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input, and hash it internally. Here, we expect that the message has already been hashed instead. :Parameters: msg_hash : hash object The hash object that holds the digest of the message being signed. emLen : int The length the final encoding must have, in bytes. with_hash_parameters : bool If True (default), include NULL parameters for the hash algorithm in the ``digestAlgorithm`` SEQUENCE. :attention: the early standard (RFC2313) stated that ``DigestInfo`` had to be BER-encoded. This means that old signatures might have length tags in indefinite form, which is not supported in DER. Such encoding cannot be reproduced by this function. :Return: An ``emLen`` byte long string that encodes the hash. """ # First, build the ASN.1 DER object DigestInfo: # # DigestInfo ::= SEQUENCE { # digestAlgorithm AlgorithmIdentifier, # digest OCTET STRING # } # # where digestAlgorithm identifies the hash function and shall be an # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. # # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= { # { OID id-md2 PARAMETERS NULL }| # { OID id-md5 PARAMETERS NULL }| # { OID id-sha1 PARAMETERS NULL }| # { OID id-sha256 PARAMETERS NULL }| # { OID id-sha384 PARAMETERS NULL }| # { OID id-sha512 PARAMETERS NULL } # } # # Appendix B.1 also says that for SHA-1/-2 algorithms, the parameters # should be omitted. They may be present, but when they are, they shall # have NULL value. digestAlgo = DerSequence([DerObjectId(msg_hash.oid).encode()]) if with_hash_parameters: digestAlgo.append(DerNull().encode()) digest = DerOctetString(msg_hash.digest()) digestInfo = DerSequence([digestAlgo.encode(), digest.encode()]).encode() # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen < len(digestInfo) + 11: raise TypeError( "Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = b'\xFF' * (emLen - len(digestInfo) - 3) return b'\x00\x01' + PS + b'\x00' + digestInfo
def unwrap(p8_private_key, passphrase=None): """Unwrap a private key from a PKCS#8 blob (clear or encrypted). Args: p8_private_key (byte string): The private key wrapped into a PKCS#8 blob, DER encoded. passphrase (byte string or string): The passphrase to use to decrypt the blob (if it is encrypted). Return: A tuple containing #. the algorithm identifier of the wrapped key (OID, dotted string) #. the private key (byte string, DER encoded) #. the associated parameters (byte string, DER encoded) or ``None`` Raises: ValueError : if decoding fails """ if passphrase: passphrase = tobytes(passphrase) found = False try: p8_private_key = PBES1.decrypt(p8_private_key, passphrase) found = True except PbesError as e: error_str = "PBES1[%s]" % str(e) except ValueError: error_str = "PBES1[Invalid]" if not found: try: p8_private_key = PBES2.decrypt(p8_private_key, passphrase) found = True except PbesError as e: error_str += ",PBES2[%s]" % str(e) except ValueError: error_str += ",PBES2[Invalid]" if not found: raise ValueError("Error decoding PKCS#8 (%s)" % error_str) pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4)) if len(pk_info) == 2 and not passphrase: raise ValueError("Not a valid clear PKCS#8 structure " "(maybe it is encrypted?)") # # PrivateKeyInfo ::= SEQUENCE { # version Version, # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, # privateKey PrivateKey, # attributes [0] IMPLICIT Attributes OPTIONAL # } # Version ::= INTEGER if pk_info[0] != 0: raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") # PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier # # EncryptedPrivateKeyInfo ::= SEQUENCE { # encryptionAlgorithm EncryptionAlgorithmIdentifier, # encryptedData EncryptedData # } # EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier # AlgorithmIdentifier ::= SEQUENCE { # algorithm OBJECT IDENTIFIER, # parameters ANY DEFINED BY algorithm OPTIONAL # } algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2)) algo_oid = DerObjectId().decode(algo[0]).value if len(algo) == 1: algo_params = None else: try: DerNull().decode(algo[1]) algo_params = None except: algo_params = algo[1] # EncryptedData ::= OCTET STRING private_key = DerOctetString().decode(pk_info[2]).payload return (algo_oid, private_key, algo_params)
def testDecode3(self): der = DerObjectId() der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x00\x01')) self.assertEquals(der.value, '1.2.840.113549.1.0.1')
def decrypt(data, passphrase): """Decrypt a piece of data using a passphrase and *PBES2*. The algorithm to use is automatically detected. :Parameters: data : byte string The piece of data to decrypt. passphrase : byte string The passphrase to use for decrypting the data. :Returns: The decrypted data, as a binary string. """ enc_private_key_info = DerSequence().decode(data, nr_elements=2) enc_algo = DerSequence().decode(enc_private_key_info[0]) encrypted_data = DerOctetString().decode( enc_private_key_info[1]).payload pbe_oid = DerObjectId().decode(enc_algo[0]).value if pbe_oid != "1.2.840.113549.1.5.13": raise PbesError("Not a PBES2 object") pbes2_params = DerSequence().decode(enc_algo[1], nr_elements=2) ### Key Derivation Function selection kdf_info = DerSequence().decode(pbes2_params[0], nr_elements=2) kdf_oid = DerObjectId().decode(kdf_info[0]).value # We only support PBKDF2 or scrypt if kdf_oid == "1.2.840.113549.1.5.12": pbkdf2_params = DerSequence().decode(kdf_info[1], nr_elements=(2, 3, 4)) salt = DerOctetString().decode(pbkdf2_params[0]).payload iteration_count = pbkdf2_params[1] if len(pbkdf2_params) > 2: kdf_key_length = pbkdf2_params[2] else: kdf_key_length = None if len(pbkdf2_params) > 3: raise PbesError("Unsupported PRF for PBKDF2") elif kdf_oid == "1.3.6.1.4.1.11591.4.11": scrypt_params = DerSequence().decode(kdf_info[1], nr_elements=(4, 5)) salt = DerOctetString().decode(scrypt_params[0]).payload iteration_count, scrypt_r, scrypt_p = [ scrypt_params[x] for x in (1, 2, 3) ] if len(scrypt_params) > 4: kdf_key_length = scrypt_params[4] else: kdf_key_length = None else: raise PbesError("Unsupported PBES2 KDF") ### Cipher selection enc_info = DerSequence().decode(pbes2_params[1]) enc_oid = DerObjectId().decode(enc_info[0]).value if enc_oid == "1.2.840.113549.3.7": # DES_EDE3_CBC ciphermod = DES3 key_size = 24 elif enc_oid == "2.16.840.1.101.3.4.1.2": # AES128_CBC ciphermod = AES key_size = 16 elif enc_oid == "2.16.840.1.101.3.4.1.22": # AES192_CBC ciphermod = AES key_size = 24 elif enc_oid == "2.16.840.1.101.3.4.1.42": # AES256_CBC ciphermod = AES key_size = 32 else: raise PbesError("Unsupported PBES2 cipher") if kdf_key_length and kdf_key_length != key_size: raise PbesError("Mismatch between PBES2 KDF parameters" " and selected cipher") IV = DerOctetString().decode(enc_info[1]).payload # Create cipher if kdf_oid == "1.2.840.113549.1.5.12": # PBKDF2 key = PBKDF2(passphrase, salt, key_size, iteration_count) else: key = scrypt(passphrase, salt, key_size, iteration_count, scrypt_r, scrypt_p) cipher = ciphermod.new(key, ciphermod.MODE_CBC, IV) # Decrypt data pt = cipher.decrypt(encrypted_data) return unpad(pt, cipher.block_size)