Example #1
0
 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'))
Example #2
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'))
Example #3
0
 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'))
Example #4
0
 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'))
Example #5
0
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
Example #6
0
 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())
Example #7
0
 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'))
Example #8
0
 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'))
Example #9
0
    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)