def testEncode4(self): # One very long integer der = DerSequence() der.append(2**2048) self.assertEqual( der.encode(), b('0\x82\x01\x05') + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
def testDecode6(self): # Two integers der = DerSequence() der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) self.assertEqual(len(der), 2) self.assertEqual(der[0], 0x180) self.assertEqual(der[1], 0xFF)
def testDecode8(self): # Only 2 other types der = DerSequence() der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) self.assertEqual(len(der), 2) self.assertEqual(der[0], b('\x24\x02\xb6\x63')) self.assertEqual(der[1], b('\x12\x00'))
def testDecode4(self): # One very long integer der = DerSequence() der.decode( b('0\x82\x01\x05') + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 2**2048)
def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): """ 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: 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. :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. :attention: the same standard defined ``DigestAlgorithm`` to be of ``AlgorithmIdentifier`` type, where the PARAMETERS item is optional. Encodings for ``MD2/4/5`` without ``PARAMETERS`` 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 } # } # digestAlgo = DerSequence([hash.oid, DerNull().encode()]) digest = DerOctetString(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 ValueError( "Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = bchr(0xFF) * (emLen - len(digestInfo) - 3) return b("\x00\x01") + PS + bchr(0x00) + digestInfo
def testDecode7(self): # One integer and 2 other types der = DerSequence() der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) self.assertEqual(len(der), 3) self.assertEqual(der[0], 0x180) self.assertEqual(der[1], b('\x24\x02\xb6\x63')) self.assertEqual(der[2], b('\x12\x00'))
def testEncode1(self): # Empty sequence der = DerSequence() self.assertEqual(der.encode(), b('0\x00')) self.assertFalse(der.hasOnlyInts()) # One single-byte integer (zero) der.append(0) self.assertEqual(der.encode(), b('0\x03\x02\x01\x00')) self.assertTrue(der.hasOnlyInts()) # Invariant self.assertEqual(der.encode(), b('0\x03\x02\x01\x00'))
def testEncode6(self): # One integer and another type (no matter what it is) der = DerSequence() der.append(0x180) der.append(b('\x00\x02\x00\x00')) self.assertEqual(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) self.assertFalse(der.hasOnlyInts())
def testErrDecode4(self): # Wrong integer format der = DerSequence() # Multi-byte encoding for zero #self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x02\x00\x00') # Negative integer self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\xFF'))
def testErrDecode3(self): # Wrong length format der = DerSequence() self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\x01\x00')) self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01'))
def testDecode1(self): # Empty sequence der = DerSequence() der.decode(b('0\x00')) self.assertEqual(len(der), 0) # One single-byte integer (zero) der.decode(b('0\x03\x02\x01\x00')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 0) # Invariant der.decode(b('0\x03\x02\x01\x00')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 0)
def testEncode2(self): # One single-byte integer (non-zero) der = DerSequence() der.append(127) self.assertEqual(der.encode(), b('0\x03\x02\x01\x7f')) # Indexing der[0] = 1 self.assertEqual(len(der), 1) self.assertEqual(der[0], 1) self.assertEqual(der[-1], 1) self.assertEqual(der.encode(), b('0\x03\x02\x01\x01')) # der[:] = [1] self.assertEqual(len(der), 1) self.assertEqual(der[0], 1) self.assertEqual(der.encode(), b('0\x03\x02\x01\x01'))
def exportKey(self, format='PEM', passphrase=None, pkcs=1): """Export this RSA key. :Parameter format: The format to use for wrapping the key. - *'DER'*. Binary encoding, always unencrypted. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. Unencrypted (default) or encrypted. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). :Type format: string :Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from. :Type passphrase: string :Parameter pkcs: The PKCS standard to follow for assembling the key. You have two choices: - with **1**, the public key is embedded into an X.509 `SubjectPublicKeyInfo` DER SEQUENCE. The private key is embedded into a `PKCS#1`_ `RSAPrivateKey` DER SEQUENCE. This mode is the default. - with **8**, the private key is embedded into a `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE. This mode is not available for public keys. PKCS standards are not relevant for the *OpenSSH* format. :Type pkcs: integer :Return: A byte string with the encoded public or private half. :Raise ValueError: When the format is unknown. .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ if passphrase is not None: passphrase = tobytes(passphrase) if format == 'OpenSSH': eb = long_to_bytes(self.e) nb = long_to_bytes(self.n) if bord(eb[0]) & 0x80: eb = bchr(0x00) + eb if bord(nb[0]) & 0x80: nb = bchr(0x00) + nb keyparts = ['ssh-rsa', eb, nb] keystring = ''.join( [struct.pack(">I", len(kp)) + kp for kp in keyparts]) return 'ssh-rsa ' + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. der = DerSequence() if self.has_private(): keyType = {1: 'RSA PRIVATE', 8: 'PRIVATE'}[pkcs] der[:] = [ 0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p - 1), self.d % (self.q - 1), inverse(self.q, self.p) ] if pkcs == 8: derkey = der.encode() der = DerSequence([0]) der.append(algorithmIdentifier) der.append(DerObject('OCTET STRING', derkey).encode()) else: keyType = "PUBLIC" der.append(algorithmIdentifier) bitmap = DerObject('BIT STRING') derPK = DerSequence([self.n, self.e]) bitmap.payload = bchr(0x00) + derPK.encode() der.append(bitmap.encode()) if format == 'DER': return der.encode() if format == 'PEM': pem = b("-----BEGIN " + keyType + " KEY-----\n") objenc = None if passphrase and keyType.endswith('PRIVATE'): # We only support 3DES for encryption import Crypro.Hash.MD5 from Crypro.Cipher import DES3 from Crypro.Protocol.KDF import PBKDF1 salt = self._randfunc(8) key = PBKDF1(passphrase, salt, 16, 1, Crypro.Hash.MD5) key += PBKDF1(key + passphrase, salt, 8, 1, Crypro.Hash.MD5) objenc = DES3.new(key, Crypro.Cipher.DES3.MODE_CBC, salt) pem += b('Proc-Type: 4,ENCRYPTED\n') pem += b('DEK-Info: DES-EDE3-CBC,') + binascii.b2a_hex( salt).upper() + b('\n\n') binaryKey = der.encode() if objenc: # Add PKCS#7-like padding padding = objenc.block_size - len( binaryKey) % objenc.block_size binaryKey = objenc.encrypt(binaryKey + bchr(padding) * padding) # Each BASE64 line can take up to 64 characters (=48 bytes of data) chunks = [ binascii.b2a_base64(binaryKey[i:i + 48]) for i in range(0, len(binaryKey), 48) ] pem += b('').join(chunks) pem += b("-----END " + keyType + " KEY-----") return pem return ValueError( "Unknown key format '%s'. Cannot export the RSA key." % format)
def testDecode3(self): # One multi-byte integer (non-zero) der = DerSequence() der.decode(b('0\x04\x02\x02\x01\x80')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 0x180)
def testEncode3(self): # One multi-byte integer (non-zero) der = DerSequence() der.append(0x180) self.assertEqual(der.encode(), b('0\x04\x02\x02\x01\x80'))
def _importKeyDER(self, externKey): """Import an RSA key (public or private half), encoded in DER form.""" try: der = DerSequence() der.decode(externKey, True) # Try PKCS#1 first, for a private key if len(der) == 9 and der.hasOnlyInts() and der[0] == 0: # ASN.1 RSAPrivateKey element del der[ 6:] # Remove d mod (p-1), d mod (q-1), and q^{-1} mod p der.append(inverse(der[4], der[5])) # Add p^{-1} mod q del der[0] # Remove version return self.construct(der[:]) # Keep on trying PKCS#1, but now for a public key if len(der) == 2: # The DER object is an RSAPublicKey SEQUENCE with two elements if der.hasOnlyInts(): return self.construct(der[:]) # The DER object is a SubjectPublicKeyInfo SEQUENCE with two elements: # an 'algorithm' (or 'algorithmIdentifier') SEQUENCE and a 'subjectPublicKey' BIT STRING. # 'algorithm' takes the value given a few lines above. # 'subjectPublicKey' encapsulates the actual ASN.1 RSAPublicKey element. if der[0] == algorithmIdentifier: bitmap = DerObject() bitmap.decode(der[1], True) if bitmap.isType('BIT STRING') and bord( bitmap.payload[0]) == 0x00: der.decode(bitmap.payload[1:], True) if len(der) == 2 and der.hasOnlyInts(): return self.construct(der[:]) # Try unencrypted PKCS#8 if der[0] == 0: # The second element in the SEQUENCE is algorithmIdentifier. # It must say RSA (see above for description). if der[1] == algorithmIdentifier: privateKey = DerObject() privateKey.decode(der[2], True) if privateKey.isType('OCTET STRING'): return self._importKeyDER(privateKey.payload) except ValueError as IndexError: pass raise ValueError("RSA key format is not supported")
def testErrDecode1(self): # Not a sequence der = DerSequence() self.assertRaises(ValueError, der.decode, b('')) self.assertRaises(ValueError, der.decode, b('\x00')) self.assertRaises(ValueError, der.decode, b('\x30'))
def testErrDecode2(self): # Wrong payload type der = DerSequence() self.assertRaises(ValueError, der.decode, b('\x30\x00\x00'), True)
def testEncode5(self): # One single-byte integer (looks negative) der = DerSequence() der.append(0xFF) self.assertEqual(der.encode(), b('0\x04\x02\x02\x00\xff'))
def testEncode6(self): # Two integers der = DerSequence() der.append(0x180) der.append(0xFF) self.assertEqual(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) self.assertTrue(der.hasOnlyInts()) # der.append(0x01) der[1:] = [9, 8] self.assertEqual(len(der), 3) self.assertEqual(der[1:], [9, 8]) self.assertEqual(der[1:-1], [9]) self.assertEqual(der.encode(), b('0\x0A\x02\x02\x01\x80\x02\x01\x09\x02\x01\x08'))
def testDecode2(self): # One single-byte integer (non-zero) der = DerSequence() der.decode(b('0\x03\x02\x01\x7f')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 127)
def testDecode5(self): # One single-byte integer (looks negative) der = DerSequence() der.decode(b('0\x04\x02\x02\x00\xff')) self.assertEqual(len(der), 1) self.assertEqual(der[0], 0xFF)
# This is probably a DER encoded key return self._importKeyDER(externKey) raise ValueError("RSA key format is not supported") #: This is the ASN.1 DER object that qualifies an algorithm as #: compliant to PKCS#1 (that is, the standard RSA). # It is found in all 'algorithm' fields (also called 'algorithmIdentifier'). # It is a SEQUENCE with the oid assigned to RSA and with its parameters (none). # 0x06 0x09 OBJECT IDENTIFIER, 9 bytes of payload # 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x01 0x01 # rsaEncryption (1 2 840 113549 1 1 1) (PKCS #1) # 0x05 0x00 NULL algorithmIdentifier = DerSequence( [b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'), DerNull().encode()]).encode() _impl = RSAImplementation() #: #: Randomly generate a fresh, new RSA key object. #: #: See `RSAImplementation.generate`. #: generate = _impl.generate #: #: Construct an RSA key object from a tuple of valid RSA components. #: #: See `RSAImplementation.construct`. #: construct = _impl.construct