Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions doc/api/quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -833,23 +833,24 @@ what this endpoint advertises to the peer as its own maximum.
added: REPLACEME
-->

* Type: {Object|undefined}
* Type: {crypto.X509Certificate|undefined}

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

### `session.peerCertificate`

<!-- YAML
added: REPLACEME
-->

* Type: {Object|undefined}
* Type: {crypto.X509Certificate|undefined}

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

### `session.ephemeralKeyInfo`

Expand Down Expand Up @@ -3499,6 +3500,7 @@ throughput issues caused by flow control.
[`application.enableConnectProtocol`]: #sessionoptionsapplication
[`application.enableDatagrams`]: #sessionoptionsapplication
[`application.qpackMaxDTableCapacity`]: #sessionoptionsapplication
[`crypto.X509Certificate`]: crypto.md#class-x509certificate
[`endpoint.maxConnectionsPerHost`]: #endpointmaxconnectionsperhost
[`endpoint.maxConnectionsTotal`]: #endpointmaxconnectionstotal
[`error.errorCode`]: #errorerrorcode
Expand Down
31 changes: 24 additions & 7 deletions lib/internal/quic/quic.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ const {
isKeyObject,
} = require('internal/crypto/keys');

const {
InternalX509Certificate,
} = require('internal/crypto/x509');

const {
FileHandle,
kHandle: kFileHandle,
Expand Down Expand Up @@ -2989,24 +2993,37 @@ class QuicSession {
}

/**
* The local certificate as an object, or undefined if not available.
* @type {object|undefined}
* The local certificate as a {@link crypto.X509Certificate}, or undefined
* if no local certificate is available. Server sessions return their
* configured certificate; client sessions return undefined unless a
* client certificate was sent.
* @type {crypto.X509Certificate|undefined}
*/
get certificate() {
QuicSession.#assertIsQuicSession(this);
if (this.destroyed) return undefined;
return this.#certificate ??= this.#handle.getCertificate();
if (this.#certificate === undefined) {
const handle = this.#handle.getCertificate();
this.#certificate = handle ? new InternalX509Certificate(handle) : null;
}
return this.#certificate ?? undefined;
}

/**
* The peer's certificate as an object, or undefined if the peer did
* not present a certificate or the session is destroyed.
* @type {object|undefined}
* The peer's certificate as a {@link crypto.X509Certificate}, or undefined
* if the peer did not present a certificate or the session is destroyed.
* @type {crypto.X509Certificate|undefined}
*/
get peerCertificate() {
QuicSession.#assertIsQuicSession(this);
if (this.destroyed) return undefined;
return this.#peerCertificate ??= this.#handle.getPeerCertificate();
if (this.#peerCertificate === undefined) {
const handle = this.#handle.getPeerCertificate();
this.#peerCertificate = handle ?
new InternalX509Certificate(handle) :
null;
}
return this.#peerCertificate ?? undefined;
}

/**
Expand Down
15 changes: 13 additions & 2 deletions test/parallel/test-quic-session-properties.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
// All three return undefined after destroy.

import { hasQuic, skip, mustCall } from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import assert from 'node:assert';
import { X509Certificate } from 'node:crypto';

const { ok, strictEqual } = assert;

// The QUIC test helpers configure both sides with the agent1 fixture cert,
const expectedCert = new X509Certificate(fixtures.readKey('agent1-cert.pem'));

if (!hasQuic) {
skip('QUIC is not enabled');
}
Expand All @@ -38,7 +43,10 @@ const serverEndpoint = await listen(mustCall(async (serverSession) => {

// Own certificate.
const cert = serverSession.certificate;
ok(cert);
ok(cert instanceof X509Certificate);
strictEqual(cert.subject, expectedCert.subject);
strictEqual(cert.issuer, expectedCert.issuer);
strictEqual(cert.fingerprint256, expectedCert.fingerprint256);

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

// Peer certificate (server's cert).
const peerCert = clientSession.peerCertificate;
ok(peerCert);
ok(peerCert instanceof X509Certificate);
strictEqual(peerCert.subject, expectedCert.subject);
strictEqual(peerCert.issuer, expectedCert.issuer);
strictEqual(peerCert.fingerprint256, expectedCert.fingerprint256);

// Ephemeral key info (client only).
const keyInfo = clientSession.ephemeralKeyInfo;
Expand Down
Loading