Skip to content

Commit 4478d4b

Browse files
committed
quic: expose QUIC certificates as JS X509Certificate, not raw handles
Signed-off-by: Tim Perry <pimterry@gmail.com>
1 parent b5da751 commit 4478d4b

3 files changed

Lines changed: 47 additions & 17 deletions

File tree

doc/api/quic.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -833,23 +833,24 @@ what this endpoint advertises to the peer as its own maximum.
833833
added: REPLACEME
834834
-->
835835

836-
* Type: {Object|undefined}
836+
* Type: {crypto.X509Certificate|undefined}
837837

838-
The local certificate as an object with properties such as `subject`,
839-
`issuer`, `valid_from`, `valid_to`, `fingerprint`, etc. Returns `undefined`
840-
if the session is destroyed or no certificate is available.
838+
The local certificate as a [`crypto.X509Certificate`][] instance. Server
839+
sessions return the certificate configured for the negotiated SNI host.
840+
Client sessions return `undefined` unless a client certificate was sent.
841+
Returns `undefined` if the session is destroyed.
841842

842843
### `session.peerCertificate`
843844

844845
<!-- YAML
845846
added: REPLACEME
846847
-->
847848

848-
* Type: {Object|undefined}
849+
* Type: {crypto.X509Certificate|undefined}
849850

850-
The peer's certificate as an object with properties such as `subject`,
851-
`issuer`, `valid_from`, `valid_to`, `fingerprint`, etc. Returns `undefined`
852-
if the session is destroyed or the peer did not present a certificate.
851+
The peer's certificate as a [`crypto.X509Certificate`][] instance. Returns
852+
`undefined` if the peer did not present a certificate or the session is
853+
destroyed.
853854

854855
### `session.ephemeralKeyInfo`
855856

@@ -3499,6 +3500,7 @@ throughput issues caused by flow control.
34993500
[`application.enableConnectProtocol`]: #sessionoptionsapplication
35003501
[`application.enableDatagrams`]: #sessionoptionsapplication
35013502
[`application.qpackMaxDTableCapacity`]: #sessionoptionsapplication
3503+
[`crypto.X509Certificate`]: crypto.md#class-x509certificate
35023504
[`endpoint.maxConnectionsPerHost`]: #endpointmaxconnectionsperhost
35033505
[`endpoint.maxConnectionsTotal`]: #endpointmaxconnectionstotal
35043506
[`error.errorCode`]: #errorerrorcode

lib/internal/quic/quic.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ const {
146146
isKeyObject,
147147
} = require('internal/crypto/keys');
148148

149+
const {
150+
InternalX509Certificate,
151+
} = require('internal/crypto/x509');
152+
149153
const {
150154
FileHandle,
151155
kHandle: kFileHandle,
@@ -2989,24 +2993,37 @@ class QuicSession {
29892993
}
29902994

29912995
/**
2992-
* The local certificate as an object, or undefined if not available.
2993-
* @type {object|undefined}
2996+
* The local certificate as a {@link crypto.X509Certificate}, or undefined
2997+
* if no local certificate is available. Server sessions return their
2998+
* configured certificate; client sessions return undefined unless a
2999+
* client certificate was sent.
3000+
* @type {crypto.X509Certificate|undefined}
29943001
*/
29953002
get certificate() {
29963003
QuicSession.#assertIsQuicSession(this);
29973004
if (this.destroyed) return undefined;
2998-
return this.#certificate ??= this.#handle.getCertificate();
3005+
if (this.#certificate === undefined) {
3006+
const handle = this.#handle.getCertificate();
3007+
this.#certificate = handle ? new InternalX509Certificate(handle) : null;
3008+
}
3009+
return this.#certificate ?? undefined;
29993010
}
30003011

30013012
/**
3002-
* The peer's certificate as an object, or undefined if the peer did
3003-
* not present a certificate or the session is destroyed.
3004-
* @type {object|undefined}
3013+
* The peer's certificate as a {@link crypto.X509Certificate}, or undefined
3014+
* if the peer did not present a certificate or the session is destroyed.
3015+
* @type {crypto.X509Certificate|undefined}
30053016
*/
30063017
get peerCertificate() {
30073018
QuicSession.#assertIsQuicSession(this);
30083019
if (this.destroyed) return undefined;
3009-
return this.#peerCertificate ??= this.#handle.getPeerCertificate();
3020+
if (this.#peerCertificate === undefined) {
3021+
const handle = this.#handle.getPeerCertificate();
3022+
this.#peerCertificate = handle ?
3023+
new InternalX509Certificate(handle) :
3024+
null;
3025+
}
3026+
return this.#peerCertificate ?? undefined;
30103027
}
30113028

30123029
/**

test/parallel/test-quic-session-properties.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
// All three return undefined after destroy.
1313

1414
import { hasQuic, skip, mustCall } from '../common/index.mjs';
15+
import * as fixtures from '../common/fixtures.mjs';
1516
import assert from 'node:assert';
17+
import { X509Certificate } from 'node:crypto';
1618

1719
const { ok, strictEqual } = assert;
1820

21+
// The QUIC test helpers configure both sides with the agent1 fixture cert,
22+
const expectedCert = new X509Certificate(fixtures.readKey('agent1-cert.pem'));
23+
1924
if (!hasQuic) {
2025
skip('QUIC is not enabled');
2126
}
@@ -38,7 +43,10 @@ const serverEndpoint = await listen(mustCall(async (serverSession) => {
3843

3944
// Own certificate.
4045
const cert = serverSession.certificate;
41-
ok(cert);
46+
ok(cert instanceof X509Certificate);
47+
strictEqual(cert.subject, expectedCert.subject);
48+
strictEqual(cert.issuer, expectedCert.issuer);
49+
strictEqual(cert.fingerprint256, expectedCert.fingerprint256);
4250

4351
// Peer certificate (client's cert — not set in this
4452
// test since we don't use verifyClient, so it's undefined).
@@ -65,7 +73,10 @@ strictEqual(clientSession.path, path);
6573

6674
// Peer certificate (server's cert).
6775
const peerCert = clientSession.peerCertificate;
68-
ok(peerCert);
76+
ok(peerCert instanceof X509Certificate);
77+
strictEqual(peerCert.subject, expectedCert.subject);
78+
strictEqual(peerCert.issuer, expectedCert.issuer);
79+
strictEqual(peerCert.fingerprint256, expectedCert.fingerprint256);
6980

7081
// Ephemeral key info (client only).
7182
const keyInfo = clientSession.ephemeralKeyInfo;

0 commit comments

Comments
 (0)