diff --git a/tlslite/constants.py b/tlslite/constants.py index 39d6cb64..d4f3d510 100644 --- a/tlslite/constants.py +++ b/tlslite/constants.py @@ -237,6 +237,12 @@ class SignatureScheme(TLSEnum): rsa_pss_sha384 = (8, 5) rsa_pss_sha512 = (8, 6) + dsa_sha1 = (2, 2) + dsa_sha224 = (3, 2) + dsa_sha256 = (4, 2) + dsa_sha384 = (5, 2) + dsa_sha512 = (6, 2) + @classmethod def toRepr(cls, value, blacklist=None): """Convert numeric type to name representation""" @@ -337,6 +343,16 @@ class AlgorithmOID(TLSEnum): SignatureScheme.rsa_pss_rsae_sha384 oid[bytes(a2b_hex('300b0609608648016503040203'))] = \ SignatureScheme.rsa_pss_rsae_sha512 + oid[bytes(a2b_hex('06072A8648CE380403'))] = \ + SignatureScheme.dsa_sha1 + oid[bytes(a2b_hex('0609608648016503040301'))] = \ + SignatureScheme.dsa_sha224 + oid[bytes(a2b_hex('0609608648016503040302'))] = \ + SignatureScheme.dsa_sha256 + oid[bytes(a2b_hex('0609608648016503040303'))] = \ + SignatureScheme.dsa_sha384 + oid[bytes(a2b_hex('0609608648016503040304'))] = \ + SignatureScheme.dsa_sha512 class GroupName(TLSEnum): diff --git a/tlslite/utils/__init__.py b/tlslite/utils/__init__.py index 4d49df64..953af7b9 100644 --- a/tlslite/utils/__init__.py +++ b/tlslite/utils/__init__.py @@ -22,6 +22,7 @@ "python_aes", "python_rc4", "python_rsakey", + "python_dsakey", "rc4", "rijndael", "rsakey", diff --git a/tlslite/utils/dsakey.py b/tlslite/utils/dsakey.py new file mode 100644 index 00000000..bb291018 --- /dev/null +++ b/tlslite/utils/dsakey.py @@ -0,0 +1,108 @@ +"""Abstract class for DSA.""" + +class DSAKey(object): + """This is an abstract base class for DSA keys. + + Particular implementations of DSA keys, such as + :py:class:`~.python_dsakey.Python_DSAKey` + ... more coming + inherit from this. + + To create or parse an DSA key, don't use one of these classes + directly. Instead, use the factory functions in + :py:class:`~tlslite.utils.keyfactory`. + """ + + def __init__(self, p, q, g, x, y): + """Create a new DSA key. + :type p: int + :param p: domain parameter, prime num defining Gaolis Field + :type q: int + :param q: domain parameter, prime factor of p-1 + :type g: int + :param g: domain parameter, generator of q-order cyclic group GP(p) + :type x: int + :param x: private key + :type y: int + :param y: public key + """ + raise NotImplementedError() + + def __len__(self): + """Return the size of the order of the curve of this key, in bits. + + :rtype: int + """ + raise NotImplementedError() + + def hasPrivateKey(self): + """Return whether or not this key has a private component. + + :rtype: bool + """ + raise NotImplementedError() + + def hashAndSign(self, data, hAlg): + """Hash and sign the passed-in bytes. + + This requires the key to have a private component and + global parameters. It performs a signature on the passed-in data + with selected hash algorithm. + + :type data: str + :param data: The data which will be hashed and signed. + + :type hAlg: str + :param hAlg: The hash algorithm that will be used to hash data + + :rtype: bytearray + :returns: An DSA signature on the passed-in data. + """ + raise NotImplementedError() + + def hashAndVerify(self, signature, data, hAlg="sha1"): + """Hash and verify the passed-in bytes with signature. + + :type signature: ASN1 bytearray + :param signature: the r, s dsa signature + + :type data: str + :param data: The data which will be hashed and verified. + + :type hAlg: str + :param hAlg: The hash algorithm that will be used to hash data + + :rtype: bool + :returns: return True if verification is OK. + """ + raise NotImplementedError() + + @staticmethod + def generate(L, N): + """Generate new key given by bit lengths L, N. + + :type L: int + :param L: length of parameter p in bits + + :type N: int + :param N: length of parameter q in bits + + :rtype: DSAkey + :returns: DSAkey(domain parameters, private key, public key) + """ + raise NotImplementedError() + + @staticmethod + def generate_qp(L, N): + """Generate new (p, q) given by bit lengths L, N. + + :type L: int + :param L: length of parameter p in bits + + :type N: int + :param N: length of parameter q in bits + + :rtype: (int, int) + :returns: new p and q key parameters + """ + raise NotImplementedError() diff --git a/tlslite/utils/keyfactory.py b/tlslite/utils/keyfactory.py index 9b18b538..c70e7453 100644 --- a/tlslite/utils/keyfactory.py +++ b/tlslite/utils/keyfactory.py @@ -8,6 +8,7 @@ from .rsakey import RSAKey from .python_rsakey import Python_RSAKey from .python_ecdsakey import Python_ECDSAKey +from .python_dsakey import Python_DSAKey from tlslite.utils import cryptomath if cryptomath.m2cryptoLoaded: @@ -233,3 +234,28 @@ def _create_public_ecdsa_key(point_x, point_y, curve_name, if impl == "python": return Python_ECDSAKey(point_x, point_y, curve_name) raise ValueError("No acceptable implementation") + +def _create_public_dsa_key(p, q, g, y, + implementations=("python",)): + """ + Convert public key parameters into concrete implementation of verifier. + + The public key in DSA consists of four integers. + + :type p: int + :param p: domain parameter, prime num defining Gaolis Field + :type q: int + :param q: domain parameter, prime factor of p-1 + :type g: int + :param g: domain parameter, generator of q-order cyclic group GP(p) + :type y: int + :param y: public key + :type implementations: iterable of str + :param implementations: list of implementations that can be used as the + concrete implementation of the verifying key (only 'python' is + supported currently) + """ + for impl in implementations: + if impl == "python": + return Python_DSAKey(p=p, q=q, g=g, y=y) + raise ValueError("No acceptable implementation") diff --git a/tlslite/utils/python_dsakey.py b/tlslite/utils/python_dsakey.py new file mode 100644 index 00000000..579a8746 --- /dev/null +++ b/tlslite/utils/python_dsakey.py @@ -0,0 +1,125 @@ +# Author: Frantisek Krenzelok + +"""Pure-Python RSA implementation.""" +from ecdsa.der import encode_sequence, encode_integer, \ +remove_sequence, remove_integer + +from .cryptomath import getRandomNumber, getRandomPrime, \ +powMod, numBits, bytesToNumber, invMod, secureHash, \ +GMPY2_LOADED, gmpyLoaded + +if GMPY2_LOADED: + from gmpy2 import mpz +elif gmpyLoaded: + from gmpy import mpz + +from .dsakey import DSAKey + +class Python_DSAKey(DSAKey): + """ + Concrete implementaion of DSA object. + for func docstring see tlslite/dsakey.py + """ + def __init__(self, p=0, q=0, g=0, x=0, y=0): + if gmpyLoaded or GMPY2_LOADED: + p = mpz(p) + q = mpz(q) + g = mpz(g) + x = mpz(x) + y = mpz(y) + self.p = p + self.q = q + self.g = g + self.private_key = x + self.public_key = y + self.key_type = "dsa" + + if p and q and p < q: + raise ValueError("q is greater than p") + + def __len__(self): + return numBits(self.p) + + def hasPrivateKey(self): + return bool(self.private_key) + + @staticmethod + def generate(L, N): + assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)] + key = Python_DSAKey() + (q, p) = Python_DSAKey.generate_qp(L, N) + + index = getRandomNumber(1, (p-1)) + g = powMod(index, int((p-1)/q), p) + x = getRandomNumber(1, q-1) + y = powMod(g, x, p) + if gmpyLoaded or GMPY2_LOADED: + p = mpz(p) + q = mpz(q) + g = mpz(g) + x = mpz(x) + y = mpz(y) + key.q = q + key.p = p + key.g = g + key.private_key = x + key.public_key = y + return key + + @staticmethod + def generate_qp(L, N): + assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)] + + q = int(getRandomPrime(N)) + while True: + p = int(getRandomPrime(L)) + if (p-1) % q: + break + return (q, p) + + def hashAndSign(self, data, hAlg="sha1"): + digest = bytesToNumber(secureHash(bytearray(data), hAlg)) + digest_size = numBits(digest) + + # extract min(|hAlg|, N) left bits of digest + N = numBits(self.q) + if N < digest_size: + digest &= ~(~0 << (digest_size - N)) + + k = getRandomNumber(1, (self.q-1)) + r = powMod(self.g, k, self.p) % self.q + s = invMod(k, self.q) * (digest + self.private_key * r) % self.q + + return encode_sequence(encode_integer(r), encode_integer(s)) + + def hashAndVerify(self, signature, data, hAlg="sha1"): + # Get r, s components from signature + digest = bytesToNumber(secureHash(bytearray(data), hAlg)) + digest_size = numBits(digest) + + # extract min(|hAlg|, N) left bits of digest + N = numBits(self.q) + if N < digest_size: + digest &= ~(~0 << (digest_size - N)) + + # get r, s keys + if not signature: + return False + body, rest = remove_sequence(signature) + if rest: + return False + r, rest = remove_integer(body) + s, rest = remove_integer(rest) + if rest: + return False + + # check the signature + if 0 < r < self.q and 0 < s < self.q: + w = invMod(s, self.q) + u1 = (digest * w) % self.q + u2 = (r * w) % self.q + v = ((powMod(self.g, u1, self.p) * \ + powMod(self.public_key, u2, self.p)) % self.p) % self.q + + return r == v + return False diff --git a/tlslite/utils/python_key.py b/tlslite/utils/python_key.py index 9ed48128..c1128d4e 100644 --- a/tlslite/utils/python_key.py +++ b/tlslite/utils/python_key.py @@ -2,6 +2,7 @@ from .python_rsakey import Python_RSAKey from .python_ecdsakey import Python_ECDSAKey +from .python_dsakey import Python_DSAKey from .pem import dePem, pemSniff from .asn1parser import ASN1Parser from .cryptomath import bytesToNumber @@ -25,7 +26,10 @@ def parsePEM(s, passwordCallback=None): return Python_Key._parse_pkcs8(bytes) elif pemSniff(s, "RSA PRIVATE KEY"): bytes = dePem(s, "RSA PRIVATE KEY") - return Python_Key._parse_ssleay(bytes) + return Python_Key._parse_ssleay(bytes, "rsa") + elif pemSniff(s, "DSA PRIVATE KEY"): + bytes = dePem(s, "DSA PRIVATE KEY") + return Python_Key._parse_dsa_ssleay(bytes) elif pemSniff(s, "EC PRIVATE KEY"): bytes = dePem(s, "EC PRIVATE KEY") return Python_Key._parse_ecc_ssleay(bytes) @@ -51,6 +55,8 @@ def _parse_pkcs8(bytes): key_type = "rsa" elif list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 10]: key_type = "rsa-pss" + elif list(oid.value) == [42, 134, 72, 206, 56, 4, 1]: + key_type = "dsa" elif list(oid.value) == [42, 134, 72, 206, 61, 2, 1]: key_type = "ecdsa" else: @@ -64,6 +70,12 @@ def _parse_pkcs8(bytes): parameters = alg_ident.getChild(1) if parameters.value != bytearray(0): raise SyntaxError("RSA parameters are not NULL") + if key_type == "dsa": + if seq_len != 2: + raise SyntaxError("Invalid encoding of algorithm identifier") + parameters = alg_ident.getChild(1) + if parameters.value == bytearray(0): + parameters = None elif key_type == "ecdsa": if seq_len != 2: raise SyntaxError("Invalid encoding of algorithm identifier") @@ -91,12 +103,14 @@ def _parse_pkcs8(bytes): if key_type == "ecdsa": return Python_Key._parse_ecdsa_private_key(private_key_parser, curve) + elif key_type == "dsa": + return Python_Key._parse_dsa_private_key(private_key_parser, parameters) else: return Python_Key._parse_asn1_private_key(private_key_parser, key_type) @staticmethod - def _parse_ssleay(data): + def _parse_ssleay(data, key_type="rsa"): """ Parse binary structure of the old SSLeay file format used by OpenSSL. @@ -104,7 +118,17 @@ def _parse_ssleay(data): """ private_key_parser = ASN1Parser(data) # "rsa" type as old format doesn't support rsa-pss parameters - return Python_Key._parse_asn1_private_key(private_key_parser, "rsa") + return Python_Key._parse_asn1_private_key(private_key_parser, key_type) + + @staticmethod + def _parse_dsa_ssleay(data): + """ + Parse binary structure of the old SSLeay file format used by OpenSSL. + + For DSA keys. + """ + private_key_parser = ASN1Parser(data) + return Python_Key._parse_dsa_private_key(private_key_parser) @staticmethod def _parse_ecc_ssleay(data): @@ -167,3 +191,18 @@ def _parse_asn1_private_key(private_key_parser, key_type): qInv = bytesToNumber(private_key_parser.getChild(8).value) return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv, key_type) + + @staticmethod + def _parse_dsa_private_key(private_key_parser, domain_parameters=None): + if domain_parameters: + p = bytesToNumber(domain_parameters.getChild(0).value) + q = bytesToNumber(domain_parameters.getChild(1).value) + g = bytesToNumber(domain_parameters.getChild(2).value) + x = bytesToNumber(private_key_parser.value) + return Python_DSAKey(p, q, g, x) + p = bytesToNumber(private_key_parser.getChild(1).value) + q = bytesToNumber(private_key_parser.getChild(2).value) + g = bytesToNumber(private_key_parser.getChild(3).value) + y = bytesToNumber(private_key_parser.getChild(4).value) + x = bytesToNumber(private_key_parser.getChild(5).value) + return Python_DSAKey(p, q, g, x, y) diff --git a/tlslite/x509.py b/tlslite/x509.py index 0e43af7c..df7b07b0 100644 --- a/tlslite/x509.py +++ b/tlslite/x509.py @@ -1,4 +1,4 @@ -# Authors: +# Authors: # Trevor Perrin # Google - parsing subject field # @@ -10,7 +10,8 @@ from .utils.asn1parser import ASN1Parser from .utils.cryptomath import * -from .utils.keyfactory import _createPublicRSAKey, _create_public_ecdsa_key +from .utils.keyfactory import _createPublicRSAKey, _create_public_ecdsa_key, \ +_create_public_dsa_key from .utils.pem import * from .utils.compat import compatHMAC, b2a_hex from .constants import AlgorithmOID, RSA_PSS_OID @@ -135,12 +136,14 @@ def parseBinary(self, cert_bytes): self.certAlg = "rsa" elif list(alg_oid) == [42, 134, 72, 134, 247, 13, 1, 1, 10]: self.certAlg = "rsa-pss" + elif list(alg_oid) == [42, 134, 72, 206, 56, 4, 1]: + self.certAlg = "dsa" elif list(alg_oid) == [42, 134, 72, 206, 61, 2, 1]: self.certAlg = "ecdsa" else: raise SyntaxError("Unrecognized AlgorithmIdentifier") - # for RSA the parameters of AlgorithmIdentifier should be a NULL + # for RSA the parameters of AlgorithmIdentifier shuld be a NULL if self.certAlg == "rsa": if alg_identifier_len != 2: raise SyntaxError("Missing parameters in AlgorithmIdentifier") @@ -152,6 +155,9 @@ def parseBinary(self, cert_bytes): self._ecdsa_pubkey_parsing( tbs_certificate.getChildBytes(subject_public_key_info_index)) return + elif self.certAlg == "dsa": + self._dsa_pubkey_parsing(subject_public_key_info) + return else: # rsa-pss pass # ignore parameters, if any - don't apply key restrictions @@ -210,6 +216,36 @@ def _ecdsa_pubkey_parsing(self, subject_public_key_info): curve_name = public_key.curve.name self.publicKey = _create_public_ecdsa_key(x, y, curve_name) + def _dsa_pubkey_parsing(self, subject_public_key_info): + """ + Convert the raw DER encoded DSA parameters into public key object + + :param subject_public_key_info: bytes like object with DER encoded + global parameters and public key in it + """ + global_parameters = (subject_public_key_info.getChild(0)).getChild(1) + # Get the subjectPublicKey + public_key = subject_public_key_info.getChild(1) + + # Adjust for BIT STRING encapsulation and get hex value + if public_key.value[0]: + raise SyntaxError() + y = public_key.value[3:] + + # Get the {A, p, q} + p = global_parameters.getChild(0) + q = global_parameters.getChild(1) + g = global_parameters.getChild(2) + + # Decode them into numbers + y = bytesToNumber(y) + p = bytesToNumber(p.value) + q = bytesToNumber(q.value) + g = bytesToNumber(g.value) + + # Create a public key instance + self.publicKey = _create_public_dsa_key(p, q, g, y) + def getFingerprint(self): """ Get the hex-encoded fingerprint of this certificate. diff --git a/unit_tests/test_tlslite_extensions.py b/unit_tests/test_tlslite_extensions.py index 25c15b7f..aad0b7b1 100644 --- a/unit_tests/test_tlslite_extensions.py +++ b/unit_tests/test_tlslite_extensions.py @@ -1690,7 +1690,7 @@ def test___repr__(self): self.assertEqual(repr(ext), "SignatureAlgorithmsExtension(" - "sigalgs=[rsa_pkcs1_sha1, rsa_pkcs1_sha256, (sha384, dsa)])") + "sigalgs=[rsa_pkcs1_sha1, rsa_pkcs1_sha256, dsa_sha384])") def test___repr___with_none(self): ext = SignatureAlgorithmsExtension() @@ -1725,14 +1725,13 @@ def test___repr__(self): algs = [SignatureScheme.rsa_pkcs1_sha1, SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.rsa_pss_pss_sha256, - (HashAlgorithm.sha384, - SignatureAlgorithm.dsa)] + SignatureScheme.dsa_sha384] ext = SignatureAlgorithmsCertExtension().create(algs) self.assertEqual(repr(ext), "SignatureAlgorithmsCertExtension(sigalgs=[" "rsa_pkcs1_sha1, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, " - "(sha384, dsa)])") + "dsa_sha384])") def test___repr___with_legacy_name(self): algs = [SignatureScheme.rsa_pss_sha256] diff --git a/unit_tests/test_tlslite_utils_python_dsakey.py b/unit_tests/test_tlslite_utils_python_dsakey.py new file mode 100644 index 00000000..8bf3baa1 --- /dev/null +++ b/unit_tests/test_tlslite_utils_python_dsakey.py @@ -0,0 +1,168 @@ + +try: + import unittest2 as unittest +except ImportError: + import unittest + +try: + import mock + from mock import call +except ImportError: + import unittest.mock as mock + from unittest.mock import call + +from tlslite.utils.python_key import Python_Key +from tlslite.utils.python_dsakey import Python_DSAKey + +class TestDSAKey(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.key_pem = ( + "-----BEGIN DSA PRIVATE KEY-----\n" + "MIGXAgEAAiEAmeFbCUhVUZgVpljXObhmRaQYIQ12YSr9zlCja2kpTiUCFQCfCyag\n" + "vEDkgK5nHqscaYlF32ekRwIgYgpNP8JjVxfJ4P3IErO07qqzWS21hSyMhsaCN0an\n" + "0OsCICUjj3Np+JO42v8Mc8oH6T8yNd5X0ssy8XdK3Bo9nfNpAhQJkJXFuhZDER1X\n" + "wOwvNiFYUPPZaA==\n" + "-----END DSA PRIVATE KEY-----\n") + + cls.key = Python_DSAKey(p=69602034731989554929546346371414762967051205729581487767213360812510562307621, + q=907978205720450240238233398695599264980368073799, + g=44344860785224683582210580276798141855549498608976964582640232671615126065387, + x=54605271259585079176392566431938393409383029096, + y=16798405106129606882295006910154614336997455047535738179977898112652777747305) + + + # Different key + key = ( "-----BEGIN DSA PRIVATE KEY-----\n" + "MIGYAgEAAiEAhOlFCxh6ZzzNUAttFeHWVe7TFplkqYsYXHeX4skwsvkCFQDqFZcN\n" + "v+YbZamq6pHs5W0j81+qhwIgWTJjpklonHoFu37GS72Sk/6mC3Y+wR44xIuxQr1H\n" + "ArMCIDOk+NSr13kYK8edl4fEdRhHRN8JOW/G42WB8oTvEQ7tAhUAyxDkbWXWyust\n" + "bxHP2TPZV9kFzYY=\n" + "-----END DSA PRIVATE KEY-----\n") + + cls.key_diff = Python_Key.parsePEM(key) + + # dsa sha1 signature of "some message to sign" + cls.sha1_sig =(b'\x30\x2C\x02\x14\x54\x07\x13\xC9\xE6\xB4\x48\x75\x19\x4D\x88\x61' + b'\xBA\x73\x46\x37\xDA\x78\x1C\xB1\x02\x14\x58\x2A\xE1\x17\x20\x46' + b'\x5A\xD9\xA8\xC0\x5F\xEA\x1A\x1A\x3D\xE5\x41\x01\x45\xDB') + + def test_parse_from_pem(self): + + parsed_key = Python_Key.parsePEM(self.key_pem) + self.assertIsInstance(parsed_key, Python_DSAKey) + self.assertTrue(parsed_key.hasPrivateKey()) + self.assertEqual(parsed_key.private_key, self.key.private_key) + self.assertEqual(parsed_key.public_key, self.key.public_key) + self.assertEqual(parsed_key.q, self.key.q) + self.assertEqual(parsed_key.p, self.key.p) + self.assertEqual(parsed_key.g, self.key.g) + + def test_generate(self): + key = Python_DSAKey.generate(1024, 160) + self.assertIsInstance(key, Python_DSAKey) + self.assertTrue(key.hasPrivateKey()) + + def test_sign_default(self): + msg = b'some message to sign' + + sig = self.key.hashAndSign(msg) + + self.assertTrue(sig) + def test_verify(self): + msg = b'some message to sign' + + self.assertTrue(self.key.hashAndVerify(self.sha1_sig, msg)) + + def test_sign_verify_malformed_signature_r(self): + msg = b'some message to sign' + # signature with r component equal to q + sig = (b'\x30\x2d\x02\x15\x00\x9f\x0b\x26\xa0\xbc\x40\xe4\x80\xae\x67\x1e\xab\x1c\x69' + b'\x89\x45\xdf\x67\xa4\x47\x02\x14\x49\x27\x07\xde\x17\x27\xa5\x78\x05\xaf\x0e' + b'\x1f\x03\x61\x10\xe4\x99\x2d\xff\x03') + + self.assertFalse(self.key_diff.hashAndVerify(sig, msg)) + + def test_sign_verify_malformed_signature_s(self): + msg = b'some message to sign' + # signature with s component equal to q + sig = (b'\x30\x2d\x02\x14\x1a\x6e\x40\x15\x68\xe5\xbd\x02\x65\xc2\x76\xf0\x97\x0a\xab' + b'\x4a\xb0\xb6\xc8\x43\x02\x15\x00\x9f\x0b\x26\xa0\xbc\x40\xe4\x80\xae\x67\x1e' + b'\xab\x1c\x69\x89\x45\xdf\x67\xa4\x47') + + self.assertFalse(self.key_diff.hashAndVerify(sig, msg)) + + def test_sign_verify_malformed_signature_unrecognized(self): + msg = b'some message to sign' + # signature with 3 integer sequence + sig = (b'\x30\x30\x02\x14\x1a\x6e\x40\x15\x68\xe5\xbd\x02\x65\xc2\x76\xf0\x97\x0a\xab' + b'\x4a\xb0\xb6\xc8\x43\x02\x15\x00\x9f\x0b\x26\xa0\xbc\x40\xe4\x80\xae\x67\x1e' + b'\xab\x1c\x69\x89\x45\xdf\x67\xa4\x47\x02\x01\x00') + + self.assertFalse(self.key_diff.hashAndVerify(sig, msg)) + + def test_sign_verify_malformed_signature_garbage(self): + msg = b'some message to sign' + # signature with garbage byte at the end + sig = (b'\x30\x2d\x02\x14\x1a\x6e\x40\x15\x68\xe5\xbd\x02\x65\xc2\x76\xf0\x97\x0a\xab' + b'\x4a\xb0\xb6\xc8\x43\x02\x15\x00\x9f\x0b\x26\xa0\xbc\x40\xe4\x80\xae\x67\x1e' + b'\xab\x1c\x69\x89\x45\xdf\x67\xa4\x47') + + self.assertFalse(self.key_diff.hashAndVerify(sig, msg)) + + def test_verify_diff_key(self): + msg = b'some message to sign' + + self.assertFalse(self.key_diff.hashAndVerify(self.sha1_sig, msg)) + + def test_verify_diff_sign(self): + msg = b'some message to sign' + + # dsa sha1 signature of "another message to sign" + sig = (b'\x30\x2D\x02\x15\x00\x88\xE8\xAF\x9C\xDA\x6D\x0B\x4A\xC4\x0E\x52' + b'\x49\xE2\xA5\x28\x08\x45\x8E\xD6\x1F\x02\x14\x14\x38\xE2\x92\x2B' + b'\x16\xA7\x4B\xB2\x2D\xEA\xFC\x23\xE3\x1B\x84\xCE\x30\x98\x32') + + self.assertFalse(self.key.hashAndVerify(sig, msg)) + + def test_sign_and_verify_with_md5(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="md5") + + self.assertTrue(self.key.hashAndVerify(sig, msg, hAlg="md5")) + + def test_sign_and_verify_with_sha1(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="sha1") + + self.assertTrue(self.key.hashAndVerify(sig, msg)) + + def test_sign_and_verify_with_sha224(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="sha224") + + self.assertTrue(self.key.hashAndVerify(sig, msg, hAlg="sha224")) + + def test_sign_and_verify_with_sha256(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="sha256") + + self.assertTrue(self.key.hashAndVerify(sig, msg, hAlg="sha256")) + + def test_sign_and_verify_with_sha384(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="sha384") + + self.assertTrue(self.key.hashAndVerify(sig, msg, hAlg="sha384")) + + def test_sign_and_verify_with_sha512(self): + msg = b"some message to sign" + + sig = self.key.hashAndSign(msg, hAlg="sha512") + + self.assertTrue(self.key.hashAndVerify(sig, msg, hAlg="sha512")) diff --git a/unit_tests/test_tlslite_utils_python_key.py b/unit_tests/test_tlslite_utils_python_key.py index d4610bad..ba4443e9 100644 --- a/unit_tests/test_tlslite_utils_python_key.py +++ b/unit_tests/test_tlslite_utils_python_key.py @@ -14,6 +14,7 @@ from tlslite.utils.python_key import Python_Key from tlslite.utils.python_rsakey import Python_RSAKey from tlslite.utils.python_ecdsakey import Python_ECDSAKey +from tlslite.utils.python_dsakey import Python_DSAKey class TestKey(unittest.TestCase): def test_rsa_key(self): @@ -141,3 +142,16 @@ def test_ecdsa_p521(self): self.assertIsInstance(parsed_key, Python_ECDSAKey) self.assertEqual(len(parsed_key), 521) + + def test_dsa_key_pkcs8(self): + key_PKCS8 = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIGEAgEAMGcGByqGSM44BAEwXAIhAJnhWwlIVVGYFaZY1zm4ZkWkGCENdmEq/c5Q\n" + "o2tpKU4lAhUAnwsmoLxA5ICuZx6rHGmJRd9npEcCIGIKTT/CY1cXyeD9yBKztO6q\n" + "s1kttYUsjIbGgjdGp9DrBBYCFAmQlcW6FkMRHVfA7C82IVhQ89lo\n" + "-----END PRIVATE KEY-----\n") + parsed_key = Python_Key.parsePEM(key_PKCS8) + self.assertIsInstance(parsed_key, Python_DSAKey) + self.assertTrue(parsed_key.hasPrivateKey()) + self.assertEqual(parsed_key.private_key, \ + 54605271259585079176392566431938393409383029096) diff --git a/unit_tests/test_tlslite_x509.py b/unit_tests/test_tlslite_x509.py index 8c5696c3..74363d98 100644 --- a/unit_tests/test_tlslite_x509.py +++ b/unit_tests/test_tlslite_x509.py @@ -12,8 +12,39 @@ from tlslite.x509 import X509 from tlslite.utils.python_ecdsakey import Python_ECDSAKey +from tlslite.utils.python_dsakey import Python_DSAKey from tlslite.x509certchain import X509CertChain +class Test_DSA_X509(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data = ( + "-----BEGIN CERTIFICATE-----\n" + "MIIBQjCCAQACFFyBKCftN0cXDwuMuZWvtW7uG2xGMAsGCWCGSAFlAwQDAjAUMRIw\n" + "EAYDVQQDDAlsb2NhbGhvc3QwHhcNMjAwOTAzMDkwNzUxWhcNMjAxMDAzMDkwNzUx\n" + "WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwgY4wZwYHKoZIzjgEATBcAiEAmeFbCUhV\n" + "UZgVpljXObhmRaQYIQ12YSr9zlCja2kpTiUCFQCfCyagvEDkgK5nHqscaYlF32ek\n" + "RwIgYgpNP8JjVxfJ4P3IErO07qqzWS21hSyMhsaCN0an0OsDIwACICUjj3Np+JO4\n" + "2v8Mc8oH6T8yNd5X0ssy8XdK3Bo9nfNpMAsGCWCGSAFlAwQDAgMvADAsAhRgjSkX\n" + "k9nkSQc2P3uA+fFEH2OOnAIUZnBeKDjTEMawkvRSXoGHhA93qQ4=\n" + "-----END CERTIFICATE-----\n") + + def test_pem(self): + x509 = X509() + x509.parse(self.data) + + self.assertIsNotNone(x509.publicKey) + self.assertIsInstance(x509.publicKey, Python_DSAKey) + self.assertEqual(x509.publicKey.public_key, + 16798405106129606882295006910154614336997455047535738179977898112652777747305) + self.assertEqual(x509.publicKey.p, + 69602034731989554929546346371414762967051205729581487767213360812510562307621) + self.assertEqual(x509.publicKey.q, + 907978205720450240238233398695599264980368073799) + self.assertEqual(x509.publicKey.g, + 44344860785224683582210580276798141855549498608976964582640232671615126065387) + + class TestX509(unittest.TestCase): @classmethod def setUpClass(cls): @@ -51,7 +82,6 @@ def test_hash(self): self.assertEqual(hash(x509_1), hash(x509_2)) self.assertEqual(x509_1, x509_2) - class TestX509CertChain(unittest.TestCase): @classmethod def setUpClass(cls):