Skip to content

Commit e5367bd

Browse files
committed
ndncert: CA integrates DataStoreBuffer
1 parent 99f8d67 commit e5367bd

6 files changed

Lines changed: 46 additions & 30 deletions

File tree

pkg/keychain-cli/src/ndncert03-ca.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ Otherwise, please disregard this message.`,
121121
}
122122
}
123123

124-
const server = Server.create({
124+
const server = await Server.create({
125125
repo,
126126
profile,
127127
signer,

pkg/ndncert/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@ndn/naming-convention2": "workspace:*",
3232
"@ndn/packet": "workspace:*",
3333
"@ndn/rdr": "workspace:*",
34+
"@ndn/repo": "workspace:*",
3435
"@ndn/tlv": "workspace:*",
3536
"@ndn/util": "workspace:*",
3637
"@types/imap": "^0.8.43",
@@ -48,7 +49,6 @@
4849
"devDependencies": {
4950
"@ndn/fw": "workspace:^",
5051
"@ndn/l3face": "workspace:^",
51-
"@ndn/repo": "workspace:*",
5252
"@types/b64-lite": "^1.4.2",
5353
"@types/koa": "^3.0.3",
5454
"@types/mailparser": "^3.4.6",

pkg/ndncert/src/client/dns-challenge.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import type { ClientChallenge, ClientChallengeContext } from "./challenge";
55
/** The "dns" challenge where client creates a DNS TXT record. */
66
export class ClientDnsChallenge implements ClientChallenge {
77
public readonly challengeId = "dns";
8+
private record = "";
9+
private token = "";
810

911
constructor(
1012
private readonly domain: string,
@@ -16,15 +18,12 @@ export class ClientDnsChallenge implements ClientChallenge {
1618
}
1719

1820
public async next(context: ClientChallengeContext): Promise<ParameterKV> {
19-
if (context.challengeStatus !== "need-record") {
20-
throw new Error(`bad challenge-status ${context.challengeStatus}`);
21+
if (context.challengeStatus === "need-record") {
22+
this.record = ParameterKV.getString(context.parameters, "record-name");
23+
this.token = ParameterKV.getString(context.parameters, "expected-value");
2124
}
2225

23-
await this.prompt(
24-
context,
25-
ParameterKV.getString(context.parameters, "record-name"),
26-
ParameterKV.getString(context.parameters, "expected-value"),
27-
);
26+
await this.prompt(context, this.record, this.token);
2827
return ParameterKV.from({ confirmation: "ready" });
2928
}
3029
}

pkg/ndncert/src/client/request.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function requestCertificate({
4949
}: ClientOptions): Promise<Certificate> {
5050
cOpts = {
5151
describe: `NDNCERT-client(${profile.prefix}, REQUEST, ${privateKey.name})`,
52-
retx: 4, // XXX retransmission may cause server rejection for duplicate SigNonce
52+
retx: 4,
5353
...cOpts,
5454
verifier: profile.publicKey,
5555
};

pkg/ndncert/src/server/server.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { produce, type Producer, type ProducerHandler, type ProducerOptions } from "@ndn/endpoint";
1+
import { DataStoreBuffer, produce, type Producer, type ProducerHandler, type ProducerOptions } from "@ndn/endpoint";
22
import { Certificate, CertNaming, type NamedVerifier } from "@ndn/keychain";
33
import { Component, type ComponentLike, type Data, type FwHint, type Signer, type ValidityPeriod } from "@ndn/packet";
44
import { Metadata, serveMetadata } from "@ndn/rdr";
5+
import { makeInMemoryDataStore } from "@ndn/repo";
56
import { KeyMap, toHex } from "@ndn/util";
67

78
import * as ndncert_crypto from "../crypto-common";
@@ -14,6 +15,8 @@ export interface ServerOptions {
1415
*
1516
* @remarks
1617
* - `.describe` defaults to "NDNCERT-CA" + CA prefix.
18+
* - `.dataBuffer` defaults to an internal `DataStoreBuffer` (required for proper operations).
19+
* - `.autoBuffer` is overridden to true (required for proper operations).
1720
* - `.announcement` is overridden as CA prefix + "/CA".
1821
*/
1922
pOpts?: ProducerOptions;
@@ -49,7 +52,7 @@ interface RepoDataStore {
4952

5053
/** NDNCERT server. */
5154
export class Server {
52-
public static create({
55+
public static async create({
5356
pOpts,
5457
repo,
5558
repoFwHint,
@@ -58,10 +61,12 @@ export class Server {
5861
probe,
5962
challenges,
6063
issuerId = "NDNts-NDNCERT",
61-
}: ServerOptions): Server {
64+
}: ServerOptions): Promise<Server> {
6265
return new Server(
6366
{
67+
dataBuffer: new DataStoreBuffer(await makeInMemoryDataStore()),
6468
...pOpts,
69+
autoBuffer: true,
6570
announcement: profile.prefix.append(C.CA),
6671
},
6772
repo,

pkg/ndncert/tests/server-client.t.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface Row {
2323
nChallengeInterest: number;
2424
lostChallengeData?: readonly number[];
2525
clientError?: unknown;
26+
serverCleared?: boolean;
2627
}
2728

2829
function makePinChallengeWithWrongInputs(nWrongInputs = 0): Row["makeChallengeLists"] {
@@ -130,14 +131,16 @@ const TABLE: Row[] = [
130131
nChallengeInterest: 2,
131132
},
132133
{
133-
summary: "pin, success with 1 Data loss",
134+
summary: "pin, success after 1 Data loss",
134135
makeChallengeLists: makePinChallengeWithWrongInputs(0),
135136
nChallengeInterest: 3,
136137
lostChallengeData: [1],
137-
clientError: "3: BadSignature",
138-
// XXX This test case should have succeeded but is presently an "expected failure", caused by:
139-
// During the CHALLENGE step, I2's SigNonce is saved by server but D2 is lost in network.
140-
// When client retransmits I2, the server would detect a duplicate SigNonce and reject the Interest.
138+
},
139+
{
140+
summary: "pin, success after 1 wrong input and 1 Data loss",
141+
makeChallengeLists: makePinChallengeWithWrongInputs(1),
142+
nChallengeInterest: 4,
143+
lostChallengeData: [2],
141144
},
142145
{
143146
summary: "pin, success after 2 wrong inputs",
@@ -219,7 +222,10 @@ const TABLE: Row[] = [
219222
{
220223
summary: "email, with IMAP",
221224
async makeChallengeLists() {
222-
const debugEnabled = process.env.NDNCERT_IMAP_DEBUG === "1";
225+
if (process.env.NDNTS_IMAP_DEBUG === "2") {
226+
throw new Error("IMAP test skipped via environ");
227+
}
228+
const debugEnabled = process.env.NDNTS_IMAP_DEBUG === "1";
223229
const { user, pass, smtp, imap, web } = await createEmailAccount();
224230
if (debugEnabled) {
225231
console.log("nodemail test account:", web, user, pass);
@@ -377,8 +383,8 @@ const TABLE: Row[] = [
377383
})],
378384
];
379385
},
380-
nChallengeInterest: 2,
381-
clientError: "wrong-record",
386+
nChallengeInterest: 4,
387+
clientError: "7: OutOfTries",
382388
},
383389
{
384390
summary: "server challenge not acceptable on client",
@@ -390,6 +396,7 @@ const TABLE: Row[] = [
390396
},
391397
nChallengeInterest: 0,
392398
clientError: "no acceptable challenge",
399+
serverCleared: false,
393400
},
394401
];
395402

@@ -454,13 +461,13 @@ beforeEach(async () => {
454461
};
455462
});
456463

457-
function startServer(serverOpts: Partial<ServerOptions> = {}, bridgeOpts: Bridge.CreateOptions & { bridge?: Bridge } = {}) {
464+
async function startServer(serverOpts: Partial<ServerOptions> = {}, bridgeOpts: Bridge.CreateOptions & { bridge?: Bridge } = {}) {
458465
const bridge = bridgeOpts.bridge ?? Bridge.create({
459466
fwA: Forwarder.getDefault(),
460467
routesAB: ["/fh", "/authority/CA", "/sub/CA"],
461468
...bridgeOpts,
462469
});
463-
const server = Server.create({
470+
const server = await Server.create({
464471
pOpts: { fw: bridge.fwB },
465472
profile: caProfile,
466473
repo,
@@ -480,7 +487,7 @@ function checkCaProfile(retrieved: CaProfile, expected: CaProfile, stringContain
480487
}
481488

482489
test("INFO command", async () => {
483-
startServer();
490+
await startServer();
484491

485492
const retrieved = await retrieveCaProfile({
486493
caPrefix: new Name("/authority"),
@@ -497,7 +504,7 @@ test("INFO command", async () => {
497504
});
498505

499506
test("unsupported or malformed commands", async () => {
500-
startServer();
507+
await startServer();
501508

502509
const [probeErr, newErr, challengeErr] = await Promise.all([
503510
consume("/authority/CA/PROBE"),
@@ -510,7 +517,7 @@ test("unsupported or malformed commands", async () => {
510517
});
511518

512519
test("probe no result", async () => {
513-
startServer({ async probe() { return {}; } });
520+
await startServer({ async probe() { return {}; } });
514521

515522
await expect(requestProbe({
516523
profile: caProfile,
@@ -519,7 +526,7 @@ test("probe no result", async () => {
519526
});
520527

521528
test("probe mismatch", async () => {
522-
startServer({
529+
await startServer({
523530
async probe() { expect.fail("unexpected server probe"); },
524531
});
525532

@@ -531,7 +538,7 @@ test("probe mismatch", async () => {
531538

532539
test("probe entries and redirects", async () => {
533540
const subCertFullName = await subCert.data.computeFullName();
534-
const { bridge } = startServer({
541+
const { bridge } = await startServer({
535542
async probe(parameters: ParameterKV) {
536543
expect(parameters.uid).toEqualUint8Array(toUtf8("my-uid"));
537544
return {
@@ -565,7 +572,7 @@ test("probe entries and redirects", async () => {
565572
},
566573
caCertFullName: redirects[0]!.caCertFullName,
567574
})).rejects.toThrow();
568-
startServer({
575+
await startServer({
569576
profile: subProfile,
570577
signer: subSigner,
571578
}, { bridge });
@@ -581,11 +588,12 @@ test.each(TABLE)("challenge $summary", { timeout: 15000 }, async ({
581588
nChallengeInterest,
582589
lostChallengeData,
583590
clientError = false,
591+
serverCleared = true,
584592
}) => {
585593
const [serverChallenges, clientChallenges] = await makeChallengeLists();
586594
const challengePrefix = new Name("/authority/CA/CHALLENGE");
587595
let nSentChallengeInterests = 0;
588-
startServer({ challenges: serverChallenges }, {
596+
const { server } = await startServer({ challenges: serverChallenges }, {
589597
async *relayAB(it) {
590598
for await (const wire of it) {
591599
const { interest } = Bridge.RelayFunc.extract(wire);
@@ -620,4 +628,8 @@ test.each(TABLE)("challenge $summary", { timeout: 15000 }, async ({
620628
await expect(reqPromise).rejects.toThrow(clientError);
621629
}
622630
expect(nSentChallengeInterests).toBe(nChallengeInterest);
631+
632+
if (serverCleared) {
633+
expect((server as any).state.size).toBe(0);
634+
}
623635
});

0 commit comments

Comments
 (0)