def import_key(encoded, passphrase=None): """Import an ECC key (public or private). :Parameters: encoded : bytes or a (multi-line) string The ECC key to import. An ECC public key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC private key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. :Keywords: passphrase : byte string The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. :Return: An ECC key object (`EccKey`) :Raise ValueError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b('-----')): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None return _import_der(der_encoded, passphrase) # OpenSSH if encoded.startswith(b('ecdsa-sha2-')): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b('-----')): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None return _import_der(der_encoded, passphrase) # OpenSSH if encoded.startswith(b('ecdsa-sha2-')): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def setUp(self): # Convert DSA key components from hex strings to integers # Each key is (p, q, g, x, y, desc) from collections import namedtuple TestKey = namedtuple('TestKey', 'p q g x y') new_keys = {} for k in self.keys: tk = TestKey(*[ t2l(y) for y in k[:-1] ]) new_keys[k[-1]] = tk self.keys = new_keys # Convert signature encoding TestSig = namedtuple('TestSig', 'message nonce result module test_key') new_signatures = [] for message, nonce, r, s, module, test_key in self.signatures: tsig = TestSig( tobytes(message), t2l(nonce), t2b(r) + t2b(s), module, self.keys[test_key] ) new_signatures.append(tsig) self.signatures = new_signatures
def setUp(self): test_vector_file = pycryptodome_filename( ("Cryptodome", "SelfTest", "Hash", "test_vectors", self.name), self.name.lower() + "-test.txt") expected = "in" self.test_vectors = [] with open(test_vector_file, "rt") as test_vector_fd: for line_number, line in enumerate(test_vector_fd): if line.strip() == "" or line.startswith("#"): continue res = re.match("%s:\t([0-9A-Fa-f]*)" % expected, line) if not res: raise ValueError("Incorrect test vector format (line %d)" % line_number) if res.group(1): bin_value = unhexlify(tobytes(res.group(1))) else: bin_value = b"" if expected == "in": input_data = bin_value expected = "key" elif expected == "key": key = bin_value expected = "hash" else: result = bin_value expected = "in" self.test_vectors.append((input_data, key, result))
def _load_tests(self, test_vector_file): expected = "in" test_vectors = [] with open(test_vector_file, "rt") as test_vector_fd: for line_number, line in enumerate(test_vector_fd): if line.strip() == "" or line.startswith("#"): continue res = re.match("%s:\t([0-9A-Fa-f]*)" % expected, line) if not res: raise ValueError("Incorrect test vector format (line %d)" % line_number) if res.group(1): bin_value = unhexlify(tobytes(res.group(1))) else: bin_value = b"" if expected == "in": input_data = bin_value expected = "key" elif expected == "key": key = bin_value expected = "hash" else: result = bin_value expected = "in" test_vectors.append((input_data, key, result)) return test_vectors
def bcrypt(password, cost, salt=None): """Hash a password into a key, using the OpenBSD bcrypt protocol. Args: password (byte string or string): The secret password or pass phrase. It must be at most 72 bytes long. It must not contain the zero byte. Unicode strings will be encoded as UTF-8. cost (integer): The exponential factor that makes it slower to compute the hash. It must be in the range 4 to 31. salt (byte string): Optional. Random byte string to thwarts dictionary and rainbow table attacks. It must be 16 bytes long. If not passed, a random value is generated. Return (byte string): The bcrypt hash Raises: ValueError: if password is longer than 72 bytes or if it contains the zero byte Note: If you want to hash passwords with no restrictions on their length, it is common practice to apply a cryptographic hash and then BASE64-encode the result. For instance:: from base64 import b64encode from Cryptodome.Hash import SHA256 from Cryptodome.Protocol.KDF import bcrypt password = b"test" b64pwd = b64encode(SHA256.new(password).digest()) bcrypt_hash = bcrypt(b64pwd, 10) """ password = tobytes(password, "utf-8") if password.find(bchr(0)[0]) != -1: raise ValueError("The password contains the zero byte") if len(password) < 72: password += b"\x00" if salt is None: salt = get_random_bytes(16) if len(salt) != 16: raise ValueError("bcrypt salt must be 16 bytes long") ctext = _bcrypt_hash(password, cost, salt, b"OrpheanBeholderScryDoubt", True) cost_enc = b"$" + bstr(str(cost).zfill(2)) salt_enc = b"$" + _bcrypt_encode(salt) hash_enc = _bcrypt_encode(ctext[:-1]) # only use 23 bytes, not 24 return b"$2a" + cost_enc + salt_enc + hash_enc
def hexverify(self, hex_mac_tag): """Return the **printable** MAC tag of the message authenticated so far. :return: The MAC tag, computed over the data processed so far. Hexadecimal encoded. :rtype: string """ self.verify(unhexlify(tobytes(hex_mac_tag)))
def _export_openssh(self, compress): if self.has_private(): raise ValueError("Cannot export OpenSSH private keys") desc = self._curve.openssh modulus_bytes = self.pointQ.size_in_bytes() if compress: first_byte = 2 + self.pointQ.y.is_odd() public_key = (bchr(first_byte) + self.pointQ.x.to_bytes(modulus_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(modulus_bytes) + self.pointQ.y.to_bytes(modulus_bytes)) middle = desc.split("-")[2] comps = (tobytes(desc), tobytes(middle), public_key) blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob))
def test_hex_digest(self): mac = self.BLAKE2.new(digest_bits=self.max_bits) digest = mac.digest() hexdigest = mac.hexdigest() # hexdigest is equivalent to digest self.assertEqual(hexlify(digest), tobytes(hexdigest)) # hexdigest does not change the state self.assertEqual(mac.hexdigest(), hexdigest) # hexdigest returns a string self.failUnless(isinstance(hexdigest, type("digest")))
def test_hex_digest(self): mac = Poly1305.new(key=self.key, cipher=AES) digest = mac.digest() hexdigest = mac.hexdigest() # hexdigest is equivalent to digest self.assertEqual(hexlify(digest), tobytes(hexdigest)) # hexdigest does not change the state self.assertEqual(mac.hexdigest(), hexdigest) # hexdigest returns a string self.failUnless(isinstance(hexdigest, type("digest")))
def test_hex_digest(self): mac = keccak.new(digest_bits=512) digest = mac.digest() hexdigest = mac.hexdigest() # hexdigest is equivalent to digest self.assertEqual(hexlify(digest), tobytes(hexdigest)) # hexdigest does not change the state self.assertEqual(mac.hexdigest(), hexdigest) # hexdigest returns a string self.assertTrue(isinstance(hexdigest, type("digest")))
def _export_openssh(self): assert not self.has_private() desc = "ecdsa-sha2-nistp256" # 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)) comps = (tobytes(desc), b("nistp256"), public_key) blob = b("").join([struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob))
def __init__(self, data, key, mac_len, custom, oid_variant, cshake, rate): # See https://tools.ietf.org/html/rfc8702 self.oid = "2.16.840.1.101.3.4.2." + oid_variant self.digest_size = mac_len self._mac = None partial_newX = _bytepad(_encode_str(tobytes(key)), rate) self._cshake = cshake._new(partial_newX, custom, b"KMAC") if data: self._cshake.update(data)
def hexverify(self, hex_mac_tag): """Verify that a given **printable** MAC (computed by another party) is valid. Args: hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string. Raises: ValueError: if the MAC does not match. It means that the message has been tampered with or that the MAC key is incorrect. """ self.verify(unhexlify(tobytes(hex_mac_tag)))
def hexverify(self, hex_mac_tag): """Verify that a given **printable** MAC (computed by another party) is valid. :Parameters: hex_mac_tag : string The expected MAC of the message, as a hexadecimal string. :Raises ValueError: if the MAC does not match. It means that the message has been tampered with or that the MAC key is incorrect. """ self.verify(unhexlify(tobytes(hex_mac_tag)))
def _load_tests(self, test_vector_file): test_vectors = [] with open(test_vector_file, "rt") as test_vector_fd: for line_number, line in enumerate(test_vector_fd): if line.strip() == "" or line.startswith("#"): continue res = re.match("digest: ([0-9A-Fa-f]*)", line) if not res: raise ValueError("Incorrect test vector format (line %d)" % line_number) test_vectors.append(unhexlify(tobytes(res.group(1)))) return test_vectors
def runTest(self): tag = unhexlify(b"fb447350c4e868c52ac3275cf9d4327e") msg = b'' for msg_len in range(5000 + 1): key = tag + strxor_c(tag, 0xFF) nonce = tag[::-1] if msg_len > 0: msg = msg + tobytes(tag[0]) auth = Poly1305.new(key=key, nonce=nonce, cipher=AES, data=msg) tag = auth.digest() # Compare against output of original DJB's poly1305aes-20050218 self.assertEqual("CDFA436DDD629C7DC20E1128530BAED2", auth.hexdigest().upper())
def _export_openssh(self): assert not self.has_private() desc = "ecdsa-sha2-nistp256" # 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)) comps = (tobytes(desc), b("nistp256"), public_key) blob = b("").join([ struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob))
def setUp(self): td = [] for tv_in in self.test_data: tv_out = [None] * len(tv_in) tv_out[0] = [] for string in tv_in[0]: tv_out[0].append(unhexlify(string.replace(" ", ""))) tv_out[1] = tobytes(tv_in[1]) # Custom tv_out[2] = unhexlify(tv_in[2].replace(" ", "")) tv_out[3] = tv_in[3] tv_out[4] = tv_in[4] td.append(tv_out) self.test_data = td
def setUp(self): test_vector_file = pycryptodome_filename( ("Cryptodome", "SelfTest", "Hash", "test_vectors", self.name), "tv1.txt") self.test_vectors = [] for line_number, line in enumerate(open(test_vector_file, "rt")): if line.strip() == "" or line.startswith("#"): continue res = re.match("digest: ([0-9A-Fa-f]*)", line) if not res: raise ValueError("Incorrect test vector format (line %d)" % line_number) self.test_vectors.append(unhexlify(tobytes(res.group(1))))
def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): """Derive one key from a password (or passphrase). This function performs key derivation according to an old version of the PKCS#5 standard (v1.5) or `RFC2898 <https://www.ietf.org/rfc/rfc2898.txt>`_. .. warning:: Newer applications should use the more secure and versatile :func:`PBKDF2` instead. Args: password (string): The secret password to generate the key from. salt (byte string): An 8 byte string to use for better protection from dictionary attacks. This value does not need to be kept secret, but it should be randomly chosen for each derivation. dkLen (integer): The length of the desired key. The default is 16 bytes, suitable for instance for :mod:`Cryptodome.Cipher.AES`. count (integer): The number of iterations to carry out. The recommendation is 1000 or more. hashAlgo (module): The hash algorithm to use, as a module or an object from the :mod:`Cryptodome.Hash` package. The digest length must be no shorter than ``dkLen``. The default algorithm is :mod:`Cryptodome.Hash.SHA1`. Return: A byte string of length ``dkLen`` that can be used as key. """ if not hashAlgo: hashAlgo = SHA1 password = tobytes(password) pHash = hashAlgo.new(password + salt) digest = pHash.digest_size if dkLen > digest: raise TypeError( "Selected hash algorithm has a too short digest (%d bytes)." % digest) if len(salt) != 8: raise ValueError("Salt is not 8 bytes long (%d bytes instead)." % len(salt)) for i in range(count - 1): pHash = pHash.new(pHash.digest()) return pHash.digest()[:dkLen]
def bcrypt(password, cost, salt=None): """Hash a password into a key, using the OpenBSD bcrypt protocol. Args: password (byte string or string): The secret password or pass phrase. It must be at most 72 bytes long. It must not contain the zero byte. Unicode strings will be encoded as UTF-8. cost (integer): The exponential factor that makes it slower to compute the hash. It must be in the range 4 to 31. A value of at least 12 is recommended. salt (byte string): Optional. Random byte string to thwarts dictionary and rainbow table attacks. It must be 16 bytes long. If not passed, a random value is generated. Return (byte string): The bcrypt hash Raises: ValueError: if password is longer than 72 bytes or if it contains the zero byte """ password = tobytes(password, "utf-8") if password.find(bchr(0)[0]) != -1: raise ValueError("The password contains the zero byte") if len(password) < 72: password += b"\x00" if salt is None: salt = get_random_bytes(16) if len(salt) != 16: raise ValueError("bcrypt salt must be 16 bytes long") ctext = _bcrypt_hash(password, cost, salt, b"OrpheanBeholderScryDoubt", True) cost_enc = b"$" + bstr(str(cost).zfill(2)) salt_enc = b"$" + _bcrypt_encode(salt) hash_enc = _bcrypt_encode(ctext[:-1]) # only use 23 bytes, not 24 return b"$2a" + cost_enc + salt_enc + hash_enc
def bcrypt_check(password, bcrypt_hash): """Verify if the provided password matches the given bcrypt hash. Args: password (byte string or string): The secret password or pass phrase to test. It must be at most 72 bytes long. It must not contain the zero byte. Unicode strings will be encoded as UTF-8. bcrypt_hash (byte string, bytearray): The reference bcrypt hash the password needs to be checked against. Raises: ValueError: if the password does not match """ bcrypt_hash = tobytes(bcrypt_hash) if len(bcrypt_hash) != 60: raise ValueError( "Incorrect length of the bcrypt hash: %d bytes instead of 60" % len(bcrypt_hash)) if bcrypt_hash[:4] != b'$2a$': raise ValueError("Unsupported prefix") p = re.compile( br'\$2a\$([0-9][0-9])\$([A-Za-z0-9./]{22,22})([A-Za-z0-9./]{31,31})') r = p.match(bcrypt_hash) if not r: raise ValueError("Incorrect bcrypt hash format") cost = int(r.group(1)) if not (4 <= cost <= 31): raise ValueError("Incorrect cost") salt = _bcrypt_decode(r.group(2)) bcrypt_hash2 = bcrypt(password, cost, salt) secret = get_random_bytes(16) mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=bcrypt_hash).digest() mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=bcrypt_hash2).digest() if mac1 != mac2: raise ValueError("Incorrect bcrypt hash")
def __init__(self, value): """Initialize the integer to the given value.""" self._mpz_p = new_mpz() self._initialized = False if isinstance(value, float): raise ValueError("A floating point type is not a natural number") self._initialized = True if isinstance(value, (int, long)): _gmp.mpz_init(self._mpz_p) result = _gmp.gmp_sscanf(tobytes(str(value)), b("%Zd"), self._mpz_p) if result != 1: raise ValueError("Error converting '%d'" % value) else: _gmp.mpz_init_set(self._mpz_p, value._mpz_p)
def update(self, data): """Authenticate the next byte string in the tuple. Args: data (bytes/bytearray/memoryview): The next byte string. """ if self._digest is not None: raise TypeError( "You cannot call 'update' after 'digest' or 'hexdigest'") if not is_bytes(data): raise TypeError("You can only call 'update' on bytes") self._cshake.update(_encode_str(tobytes(data))) return self
def _import_openssh(encoded): keystring = binascii.a2b_base64(encoded.split(b' ')[1]) keyparts = [] while len(keystring) > 4: lk = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + lk]) keystring = keystring[4 + lk:] for curve_name, curve in _curves.items(): middle = tobytes(curve.openssh.split("-")[2]) if keyparts[1] == middle: break else: raise ValueError("Unsupported ECC curve") return _import_public_der(curve.oid, keyparts[2])
def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): """Derive one key from a password (or passphrase). This function performs key derivation according to an old version of the PKCS#5 standard (v1.5) or `RFC2898 <https://www.ietf.org/rfc/rfc2898.txt>`_. .. warning:: Newer applications should use the more secure and versatile :func:`PBKDF2` instead. Args: password (string): The secret password to generate the key from. salt (byte string): An 8 byte string to use for better protection from dictionary attacks. This value does not need to be kept secret, but it should be randomly chosen for each derivation. dkLen (integer): The length of the desired key. The default is 16 bytes, suitable for instance for :mod:`Cryptodome.Cipher.AES`. count (integer): The number of iterations to carry out. The recommendation is 1000 or more. hashAlgo (module): The hash algorithm to use, as a module or an object from the :mod:`Cryptodome.Hash` package. The digest length must be no shorter than ``dkLen``. The default algorithm is :mod:`Cryptodome.Hash.SHA1`. Return: A byte string of length ``dkLen`` that can be used as key. """ if not hashAlgo: hashAlgo = SHA1 password = tobytes(password) pHash = hashAlgo.new(password+salt) digest = pHash.digest_size if dkLen > digest: raise TypeError("Selected hash algorithm has a too short digest (%d bytes)." % digest) if len(salt) != 8: raise ValueError("Salt is not 8 bytes long (%d bytes instead)." % len(salt)) for i in xrange(count-1): pHash = pHash.new(pHash.digest()) return pHash.digest()[:dkLen]
def setUp(self): test_vector_file = pycryptodome_filename( ("Cryptodome", "SelfTest", "Hash", "test_vectors", self.name), "tv2.txt") self.test_vectors = [] with open(test_vector_file, "rt") as test_vector_fd: for line_number, line in enumerate(test_vector_fd): if line.strip() == "" or line.startswith("#"): continue res = re.match(r"digest\(([0-9]+)\): ([0-9A-Fa-f]*)", line) if not res: raise ValueError("Incorrect test vector format (line %d)" % line_number) key_size = int(res.group(1)) result = unhexlify(tobytes(res.group(2))) self.test_vectors.append((key_size, result))
def _export_openssh(self, compress): if self.has_private(): raise ValueError("Cannot export OpenSSH private keys") desc = "ecdsa-sha2-nistp256" order_bytes = _curve.order.size_in_bytes() if compress: first_byte = 2 + self.pointQ.y.is_odd() public_key = (bchr(first_byte) + self.pointQ.x.to_bytes(order_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(order_bytes) + self.pointQ.y.to_bytes(order_bytes)) comps = (tobytes(desc), b"nistp256", public_key) blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob))
def _export_openssh(self, compress): if self.has_private(): raise ValueError("Cannot export OpenSSH private keys") desc = "ecdsa-sha2-nistp256" order_bytes = _curve.order.size_in_bytes() if compress: first_byte = 2 + self.pointQ.y.is_odd() public_key = (bchr(first_byte) + self.pointQ.x.to_bytes(order_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(order_bytes) + self.pointQ.y.to_bytes(order_bytes)) comps = (tobytes(desc), b"nistp256", public_key) blob = b"".join([ struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob))
def _bcrypt_encode(data): s = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" bits = [] for c in data: bits_c = bin(bord(c))[2:].zfill(8) bits.append(bstr(bits_c)) bits = b"".join(bits) bits6 = [bits[idx:idx + 6] for idx in range(0, len(bits), 6)] result = [] for g in bits6[:-1]: idx = int(g, 2) result.append(s[idx]) g = bits6[-1] idx = int(g, 2) << (6 - len(g)) result.append(s[idx]) result = "".join(result) return tobytes(result)
def export_key(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. Args: format (string): The format to use for wrapping the key: - *'PEM'*. (*Default*) Text encoding, done according to `RFC1421`_/`RFC1423`_. - *'DER'*. Binary encoding. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). passphrase (string): (*For private keys only*) The pass phrase used for protecting the output. pkcs (integer): (*For private keys only*) The ASN.1 structure to use for serializing the key. Note that even in case of PEM encoding, there is an inner ASN.1 DER structure. With ``pkcs=1`` (*default*), the private key is encoded in a simple `PKCS#1`_ structure (``RSAPrivateKey``). With ``pkcs=8``, the private key is encoded in a `PKCS#8`_ structure (``PrivateKeyInfo``). .. note:: This parameter is ignored for a public key. For DER and PEM, an ASN.1 DER ``SubjectPublicKeyInfo`` structure is always used. protection (string): (*For private keys only*) The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on :attr:`format`: - For *'DER'*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* scheme is used. The following operations are performed: 1. A 16 byte Triple DES key is derived from the passphrase using :func:`Cryptodome.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of :mod:`Cryptodome.Hash.HMAC`. 2. The private key is encrypted using CBC. 3. The encrypted key is encoded according to PKCS#8. - For *'PEM'*, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Specifying a value for :attr:`protection` is only meaningful for PKCS#8 (that is, ``pkcs=8``) and only if a pass phrase is present too. The supported schemes for PKCS#8 are listed in the :mod:`Cryptodome.IO.PKCS8` module (see :attr:`wrap_algo` parameter). randfunc (callable): A function that provides random bytes. Only used for PEM encoding. The default is :func:`Cryptodome.Random.get_random_bytes`. Returns: byte string: the encoded key Raises: ValueError:when the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. .. warning:: If you don't provide a pass phrase, the private key will be exported in the clear! .. _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 randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': e_bytes, n_bytes = [x.to_bytes() for x in (self._e, self._n)] if bord(e_bytes[0]) & 0x80: e_bytes = b'\x00' + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = b'\x00' + n_bytes keyparts = [b'ssh-rsa', e_bytes, n_bytes] keystring = b''.join([struct.pack(">I", len(kp)) + kp for kp in keyparts]) return b'ssh-rsa ' + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. if self.has_private(): binary_key = DerSequence([0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p-1), self.d % (self.q-1), Integer(self.q).inverse(self.p) ]).encode() if pkcs == 1: key_type = 'RSA PRIVATE KEY' if format == 'DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info(oid, DerSequence([self.n, self.e]) ) if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
def hexverify(self, hex_mac_tag): self.verify(unhexlify(tobytes(hex_mac_tag)))
def decode(pem_data, passphrase=None): """Decode a PEM block into binary. Args: pem_data (string): The PEM block. passphrase (byte string): If given and the PEM block is encrypted, the key will be derived from the passphrase. Returns: A tuple with the binary data, the marker string, and a boolean to indicate if decryption was performed. Raises: ValueError: if decoding fails, if the PEM file is encrypted and no passphrase has been provided or if the passphrase is incorrect. """ # Verify Pre-Encapsulation Boundary r = re.compile(r"\s*-----BEGIN (.*)-----\s+") m = r.match(pem_data) if not m: raise ValueError("Not a valid PEM pre boundary") marker = m.group(1) # Verify Post-Encapsulation Boundary r = re.compile(r"-----END (.*)-----\s*$") m = r.search(pem_data) if not m or m.group(1) != marker: raise ValueError("Not a valid PEM post boundary") # Removes spaces and slit on lines lines = pem_data.replace(" ", '').split() # Decrypts, if necessary if lines[1].startswith('Proc-Type:4,ENCRYPTED'): if not passphrase: raise ValueError("PEM is encrypted, but no passphrase available") DEK = lines[2].split(':') if len(DEK) != 2 or DEK[0] != 'DEK-Info': raise ValueError("PEM encryption format not supported.") algo, salt = DEK[1].split(',') salt = unhexlify(tobytes(salt)) if algo == "DES-CBC": # This is EVP_BytesToKey in OpenSSL key = PBKDF1(passphrase, salt, 8, 1, MD5) objdec = DES.new(key, DES.MODE_CBC, salt) elif algo == "DES-EDE3-CBC": # Note that EVP_BytesToKey is note exactly the same as PBKDF1 key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objdec = DES3.new(key, DES3.MODE_CBC, salt) elif algo == "AES-128-CBC": key = PBKDF1(passphrase, salt[:8], 16, 1, MD5) objdec = AES.new(key, AES.MODE_CBC, salt) else: raise ValueError("Unsupport PEM encryption algorithm (%s)." % algo) lines = lines[2:] else: objdec = None # Decode body data = a2b_base64(''.join(lines[1:-1])) enc_flag = False if objdec: data = unpad(objdec.decrypt(data), objdec.block_size) enc_flag = True return (data, marker, enc_flag)
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b'-----'): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith(b'ecdsa-sha2-'): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def get_tag_random(tag, length): return SHAKE128.new(data=tobytes(tag)).read(length)
def xl(text): return tostr(hexlify(tobytes(text)))
def PBKDF2(password, salt, dkLen=16, count=1000, prf=None, hmac_hash_module=None): """Derive one or more keys from a password (or passphrase). This function performs key derivation according to the PKCS#5 standard (v2.0). Args: password (string or byte string): The secret password to generate the key from. salt (string or byte string): A (byte) string to use for better protection from dictionary attacks. This value does not need to be kept secret, but it should be randomly chosen for each derivation. It is recommended to be at least 8 bytes long. dkLen (integer): The cumulative length of the desired keys. count (integer): The number of iterations to carry out. prf (callable): A pseudorandom function. It must be a function that returns a pseudorandom string from two parameters: a secret and a salt. If not specified, **HMAC-SHA1** is used. hmac_hash_module (module): A module from `Cryptodome.Hash` implementing a Merkle-Damgard cryptographic hash, which PBKDF2 must use in combination with HMAC. This parameter is mutually exclusive with ``prf``. Return: A byte string of length ``dkLen`` that can be used as key material. If you wanted multiple keys, just break up this string into segments of the desired length. """ password = tobytes(password) salt = tobytes(salt) if prf and hmac_hash_module: raise ValueError("'prf' and 'hmac_hash_module' are mutually exlusive") if prf is None and hmac_hash_module is None: hmac_hash_module = SHA1 if prf or not hasattr(hmac_hash_module, "_pbkdf2_hmac_assist"): # Generic (and slow) implementation if prf is None: prf = lambda p,s: HMAC.new(p, s, hmac_hash_module).digest() def link(s): s[0], s[1] = s[1], prf(password, s[1]) return s[0] key = b'' i = 1 while len(key)<dkLen: s = [ prf(password, salt + struct.pack(">I", i)) ] * 2 key += reduce(strxor, (link(s) for j in range(count)) ) i += 1 else: # Optimized implementation key = b'' i = 1 while len(key)<dkLen: base = HMAC.new(password, b"", hmac_hash_module) first_digest = base.copy().update(salt + struct.pack(">I", i)).digest() key += base._pbkdf2_hmac_assist(first_digest, count) i += 1 return key[:dkLen]
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. :Parameters: format : string The format to use for wrapping the key: - *'DER'*. Binary encoding. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). passphrase : string For private keys only. The pass phrase used for deriving the encryption key. pkcs : integer For *DER* and *PEM* format only. The PKCS standard to follow for assembling the components of the key. You have two choices: - **1** (default): the public key is embedded into an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE. The private key is embedded into a `PKCS#1`_ ``RSAPrivateKey`` DER SEQUENCE. - **8**: the private key is embedded into a `PKCS#8`_ ``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used for public keys. protection : string The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on ``format``: - For *DER*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* scheme is used. The following operations are performed: 1. A 16 byte Triple DES key is derived from the passphrase using `Cryptodome.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of `Cryptodome.Hash.HMAC`. 2. The private key is encrypted using CBC. 3. The encrypted key is encoded according to PKCS#8. - For *PEM*, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Specifying a value for ``protection`` is only meaningful for PKCS#8 (that is, ``pkcs=8``) and only if a pass phrase is present too. The supported schemes for PKCS#8 are listed in the `Cryptodome.IO.PKCS8` module (see ``wrap_algo`` parameter). randfunc : callable A function that provides random bytes. Only used for PEM encoding. The default is `Cryptodome.Random.get_random_bytes`. :Return: A byte string with the encoded public or private half of the key. :Raise ValueError: When the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. :attention: If you don't provide a pass phrase, the private key will be exported in the clear! .. _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 randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': e_bytes, n_bytes = [x.to_bytes() for x in (self._e, self._n)] if bord(e_bytes[0]) & 0x80: e_bytes = bchr(0) + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = bchr(0) + n_bytes keyparts = [b('ssh-rsa'), e_bytes, n_bytes] keystring = b('').join([struct.pack(">I", len(kp)) + kp for kp in keyparts]) return b('ssh-rsa ') + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. if self.has_private(): binary_key = DerSequence([0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p-1), self.d % (self.q-1), Integer(self.q).inverse(self.p) ]).encode() if pkcs == 1: key_type = 'RSA PRIVATE KEY' if format == 'DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: key_type = "RSA PUBLIC KEY" binary_key = _create_subject_public_key_info(oid, DerSequence([self.n, self.e]) ) if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
class Det_ECDSA_Tests(unittest.TestCase): key_priv = ECC.construct( curve="P-256", d=0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721) key_pub = key_priv.public_key() # This is a sequence of items: # message, k, r, s, hash module # taken from RFC6979 signatures_ = ( ("sample", "882905F1227FD620FBF2ABF21244F0BA83D0DC3A9103DBBEE43A1FB858109DB4", "61340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D32", "6D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB", SHA1), ("sample", "103F90EE9DC52E5E7FB5132B7033C63066D194321491862059967C715985D473", "53B2FFF5D1752B2C689DF257C04C40A587FABABB3F6FC2702F1343AF7CA9AA3F", "B9AFB64FDC03DC1A131C7D2386D11E349F070AA432A4ACC918BEA988BF75C74C", SHA224), ("sample", "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60", "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716", "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8", SHA256), ("sample", "09F634B188CEFD98E7EC88B1AA9852D734D0BC272F7D2A47DECC6EBEB375AAD4", "0EAFEA039B20E9B42309FB1D89E213057CBF973DC0CFC8F129EDDDC800EF7719", "4861F0491E6998B9455193E34E7B0D284DDD7149A74B95B9261F13ABDE940954", SHA384), ("sample", "5FA81C63109BADB88C1F367B47DA606DA28CAD69AA22C4FE6AD7DF73A7173AA5", "8496A60B5E9B47C825488827E0495B0E3FA109EC4568FD3F8D1097678EB97F00", "2362AB1ADBE2B8ADF9CB9EDAB740EA6049C028114F2460F96554F61FAE3302FE", SHA512), ("test", "8C9520267C55D6B980DF741E56B4ADEE114D84FBFA2E62137954164028632A2E", "0CBCC86FD6ABD1D99E703E1EC50069EE5C0B4BA4B9AC60E409E8EC5910D81A89", "01B9D7B73DFAA60D5651EC4591A0136F87653E0FD780C3B1BC872FFDEAE479B1", SHA1), ("test", "669F4426F2688B8BE0DB3A6BD1989BDAEFFF84B649EEB84F3DD26080F667FAA7", "C37EDB6F0AE79D47C3C27E962FA269BB4F441770357E114EE511F662EC34A692", "C820053A05791E521FCAAD6042D40AEA1D6B1A540138558F47D0719800E18F2D", SHA224), ("test", "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0", "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367", "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083", SHA256), ("test", "16AEFFA357260B04B1DD199693960740066C1A8F3E8EDD79070AA914D361B3B8", "83910E8B48BB0C74244EBDF7F07A1C5413D61472BD941EF3920E623FBCCEBEB6", "8DDBEC54CF8CD5874883841D712142A56A8D0F218F5003CB0296B6B509619F2C", SHA384), ("test", "6915D11632ACA3C40D5D51C08DAF9C555933819548784480E93499000D9F0B7F", "461D93F31B6540894788FD206C07CFA0CC35F46FA3C91816FFF1040AD1581A04", "39AF9F15DE0DB8D97E72719C74820D304CE5226E32DEDAE67519E840D1194E55", SHA512)) signatures = [] for a, b, c, d, e in signatures_: new_tv = (tobytes(a), unhexlify(b), unhexlify(c), unhexlify(d), e) signatures.append(new_tv) def shortDescription(self): return "Deterministic ECDSA Tests" def test_loopback(self): hashed_msg = SHA512.new(b("test")) signer = DSS.new(self.key_priv, 'deterministic-rfc6979') signature = signer.sign(hashed_msg) verifier = DSS.new(self.key_pub, 'deterministic-rfc6979') verifier.verify(hashed_msg, signature) def test_data_rfc6979(self): signer = DSS.new(self.key_priv, 'deterministic-rfc6979') for message, k, r, s, module in self.signatures: hash_obj = module.new(message) result = signer.sign(hash_obj) self.assertEqual(r + s, result)
def export_key(self, **kwargs): """Export this ECC key. :Keywords: format : string The format to use for wrapping the key: - *'DER'*. The key will be encoded in an ASN.1 DER_ structure (binary). - *'PEM'*. The key will be encoded in a PEM_ envelope (ASCII). - *'OpenSSH'*. The key will be encoded in the OpenSSH_ format (ASCII, public keys only). passphrase : byte string or string The passphrase to use for protecting the private key. *If not provided, the private key will remain in clear form!* use_pkcs8 : boolean In case of a private key, whether the PKCS#8_ representation should be (internally) used. By default it will. Not using PKCS#8 when exporting a private key in password-protected PEM_ form means that the much weaker and unflexible `PEM encryption`_ mechanism will be used. PKCS#8 is therefore always recommended. protection : string In case of a private key being exported with password-protection and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be present and be a valid algorithm supported by `Cryptodome.IO.PKCS8`. It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. :Note: In case of a private key being exported with password-protection and PKCS#8_ (both ``DER`` and ``PEM`` formats), all additional parameters will be passed to `Cryptodome.IO.PKCS8`. .. _DER: http://www.ietf.org/rfc/rfc5915.txt .. _PEM: http://www.ietf.org/rfc/rfc1421.txt .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt :Return: A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key. """ args = kwargs.copy() ext_format = args.pop("format") if ext_format not in ("PEM", "DER", "OpenSSH"): raise ValueError("Unknown format '%s'" % ext_format) if self.has_private(): passphrase = args.pop("passphrase", None) if isinstance(passphrase, basestring): passphrase = tobytes(passphrase) if not passphrase: raise ValueError("Empty passphrase") use_pkcs8 = args.pop("use_pkcs8", True) if ext_format == "PEM": if use_pkcs8: if passphrase: return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args) else: return self._export_private_clear_pkcs8_in_clear_pem() else: return self._export_private_pem(passphrase, **args) elif ext_format == "DER": # DER if passphrase and not use_pkcs8: raise ValueError("Private keys can only be encrpyted with DER using PKCS#8") if use_pkcs8: return self._export_pkcs8(passphrase=passphrase, **args) else: return self._export_private_der() else: raise ValueError("Private keys cannot be exported in OpenSSH format") else: # Public key if args: raise ValueError("Unexpected parameters: '%s'" % args) if ext_format == "PEM": return self._export_public_pem() elif ext_format == "DER": return self._export_subjectPublicKeyInfo() else: return self._export_openssh()
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _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 """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def export_key(self, **kwargs): """Export this ECC key. Args: format (string): The format to use for encoding the key: - *'DER'*. The key will be encoded in ASN.1 DER format (binary). For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure defined in `RFC5480`_ will be used. For a private key, the ASN.1 ``ECPrivateKey`` structure defined in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope, see the ``use_pkcs8`` flag below). - *'PEM'*. The key will be encoded in a PEM_ envelope (ASCII). - *'OpenSSH'*. The key will be encoded in the OpenSSH_ format (ASCII, public keys only). passphrase (byte string or string): The passphrase to use for protecting the private key. use_pkcs8 (boolean): If ``True`` (default and recommended), the `PKCS#8`_ representation will be used. If ``False``, the much weaker `PEM encryption`_ mechanism will be used. protection (string): When a private key is exported with password-protection and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be present and be a valid algorithm supported by :mod:`Cryptodome.IO.PKCS8`. It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. compress (boolean): If ``True``, a more compact representation of the public key (X-coordinate only) is used. If ``False`` (default), the full public key (in both its coordinates) will be exported. .. warning:: If you don't provide a passphrase, the private key will be exported in the clear! .. note:: When exporting a private key with password-protection and `PKCS#8`_ (both ``DER`` and ``PEM`` formats), any extra parameters is passed to :mod:`Cryptodome.IO.PKCS8`. .. _PEM: http://www.ietf.org/rfc/rfc1421.txt .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt .. _RFC5480: https://tools.ietf.org/html/rfc5480 .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt Returns: A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key. """ args = kwargs.copy() ext_format = args.pop("format") if ext_format not in ("PEM", "DER", "OpenSSH"): raise ValueError("Unknown format '%s'" % ext_format) compress = args.pop("compress", False) if self.has_private(): passphrase = args.pop("passphrase", None) if is_string(passphrase): passphrase = tobytes(passphrase) if not passphrase: raise ValueError("Empty passphrase") use_pkcs8 = args.pop("use_pkcs8", True) if ext_format == "PEM": if use_pkcs8: if passphrase: return self._export_private_encrypted_pkcs8_in_clear_pem( passphrase, **args) else: return self._export_private_clear_pkcs8_in_clear_pem() else: return self._export_private_pem(passphrase, **args) elif ext_format == "DER": # DER if passphrase and not use_pkcs8: raise ValueError( "Private keys can only be encrpyted with DER using PKCS#8" ) if use_pkcs8: return self._export_pkcs8(passphrase=passphrase, **args) else: return self._export_private_der() else: raise ValueError( "Private keys cannot be exported in OpenSSH format") else: # Public key if args: raise ValueError("Unexpected parameters: '%s'" % args) if ext_format == "PEM": return self._export_public_pem(compress) elif ext_format == "DER": return self._export_subjectPublicKeyInfo(compress) else: return self._export_openssh(compress)
def export_key(self, **kwargs): """Export this ECC key. Args: format (string): The format to use for encoding the key: - *'DER'*. The key will be encoded in ASN.1 DER format (binary). For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure defined in `RFC5480`_ will be used. For a private key, the ASN.1 ``ECPrivateKey`` structure defined in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope, see the ``use_pkcs8`` flag below). - *'PEM'*. The key will be encoded in a PEM_ envelope (ASCII). - *'OpenSSH'*. The key will be encoded in the OpenSSH_ format (ASCII, public keys only). passphrase (byte string or string): The passphrase to use for protecting the private key. use_pkcs8 (boolean): If ``True`` (default and recommended), the `PKCS#8`_ representation will be used. If ``False``, the much weaker `PEM encryption`_ mechanism will be used. protection (string): When a private key is exported with password-protection and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be present and be a valid algorithm supported by :mod:`Cryptodome.IO.PKCS8`. It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. compress (boolean): If ``True``, a more compact representation of the public key (X-coordinate only) is used. If ``False`` (default), the full public key (in both its coordinates) will be exported. .. warning:: If you don't provide a passphrase, the private key will be exported in the clear! .. note:: When exporting a private key with password-protection and `PKCS#8`_ (both ``DER`` and ``PEM`` formats), any extra parameters is passed to :mod:`Cryptodome.IO.PKCS8`. .. _PEM: http://www.ietf.org/rfc/rfc1421.txt .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt .. _RFC5480: https://tools.ietf.org/html/rfc5480 .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt Returns: A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key. """ args = kwargs.copy() ext_format = args.pop("format") if ext_format not in ("PEM", "DER", "OpenSSH"): raise ValueError("Unknown format '%s'" % ext_format) compress = args.pop("compress", False) if self.has_private(): passphrase = args.pop("passphrase", None) if is_string(passphrase): passphrase = tobytes(passphrase) if not passphrase: raise ValueError("Empty passphrase") use_pkcs8 = args.pop("use_pkcs8", True) if ext_format == "PEM": if use_pkcs8: if passphrase: return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args) else: return self._export_private_clear_pkcs8_in_clear_pem() else: return self._export_private_pem(passphrase, **args) elif ext_format == "DER": # DER if passphrase and not use_pkcs8: raise ValueError("Private keys can only be encrpyted with DER using PKCS#8") if use_pkcs8: return self._export_pkcs8(passphrase=passphrase, **args) else: return self._export_private_der() else: raise ValueError("Private keys cannot be exported in OpenSSH format") else: # Public key if args: raise ValueError("Unexpected parameters: '%s'" % args) if ext_format == "PEM": return self._export_public_pem(compress) elif ext_format == "DER": return self._export_subjectPublicKeyInfo(compress) else: return self._export_openssh(compress)
def export_key(self, format='PEM', pkcs8=None, passphrase=None, protection=None, randfunc=None): """Export this DSA key. Args: format (string): The encoding for the output: - *'PEM'* (default). ASCII as per `RFC1421`_/ `RFC1423`_. - *'DER'*. Binary ASN.1 encoding. - *'OpenSSH'*. ASCII one-liner as per `RFC4253`_. Only suitable for public keys, not for private keys. passphrase (string): *Private keys only*. The pass phrase to protect the output. pkcs8 (boolean): *Private keys only*. If ``True`` (default), the key is encoded with `PKCS#8`_. If ``False``, it is encoded in the custom OpenSSL/OpenSSH container. protection (string): *Only in combination with a pass phrase*. The encryption scheme to use to protect the output. If :data:`pkcs8` takes value ``True``, this is the PKCS#8 algorithm to use for deriving the secret and encrypting the private DSA key. For a complete list of algorithms, see :mod:`Cryptodome.IO.PKCS8`. The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. If :data:`pkcs8` is ``False``, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Parameter :data:`protection` is then ignored. The combination ``format='DER'`` and ``pkcs8=False`` is not allowed if a passphrase is present. randfunc (callable): A function that returns random bytes. By default it is :func:`Cryptodome.Random.get_random_bytes`. Returns: byte string : the encoded key Raises: ValueError : when the format is unknown or when you try to encrypt a private key with *DER* format and OpenSSL/OpenSSH. .. warning:: If you don't provide a pass phrase, the private key will be exported in the clear! .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ if passphrase is not None: passphrase = tobytes(passphrase) if randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': tup1 = [self._key[x].to_bytes() for x in ('p', 'q', 'g', 'y')] def func(x): if (bord(x[0]) & 0x80): return bchr(0) + x else: return x tup2 = [func(x) for x in tup1] keyparts = [b'ssh-dss'] + tup2 keystring = b''.join( [struct.pack(">I", len(kp)) + kp for kp in keyparts] ) return b'ssh-dss ' + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. params = DerSequence([self.p, self.q, self.g]) if self.has_private(): if pkcs8 is None: pkcs8 = True if pkcs8: if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' private_key = DerInteger(self.x).encode() binary_key = PKCS8.wrap( private_key, oid, passphrase, protection, key_params=params, randfunc=randfunc ) if passphrase: key_type = 'ENCRYPTED PRIVATE' else: key_type = 'PRIVATE' passphrase = None else: if format != 'PEM' and passphrase: raise ValueError("DSA private key cannot be encrypted") ints = [0, self.p, self.q, self.g, self.y, self.x] binary_key = DerSequence(ints).encode() key_type = "DSA PRIVATE" else: if pkcs8: raise ValueError("PKCS#8 is only meaningful for private keys") binary_key = _create_subject_public_key_info(oid, DerInteger(self.y), params) key_type = "PUBLIC" if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode( binary_key, key_type + " KEY", passphrase, randfunc ) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format)
def t2b(hexstring): ws = hexstring.replace(" ", "").replace("\n", "") return unhexlify(tobytes(ws))
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----'): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b'ssh-dss '): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b"ssh-dss": tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
class SHAKEVectors(unittest.TestCase): pass test_vectors_128 = load_tests(("Cryptodome", "SelfTest", "Hash", "test_vectors", "SHA3"), "ShortMsgKAT_SHAKE128.txt", "Short Messages KAT SHAKE128", { "len" : lambda x: int(x) } ) for idx, tv in enumerate(test_vectors_128): if tv.len == 0: data = b("") else: data = tobytes(tv.msg) def new_test(self, data=data, result=tv.md): hobj = SHAKE128.new(data=data) digest = hobj.read(len(result)) self.assertEqual(digest, result) setattr(SHAKEVectors, "test_128_%d" % idx, new_test) test_vectors_256 = load_tests(("Cryptodome", "SelfTest", "Hash", "test_vectors", "SHA3"), "ShortMsgKAT_SHAKE256.txt", "Short Messages KAT SHAKE256", { "len" : lambda x: int(x) } ) for idx, tv in enumerate(test_vectors_256):
def t2b(hex_string): shorter = re.sub(b'\s+', b'', tobytes(hex_string)) return unhexlify(shorter)