Skip to content

Commit

Permalink
Merge pull request #502 from tlsfuzzer/non-strict-dsa
Browse files Browse the repository at this point in the history
Extend DSA functionality
  • Loading branch information
tomato42 committed Aug 24, 2023
2 parents c4873fe + c18ab97 commit 8370b03
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 20 deletions.
26 changes: 26 additions & 0 deletions tests/clientDSACert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEYzCCBBGgAwIBAgIUeLiaH57flwaXt7G1ae3irblrBR0wCwYJYIZIAWUDBAMC
MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yMzA4MDQxNjQ5MzhaGA8yMTIzMDcx
MTE2NDkzOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIDQjCCAjUGByqGSM44BAEw
ggIoAoIBAQCKiKCtENzZfSCSfewPFVgV0vmOVw8WqO4uhiN/nSddlrxNOS58f5fk
whAmtKxH7mQqlFPADuscHy5NQl9GsKZPi5ogVtxImJwP7enyf056tcOCtCUWnMIQ
eEossgWdYE51KQeX+Hmr3XHqJwpsAW9yNkDS46SXut+uAAXLoQ2w+a71BJavCCuZ
P5CqOjMbKVDBMIGPwDUKaL1prq2VBVSZ8WTiAtqqgaQUZQuV88G5R46teyNdOHt8
zonXF8/6PUuDbI3KXvQZitLo6dhXGrAg99Pwg9k9ztzUJOeZ7kU6DZFaawRT+/9J
f6iG0Le20jVc9aw9SbWNfJBY7szeaq11Ah0A/Rgp6nI7FlqEUqL9GaNBm+2B/E1D
P3Z5n3prNQKCAQBV7L0KOF5OnXYMNUrVSZ7qP9hMP4dnhVPvqVa0LJhXbmYx8CO+
kdPGbX2t8skg5yM1vK+AZ/nM5tlbA2R/F6Nol3X9lwUwQT0RRlhsHoT7kTVMU+Ip
lv1C3X+H3GthGAnMmGutGsK+48sHl6QAPmy8f6uVHWzh6WQe7pROZPD410U8t2GM
wRUjlo8n5NdQx/Qw7bvT88HX3FpybNQQJvsefeX5gEpK8JQSLhOEEjAqafrJHtBM
FBDsdpn5/5/H9gTBXqjq6zJRxQP+xggmLj2Eo9B249ANmi57pixM2m9NFXVneJJN
zlIZ2DIEzOX8TVjDFyGLmldXIz2/BqG6dmYDA4IBBQACggEAQDA5L5QJubxun5sH
w31gN/oXWyxF9qXoaUyLpUpt2V2lebNCJ6bdR+wstA7ddyAb/pM6kjnjEv+UxD3c
SjzMtW+Eehn8kXv4gdYyJeJjFcziV5sFvjW6RGAQX0+YO1gAv8BxjGzySNoc7bpp
Ta5YywN0sRPG3U0KsVGyBXakYNoS3Hq7n1JnrSuGBKVje1fQyesRtpkWBfD6Dlog
iciOy/iQnTsMCFrlrETJPfOAJ12Gzsr+lL9TLVsIP9tAk++Arp1H/rICf2QpIjsZ
uEehHgg3oA62RecmMBf+7HX2+bZeoRyYYCe6DMyDS7HLzKA/y31iw7E9VLNyKU4q
i6VPLqNTMFEwHQYDVR0OBBYEFBFcfDeT5cNGJwP89U5VkvxojDuzMB8GA1UdIwQY
MBaAFBFcfDeT5cNGJwP89U5VkvxojDuzMA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZI
AWUDBAMCAz8AMDwCHGaJRUSP9RhmtFPC7UZQ3KjyELgZ/vUcfoJa3ysCHEUavN6C
jExdFwdKI/98/jX7Qz7IlKnDbww8K9c=
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions tests/clientDSAKey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN PRIVATE KEY-----
MIICXAIBADCCAjUGByqGSM44BAEwggIoAoIBAQCKiKCtENzZfSCSfewPFVgV0vmO
Vw8WqO4uhiN/nSddlrxNOS58f5fkwhAmtKxH7mQqlFPADuscHy5NQl9GsKZPi5og
VtxImJwP7enyf056tcOCtCUWnMIQeEossgWdYE51KQeX+Hmr3XHqJwpsAW9yNkDS
46SXut+uAAXLoQ2w+a71BJavCCuZP5CqOjMbKVDBMIGPwDUKaL1prq2VBVSZ8WTi
AtqqgaQUZQuV88G5R46teyNdOHt8zonXF8/6PUuDbI3KXvQZitLo6dhXGrAg99Pw
g9k9ztzUJOeZ7kU6DZFaawRT+/9Jf6iG0Le20jVc9aw9SbWNfJBY7szeaq11Ah0A
/Rgp6nI7FlqEUqL9GaNBm+2B/E1DP3Z5n3prNQKCAQBV7L0KOF5OnXYMNUrVSZ7q
P9hMP4dnhVPvqVa0LJhXbmYx8CO+kdPGbX2t8skg5yM1vK+AZ/nM5tlbA2R/F6No
l3X9lwUwQT0RRlhsHoT7kTVMU+Iplv1C3X+H3GthGAnMmGutGsK+48sHl6QAPmy8
f6uVHWzh6WQe7pROZPD410U8t2GMwRUjlo8n5NdQx/Qw7bvT88HX3FpybNQQJvse
feX5gEpK8JQSLhOEEjAqafrJHtBMFBDsdpn5/5/H9gTBXqjq6zJRxQP+xggmLj2E
o9B249ANmi57pixM2m9NFXVneJJNzlIZ2DIEzOX8TVjDFyGLmldXIz2/BqG6dmYD
BB4CHCEci705ssiT3yVp22s8gZEDANgtPXZaMjAFEMw=
-----END PRIVATE KEY-----
39 changes: 39 additions & 0 deletions tests/tlstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,29 @@ def connect():

test_no += 1

print("Test {0} - good mutual X.509 DSA, TLSv1.2".format(test_no))
with open(os.path.join(dir, "clientDSACert.pem")) as f:
x509DSACert = X509().parse(f.read())
x509DSAChain = X509CertChain([x509DSACert])
with open(os.path.join(dir, "clientDSAKey.pem")) as f:
x509DSAKey = parsePEMKey(f.read(), private=True)

synchro.recv(1)
connection = connect()
settings = HandshakeSettings()
settings.minVersion = (3, 3)
settings.maxVersion = (3, 3)
connection.handshakeClientCert(x509DSAChain, x509DSAKey, settings=settings)
testConnClient(connection)
assert connection.session.cipherSuite in\
constants.CipherSuite.dheDsaSuites
assert isinstance(connection.session.serverCertChain, X509CertChain)
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type\
== "dsa"
connection.close()

test_no += 1

print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
synchro.recv(1)
connection = connect()
Expand Down Expand Up @@ -2683,6 +2706,22 @@ def connect():

test_no += 1

print("Test {0} - good mutual X.509 DSA, TLSv1.2".format(test_no))
synchro.send(b'R')
connection = connect()
settings = HandshakeSettings()
settings.minVersion = (3, 3)
settings.maxVersion = (3, 3)
connection.handshakeServer(certChain=x509ChainDSA, reqCert=True,
privateKey=x509KeyDSA, settings=settings)
assert(isinstance(connection.session.clientCertChain, X509CertChain))
assert connection.session.clientCertChain.getEndEntityPublicKey().key_type\
== "dsa"
testConnServer(connection)
connection.close()

test_no += 1

print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
synchro.send(b'R')
connection = connect()
Expand Down
12 changes: 6 additions & 6 deletions tlslite/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -1515,13 +1515,13 @@ def getEcdsaSuites(cls, settings, version=None):

#: DHE key exchange, DSA authentication
dheDsaSuites = []
dheDsaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
dheDsaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)

@classmethod
def getDheDsaSuites(cls, settings, version=None):
Expand Down
10 changes: 10 additions & 0 deletions tlslite/keyexchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ def calcVerifyBytes(version, handshakeHashes, signatureAlg,
SignatureScheme.ed448):
hashName = "intrinsic"
padding = None
elif signatureAlg[1] == SignatureAlgorithm.dsa:
hashName = HashAlgorithm.toRepr(signatureAlg[0])
padding = None
elif signatureAlg[1] != SignatureAlgorithm.ecdsa:
scheme = SignatureScheme.toRepr(signatureAlg)
if scheme is None:
Expand Down Expand Up @@ -455,6 +458,13 @@ def makeCertificateVerify(version, handshakeHashes, validSigAlgs,
verifyBytes = verifyBytes[:privateKey.private_key.curve.baselen]
sig_func = privateKey.sign
ver_func = privateKey.verify
elif signatureAlgorithm and \
signatureAlgorithm[1] == SignatureAlgorithm.dsa:
padding = None
hashName = HashAlgorithm.toRepr(signatureAlgorithm[0])
saltLen = None
sig_func = privateKey.sign
ver_func = privateKey.verify
else:
scheme = SignatureScheme.toRepr(signatureAlgorithm)
# for pkcs1 signatures hash is used to add PKCS#1 prefix, but
Expand Down
25 changes: 18 additions & 7 deletions tlslite/tlsconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,7 +1700,7 @@ def _clientKeyExchange(self, settings, cipherSuite,
#abort if Certificate Request with inappropriate ciphersuite
if cipherSuite not in CipherSuite.certAllSuites \
and cipherSuite not in CipherSuite.ecdheEcdsaSuites \
and CipherSuite not in CipherSuite.dheDsaSuites\
and cipherSuite not in CipherSuite.dheDsaSuites\
or cipherSuite in CipherSuite.srpAllSuites:
for result in self._sendError(\
AlertDescription.unexpected_message,
Expand Down Expand Up @@ -1767,7 +1767,6 @@ def _clientKeyExchange(self, settings, cipherSuite,

#Send Certificate if we were asked for it
if certificateRequest:

# if a peer doesn't advertise support for any algorithm in TLSv1.2,
# support for SHA1+RSA can be assumed
if self.version == (3, 3)\
Expand Down Expand Up @@ -2826,7 +2825,7 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
ctx = b''

# Get list of valid Signing Algorithms
# we don't support DSA for client certificates yet
# DSA is not supported for TLS 1.3
cr_settings = settings.validate()
cr_settings.dsaSigHashes = []
valid_sig_algs = self._sigHashesToList(cr_settings)
Expand Down Expand Up @@ -4206,11 +4205,17 @@ def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
if not reqCAs:
reqCAs = []
cr_settings = settings.validate()
# we don't support DSA in client certificates yet
cr_settings.dsaSigHashes = []
valid_sig_algs = self._sigHashesToList(cr_settings)
certificateRequest.create([ClientCertificateType.rsa_sign,
ClientCertificateType.ecdsa_sign],

cert_types = []
if cr_settings.rsaSigHashes:
cert_types.append(ClientCertificateType.rsa_sign)
if cr_settings.ecdsaSigHashes or cr_settings.more_sig_schemes:
cert_types.append(ClientCertificateType.ecdsa_sign)
if cr_settings.dsaSigHashes:
cert_types.append(ClientCertificateType.dss_sign)

certificateRequest.create(cert_types,
reqCAs,
valid_sig_algs)
msgs.append(certificateRequest)
Expand Down Expand Up @@ -4327,6 +4332,12 @@ def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
salt_len = None
padding = None
ver_func = public_key.hashAndVerify
elif signatureAlgorithm and \
signatureAlgorithm[1] == SignatureAlgorithm.dsa:
padding = None
hash_name = HashAlgorithm.toRepr(signatureAlgorithm[0])
salt_len = None
ver_func = public_key.verify
elif not signatureAlgorithm or \
signatureAlgorithm[1] != SignatureAlgorithm.ecdsa:
scheme = SignatureScheme.toRepr(signatureAlgorithm)
Expand Down
53 changes: 46 additions & 7 deletions tlslite/utils/python_dsakey.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ def __init__(self, p=0, q=0, g=0, x=0, y=0):
self.g = g
self.private_key = x
self.public_key = y
if self.private_key and not self.public_key:
self.public_key = powMod(g, self.private_key, p)
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)

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

def sign(self, data):
def sign(self, data, padding=None, hashAlg=None, saltLen=None):
"""
:type data: bytearray
:param data: The value which will be signed (generally a binary
encoding of hash output.
:type padding: str
:param padding: Ignored, present for API compatibility with RSA
:type hashAlg: str
:param hashAlg: name of hash that was used for calculating the bytes
:type saltLen: int
:param saltLen: Ignored, present for API compatibility with RSA
"""
N = numBits(self.q)
digest_len = len(data) * 8
digest = bytesToNumber(data)
if N < digest_len:
digest >>= digest_len - N

k = getRandomNumber(1, (self.q-1))
if gmpyLoaded or GMPY2_LOADED:
k = mpz(k)
digest = mpz(digest)
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 verify(self, signature, hashData):
def verify(self, signature, hashData, padding=None, hashAlg=None,
saltLen=None):
"""Verify the passed-in bytes with the signature.
This verifies a DSA signature on the passed-in data.
:type signature: bytearray
:param signature: The signature.
:type hashData: bytearray
:param hashData: The value which will be verified.
:type padding: str
:param padding: Ignored, present for API compatibility with RSA
:type hashAlg: str
:param hashAlg: Ignored, present for API compatibility with RSA
:type saltLen: str
:param saltLen: Ignored, present for API compatibility with RSA
:rtype: bool
:returns: Whether the signature matches the passed-in data.
"""
N = numBits(self.q)
digest_len = len(hashData) * 8
digest = bytesToNumber(hashData)
Expand Down Expand Up @@ -125,8 +164,8 @@ def verify(self, signature, hashData):
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
v = ((powMod(self.g, u1, self.p) * \
powMod(self.public_key, u2, self.p)) % self.p) % self.q
return r == v
return False

Expand Down

0 comments on commit 8370b03

Please sign in to comment.