Skip to content

Commit 8370b03

Browse files
authored
Merge pull request #502 from tlsfuzzer/non-strict-dsa
Extend DSA functionality
2 parents c4873fe + c18ab97 commit 8370b03

File tree

7 files changed

+160
-20
lines changed

7 files changed

+160
-20
lines changed

tests/clientDSACert.pem

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEYzCCBBGgAwIBAgIUeLiaH57flwaXt7G1ae3irblrBR0wCwYJYIZIAWUDBAMC
3+
MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yMzA4MDQxNjQ5MzhaGA8yMTIzMDcx
4+
MTE2NDkzOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIDQjCCAjUGByqGSM44BAEw
5+
ggIoAoIBAQCKiKCtENzZfSCSfewPFVgV0vmOVw8WqO4uhiN/nSddlrxNOS58f5fk
6+
whAmtKxH7mQqlFPADuscHy5NQl9GsKZPi5ogVtxImJwP7enyf056tcOCtCUWnMIQ
7+
eEossgWdYE51KQeX+Hmr3XHqJwpsAW9yNkDS46SXut+uAAXLoQ2w+a71BJavCCuZ
8+
P5CqOjMbKVDBMIGPwDUKaL1prq2VBVSZ8WTiAtqqgaQUZQuV88G5R46teyNdOHt8
9+
zonXF8/6PUuDbI3KXvQZitLo6dhXGrAg99Pwg9k9ztzUJOeZ7kU6DZFaawRT+/9J
10+
f6iG0Le20jVc9aw9SbWNfJBY7szeaq11Ah0A/Rgp6nI7FlqEUqL9GaNBm+2B/E1D
11+
P3Z5n3prNQKCAQBV7L0KOF5OnXYMNUrVSZ7qP9hMP4dnhVPvqVa0LJhXbmYx8CO+
12+
kdPGbX2t8skg5yM1vK+AZ/nM5tlbA2R/F6Nol3X9lwUwQT0RRlhsHoT7kTVMU+Ip
13+
lv1C3X+H3GthGAnMmGutGsK+48sHl6QAPmy8f6uVHWzh6WQe7pROZPD410U8t2GM
14+
wRUjlo8n5NdQx/Qw7bvT88HX3FpybNQQJvsefeX5gEpK8JQSLhOEEjAqafrJHtBM
15+
FBDsdpn5/5/H9gTBXqjq6zJRxQP+xggmLj2Eo9B249ANmi57pixM2m9NFXVneJJN
16+
zlIZ2DIEzOX8TVjDFyGLmldXIz2/BqG6dmYDA4IBBQACggEAQDA5L5QJubxun5sH
17+
w31gN/oXWyxF9qXoaUyLpUpt2V2lebNCJ6bdR+wstA7ddyAb/pM6kjnjEv+UxD3c
18+
SjzMtW+Eehn8kXv4gdYyJeJjFcziV5sFvjW6RGAQX0+YO1gAv8BxjGzySNoc7bpp
19+
Ta5YywN0sRPG3U0KsVGyBXakYNoS3Hq7n1JnrSuGBKVje1fQyesRtpkWBfD6Dlog
20+
iciOy/iQnTsMCFrlrETJPfOAJ12Gzsr+lL9TLVsIP9tAk++Arp1H/rICf2QpIjsZ
21+
uEehHgg3oA62RecmMBf+7HX2+bZeoRyYYCe6DMyDS7HLzKA/y31iw7E9VLNyKU4q
22+
i6VPLqNTMFEwHQYDVR0OBBYEFBFcfDeT5cNGJwP89U5VkvxojDuzMB8GA1UdIwQY
23+
MBaAFBFcfDeT5cNGJwP89U5VkvxojDuzMA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZI
24+
AWUDBAMCAz8AMDwCHGaJRUSP9RhmtFPC7UZQ3KjyELgZ/vUcfoJa3ysCHEUavN6C
25+
jExdFwdKI/98/jX7Qz7IlKnDbww8K9c=
26+
-----END CERTIFICATE-----

tests/clientDSAKey.pem

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIICXAIBADCCAjUGByqGSM44BAEwggIoAoIBAQCKiKCtENzZfSCSfewPFVgV0vmO
3+
Vw8WqO4uhiN/nSddlrxNOS58f5fkwhAmtKxH7mQqlFPADuscHy5NQl9GsKZPi5og
4+
VtxImJwP7enyf056tcOCtCUWnMIQeEossgWdYE51KQeX+Hmr3XHqJwpsAW9yNkDS
5+
46SXut+uAAXLoQ2w+a71BJavCCuZP5CqOjMbKVDBMIGPwDUKaL1prq2VBVSZ8WTi
6+
AtqqgaQUZQuV88G5R46teyNdOHt8zonXF8/6PUuDbI3KXvQZitLo6dhXGrAg99Pw
7+
g9k9ztzUJOeZ7kU6DZFaawRT+/9Jf6iG0Le20jVc9aw9SbWNfJBY7szeaq11Ah0A
8+
/Rgp6nI7FlqEUqL9GaNBm+2B/E1DP3Z5n3prNQKCAQBV7L0KOF5OnXYMNUrVSZ7q
9+
P9hMP4dnhVPvqVa0LJhXbmYx8CO+kdPGbX2t8skg5yM1vK+AZ/nM5tlbA2R/F6No
10+
l3X9lwUwQT0RRlhsHoT7kTVMU+Iplv1C3X+H3GthGAnMmGutGsK+48sHl6QAPmy8
11+
f6uVHWzh6WQe7pROZPD410U8t2GMwRUjlo8n5NdQx/Qw7bvT88HX3FpybNQQJvse
12+
feX5gEpK8JQSLhOEEjAqafrJHtBMFBDsdpn5/5/H9gTBXqjq6zJRxQP+xggmLj2E
13+
o9B249ANmi57pixM2m9NFXVneJJNzlIZ2DIEzOX8TVjDFyGLmldXIz2/BqG6dmYD
14+
BB4CHCEci705ssiT3yVp22s8gZEDANgtPXZaMjAFEMw=
15+
-----END PRIVATE KEY-----

tests/tlstest.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,29 @@ def connect():
999999

10001000
test_no += 1
10011001

1002+
print("Test {0} - good mutual X.509 DSA, TLSv1.2".format(test_no))
1003+
with open(os.path.join(dir, "clientDSACert.pem")) as f:
1004+
x509DSACert = X509().parse(f.read())
1005+
x509DSAChain = X509CertChain([x509DSACert])
1006+
with open(os.path.join(dir, "clientDSAKey.pem")) as f:
1007+
x509DSAKey = parsePEMKey(f.read(), private=True)
1008+
1009+
synchro.recv(1)
1010+
connection = connect()
1011+
settings = HandshakeSettings()
1012+
settings.minVersion = (3, 3)
1013+
settings.maxVersion = (3, 3)
1014+
connection.handshakeClientCert(x509DSAChain, x509DSAKey, settings=settings)
1015+
testConnClient(connection)
1016+
assert connection.session.cipherSuite in\
1017+
constants.CipherSuite.dheDsaSuites
1018+
assert isinstance(connection.session.serverCertChain, X509CertChain)
1019+
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type\
1020+
== "dsa"
1021+
connection.close()
1022+
1023+
test_no += 1
1024+
10021025
print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
10031026
synchro.recv(1)
10041027
connection = connect()
@@ -2683,6 +2706,22 @@ def connect():
26832706

26842707
test_no += 1
26852708

2709+
print("Test {0} - good mutual X.509 DSA, TLSv1.2".format(test_no))
2710+
synchro.send(b'R')
2711+
connection = connect()
2712+
settings = HandshakeSettings()
2713+
settings.minVersion = (3, 3)
2714+
settings.maxVersion = (3, 3)
2715+
connection.handshakeServer(certChain=x509ChainDSA, reqCert=True,
2716+
privateKey=x509KeyDSA, settings=settings)
2717+
assert(isinstance(connection.session.clientCertChain, X509CertChain))
2718+
assert connection.session.clientCertChain.getEndEntityPublicKey().key_type\
2719+
== "dsa"
2720+
testConnServer(connection)
2721+
connection.close()
2722+
2723+
test_no += 1
2724+
26862725
print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
26872726
synchro.send(b'R')
26882727
connection = connect()

tlslite/constants.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,13 +1515,13 @@ def getEcdsaSuites(cls, settings, version=None):
15151515

15161516
#: DHE key exchange, DSA authentication
15171517
dheDsaSuites = []
1518-
dheDsaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
1519-
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
1520-
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
1521-
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
1522-
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
1523-
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
15241518
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)
1519+
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
1520+
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
1521+
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
1522+
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
1523+
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
1524+
dheDsaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
15251525

15261526
@classmethod
15271527
def getDheDsaSuites(cls, settings, version=None):

tlslite/keyexchange.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ def calcVerifyBytes(version, handshakeHashes, signatureAlg,
373373
SignatureScheme.ed448):
374374
hashName = "intrinsic"
375375
padding = None
376+
elif signatureAlg[1] == SignatureAlgorithm.dsa:
377+
hashName = HashAlgorithm.toRepr(signatureAlg[0])
378+
padding = None
376379
elif signatureAlg[1] != SignatureAlgorithm.ecdsa:
377380
scheme = SignatureScheme.toRepr(signatureAlg)
378381
if scheme is None:
@@ -455,6 +458,13 @@ def makeCertificateVerify(version, handshakeHashes, validSigAlgs,
455458
verifyBytes = verifyBytes[:privateKey.private_key.curve.baselen]
456459
sig_func = privateKey.sign
457460
ver_func = privateKey.verify
461+
elif signatureAlgorithm and \
462+
signatureAlgorithm[1] == SignatureAlgorithm.dsa:
463+
padding = None
464+
hashName = HashAlgorithm.toRepr(signatureAlgorithm[0])
465+
saltLen = None
466+
sig_func = privateKey.sign
467+
ver_func = privateKey.verify
458468
else:
459469
scheme = SignatureScheme.toRepr(signatureAlgorithm)
460470
# for pkcs1 signatures hash is used to add PKCS#1 prefix, but

tlslite/tlsconnection.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,7 @@ def _clientKeyExchange(self, settings, cipherSuite,
17001700
#abort if Certificate Request with inappropriate ciphersuite
17011701
if cipherSuite not in CipherSuite.certAllSuites \
17021702
and cipherSuite not in CipherSuite.ecdheEcdsaSuites \
1703-
and CipherSuite not in CipherSuite.dheDsaSuites\
1703+
and cipherSuite not in CipherSuite.dheDsaSuites\
17041704
or cipherSuite in CipherSuite.srpAllSuites:
17051705
for result in self._sendError(\
17061706
AlertDescription.unexpected_message,
@@ -1767,7 +1767,6 @@ def _clientKeyExchange(self, settings, cipherSuite,
17671767

17681768
#Send Certificate if we were asked for it
17691769
if certificateRequest:
1770-
17711770
# if a peer doesn't advertise support for any algorithm in TLSv1.2,
17721771
# support for SHA1+RSA can be assumed
17731772
if self.version == (3, 3)\
@@ -2826,7 +2825,7 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
28262825
ctx = b''
28272826

28282827
# Get list of valid Signing Algorithms
2829-
# we don't support DSA for client certificates yet
2828+
# DSA is not supported for TLS 1.3
28302829
cr_settings = settings.validate()
28312830
cr_settings.dsaSigHashes = []
28322831
valid_sig_algs = self._sigHashesToList(cr_settings)
@@ -4206,11 +4205,17 @@ def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
42064205
if not reqCAs:
42074206
reqCAs = []
42084207
cr_settings = settings.validate()
4209-
# we don't support DSA in client certificates yet
4210-
cr_settings.dsaSigHashes = []
42114208
valid_sig_algs = self._sigHashesToList(cr_settings)
4212-
certificateRequest.create([ClientCertificateType.rsa_sign,
4213-
ClientCertificateType.ecdsa_sign],
4209+
4210+
cert_types = []
4211+
if cr_settings.rsaSigHashes:
4212+
cert_types.append(ClientCertificateType.rsa_sign)
4213+
if cr_settings.ecdsaSigHashes or cr_settings.more_sig_schemes:
4214+
cert_types.append(ClientCertificateType.ecdsa_sign)
4215+
if cr_settings.dsaSigHashes:
4216+
cert_types.append(ClientCertificateType.dss_sign)
4217+
4218+
certificateRequest.create(cert_types,
42144219
reqCAs,
42154220
valid_sig_algs)
42164221
msgs.append(certificateRequest)
@@ -4327,6 +4332,12 @@ def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
43274332
salt_len = None
43284333
padding = None
43294334
ver_func = public_key.hashAndVerify
4335+
elif signatureAlgorithm and \
4336+
signatureAlgorithm[1] == SignatureAlgorithm.dsa:
4337+
padding = None
4338+
hash_name = HashAlgorithm.toRepr(signatureAlgorithm[0])
4339+
salt_len = None
4340+
ver_func = public_key.verify
43304341
elif not signatureAlgorithm or \
43314342
signatureAlgorithm[1] != SignatureAlgorithm.ecdsa:
43324343
scheme = SignatureScheme.toRepr(signatureAlgorithm)

tlslite/utils/python_dsakey.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@ def __init__(self, p=0, q=0, g=0, x=0, y=0):
3333
self.g = g
3434
self.private_key = x
3535
self.public_key = y
36+
if self.private_key and not self.public_key:
37+
self.public_key = powMod(g, self.private_key, p)
3638
self.key_type = "dsa"
3739

38-
if p and q and p < q:
39-
raise ValueError("q is greater than p")
40-
4140
def __len__(self):
4241
return numBits(self.p)
4342

@@ -82,20 +81,60 @@ def hashAndSign(self, data, hAlg="sha1"):
8281
hashData = (secureHash(bytearray(data), hAlg))
8382
return self.sign(hashData)
8483

85-
def sign(self, data):
84+
def sign(self, data, padding=None, hashAlg=None, saltLen=None):
85+
"""
86+
:type data: bytearray
87+
:param data: The value which will be signed (generally a binary
88+
encoding of hash output.
89+
90+
:type padding: str
91+
:param padding: Ignored, present for API compatibility with RSA
92+
93+
:type hashAlg: str
94+
:param hashAlg: name of hash that was used for calculating the bytes
95+
96+
:type saltLen: int
97+
:param saltLen: Ignored, present for API compatibility with RSA
98+
"""
8699
N = numBits(self.q)
87100
digest_len = len(data) * 8
88101
digest = bytesToNumber(data)
89102
if N < digest_len:
90103
digest >>= digest_len - N
91104

92105
k = getRandomNumber(1, (self.q-1))
106+
if gmpyLoaded or GMPY2_LOADED:
107+
k = mpz(k)
108+
digest = mpz(digest)
93109
r = powMod(self.g, k, self.p) % self.q
94110
s = invMod(k, self.q) * (digest + self.private_key * r) % self.q
95111

96112
return encode_sequence(encode_integer(r), encode_integer(s))
97113

98-
def verify(self, signature, hashData):
114+
def verify(self, signature, hashData, padding=None, hashAlg=None,
115+
saltLen=None):
116+
"""Verify the passed-in bytes with the signature.
117+
118+
This verifies a DSA signature on the passed-in data.
119+
120+
:type signature: bytearray
121+
:param signature: The signature.
122+
123+
:type hashData: bytearray
124+
:param hashData: The value which will be verified.
125+
126+
:type padding: str
127+
:param padding: Ignored, present for API compatibility with RSA
128+
129+
:type hashAlg: str
130+
:param hashAlg: Ignored, present for API compatibility with RSA
131+
132+
:type saltLen: str
133+
:param saltLen: Ignored, present for API compatibility with RSA
134+
135+
:rtype: bool
136+
:returns: Whether the signature matches the passed-in data.
137+
"""
99138
N = numBits(self.q)
100139
digest_len = len(hashData) * 8
101140
digest = bytesToNumber(hashData)
@@ -125,8 +164,8 @@ def verify(self, signature, hashData):
125164
w = invMod(s, self.q)
126165
u1 = (digest * w) % self.q
127166
u2 = (r * w) % self.q
128-
v = ((powMod(self.g, u1, self.p) * \
129-
powMod(self.public_key, u2, self.p)) % self.p) % self.q
167+
v = ((powMod(self.g, u1, self.p) * \
168+
powMod(self.public_key, u2, self.p)) % self.p) % self.q
130169
return r == v
131170
return False
132171

0 commit comments

Comments
 (0)