Skip to content

Commit 8c97544

Browse files
committed
Add support for EdDSA on server side messages
1 parent 13abe07 commit 8c97544

21 files changed

+735
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*venv*
12
*.pyc
23
.project
34
.pydevproject

tests/serverEd25519Cert.pem

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBPDCB76ADAgECAhQkqENccCvOQyI4iKFuuOKwl860bTAFBgMrZXAwFDESMBAG
3+
A1UEAwwJbG9jYWxob3N0MB4XDTIxMDcyNjE0MjcwN1oXDTIxMDgyNTE0MjcwN1ow
4+
FDESMBAGA1UEAwwJbG9jYWxob3N0MCowBQYDK2VwAyEA1KMGmAZealfgakBuCx/E
5+
n69fo072qm90eM40ulGex0ajUzBRMB0GA1UdDgQWBBTHKWv5l/SxnkkYJhh5r3Pv
6+
ESAh1DAfBgNVHSMEGDAWgBTHKWv5l/SxnkkYJhh5r3PvESAh1DAPBgNVHRMBAf8E
7+
BTADAQH/MAUGAytlcANBAF/vSBfOHAdRl29sWDTkuqy1dCuSf7j7jKE/Be8Fk7xs
8+
WteXJmIa0HlRAZjxNfWbsSGLnTYbsGTbxKx3QU9H9g0=
9+
-----END CERTIFICATE-----

tests/serverEd25519Key.pem

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MC4CAQAwBQYDK2VwBCIEIAjtEwCECqbot5RZxSmiNDWcPp+Xc9Y9WJcUhti3JgSP
3+
-----END PRIVATE KEY-----

tests/serverEd448Cert.pem

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBiDCCAQigAwIBAgIUZoaDDgE5Cy2GuAMtk4lnsmrPF04wBQYDK2VxMBQxEjAQ
3+
BgNVBAMMCWxvY2FsaG9zdDAeFw0yMTA3MjYxODAzMzhaFw0yMTA4MjUxODAzMzha
4+
MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDBDMAUGAytlcQM6AKxTNGJ39O4kUx7BopPK
5+
prb1Jkoo0csq0Cmpa+VhpDlbR9/gVsb3pchexzjxXyRkNv71naHmOkQvAKNTMFEw
6+
HQYDVR0OBBYEFBb153yRh5IZOfBxoakGVuviFKujMB8GA1UdIwQYMBaAFBb153yR
7+
h5IZOfBxoakGVuviFKujMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VxA3MAiXEqTPRb
8+
u+56ebfiGjdE++H+YvHVxxxycqKAIAikfsLFfw2LUGQVBMhl+nzS4zRDOKa34uGz
9+
DwEApFuOWurH/y8zqM5NFyXfwbHRlhG4xwUet52CbrtC7Dy1HYnvWdEjbKDSJXpJ
10+
MmNSiO0oBtQ62CsA
11+
-----END CERTIFICATE-----

tests/serverEd448Key.pem

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MEcCAQAwBQYDK2VxBDsEOWC42wrEHt4sse84L8oi/2LfqtYvT+Xwd5USLJuAUi6h
3+
Ht8RBuFGD/DoZIfwfBgBfemM56jAnbQIug==
4+
-----END PRIVATE KEY-----

tests/tlstest.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,40 @@ def connect():
538538

539539
test_no += 1
540540

541+
print("Test {0} - good X.509 Ed25519, TLSv1.3".format(test_no))
542+
synchro.recv(1)
543+
connection = connect()
544+
settings = HandshakeSettings()
545+
settings.minVersion = (3, 4)
546+
settings.maxVersion = (3, 4)
547+
connection.handshakeClientCert(settings=settings)
548+
testConnClient(connection)
549+
assert connection.session.cipherSuite in\
550+
constants.CipherSuite.tls13Suites
551+
assert isinstance(connection.session.serverCertChain, X509CertChain)
552+
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type \
553+
== "Ed25519"
554+
connection.close()
555+
556+
test_no += 1
557+
558+
print("Test {0} - good X.509 Ed448, TLSv1.3".format(test_no))
559+
synchro.recv(1)
560+
connection = connect()
561+
settings = HandshakeSettings()
562+
settings.minVersion = (3, 4)
563+
settings.maxVersion = (3, 4)
564+
connection.handshakeClientCert(settings=settings)
565+
testConnClient(connection)
566+
assert connection.session.cipherSuite in\
567+
constants.CipherSuite.tls13Suites
568+
assert isinstance(connection.session.serverCertChain, X509CertChain)
569+
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type \
570+
== "Ed448"
571+
connection.close()
572+
573+
test_no += 1
574+
541575
print("Test {0} - good RSA and ECDSA, TLSv1.3, rsa"
542576
.format(test_no))
543577
synchro.recv(1)
@@ -877,6 +911,42 @@ def connect():
877911
assert isinstance(connection.session.serverCertChain, X509CertChain)
878912
connection.close()
879913

914+
test_no += 1
915+
916+
print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
917+
synchro.recv(1)
918+
connection = connect()
919+
settings = HandshakeSettings()
920+
settings.minVersion = (3, 3)
921+
settings.maxVersion = (3, 3)
922+
connection.handshakeClientCert(settings=settings)
923+
testConnClient(connection)
924+
assert connection.session.cipherSuite in\
925+
constants.CipherSuite.ecdheEcdsaSuites
926+
assert isinstance(connection.session.serverCertChain, X509CertChain)
927+
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type \
928+
== "Ed25519"
929+
connection.close()
930+
931+
test_no += 1
932+
933+
print("Test {0} - good X.509 Ed448, TLSv1.2".format(test_no))
934+
synchro.recv(1)
935+
connection = connect()
936+
settings = HandshakeSettings()
937+
settings.minVersion = (3, 3)
938+
settings.maxVersion = (3, 3)
939+
connection.handshakeClientCert(settings=settings)
940+
testConnClient(connection)
941+
assert connection.session.cipherSuite in\
942+
constants.CipherSuite.ecdheEcdsaSuites
943+
assert isinstance(connection.session.serverCertChain, X509CertChain)
944+
assert connection.session.serverCertChain.getEndEntityPublicKey().key_type \
945+
== "Ed448"
946+
connection.close()
947+
948+
test_no += 1
949+
880950
print("Test {0} - good mutual X.509, TLSv1.3 no certs".format(test_no))
881951
synchro.recv(1)
882952
connection = connect()
@@ -1739,6 +1809,23 @@ def connect():
17391809
with open(os.path.join(dir, "serverDSAKey.pem")) as f:
17401810
x509KeyDSA = parsePEMKey(f.read(), private=True,
17411811
implementations=["python"])
1812+
1813+
with open(os.path.join(dir, "serverEd25519Cert.pem")) as f:
1814+
x509CertEd25519 = X509().parse(f.read())
1815+
x509Ed25519Chain = X509CertChain([x509CertEd25519])
1816+
assert x509CertEd25519.certAlg == "Ed25519"
1817+
with open(os.path.join(dir, "serverEd25519Key.pem")) as f:
1818+
x509Ed25519Key = parsePEMKey(f.read(), private=True,
1819+
implementations=["python"])
1820+
1821+
with open(os.path.join(dir, "serverEd448Cert.pem")) as f:
1822+
x509CertEd448 = X509().parse(f.read())
1823+
x509Ed448Chain = X509CertChain([x509CertEd448])
1824+
assert x509CertEd448.certAlg == "Ed448"
1825+
with open(os.path.join(dir, "serverEd448Key.pem")) as f:
1826+
x509Ed448Key = parsePEMKey(f.read(), private=True,
1827+
implementations=["python"])
1828+
17421829
test_no = 0
17431830

17441831
print("Test {0} - Anonymous server handshake".format(test_no))
@@ -2080,6 +2167,34 @@ def connect():
20802167

20812168
test_no += 1
20822169

2170+
print("Test {0} - good X.509 Ed25519, TLSv1.3".format(test_no))
2171+
synchro.send(b'R')
2172+
connection = connect()
2173+
settings = HandshakeSettings()
2174+
settings.minVersion = (3, 4)
2175+
settings.maxVersion = (3, 4)
2176+
connection.handshakeServer(certChain=x509Ed25519Chain,
2177+
privateKey=x509Ed25519Key, settings=settings)
2178+
assert connection.extendedMasterSecret
2179+
testConnServer(connection)
2180+
connection.close()
2181+
2182+
test_no += 1
2183+
2184+
print("Test {0} - good X.509 Ed448, TLSv1.3".format(test_no))
2185+
synchro.send(b'R')
2186+
connection = connect()
2187+
settings = HandshakeSettings()
2188+
settings.minVersion = (3, 4)
2189+
settings.maxVersion = (3, 4)
2190+
connection.handshakeServer(certChain=x509Ed448Chain,
2191+
privateKey=x509Ed448Key, settings=settings)
2192+
assert connection.extendedMasterSecret
2193+
testConnServer(connection)
2194+
connection.close()
2195+
2196+
test_no += 1
2197+
20832198
for prot in ["TLSv1.3", "TLSv1.2"]:
20842199
for c_type, exp_chain in (("rsa", x509Chain),
20852200
("ecdsa", x509ecdsaChain)):
@@ -2347,6 +2462,32 @@ def connect():
23472462

23482463
test_no += 1
23492464

2465+
print("Test {0} - good X.509 Ed25519, TLSv1.2".format(test_no))
2466+
synchro.send(b'R')
2467+
connection = connect()
2468+
settings = HandshakeSettings()
2469+
settings.minVersion = (3, 3)
2470+
settings.maxVersion = (3, 3)
2471+
connection.handshakeServer(certChain=x509Ed25519Chain,
2472+
privateKey=x509Ed25519Key, settings=settings)
2473+
testConnServer(connection)
2474+
connection.close()
2475+
2476+
test_no += 1
2477+
2478+
print("Test {0} - good X.509 Ed448, TLSv1.2".format(test_no))
2479+
synchro.send(b'R')
2480+
connection = connect()
2481+
settings = HandshakeSettings()
2482+
settings.minVersion = (3, 3)
2483+
settings.maxVersion = (3, 3)
2484+
connection.handshakeServer(certChain=x509Ed448Chain,
2485+
privateKey=x509Ed448Key, settings=settings)
2486+
testConnServer(connection)
2487+
connection.close()
2488+
2489+
test_no += 1
2490+
23502491
print("Test {0} - good mutual X.509, TLSv1.3 no certs".format(test_no))
23512492
synchro.send(b'R')
23522493
connection = connect()

tlslite/constants.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,8 @@ def getKeyType(scheme):
260260
261261
E.g. for "rsa_pkcs1_sha1" it returns "rsa"
262262
"""
263-
# they need to be threated as ECDSA algorithms, see RFC 8422
264263
if scheme in ("ed25519", "ed448"):
265-
return "ecdsa"
264+
return "eddsa"
266265
try:
267266
getattr(SignatureScheme, scheme)
268267
except AttributeError:
@@ -363,6 +362,10 @@ class AlgorithmOID(TLSEnum):
363362
SignatureScheme.dsa_sha384
364363
oid[bytes(a2b_hex('0609608648016503040304'))] = \
365364
SignatureScheme.dsa_sha512
365+
oid[bytes(a2b_hex('06032b6570'))] = \
366+
SignatureScheme.ed25519
367+
oid[bytes(a2b_hex('06032b6571'))] = \
368+
SignatureScheme.ed448
366369

367370

368371
class GroupName(TLSEnum):
@@ -1265,7 +1268,7 @@ def filter_for_certificate(suites, cert_chain):
12651268
# rsa-pss
12661269
includeSuites.symmetric_difference_update(
12671270
CipherSuite.certSuites)
1268-
if cert_chain.x509List[0].certAlg == "ecdsa":
1271+
if cert_chain.x509List[0].certAlg in ("ecdsa", "Ed25519", "Ed448"):
12691272
includeSuites.update(CipherSuite.ecdheEcdsaSuites)
12701273
if cert_chain.x509List[0].certAlg == "dsa":
12711274
includeSuites.update(CipherSuite.dheDsaSuites)

tlslite/handshakesettings.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
DSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
3232
ECDSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
3333
ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ["md5"]
34+
SIGNATURE_SCHEMES = ["Ed25519", "Ed448"]
3435
RSA_SCHEMES = ["pss", "pkcs1"]
3536
# while secp521r1 is the most secure, it's also much slower than the others
3637
# so place it as the last one
@@ -255,8 +256,15 @@ class HandshakeSettings(object):
255256
The allowed hashes are: "sha1", "sha224", "sha256",
256257
"sha384" and "sha512".
257258
259+
"vartype more_sig_schemes: list(str)
260+
:ivar more_sig_schemes: List of additional signatures schemes (ones
261+
that don't use RSA-PKCS#1 v1.5, RSA-PSS, DSA, or ECDSA) to advertise
262+
as supported.
263+
Currently supported are: "Ed25519", and "Ed448".
264+
258265
:vartype eccCurves: list(str)
259-
:ivar eccCurves: List of named curves that are to be supported
266+
:ivar eccCurves: List of named curves that are to be advertised as
267+
supported in supported_groups extension.
260268
261269
:vartype useEncryptThenMAC: bool
262270
:ivar useEncryptThenMAC: whether to support the encrypt then MAC extension
@@ -367,6 +375,7 @@ def _init_misc_extensions(self):
367375
self.sendFallbackSCSV = False
368376
self.useEncryptThenMAC = True
369377
self.ecdsaSigHashes = list(ECDSA_SIGNATURE_HASHES)
378+
self.more_sig_schemes = list(SIGNATURE_SCHEMES)
370379
self.usePaddingExtension = True
371380
self.useExtendedMasterSecret = True
372381
self.requireExtendedMasterSecret = False
@@ -467,6 +476,12 @@ def _sanityCheckECDHSettings(other):
467476
raise ValueError("Unknown ECDSA signature hash: '{0}'".\
468477
format(unknownSigHash))
469478

479+
unknownSigHash = not_matching(other.more_sig_schemes,
480+
SIGNATURE_SCHEMES)
481+
if unknownSigHash:
482+
raise ValueError("Unkonwn more_sig_schemes specified: '{0}'"
483+
.format(unknownSigHash))
484+
470485
unknownDHGroup = not_matching(other.dhGroups, ALL_DH_GROUP_NAMES)
471486
if unknownDHGroup:
472487
raise ValueError("Unknown FFDHE group name: '{0}'"
@@ -529,7 +544,8 @@ def _sanityCheckPrimitivesNames(other):
529544
.format(unknownSigHash))
530545

531546
if not other.rsaSigHashes and not other.ecdsaSigHashes and \
532-
not other.dsaSigHashes and other.maxVersion >= (3, 3):
547+
not other.dsaSigHashes and not other.more_sig_schemes and \
548+
other.maxVersion >= (3, 3):
533549
raise ValueError("TLS 1.2 requires signature algorithms to be set")
534550

535551
@staticmethod
@@ -688,6 +704,7 @@ def _copy_key_settings(self, other):
688704
other.rsaSchemes = self.rsaSchemes
689705
other.dsaSigHashes = self.dsaSigHashes
690706
other.ecdsaSigHashes = self.ecdsaSigHashes
707+
other.more_sig_schemes = self.more_sig_schemes
691708
other.virtual_hosts = self.virtual_hosts
692709
# DH key params
693710
other.eccCurves = self.eccCurves

tlslite/keyexchange.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,34 @@ def _tls12_sign_dsa_SKE(self, serverKeyExchange, sigHash=None):
123123
hashBytes):
124124
raise TLSInternalError("Server Key Exchange signature invalid")
125125

126+
def _tls12_sign_eddsa_ske(self, server_key_exchange, sig_hash):
127+
"""Sign a TLSv1.2 SKE message."""
128+
server_key_exchange.hashAlg, server_key_exchange.signAlg = \
129+
getattr(SignatureScheme, sig_hash)
130+
pad_type = None
131+
hash_name = None
132+
salt_len = None
133+
134+
hash_bytes = server_key_exchange.hash(self.clientHello.random,
135+
self.serverHello.random)
136+
137+
server_key_exchange.signature = \
138+
self.privateKey.hashAndSign(hash_bytes,
139+
pad_type,
140+
hash_name,
141+
salt_len)
142+
143+
if not server_key_exchange.signature:
144+
raise TLSInternalError("Empty signature")
145+
146+
if not self.privateKey.hashAndVerify(
147+
server_key_exchange.signature,
148+
hash_bytes,
149+
pad_type,
150+
hash_name,
151+
salt_len):
152+
raise TLSInternalError("Server Key Exchange signature invalid")
153+
126154
def _tls12_signSKE(self, serverKeyExchange, sigHash=None):
127155
"""Sign a TLSv1.2 SKE message."""
128156
try:
@@ -189,6 +217,8 @@ def signServerKeyExchange(self, serverKeyExchange, sigHash=None):
189217
self._tls12_sign_ecdsa_SKE(serverKeyExchange, sigHash)
190218
elif self.privateKey.key_type == "dsa":
191219
self._tls12_sign_dsa_SKE(serverKeyExchange, sigHash)
220+
elif self.privateKey.key_type in ("Ed25519", "Ed448"):
221+
self._tls12_sign_eddsa_ske(serverKeyExchange, sigHash)
192222
else:
193223
self._tls12_signSKE(serverKeyExchange, sigHash)
194224

@@ -210,6 +240,21 @@ def _tls12_verify_ecdsa_SKE(serverKeyExchange, publicKey, clientRandom,
210240
raise TLSDecryptionFailed("Server Key Exchange signature "
211241
"invalid")
212242

243+
@staticmethod
244+
def _tls12_verify_eddsa_ske(server_key_exchange, public_key, client_random,
245+
server_random, valid_sig_algs):
246+
"""Verify SeverKeyExchange messages with EdDSA signatures."""
247+
del valid_sig_algs
248+
sig_bytes = server_key_exchange.signature
249+
if not sig_bytes:
250+
raise TLSIllegalParameterException("Empty signature")
251+
252+
hash_bytes = server_key_exchange.hash(client_random, server_random)
253+
254+
if not public_key.hashAndVerify(sig_bytes,
255+
hash_bytes):
256+
raise TLSDecryptionFailed("Server Key Exchange signature invalid")
257+
213258
@staticmethod
214259
def _tls12_verify_dsa_SKE(serverKeyExchange, publicKey, clientRandom,
215260
serverRandom, validSigAlgs):
@@ -229,6 +274,13 @@ def _tls12_verify_SKE(serverKeyExchange, publicKey, clientRandom,
229274
raise TLSIllegalParameterException("Server selected "
230275
"invalid signature "
231276
"algorithm")
277+
if (serverKeyExchange.hashAlg, serverKeyExchange.signAlg) in (
278+
SignatureScheme.ed25519, SignatureScheme.ed448):
279+
return KeyExchange._tls12_verify_eddsa_ske(serverKeyExchange,
280+
publicKey,
281+
clientRandom,
282+
serverRandom,
283+
validSigAlgs)
232284
if serverKeyExchange.signAlg == SignatureAlgorithm.ecdsa:
233285
return KeyExchange._tls12_verify_ecdsa_SKE(serverKeyExchange,
234286
publicKey,
@@ -344,7 +396,8 @@ def calcVerifyBytes(version, handshakeHashes, signatureAlg,
344396
b' CertificateVerify' +
345397
b'\x00') + \
346398
handshakeHashes.digest(prf_name)
347-
verifyBytes = secureHash(verifyBytes, hash_name)
399+
if hash_name != "intrinsic":
400+
verifyBytes = secureHash(verifyBytes, hash_name)
348401
else:
349402
raise ValueError("Unsupported TLS version {0}".format(version))
350403
return verifyBytes

0 commit comments

Comments
 (0)