Skip to content

Commit 4be8abe

Browse files
pimterryaduh95
authored andcommitted
quic: expose QUIC certificates as JS X509Certificate, not raw handles
Signed-off-by: Tim Perry <pimterry@gmail.com> PR-URL: #63191 Backport-PR-URL: #64675 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
1 parent e264c5c commit 4be8abe

3 files changed

Lines changed: 52 additions & 18 deletions

File tree

doc/api/quic.md

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

1407-
* Type: {Object|undefined}
1407+
* Type: {crypto.X509Certificate|undefined}
14081408

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

14131414
### `session.peerCertificate`
14141415

14151416
<!-- YAML
14161417
added: REPLACEME
14171418
-->
14181419

1419-
* Type: {Object|undefined}
1420+
* Type: {crypto.X509Certificate|undefined}
14201421

1421-
The peer's certificate as an object with properties such as `subject`,
1422-
`issuer`, `valid_from`, `valid_to`, `fingerprint`, etc. Returns `undefined`
1423-
if the session is destroyed or the peer did not present a certificate.
1422+
The peer's certificate as a [`crypto.X509Certificate`][] instance. Returns
1423+
`undefined` if the peer did not present a certificate or the session is
1424+
destroyed.
14241425

14251426
### `session.ephemeralKeyInfo`
14261427

@@ -4422,6 +4423,7 @@ throughput issues caused by flow control.
44224423
[`application.enableConnectProtocol`]: #sessionoptionsapplication
44234424
[`application.enableDatagrams`]: #sessionoptionsapplication
44244425
[`application.qpackMaxDTableCapacity`]: #sessionoptionsapplication
4426+
[`crypto.X509Certificate`]: crypto.md#class-x509certificate
44254427
[`endpoint.busy`]: #endpointbusy
44264428
[`endpoint.maxConnectionsPerHost`]: #endpointmaxconnectionsperhost
44274429
[`endpoint.maxConnectionsTotal`]: #endpointmaxconnectionstotal

lib/internal/quic/quic.js

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

155+
const {
156+
InternalX509Certificate,
157+
} = require('internal/crypto/x509');
158+
155159
const {
156160
FileHandle,
157161
kHandle: kFileHandle,
@@ -3180,24 +3184,37 @@ class QuicSession {
31803184
}
31813185

31823186
/**
3183-
* The local certificate as an object, or undefined if not available.
3184-
* @type {object|undefined}
3187+
* The local certificate as a {@link crypto.X509Certificate}, or undefined
3188+
* if no local certificate is available. Server sessions return their
3189+
* configured certificate; client sessions return undefined unless a
3190+
* client certificate was sent.
3191+
* @type {crypto.X509Certificate|undefined}
31853192
*/
31863193
get certificate() {
31873194
assertIsQuicSession(this);
31883195
if (this.destroyed) return undefined;
3189-
return this.#inner.certificate ??= this.#handle.getCertificate();
3196+
if (this.#inner.certificate === undefined) {
3197+
const handle = this.#handle.getCertificate();
3198+
this.#inner.certificate = handle ? new InternalX509Certificate(handle) : null;
3199+
}
3200+
return this.#inner.certificate ?? undefined;
31903201
}
31913202

31923203
/**
3193-
* The peer's certificate as an object, or undefined if the peer did
3194-
* not present a certificate or the session is destroyed.
3195-
* @type {object|undefined}
3204+
* The peer's certificate as a {@link crypto.X509Certificate}, or undefined
3205+
* if the peer did not present a certificate or the session is destroyed.
3206+
* @type {crypto.X509Certificate|undefined}
31963207
*/
31973208
get peerCertificate() {
31983209
assertIsQuicSession(this);
31993210
if (this.destroyed) return undefined;
3200-
return this.#inner.peerCertificate ??= this.#handle.getPeerCertificate();
3211+
if (this.#inner.peerCertificate === undefined) {
3212+
const handle = this.#handle.getPeerCertificate();
3213+
this.#inner.peerCertificate = handle ?
3214+
new InternalX509Certificate(handle) :
3215+
null;
3216+
}
3217+
return this.#inner.peerCertificate ?? undefined;
32013218
}
32023219

32033220
/**

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,20 @@
1111
// All three cached.
1212
// All three return undefined after destroy.
1313

14-
import { hasQuic, skip, mustCall } from '../common/index.mjs';
14+
import { hasQuic, skip, mustCall, hasCrypto } from '../common/index.mjs';
15+
import * as fixtures from '../common/fixtures.mjs';
1516
import assert from 'node:assert';
1617

1718
const { ok, strictEqual } = assert;
1819

20+
if (!hasCrypto)
21+
skip('missing crypto');
22+
23+
const { X509Certificate } = await import('node:crypto');
24+
25+
// The QUIC test helpers configure both sides with the agent1 fixture cert,
26+
const expectedCert = new X509Certificate(fixtures.readKey('agent1-cert.pem'));
27+
1928
if (!hasQuic) {
2029
skip('QUIC is not enabled');
2130
}
@@ -38,7 +47,10 @@ const serverEndpoint = await listen(mustCall(async (serverSession) => {
3847

3948
// Own certificate.
4049
const cert = serverSession.certificate;
41-
ok(cert);
50+
ok(cert instanceof X509Certificate);
51+
strictEqual(cert.subject, expectedCert.subject);
52+
strictEqual(cert.issuer, expectedCert.issuer);
53+
strictEqual(cert.fingerprint256, expectedCert.fingerprint256);
4254

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

6678
// Peer certificate (server's cert).
6779
const peerCert = clientSession.peerCertificate;
68-
ok(peerCert);
80+
ok(peerCert instanceof X509Certificate);
81+
strictEqual(peerCert.subject, expectedCert.subject);
82+
strictEqual(peerCert.issuer, expectedCert.issuer);
83+
strictEqual(peerCert.fingerprint256, expectedCert.fingerprint256);
6984

7085
// Ephemeral key info (client only).
7186
const keyInfo = clientSession.ephemeralKeyInfo;

0 commit comments

Comments
 (0)