Skip to content

Commit 7a195ee

Browse files
committed
quic-transport: serverCertificateHashes browser test
1 parent 70dbb13 commit 7a195ee

10 files changed

Lines changed: 87 additions & 25 deletions

File tree

integ/browser-tests/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ docker run -it --rm \
2323
--cap-add SYS_ADMIN \
2424
--user $(id -u):$(id -g) \
2525
--workdir $(pwd)/integ/browser-tests \
26-
satantime/puppeteer-node:24-slim bash
26+
satantime/puppeteer-node:24-trixie-slim bash
2727
```
2828

2929
Type `corepack pnpm test` to run the tests.

integ/browser-tests/tests/transport/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ import type { TestRecord } from "@ndn/l3face/test-fixture/transport";
33
declare global {
44
function connectWsTransportPair(uri: string): Promise<void>;
55
function testWsTransportPair(): Promise<TestRecord>;
6+
function connectH3TransportPair(uri: string, certHash: string): Promise<void>;
7+
function testH3TransportPair(): Promise<TestRecord>;
68
}

integ/browser-tests/tests/transport/browser.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ globalThis.connectWsTransportPair = async (uri) => {
2121

2222
globalThis.testWsTransportPair = async () => testTransport(...wsPair);
2323

24+
let h3Pair: [H3Transport, H3Transport];
25+
26+
globalThis.connectH3TransportPair = async (uri, certHash) => {
27+
const serverCertificateHashes = [{ algorithm: "sha-256", value: fromHex(certHash) }];
28+
h3Pair = await Promise.all([
29+
H3Transport.connect(uri, { serverCertificateHashes }),
30+
H3Transport.connect(uri, { serverCertificateHashes }),
31+
]);
32+
};
33+
34+
globalThis.testH3TransportPair = async () => testTransport(...h3Pair);
35+
2436
async function facePing(pingPrefix: string) {
2537
const names: string[] = [];
2638
const rtts: number[] = [];
Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
11
import "./api";
22

33
import * as TestTransport from "@ndn/l3face/test-fixture/transport";
4-
import { Closers } from "@ndn/util";
4+
import { bridgeSessions, WtServer } from "@ndn/quic-transport/test-fixture/wt-server";
5+
import { Closers, toHex } from "@ndn/util";
56
import { bridgeWebSockets, WsServer } from "@ndn/ws-transport/test-fixture/ws-server";
67
import { beforeEach, test } from "vitest";
78

89
import { navigateToPage, pageInvoke } from "../../test-fixture/pptr";
910

1011
const closers = new Closers();
11-
let server: WsServer;
1212
beforeEach(async () => {
13-
server = await WsServer.create();
14-
closers.push(server);
1513
await navigateToPage(import.meta);
1614
return closers.close;
1715
});
1816

19-
test("pair", async () => {
17+
test("WebSocket pair", async () => {
18+
const server = await WsServer.create();
19+
closers.push(server);
20+
2021
await pageInvoke<typeof globalThis.connectWsTransportPair>("connectWsTransportPair", server.uri);
2122
const sockets = await server.waitNClients(2);
2223
bridgeWebSockets(sockets);
2324

2425
const result = await pageInvoke<typeof globalThis.testWsTransportPair>("testWsTransportPair");
2526
TestTransport.check(result);
2627
});
28+
29+
test("HTTP/3 pair", async () => {
30+
const server = await WtServer.create();
31+
closers.push(server);
32+
33+
await pageInvoke<typeof globalThis.connectH3TransportPair>("connectH3TransportPair", server.uri, toHex(server.certHash));
34+
const sessions = await server.waitNClients(2);
35+
bridgeSessions(sessions);
36+
37+
const result = await pageInvoke<typeof globalThis.testH3TransportPair>("testH3TransportPair");
38+
TestTransport.check(result);
39+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"test": "vitest",
1414
"typedoc": "bash mk/typedoc.sh"
1515
},
16-
"packageManager": "pnpm@11.19.0+sha512.7881f3ed590d472c4a955e2b88b2121791116066dcc88cbca3849ec9b60f1bbaa6d2ccb221fa91da4e1c65bef2bcbe379365aea7ac539c7bf86dedc3a1b22dce",
16+
"packageManager": "pnpm@11.20.0+sha512.9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee",
1717
"devDependencies": {
1818
"@types/node": "26.1.2",
1919
"@typescript/native": "npm:typescript@^7.0.2",

pkg/quic-transport/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"tslib": "^2.8.1"
3333
},
3434
"devDependencies": {
35-
"@ndn/node-transport": "workspace:*"
35+
"@ndn/node-transport": "workspace:*",
36+
"selfsigned": "^5.5.0"
3637
}
3738
}

pkg/quic-transport/src/wt_node.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { WebTransport as bunWebTransport } from "@webtransport-bun/webtransport";
1+
import { WebTransport as bunWebTransport, type WebTransportClientOptions } from "@webtransport-bun/webtransport";
22

33
import type { H3Transport } from "./h3-transport";
44

55
export const supported = true;
66

77
export function makeWebTransport(uri: string, opts: WebTransportOptions): WebTransport {
8-
const { insecureSkipVerify = false } = opts as H3Transport.Options;
9-
void opts;
8+
const { insecureSkipVerify = false, serverCertificateHashes } = opts as H3Transport.Options;
109
return new bunWebTransport(uri, {
1110
strictW3CErrors: true,
11+
serverCertificateHashes: serverCertificateHashes as WebTransportClientOptions["serverCertificateHashes"],
1212
tls: { insecureSkipVerify },
1313
}) as unknown as WebTransport;
1414
}

pkg/quic-transport/test-fixture/wt-server.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,61 @@
11
import { TestServer } from "@ndn/node-transport/test-fixture/net-server";
2-
import { assert } from "@ndn/util";
3-
import { type ClientSession, createServer, type ServerSession, type WebTransportServer } from "@webtransport-bun/webtransport";
2+
import { assert, sha256 } from "@ndn/util";
3+
import { type ClientSession, createServer, type ServerSession, type ServerTlsSniEntry, type WebTransportServer } from "@webtransport-bun/webtransport";
4+
import selfsigned from "selfsigned";
5+
6+
export async function makeSelfSigned(): Promise<ServerTlsSniEntry & { certHash: Uint8Array<ArrayBuffer> }> {
7+
const serverName = "127.0.0.1";
8+
const notBeforeDate = new Date();
9+
const notAfterDate = new Date(notBeforeDate.getTime() + 86400_000);
10+
const generated = await selfsigned.generate([
11+
{ name: "commonName", value: serverName },
12+
], {
13+
notBeforeDate,
14+
notAfterDate,
15+
keyType: "ec",
16+
curve: "P-256",
17+
algorithm: "sha256",
18+
extensions: [
19+
{ name: "subjectAltName", altNames: [{ type: 7, ip: serverName }] },
20+
],
21+
});
22+
23+
const certDer = Buffer.from(generated.cert.replaceAll(/(-----(BEGIN|END) CERTIFICATE-----)|\s/g, ""), "base64");
24+
return {
25+
serverName,
26+
certPem: generated.cert,
27+
keyPem: generated.private,
28+
certHash: await sha256(certDer),
29+
};
30+
}
431

532
/** WebTransport test server. */
633
export class WtServer extends TestServer<WebTransportServer, ServerSession> {
734
public static async create(): Promise<WtServer> {
835
let self!: WtServer; // eslint-disable-line prefer-const
936

37+
const { certHash, ...tls } = await makeSelfSigned();
1038
const port = 16384 + Math.trunc(Math.random() * 16384);
1139
const server = createServer({
1240
port,
13-
tls: { certPem: "", keyPem: "", allowSelfSigned: true },
41+
tls,
1442
onSession: (sess) => self.onSession(sess),
1543
});
1644

17-
self = new WtServer(server, port);
45+
self = new WtServer(server, `https://127.0.0.1:${port}`, certHash);
1846
return self;
1947
}
2048

21-
/** WebTransport server URI. */
22-
public readonly uri: string;
23-
24-
constructor(server: WebTransportServer, port: number) {
49+
/**
50+
* @param port - WebTransport server URI.
51+
* @param certHash - WebTransport server certificate hash.
52+
*/
53+
constructor(server: WebTransportServer, public readonly uri: string, public readonly certHash: Uint8Array<ArrayBuffer>) {
2554
super(server);
26-
this.uri = `https://127.0.0.1:${port}`;
55+
}
56+
57+
public get serverCertificateHashes(): WebTransportHash[] {
58+
return [{ algorithm: "sha-256", value: this.certHash }];
2759
}
2860

2961
private waitNewSession?: () => void;

pkg/quic-transport/tests/h3-transport.t.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ beforeEach(async () => {
1515
});
1616

1717
test("pair", async () => {
18+
const { uri } = server;
1819
const [tA, tB, sockets] = await Promise.all([
19-
H3Transport.connect(server.uri, { insecureSkipVerify: true }),
20-
H3Transport.connect(server.uri, { insecureSkipVerify: true }),
20+
H3Transport.connect(uri, { insecureSkipVerify: true }),
21+
H3Transport.connect(uri, { insecureSkipVerify: true }),
2122
server.waitNClients(2),
2223
]);
2324

24-
expect(tA.toString()).toBe(`H3(${server.uri})`);
25+
expect(tA.toString()).toBe(`H3(${uri})`);
2526

2627
bridgeSessions(sockets);
2728
TestTransport.check(await TestTransport.execute(tA, tB));
@@ -34,7 +35,8 @@ test("connect error", async () => {
3435
});
3536

3637
test("reopen", async () => {
37-
const transport = await H3Transport.connect(server.uri, { insecureSkipVerify: true });
38+
const { uri } = server;
39+
const transport = await H3Transport.connect(uri, { insecureSkipVerify: true });
3840
await TestReopen.run(
3941
transport,
4042
server.waitNClients,

pkg/util/src/crypto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {
77
}
88

99
/** Compute SHA256 digest. */
10-
export async function sha256(input: Uint8Array): Promise<Uint8Array> {
10+
export async function sha256(input: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {
1111
const digest = await crypto.subtle.digest("SHA-256", asBufferSource(input));
1212
return new Uint8Array(digest);
1313
}

0 commit comments

Comments
 (0)