Skip to content

Commit 945f0ff

Browse files
committed
keychain-cli: NDNCERT dns-01 challenge
1 parent f4de0b4 commit 945f0ff

3 files changed

Lines changed: 55 additions & 25 deletions

File tree

pkg/keychain-cli/README.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,9 @@ See `@ndn/ndnsec` package for more information.
198198
* `--dns-domain` specifies domain name to use in the DNS challenge.
199199
* You may specify multiple challenges, and the first one allowed by the server will be used.
200200

201-
### NDNCERT examples
201+
### NDNCERT examples using NDNts CA
202202

203-
CA setup with PIN challenge:
203+
CA setup with PIN & DNS & DNS-01 challenges:
204204

205205
```bash
206206
# generate CA key
@@ -212,9 +212,9 @@ NDNTS_KEYCHAIN=/tmp/ca-keychain ndnts-keychain ndncert03-make-profile --out /tmp
212212
# display CA profile
213213
ndnts-keychain ndncert03-show-profile --profile /tmp/ca.data
214214

215-
# start CA with PIN challenge
215+
# start CA with three challenges
216216
nfd-start
217-
NDNTS_KEYCHAIN=/tmp/ca-keychain ndnts-keychain ndncert03-ca --profile /tmp/ca.data --store /tmp/ca-repo --challenge pin
217+
NDNTS_KEYCHAIN=/tmp/ca-keychain ndnts-keychain ndncert03-ca --profile /tmp/ca.data --store /tmp/ca-repo --challenge pin --challenge dns --challenge dns-01
218218
```
219219

220220
Client using PIN challenge, with NDNts keychain:
@@ -245,6 +245,20 @@ ndnts-keychain ndncert03-client --profile /tmp/ca.data --ndnsec --key $REQKEY --
245245
ndnsec list -c
246246
```
247247

248+
Client using DNS or DNS-01 challenge, with NDNts keychain:
249+
250+
```bash
251+
# generate key pair
252+
REQCERT=$(NDNTS_KEYCHAIN=/tmp/req-keychain ndnts-keychain gen-key /B)
253+
REQKEY=$(echo $REQCERT | gawk 'BEGIN { FS=OFS="/" } { NF-=2; print }')
254+
255+
# request certificate with DNS or DNS-01 challenge
256+
NDNTS_KEYCHAIN=/tmp/req-keychain ndnts-keychain ndncert03-client --profile /tmp/ca.data --key $REQKEY --challenge dns-01 --challenge dns --dns-domain example.net
257+
258+
# view certificates
259+
NDNTS_KEYCHAIN=/tmp/req-keychain ndnts-keychain list-certs
260+
```
261+
248262
Email challenge, NDNts keychain on client side:
249263

250264
```bash
@@ -302,6 +316,8 @@ ndnsec list -c
302316
ndnsec cert-dump -p -i /E
303317
```
304318

319+
### NDNCERT examples for global NDN testbed
320+
305321
Email challenge with Ethereal Email, for global NDN testbed:
306322

307323
```bash
@@ -341,5 +357,5 @@ PROBE_EMAIL=$(openssl rand -hex 8)@ucla.edu
341357

342358
# request certificate from the root CA
343359
ndnts-keychain ndncert03-client --profile /tmp/ndn-root-ca.client.conf --pp email $PROBE_EMAIL \
344-
--challenge dns --dns-domain d.yoursunny.dev
360+
--challenge dns --dns-domain example.net
345361
```

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { exitClosers, openUplinks } from "@ndn/cli-common";
22
import { createVerifier, SigningAlgorithmListFull } from "@ndn/keychain";
3-
import { Server, type ServerChallenge, ServerEmailChallenge, ServerNopChallenge, ServerPinChallenge, ServerPossessionChallenge } from "@ndn/ndncert";
3+
import { Server, type ServerChallenge, ServerDns01Challenge, ServerDnsChallenge, ServerEmailChallenge, ServerNopChallenge, ServerPinChallenge, ServerPossessionChallenge } from "@ndn/ndncert";
44
import type { Verifier } from "@ndn/packet";
55
import { makePersistentDataStore, PrefixRegShorter, RepoProducer } from "@ndn/repo";
66
import { toHex } from "@ndn/util";
@@ -15,7 +15,8 @@ interface Args {
1515
profile: string;
1616
store: string;
1717
challenge: readonly string[];
18-
possessionIssuer?: string;
18+
"possession-issuer"?: string;
19+
"doh-server": string;
1920
}
2021

2122
export const Ndncert03CaCommand: CommandModule<{}, Args> = {
@@ -36,7 +37,7 @@ export const Ndncert03CaCommand: CommandModule<{}, Args> = {
3637
})
3738
.option("challenge", {
3839
array: true,
39-
choices: ["nop", "pin", "email", "possession"],
40+
choices: ["nop", "pin", "email", "possession", "dns", "dns-01"],
4041
demandOption: true,
4142
desc: "supported challenges",
4243
type: "string",
@@ -45,10 +46,15 @@ export const Ndncert03CaCommand: CommandModule<{}, Args> = {
4546
defaultDescription: "CA certificate",
4647
desc: "possession challenge - existing issuer certificate file",
4748
type: "string",
49+
})
50+
.option("doh-server", {
51+
desc: "dns/dns-01 challenge - DNS over HTTPS server",
52+
default: "https://cloudflare-dns.com/dns-query",
53+
type: "string",
4854
});
4955
},
5056

51-
async handler({ profile: profileFile, store, challenge: challengeIds, possessionIssuer }) {
57+
async handler({ profile: profileFile, store, challenge: challengeIds, "possession-issuer": possessionIssuer, "doh-server": dohServer }) {
5258
await openUplinks();
5359

5460
const profile = await inputCaProfile(profileFile, true);
@@ -118,6 +124,14 @@ Otherwise, please disregard this message.`,
118124
challenges.push(new ServerPossessionChallenge(verifier));
119125
break;
120126
}
127+
case "dns": {
128+
challenges.push(new ServerDnsChallenge({ dohServer }));
129+
break;
130+
}
131+
case "dns-01": {
132+
challenges.push(new ServerDns01Challenge({ dohServer }));
133+
break;
134+
}
121135
}
122136
}
123137

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
33
import { openUplinks } from "@ndn/cli-common";
44
import { CertNaming, generateSigningKey, type KeyChain, type NamedSigner, type NamedVerifier } from "@ndn/keychain";
55
import { AltUri } from "@ndn/naming-convention2";
6-
import { type CaProfile, type ClientChallenge, type ClientChallengeContext, ClientDnsChallenge, ClientEmailChallenge, type ClientEmailInboxImap, ClientNopChallenge, ClientPinChallenge, type ClientPinLikeChallenge, ClientPossessionChallenge, matchProbe, requestCertificate, requestProbe } from "@ndn/ndncert";
6+
import { type CaProfile, type ClientChallenge, type ClientChallengeContext, ClientDns01Challenge, ClientDnsChallenge, ClientEmailChallenge, type ClientEmailInboxImap, ClientNopChallenge, ClientPinChallenge, type ClientPinLikeChallenge, ClientPossessionChallenge, matchProbe, requestCertificate, requestProbe } from "@ndn/ndncert";
77
import { NdnsecKeyChain } from "@ndn/ndnsec";
88
import { Name } from "@ndn/packet";
99
import { console, toHex } from "@ndn/util";
@@ -51,7 +51,7 @@ export const Ndncert03ClientCommand: CommandModule<{}, Args> = {
5151
.option("challenge", {
5252
demandOption: true,
5353
array: true,
54-
choices: ["nop", "pin", "email", "possession", "dns"],
54+
choices: ["nop", "pin", "email", "possession", "dns", "dns-01"],
5555
desc: "supported challenges",
5656
type: "string",
5757
})
@@ -71,7 +71,7 @@ export const Ndncert03ClientCommand: CommandModule<{}, Args> = {
7171
type: "string",
7272
})
7373
.option("dns-domain", {
74-
desc: "dns challenge - domain name",
74+
desc: "dns/dns-01 challenge - domain name",
7575
type: "string",
7676
})
7777
.check(({ key }) => {
@@ -96,8 +96,8 @@ export const Ndncert03ClientCommand: CommandModule<{}, Args> = {
9696
return true;
9797
})
9898
.check(({ challenge, "dns-domain": domain }) => {
99-
if (challenge.includes("dns") && !domain?.includes(".")) {
100-
throw new Error("dns challenge enabled but --dns-domain is absent");
99+
if ((challenge.includes("dns") || challenge.includes("dns-01")) && !domain?.includes(".")) {
100+
throw new Error("dns/dns-01 challenge enabled but --dns-domain is absent");
101101
}
102102
return true;
103103
});
@@ -202,6 +202,10 @@ class InteractiveClient {
202202
challenges.push(new ClientDnsChallenge(this.args["dns-domain"]!, this.promptDns()));
203203
break;
204204
}
205+
case "dns-01": {
206+
challenges.push(new ClientDns01Challenge(this.args["dns-domain"]!, this.promptDns()));
207+
break;
208+
}
205209
}
206210
}
207211
return challenges;
@@ -232,17 +236,13 @@ class InteractiveClient {
232236
private promptDns(): ClientDnsChallenge.Prompt {
233237
return async (_context, recordName, expectedValue) => {
234238
console.log(`\nDNS record for certificate request\n${recordName}\tIN\tTXT\n${expectedValue}\n`);
235-
while (true) {
236-
const response = await prompts({
237-
type: "confirm",
238-
name: "ready",
239-
message: "Confirm when DNS record is ready:",
240-
initial: true,
241-
});
242-
if (response.ready) {
243-
return;
244-
}
245-
}
239+
await prompts({
240+
type: "invisible",
241+
name: "ready",
242+
message: "Press ENTER when DNS record is ready:",
243+
}, {
244+
onCancel: () => { throw new Error("challenge aborted"); },
245+
});
246246
};
247247
}
248248
}

0 commit comments

Comments
 (0)