66# Apache License, version 2.0, (LICENSE-APACHEv2)
77# MIT license (LICENSE-MIT)
88import unittest2
9- import bearssl/ [x509]
9+ import bearssl/ [ssl, x509]
1010import stew/ byteutils
1111import " .." / chronos/ unittest2/ asynctests
1212import " .." / chronos/ streams/ [tlsstream, chunkstream, boundstream]
@@ -16,6 +16,11 @@ import ".."/chronos/streams/[tlsstream, chunkstream, boundstream]
1616# To create self-signed certificate and key you can use openssl
1717# openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes \
1818# -keyout example-com.key.pem -days 3650 -out example-com.cert.pem
19+ #
20+ # To create EC (P-256) self-signed certificate and key:
21+ # openssl ecparam -genkey -name prime256v1 | \
22+ # openssl pkcs8 -topk8 -nocrypt -outform PEM > ec.key.pem
23+ # openssl req -new -x509 -sha256 -key ec.key.pem -days 3650 -out ec.cert.pem
1924
2025const SelfSignedRsaKey = """
2126-----BEGIN PRIVATE KEY-----
@@ -74,7 +79,35 @@ N8r5CwGcIX/XPC3lKazzbZ8baA==
7479-----END CERTIFICATE-----
7580"""
7681
82+ # This SSL EC certificate will expire 31 March 2036.
83+ const SelfSignedEcKey = """
84+ -----BEGIN PRIVATE KEY-----
85+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgC9VcD8axNnS+wzKg
86+ Ng62Lb3LmOiLcUHt7yOS7pY/isyhRANCAARqirbb0fF2B7hLtXUNWFhE49OtAb9X
87+ QrDOboBNumfzsE/kxgaQ3/T7EQAaaXMOVLRFtOKdLBOEsx51wZJZEICU
88+ -----END PRIVATE KEY-----
89+ """
90+
91+ const SelfSignedEcCert = """
92+ -----BEGIN CERTIFICATE-----
93+ MIICEzCCAbmgAwIBAgIUHYNpklhNSMzLR3zBV4jDbwCX/NgwCgYIKoZIzj0EAwIw
94+ XzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
95+ dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQzODA4
96+ MB4XDTI2MDQwMzA0NTc1MVoXDTM2MDMzMTA0NTc1MVowXzELMAkGA1UEBhMCQVUx
97+ EzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg
98+ UHR5IEx0ZDEYMBYGA1UEAwwPMTI3LjAuMC4xOjQzODA4MFkwEwYHKoZIzj0CAQYI
99+ KoZIzj0DAQcDQgAEaoq229Hxdge4S7V1DVhYROPTrQG/V0Kwzm6ATbpn87BP5MYG
100+ kN/0+xEAGmlzDlS0RbTinSwThLMedcGSWRCAlKNTMFEwHQYDVR0OBBYEFOy8CFwI
101+ iU3eg8xtAMrHJFmX59bcMB8GA1UdIwQYMBaAFOy8CFwIiU3eg8xtAMrHJFmX59bc
102+ MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAPJKbWOf7MWgfCky
103+ vf85DkBfIBMo2PM8WrgJYYSRkMmqAiBOcPebl59ZX11s4OhNa9BETOuCIE6kVtNm
104+ GaLrNaHO6A==
105+ -----END CERTIFICATE-----
106+ """
107+
77108let SelfSignedTrustAnchors {.importc : " SelfSignedTAs" .}: array [1 , X509TrustAnchor ]
109+ let SelfSignedEcTrustAnchors {.importc : " SelfSignedEcTAs" .}:
110+ array [1 , X509TrustAnchor ]
78111{.compile : " testasyncstream.c" .}
79112
80113proc createBigMessage (message: string , size: int ): seq [byte ] =
@@ -944,6 +977,124 @@ suite "AsyncStream/TLSStream":
944977 let res = waitFor checkTrustAnchors (" Some message" )
945978 check res == " Some message\r\n "
946979
980+ test " Client certificate authentication" :
981+ proc checkClientCert (testMessage: string ): Future [string ] {.async .} =
982+ var key = TLSPrivateKey .init (SelfSignedRsaKey )
983+ var cert = TLSCertificate .init (SelfSignedRsaCert )
984+
985+ proc serveClient (server: StreamServer ,
986+ transp: StreamTransport ) {.async : (raises: []).} =
987+ try :
988+ var reader = newAsyncStreamReader (transp)
989+ var writer = newAsyncStreamWriter (transp)
990+ var sstream = newTLSServerAsyncStream (reader, writer, key, cert,
991+ flags = {TLSFlags .NoRenegotiation })
992+ # Configure client certificate authentication via BearSSL API.
993+ # serverX509 must be stored on TLSAsyncStream to ensure it
994+ # outlives the TLS session (BearSSL holds a pointer to it).
995+ x509MinimalInitFull (sstream.x509,
996+ unsafeAddr SelfSignedTrustAnchors [0 ],
997+ uint (len (SelfSignedTrustAnchors )))
998+ sslEngineSetDefaultRsavrfy (sstream.scontext.eng)
999+ sslEngineSetDefaultEcdsa (sstream.scontext.eng)
1000+ sslServerSetTrustAnchorNamesAlt (sstream.scontext,
1001+ unsafeAddr SelfSignedTrustAnchors [0 ],
1002+ uint (len (SelfSignedTrustAnchors )))
1003+ sslEngineSetX509 (sstream.scontext.eng,
1004+ X509ClassPointerConst (addr sstream.x509.vtable))
1005+ discard sslServerReset (sstream.scontext)
1006+ await handshake (sstream)
1007+ await sstream.writer.write (testMessage & " \r\n " )
1008+ await sstream.writer.finish ()
1009+ await sstream.writer.closeWait ()
1010+ await sstream.reader.closeWait ()
1011+ await reader.closeWait ()
1012+ await writer.closeWait ()
1013+ await transp.closeWait ()
1014+ server.stop ()
1015+ server.close ()
1016+ except CatchableError as exc:
1017+ raiseAssert exc.msg
1018+
1019+ var server = createStreamServer (initTAddress (" 127.0.0.1:0" ),
1020+ serveClient, {ReuseAddr })
1021+ server.start ()
1022+ var conn = await connect (server.localAddress ())
1023+ var creader = newAsyncStreamReader (conn)
1024+ var cwriter = newAsyncStreamWriter (conn)
1025+ let flags = {NoVerifyHost , NoVerifyServerName }
1026+ var cstream = newTLSClientAsyncStream (creader, cwriter, " " ,
1027+ flags = flags, certificate = cert, privateKey = key)
1028+ let res = await cstream.reader.read ()
1029+ await cstream.reader.closeWait ()
1030+ await cstream.writer.closeWait ()
1031+ await creader.closeWait ()
1032+ await cwriter.closeWait ()
1033+ await conn.closeWait ()
1034+ await server.join ()
1035+ return string .fromBytes (res)
1036+ let res = waitFor checkClientCert (" Client cert test" )
1037+ check res == " Client cert test\r\n "
1038+
1039+ test " Client certificate authentication (EC)" :
1040+ proc checkClientCertEc (testMessage: string ): Future [string ] {.async .} =
1041+ var key = TLSPrivateKey .init (SelfSignedEcKey )
1042+ var cert = TLSCertificate .init (SelfSignedEcCert )
1043+
1044+ proc serveClient (server: StreamServer ,
1045+ transp: StreamTransport ) {.async : (raises: []).} =
1046+ try :
1047+ var reader = newAsyncStreamReader (transp)
1048+ var writer = newAsyncStreamWriter (transp)
1049+ var sstream = newTLSServerAsyncStream (reader, writer, key, cert,
1050+ flags = {TLSFlags .NoRenegotiation })
1051+ # Configure client certificate authentication via BearSSL API.
1052+ # serverX509 must be stored on TLSAsyncStream to ensure it
1053+ # outlives the TLS session (BearSSL holds a pointer to it).
1054+ x509MinimalInitFull (sstream.x509,
1055+ unsafeAddr SelfSignedEcTrustAnchors [0 ],
1056+ uint (len (SelfSignedEcTrustAnchors )))
1057+ sslEngineSetDefaultRsavrfy (sstream.scontext.eng)
1058+ sslEngineSetDefaultEcdsa (sstream.scontext.eng)
1059+ sslServerSetTrustAnchorNamesAlt (sstream.scontext,
1060+ unsafeAddr SelfSignedEcTrustAnchors [0 ],
1061+ uint (len (SelfSignedEcTrustAnchors )))
1062+ sslEngineSetX509 (sstream.scontext.eng,
1063+ X509ClassPointerConst (addr sstream.x509.vtable))
1064+ discard sslServerReset (sstream.scontext)
1065+ await handshake (sstream)
1066+ await sstream.writer.write (testMessage & " \r\n " )
1067+ await sstream.writer.finish ()
1068+ await sstream.writer.closeWait ()
1069+ await sstream.reader.closeWait ()
1070+ await reader.closeWait ()
1071+ await writer.closeWait ()
1072+ await transp.closeWait ()
1073+ server.stop ()
1074+ server.close ()
1075+ except CatchableError as exc:
1076+ raiseAssert exc.msg
1077+
1078+ var server = createStreamServer (initTAddress (" 127.0.0.1:0" ),
1079+ serveClient, {ReuseAddr })
1080+ server.start ()
1081+ var conn = await connect (server.localAddress ())
1082+ var creader = newAsyncStreamReader (conn)
1083+ var cwriter = newAsyncStreamWriter (conn)
1084+ let flags = {NoVerifyHost , NoVerifyServerName }
1085+ var cstream = newTLSClientAsyncStream (creader, cwriter, " " ,
1086+ flags = flags, certificate = cert, privateKey = key)
1087+ let res = await cstream.reader.read ()
1088+ await cstream.reader.closeWait ()
1089+ await cstream.writer.closeWait ()
1090+ await creader.closeWait ()
1091+ await cwriter.closeWait ()
1092+ await conn.closeWait ()
1093+ await server.join ()
1094+ return string .fromBytes (res)
1095+ let res = waitFor checkClientCertEc (" EC client cert test" )
1096+ check res == " EC client cert test\r\n "
1097+
9471098suite " AsyncStream/BoundedStream" :
9481099 teardown:
9491100 checkLeaks ()
0 commit comments