mirror of
https://github.com/vacp2p/nim-libp2p.git
synced 2026-01-09 02:38:19 -05:00
chore(quic): add tests with invalid certs (#1297)
This commit is contained in:
@@ -146,12 +146,16 @@ method close*(m: QuicMuxer) {.async: (raises: []).} =
|
||||
# Transport
|
||||
type QuicUpgrade = ref object of Upgrade
|
||||
|
||||
type CertGenerator =
|
||||
proc(kp: KeyPair): CertificateX509 {.gcsafe, raises: [TLSCertificateError].}
|
||||
|
||||
type QuicTransport* = ref object of Transport
|
||||
listener: Listener
|
||||
client: QuicClient
|
||||
privateKey: PrivateKey
|
||||
connections: seq[P2PConnection]
|
||||
rng: ref HmacDrbgContext
|
||||
certGenerator: CertGenerator
|
||||
|
||||
proc makeCertificateVerifier(): CertificateVerifier =
|
||||
proc certificateVerifier(certificatesDer: seq[seq[byte]]): bool =
|
||||
@@ -171,8 +175,29 @@ proc makeCertificateVerifier(): CertificateVerifier =
|
||||
|
||||
return CustomCertificateVerifier.init(certificateVerifier)
|
||||
|
||||
func new*(_: type QuicTransport, u: Upgrade, privateKey: PrivateKey): QuicTransport =
|
||||
return QuicTransport(upgrader: QuicUpgrade(ms: u.ms), privateKey: privateKey)
|
||||
proc defaultCertGenerator(
|
||||
kp: KeyPair
|
||||
): CertificateX509 {.gcsafe, raises: [TLSCertificateError].} =
|
||||
return generateX509(kp, encodingFormat = EncodingFormat.PEM)
|
||||
|
||||
proc new*(_: type QuicTransport, u: Upgrade, privateKey: PrivateKey): QuicTransport =
|
||||
return QuicTransport(
|
||||
upgrader: QuicUpgrade(ms: u.ms),
|
||||
privateKey: privateKey,
|
||||
certGenerator: defaultCertGenerator,
|
||||
)
|
||||
|
||||
proc new*(
|
||||
_: type QuicTransport,
|
||||
u: Upgrade,
|
||||
privateKey: PrivateKey,
|
||||
certGenerator: CertGenerator,
|
||||
): QuicTransport =
|
||||
return QuicTransport(
|
||||
upgrader: QuicUpgrade(ms: u.ms),
|
||||
privateKey: privateKey,
|
||||
certGenerator: certGenerator,
|
||||
)
|
||||
|
||||
method handles*(transport: QuicTransport, address: MultiAddress): bool {.raises: [].} =
|
||||
if not procCall Transport(transport).handles(address):
|
||||
@@ -189,14 +214,13 @@ method start*(
|
||||
doAssert false, "could not obtain public key"
|
||||
return
|
||||
|
||||
let keypair = KeyPair(seckey: self.privateKey, pubkey: pubkey)
|
||||
let certTuple = generate(keypair, encodingFormat = EncodingFormat.PEM)
|
||||
|
||||
try:
|
||||
if self.rng.isNil:
|
||||
self.rng = newRng()
|
||||
|
||||
let cert = self.certGenerator(KeyPair(seckey: self.privateKey, pubkey: pubkey))
|
||||
let tlsConfig = TLSConfig.init(
|
||||
certTuple[0], certTuple[1], @[alpn], Opt.some(makeCertificateVerifier())
|
||||
cert.certificate, cert.privateKey, @[alpn], Opt.some(makeCertificateVerifier())
|
||||
)
|
||||
self.client = QuicClient.init(tlsConfig, rng = self.rng)
|
||||
self.listener =
|
||||
@@ -207,6 +231,8 @@ method start*(
|
||||
MultiAddress.init("/quic-v1").get()
|
||||
except QuicConfigError as exc:
|
||||
doAssert false, "invalid quic setup: " & $exc.msg
|
||||
except TLSCertificateError as exc:
|
||||
raise (ref QuicTransportError)(msg: exc.msg, parent: exc)
|
||||
except QuicError as exc:
|
||||
raise (ref QuicTransportError)(msg: exc.msg, parent: exc)
|
||||
except TransportOsError as exc:
|
||||
|
||||
@@ -43,6 +43,11 @@ type
|
||||
validFrom: Time
|
||||
validTo: Time
|
||||
|
||||
CertificateX509* = object
|
||||
certificate*: seq[byte]
|
||||
# Complete ASN.1 DER content (certificate, signature algorithm and signature).
|
||||
privateKey*: seq[byte] # Private key used to sign certificate
|
||||
|
||||
type EncodingFormat* = enum
|
||||
DER
|
||||
PEM
|
||||
@@ -169,12 +174,12 @@ proc makeExtValues(
|
||||
|
||||
return (signature.toCertBuffer(), pubKeyBytes.toCertBuffer())
|
||||
|
||||
proc generate*(
|
||||
proc generateX509*(
|
||||
identityKeyPair: KeyPair,
|
||||
validFrom: Time = fromUnix(157813200),
|
||||
validTo: Time = fromUnix(67090165200),
|
||||
encodingFormat: EncodingFormat = EncodingFormat.DER,
|
||||
): tuple[raw: seq[byte], privateKey: seq[byte]] {.
|
||||
): CertificateX509 {.
|
||||
raises: [
|
||||
KeyGenerationError, IdentitySigningError, IdentityPubKeySerializationError,
|
||||
CertificateCreationError, CertificatePubKeySerializationError,
|
||||
@@ -230,8 +235,7 @@ proc generate*(
|
||||
cert_free_buffer(certificate)
|
||||
cert_free_buffer(privKDer)
|
||||
|
||||
# Return the Serialized Certificate and Private Key
|
||||
return (outputCertificate, outputPrivateKey)
|
||||
return CertificateX509(certificate: outputCertificate, privateKey: outputPrivateKey)
|
||||
|
||||
proc parseCertTime*(certTime: string): Time {.raises: [TimeParseError].} =
|
||||
var timeNoZone = certTime[0 ..^ 5] # removes GMT part
|
||||
|
||||
@@ -6,35 +6,70 @@ import
|
||||
stream/connection,
|
||||
transports/transport,
|
||||
transports/quictransport,
|
||||
transports/tls/certificate,
|
||||
upgrademngrs/upgrade,
|
||||
multiaddress,
|
||||
errors,
|
||||
wire,
|
||||
]
|
||||
|
||||
import ./helpers, ./commontransport
|
||||
|
||||
proc createServerAcceptConn(
|
||||
server: QuicTransport
|
||||
): proc(): Future[void] {.
|
||||
async: (raises: [transport.TransportError, LPStreamError, CancelledError])
|
||||
.} =
|
||||
proc handler() {.
|
||||
async: (raises: [transport.TransportError, LPStreamError, CancelledError])
|
||||
.} =
|
||||
let conn = await server.accept()
|
||||
let stream = await getStream(QuicSession(conn), Direction.In)
|
||||
var resp: array[6, byte]
|
||||
await stream.readExactly(addr resp, 6)
|
||||
check string.fromBytes(resp) == "client"
|
||||
|
||||
await stream.write("server")
|
||||
await stream.close()
|
||||
await server.stop()
|
||||
|
||||
return handler
|
||||
|
||||
proc invalidCertGenerator(
|
||||
kp: KeyPair
|
||||
): CertificateX509 {.gcsafe, raises: [TLSCertificateError].} =
|
||||
try:
|
||||
let keyNew = PrivateKey.random(ECDSA, (newRng())[]).get()
|
||||
let pubkey = keyNew.getPublicKey().get()
|
||||
# invalidKp has pubkey that does not match seckey
|
||||
let invalidKp = KeyPair(seckey: kp.seckey, pubkey: pubkey)
|
||||
return generateX509(invalidKp, encodingFormat = EncodingFormat.PEM)
|
||||
except ResultError[crypto.CryptoError]:
|
||||
raiseAssert "private key should be set"
|
||||
|
||||
proc createTransport(withInvalidCert: bool = false): Future[QuicTransport] {.async.} =
|
||||
let ma = @[MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()]
|
||||
let privateKey = PrivateKey.random(ECDSA, (newRng())[]).tryGet()
|
||||
let trans =
|
||||
if withInvalidCert:
|
||||
QuicTransport.new(Upgrade(), privateKey, invalidCertGenerator)
|
||||
else:
|
||||
QuicTransport.new(Upgrade(), privateKey)
|
||||
await trans.start(ma)
|
||||
|
||||
return trans
|
||||
|
||||
suite "Quic transport":
|
||||
asyncTest "can handle local address":
|
||||
let ma = @[MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()]
|
||||
let privateKey = PrivateKey.random(ECDSA, (newRng())[]).tryGet()
|
||||
let transport1 = QuicTransport.new(Upgrade(), privateKey)
|
||||
await transport1.start(ma)
|
||||
check transport1.handles(transport1.addrs[0])
|
||||
await transport1.stop()
|
||||
#
|
||||
let trans = await createTransport()
|
||||
check trans.handles(trans.addrs[0])
|
||||
await trans.stop()
|
||||
|
||||
asyncTest "transport e2e":
|
||||
let serverMA = @[MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()]
|
||||
let clientMA = @[MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()]
|
||||
let privateKey = PrivateKey.random(ECDSA, (newRng())[]).tryGet()
|
||||
let server: QuicTransport = QuicTransport.new(Upgrade(), privateKey)
|
||||
await server.start(serverMA)
|
||||
let server = await createTransport()
|
||||
asyncSpawn createServerAcceptConn(server)()
|
||||
|
||||
proc runClient() {.async.} =
|
||||
let rng = newRng()
|
||||
let privateKey = PrivateKey.random(ECDSA, (rng)[]).tryGet()
|
||||
let client: QuicTransport = QuicTransport.new(Upgrade(), privateKey)
|
||||
await client.start(clientMA)
|
||||
let client = await createTransport()
|
||||
let conn = await client.dial("", server.addrs[0])
|
||||
let stream = await getStream(QuicSession(conn), Direction.Out)
|
||||
await stream.write("client")
|
||||
@@ -44,16 +79,31 @@ suite "Quic transport":
|
||||
check string.fromBytes(resp) == "server"
|
||||
await client.stop()
|
||||
|
||||
proc serverAcceptHandler() {.async.} =
|
||||
let conn = await server.accept()
|
||||
let stream = await getStream(QuicSession(conn), Direction.In)
|
||||
var resp: array[6, byte]
|
||||
await stream.readExactly(addr resp, 6)
|
||||
check string.fromBytes(resp) == "client"
|
||||
|
||||
await stream.write("server")
|
||||
await stream.close()
|
||||
await server.stop()
|
||||
|
||||
asyncSpawn serverAcceptHandler()
|
||||
await runClient()
|
||||
|
||||
asyncTest "transport e2e - invalid cert - server":
|
||||
let server = await createTransport(true)
|
||||
asyncSpawn createServerAcceptConn(server)()
|
||||
|
||||
proc runClient() {.async.} =
|
||||
let client = await createTransport()
|
||||
try:
|
||||
discard await client.dial("", server.addrs[0])
|
||||
check false # conn should be refused by client
|
||||
except QuicTransportDialError:
|
||||
discard
|
||||
await client.stop()
|
||||
|
||||
await runClient()
|
||||
|
||||
asyncTest "transport e2e - invalid cert - client":
|
||||
let server = await createTransport()
|
||||
asyncSpawn createServerAcceptConn(server)()
|
||||
|
||||
proc runClient() {.async.} =
|
||||
let client = await createTransport(true)
|
||||
let connFut = client.dial("", server.addrs[0])
|
||||
# conn should be refused by server
|
||||
check not await connFut.withTimeout(1.seconds)
|
||||
|
||||
await runClient()
|
||||
|
||||
@@ -13,8 +13,8 @@ suite "Certificate roundtrip tests":
|
||||
let keypair = KeyPair.random(scheme, rng[]).tryGet()
|
||||
let peerId = PeerId.init(keypair.pubkey).tryGet()
|
||||
|
||||
let certTuple = generate(keypair)
|
||||
let cert = parse(certTuple.raw)
|
||||
let certX509 = generateX509(keypair, encodingFormat = EncodingFormat.DER)
|
||||
let cert = parse(certX509.certificate)
|
||||
|
||||
check peerId == cert.peerId()
|
||||
check cert.publicKey().scheme == scheme
|
||||
@@ -27,22 +27,22 @@ suite "Certificate roundtrip tests":
|
||||
# past
|
||||
var validFrom = (now() - 3.days).toTime()
|
||||
var validTo = (now() - 1.days).toTime()
|
||||
var certTuple = generate(keypair, validFrom, validTo)
|
||||
var cert = parse(certTuple.raw)
|
||||
var certX509 = generateX509(keypair, validFrom, validTo)
|
||||
var cert = parse(certX509.certificate)
|
||||
check not cert.verify()
|
||||
|
||||
# future
|
||||
validFrom = (now() + 1.days).toTime()
|
||||
validTo = (now() + 3.days).toTime()
|
||||
certTuple = generate(keypair, validFrom, validTo)
|
||||
cert = parse(certTuple.raw)
|
||||
certX509 = generateX509(keypair, validFrom, validTo)
|
||||
cert = parse(certX509.certificate)
|
||||
check not cert.verify()
|
||||
|
||||
# twisted from-to
|
||||
validFrom = (now() + 3.days).toTime()
|
||||
validTo = (now() - 3.days).toTime()
|
||||
certTuple = generate(keypair, validFrom, validTo)
|
||||
cert = parse(certTuple.raw)
|
||||
certX509 = generateX509(keypair, validFrom, validTo)
|
||||
cert = parse(certX509.certificate)
|
||||
check not cert.verify()
|
||||
|
||||
## Test vectors are equivalents to https://github.com/libp2p/specs/blob/master/tls/tls.md#test-vectors.
|
||||
|
||||
Reference in New Issue
Block a user