Skip to content

Commit 8a80c05

Browse files
committed
feat: Implement RFC 9345 Delegated Credentials
tests: test creation of DC, signing with different keys and checking connection.
1 parent 6805bcb commit 8a80c05

File tree

14 files changed

+1150
-206
lines changed

14 files changed

+1150
-206
lines changed

scripts/tls.py

Lines changed: 277 additions & 18 deletions
Large diffs are not rendered by default.

tests/serverX509DCKey.pem

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCT/Pf92sZZd5nP
3+
5WUs+ilhN2GxgSx1ylXyARar2MGWfYA/b0eMPsfVf+bLpQX/Da2drrq7L1HxTaId
4+
NUYsh/nvPLxROkA4AmECahFZSCkJZ6VdVgO2cMu7SNE9L7vZXIo22aL+COBqZ1xn
5+
vjMUyVXnrs02fMfcn6c/FIO0bd2YkRUiOP2iBAhCn6GmHZeJ0/OMHB7VJ0Ozpy63
6+
xsONxtIBgPEimQFlTCgmMmBjDzyquTIqVZ0ajQnHNKM4BaBcsu788sttPCh3i7EQ
7+
4sAI8Pw8tmPxs/nb2fMNgQEd+wjvIHTss82hzu9XUTDt9CAOWHyRCUi5D9a6dMS4
8+
oscRpQiPAgMBAAECggEAEy4gPiiSuJnFt6o1mMS7hDwXT1g8mO+mf/0gIRmwzX5q
9+
ls4nacfhQoyXLyGuS0ZMkDlLPmN9rVawgjSbab4d6KHojmaMWDYGuLdilD3EA9IJ
10+
HrW9OXIZFab0Z4e+Qwe5ai5+74na/C91TITcPf9yQNrpAfzeMnwGwyg3gbUTmWhR
11+
2AriFcTdWINmZKNKbOS0Fkw/Kn7zPAidjSanvthNdQPNi1k1fob0rj05Q57kEe4d
12+
lOC5j79mYV3XUWlyf6g8knmoBsQ1Y0gfFyro1R3VtAJSlU8KPJ41bhlE5kpiUZYb
13+
cWRCJYF3anRxTxo8getRLffE19en0i+z99tZIzgwoQKBgQDOgU5/2DK6ussAm9kv
14+
6IPOuwr9gEaYQAMmgG08ck2CCW7J/MGAGXURtZOMf5zSsMBvHXT/xvAWBuElilbF
15+
9/DNHz51Ts2GvyqPSzSFHxQUo3ljV6n/qIXP+7Xb3xwzjGVzXl2CfPeI0hxVZmz+
16+
W6jcvczqyOXxjH+WZFmgLnzJSQKBgQC3dTFQYSWJpXb9gVNSWLRbN6ZVgoEjMbOJ
17+
N8Pzj+3hcKYeQJUdH1dxbGdjFUrCrkHVWEaXZ7eH5B/fWd+uTBUgwnO0FExjpkAm
18+
EsakhP96vdN/jtax0S7S1Jcn6Ty6YdBAzXT9JsguxAUvVE4uSpdrdxpJF/4F6key
19+
WdoXE9JbFwKBgG52qvgmPVS3sPm9ZFuFRGSkl0dtg9XTgBvrXQOVnTJvO01fIF8W
20+
vxHfEHN6m/f0RqvplPlxgGI4Ad3j93DkpXIEQZPcuIJY5jpKn2iKbGJx4/ApJ62z
21+
hwjve6OG4H4OnwIsu1ae5IbS5gckyC7z9wtFmEULfD1Oy702Jt9RnrzJAoGAbtug
22+
SwQJHN4hwxpM8SutAJnmJzHPOycjaD2MaTeF9X6OwyUfdhOkUWPCLbuGC5IlMfg/
23+
3+nKm5EcOWkjoz1SXxNhu2Wwq16g0ODzrCK6Br+CeEgmMBlJhBj2piVoju/gWehN
24+
U1QGD0xgHbOB8rMcQNIdziFzXLuvS3TENsHBkU0CgYBg4mTavRdbMwDUN07aRpt8
25+
+EaJJnmxWRl0wD9yIvFVZa0wPRYd54/HOMj5dqHHklgn1eb+HwxdyEYw/kUMn+oC
26+
roPHTA4hYXDMqAHkXMC6sDmG5MVgP6ozWEYVbiK/5y6QsTozLciNpwA0STGHmtcC
27+
kQpskxTgLWPCkzB6RvWqzw==
28+
-----END PRIVATE KEY-----

tests/serverX509DCPub.pem

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk/z3/drGWXeZz+VlLPop
3+
YTdhsYEsdcpV8gEWq9jBln2AP29HjD7H1X/my6UF/w2tna66uy9R8U2iHTVGLIf5
4+
7zy8UTpAOAJhAmoRWUgpCWelXVYDtnDLu0jRPS+72VyKNtmi/gjgamdcZ74zFMlV
5+
567NNnzH3J+nPxSDtG3dmJEVIjj9ogQIQp+hph2XidPzjBwe1SdDs6cut8bDjcbS
6+
AYDxIpkBZUwoJjJgYw88qrkyKlWdGo0JxzSjOAWgXLLu/PLLbTwod4uxEOLACPD8
7+
PLZj8bP529nzDYEBHfsI7yB07LPNoc7vV1Ew7fQgDlh8kQlIuQ/WunTEuKLHEaUI
8+
jwIDAQAB
9+
-----END PUBLIC KEY-----

tests/tlstest.py

Lines changed: 198 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,39 @@
1919
import socket
2020
import time
2121
import timeit
22-
import getopt
22+
import hashlib
2323
from tempfile import mkstemp
24+
25+
from tlslite.x509 import DelegatedCredential, Credential
2426
try:
2527
from BaseHTTPServer import HTTPServer
2628
from SimpleHTTPServer import SimpleHTTPRequestHandler
2729
except ImportError:
2830
from http.server import HTTPServer, SimpleHTTPRequestHandler
2931

3032
from tlslite import TLSConnection, Fault, HandshakeSettings, \
31-
X509, X509CertChain, IMAP4_TLS, VerifierDB, Session, SessionCache, \
33+
X509, X509CertChain, IMAP4_TLS, VerifierDB, SessionCache, \
3234
parsePEMKey, constants, \
3335
AlertDescription, HTTPTLSConnection, TLSSocketServerMixIn, \
3436
POP3_TLS, m2cryptoLoaded, pycryptoLoaded, gmpyLoaded, tackpyLoaded, \
3537
Checker, __version__
36-
from tlslite.handshakesettings import VirtualHost, Keypair
38+
from tlslite.handshakesettings import VirtualHost, Keypair, DC_VALID_TIME
3739

3840
from tlslite.errors import *
39-
from tlslite.utils.cryptomath import prngName, getRandomBytes
41+
from tlslite.utils.cryptomath import prngName, getRandomBytes, \
42+
numberToByteArray
4043
try:
4144
import xmlrpclib
4245
except ImportError:
4346
# Python 3
4447
from xmlrpc import client as xmlrpclib
4548
import ssl
4649
from tlslite import *
47-
from tlslite.constants import KeyUpdateMessageType, ECPointFormat, SignatureScheme
50+
from tlslite.constants import TLS_1_3_BRAINPOOL_SIG_SCHEMES, \
51+
HashAlgorithm, KeyUpdateMessageType, ECPointFormat, \
52+
SignatureAlgorithm, SignatureScheme
53+
from tlslite.utils.pem import dePem
54+
from tlslite.utils.codec import Parser
4855

4956
try:
5057
from tack.structures.Tack import Tack
@@ -1910,6 +1917,84 @@ def heartbeat_response_check(message):
19101917

19111918
test_no += 1
19121919

1920+
print("Test {0} - Delegated Credential test: RSA cert".format(test_no))
1921+
synchro.recv(1)
1922+
connection = connect()
1923+
settings = HandshakeSettings()
1924+
settings.maxVersion = (3, 4)
1925+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
1926+
connection.handshakeClientCert(settings=settings)
1927+
assert connection.session.delegated_credential is not None
1928+
assert isinstance(connection.session.delegated_credential,
1929+
DelegatedCredential)
1930+
assert connection.session.delegated_credential.algorithm == SignatureScheme.rsa_pss_pss_sha256
1931+
testConnClient(connection)
1932+
connection.close()
1933+
1934+
test_no += 1
1935+
1936+
print("Test {0} - Delegated Credential test: ECDSA cert".format(test_no))
1937+
synchro.recv(1)
1938+
connection = connect()
1939+
settings = HandshakeSettings()
1940+
settings.maxVersion = (3, 4)
1941+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
1942+
connection.handshakeClientCert(settings=settings)
1943+
assert connection.session.delegated_credential is not None
1944+
assert isinstance(connection.session.delegated_credential,
1945+
DelegatedCredential)
1946+
assert connection.session.delegated_credential.algorithm == SignatureScheme.ecdsa_secp256r1_sha256
1947+
testConnClient(connection)
1948+
connection.close()
1949+
1950+
test_no += 1
1951+
1952+
print("Test {0} - Delegated Credential test: Ed2551 cert".format(test_no))
1953+
synchro.recv(1)
1954+
connection = connect()
1955+
settings = HandshakeSettings()
1956+
settings.maxVersion = (3, 4)
1957+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
1958+
connection.handshakeClientCert(settings=settings)
1959+
assert connection.session.delegated_credential is not None
1960+
assert isinstance(connection.session.delegated_credential,
1961+
DelegatedCredential)
1962+
assert connection.session.delegated_credential.algorithm == SignatureScheme.ed25519
1963+
testConnClient(connection)
1964+
connection.close()
1965+
1966+
test_no += 1
1967+
1968+
print("Test {0} - Delegated Credential test: brainpoolP256r1tls13 cert".format(test_no))
1969+
synchro.recv(1)
1970+
connection = connect()
1971+
settings = HandshakeSettings()
1972+
settings.maxVersion = (3, 4)
1973+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
1974+
connection.handshakeClientCert(settings=settings)
1975+
assert connection.session.delegated_credential is not None
1976+
assert isinstance(connection.session.delegated_credential,
1977+
DelegatedCredential)
1978+
assert connection.session.delegated_credential.algorithm == SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256
1979+
testConnClient(connection)
1980+
connection.close()
1981+
1982+
test_no += 1
1983+
1984+
print("Test {0} - good X.509 TLSv1.3, no DC on client side)".format(test_no))
1985+
synchro.recv(1)
1986+
settings = HandshakeSettings()
1987+
settings.certificate_compression_receive = []
1988+
settings.certificate_compression_send = []
1989+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
1990+
connection = connect()
1991+
connection.handshakeClientCert(serverName=address[0],
1992+
settings=settings)
1993+
testConnClient(connection)
1994+
assert connection.server_cert_compression_algo is None
1995+
assert connection.client_cert_compression_algo is None
1996+
connection.close()
1997+
19131998
print('Test {0} - good standard XMLRPC https client'.format(test_no))
19141999
address = address[0], address[1]+1
19152000
synchro.recv(1)
@@ -1967,6 +2052,8 @@ def heartbeat_response_check(message):
19672052
print("Non-critical error: socket error trying to reach internet "
19682053
"server: ", e)
19692054

2055+
2056+
19702057
synchro.close()
19712058

19722059
if not badFault:
@@ -2119,6 +2206,12 @@ def connect():
21192206
with open(os.path.join(dir, "serverEd448Key.pem")) as f:
21202207
x509Ed448Key = parsePEMKey(f.read(), private=True,
21212208
implementations=["python"])
2209+
with open(os.path.join(dir, "serverX509DCKey.pem")) as f:
2210+
x509DCKey = parsePEMKey(f.read(), private=True,
2211+
implementations=["python"])
2212+
2213+
with open(os.path.join(dir, "serverX509DCPub.pem")) as f:
2214+
X509DCPub = dePem(f.read(), "PUBLIC KEY")
21222215

21232216
test_no = 0
21242217

@@ -3655,6 +3748,106 @@ def heartbeat_response_check(message):
36553748

36563749
test_no +=1
36573750

3751+
print("Test {0}-{1} - Delegated Credential test".format(test_no, test_no + 3))
3752+
cert_alg = [(x509Chain, x509Key, SignatureScheme.rsa_pss_pss_sha256),
3753+
(x509ecdsaChain, x509ecdsaKey, SignatureScheme.ecdsa_secp256r1_sha256),
3754+
(x509Ed25519Chain, x509Ed25519Key, SignatureScheme.ed25519),
3755+
(x509ecdsaBrainpoolP256r1Chain,
3756+
x509ecdsaBrainpoolP256r1Key,
3757+
SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256)
3758+
]
3759+
for value in cert_alg:
3760+
synchro.send(b'R')
3761+
connection = connect()
3762+
3763+
cert_chain, private_key, sig_alg = value
3764+
scheme = SignatureScheme.toRepr(sig_alg)
3765+
dc_sig_alg = SignatureScheme.rsa_pss_pss_sha256
3766+
3767+
cert_bytes = cert_chain.x509List[0].bytes
3768+
valid_time = int(time.time()) + DC_VALID_TIME
3769+
cred_bytes = bytearray(numberToByteArray(valid_time) +
3770+
numberToByteArray(dc_sig_alg[0]) +
3771+
numberToByteArray(dc_sig_alg[1]) +
3772+
X509DCPub)
3773+
cred = Credential(valid_time=valid_time,
3774+
dc_cert_verify_algorithm=dc_sig_alg,
3775+
subject_public_key_info=X509DCPub,
3776+
bytes=cred_bytes)
3777+
3778+
bytes_to_sign = DelegatedCredential.compute_certificate_dc_sig_context(
3779+
cert_bytes,
3780+
cred_bytes,
3781+
sig_alg)
3782+
3783+
if sig_alg in (SignatureScheme.ed25519,
3784+
SignatureScheme.ed448):
3785+
hashName = "intrinsic"
3786+
padType = None
3787+
saltLen = None
3788+
sig_func = private_key.hashAndSign
3789+
ver_func = private_key.hashAndVerify
3790+
elif sig_alg[1] == SignatureAlgorithm.ecdsa:
3791+
hashName = HashAlgorithm.toRepr(sig_alg[0])
3792+
padType = None
3793+
saltLen = None
3794+
sig_func = private_key.hashAndSign
3795+
ver_func = private_key.hashAndVerify
3796+
elif sig_alg in TLS_1_3_BRAINPOOL_SIG_SCHEMES:
3797+
hashName = SignatureScheme.getHash(scheme)
3798+
padType = None
3799+
saltLen = None
3800+
sig_func = private_key.hashAndSign
3801+
ver_func = private_key.hashAndVerify
3802+
else:
3803+
padType = SignatureScheme.getPadding(scheme)
3804+
hashName = SignatureScheme.getHash(scheme)
3805+
saltLen = getattr(hashlib, hashName)().digest_size
3806+
sig_func = private_key.hashAndSign
3807+
ver_func = private_key.hashAndVerify
3808+
3809+
signature = sig_func(bytes_to_sign,
3810+
padType,
3811+
hashName,
3812+
saltLen)
3813+
if not ver_func(signature, bytes_to_sign,
3814+
padType,
3815+
hashName,
3816+
saltLen):
3817+
raise ValueError("Delegated Credential signature failed")
3818+
3819+
3820+
delegated_credential = DelegatedCredential(cred=cred,
3821+
algorithm=sig_alg,
3822+
signature=signature)
3823+
3824+
settings = HandshakeSettings()
3825+
settings.maxVersion = (3, 4)
3826+
settings.dc_sig_algs = [SignatureScheme.rsa_pss_pss_sha256]
3827+
connection.handshakeServer(certChain=cert_chain,
3828+
privateKey=None,
3829+
dc_key=x509DCKey,
3830+
del_cred=delegated_credential,
3831+
settings=settings)
3832+
assert connection.session.delegated_credential is not None
3833+
assert isinstance(connection.session.delegated_credential,
3834+
DelegatedCredential)
3835+
testConnServer(connection)
3836+
connection.close()
3837+
3838+
test_no += 4
3839+
3840+
print("Test {0} - good X.509 TLSv1.3 (no DC on client side)".format(test_no))
3841+
synchro.send(b'R')
3842+
connection = connect()
3843+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key)
3844+
assert connection.server_cert_compression_algo is None
3845+
assert connection.client_cert_compression_algo is None
3846+
testConnServer(connection)
3847+
connection.close()
3848+
3849+
test_no += 1
3850+
36583851
print("Tests {0}-{1} - XMLRPXC server".format(test_no, test_no + 2))
36593852

36603853
address = address[0], address[1]+1

tlslite/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class ExtensionType(TLSEnum):
181181
post_handshake_auth = 49 # TLS 1.3
182182
signature_algorithms_cert = 50 # TLS 1.3
183183
key_share = 51 # TLS 1.3
184+
delegated_credential = 34 # TLS 1.3, RFC 9345
184185
supports_npn = 13172
185186
tack = 0xF300
186187
renegotiation_info = 0xff01 # RFC 5746

0 commit comments

Comments
 (0)