@interncom/diplomatic 0.13.6 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.mjs +2 -2
- package/dist/cli/shared/crypto/enclave.d.ts +28 -10
- package/dist/cli/shared/seed.d.ts +1 -1
- package/dist/cli/shared/webauthn/common.d.ts +11 -1
- package/dist/cli/shared/webauthn/prf.d.ts +16 -5
- package/dist/web/index.d.ts +5 -5
- package/dist/web/index.mjs +1 -1
- package/dist/web/passkey/prf-store.d.ts +65 -29
- package/dist/web/shared/crypto/enclave.d.ts +28 -10
- package/dist/web/shared/seed.d.ts +1 -1
- package/dist/web/shared/webauthn/common.d.ts +11 -1
- package/dist/web/shared/webauthn/prf.d.ts +16 -5
- package/dist/web/stores/idb/seed.d.ts +10 -12
- package/dist/web/types.d.ts +2 -2
- package/dist/web/worker.mjs +1 -1
- package/package.json +1 -1
|
@@ -2,58 +2,94 @@ import { Enclave, type PasskeyPrfOpts } from "../shared/crypto/enclave";
|
|
|
2
2
|
import { type SealedMasterKey } from "../shared/seed";
|
|
3
3
|
import { type ValStat } from "../shared/valstat";
|
|
4
4
|
import type { ISeedStore, SetSeedOpts } from "../types";
|
|
5
|
-
import type
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { type PrfRp } from "../shared/webauthn/prf";
|
|
6
|
+
import { type AuthenticatorAttachmentName, type EnrolledOs } from "../shared/webauthn/common";
|
|
7
|
+
/** Max bindings on one device. Keeps allowCredentials small. */
|
|
8
|
+
export declare const KEYRING_MAX = 8;
|
|
9
|
+
export type KeyringType = "prf";
|
|
10
|
+
/** One PRF binding (one binding key). Future types (shard, …) share this list. */
|
|
11
|
+
export type KeyringEntry = {
|
|
12
|
+
type: "prf";
|
|
8
13
|
sealedMaster: SealedMasterKey;
|
|
9
|
-
|
|
14
|
+
credId: Uint8Array;
|
|
15
|
+
/** blake3(master ‖ bindtag ‖ credId); check-only, not a display fingerprint. */
|
|
16
|
+
tag: Uint8Array;
|
|
17
|
+
userId?: Uint8Array;
|
|
18
|
+
nick?: string;
|
|
19
|
+
enrolledAt: number;
|
|
20
|
+
lastUsedAt?: number;
|
|
21
|
+
attachment?: AuthenticatorAttachmentName;
|
|
22
|
+
transports?: string[];
|
|
23
|
+
aaguid?: Uint8Array;
|
|
24
|
+
enrolledOs?: EnrolledOs;
|
|
25
|
+
};
|
|
26
|
+
/** Local keyring: one PRF salt, a flat list of typed bindings. */
|
|
27
|
+
export type Keyring = {
|
|
10
28
|
salt: Uint8Array;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
entries: KeyringEntry[];
|
|
30
|
+
};
|
|
31
|
+
/** Public row — no sealed bytes. */
|
|
32
|
+
export type KeyringRow = {
|
|
33
|
+
type: "prf";
|
|
34
|
+
credId: Uint8Array;
|
|
35
|
+
userId?: Uint8Array;
|
|
36
|
+
nick?: string;
|
|
37
|
+
enrolledAt: number;
|
|
38
|
+
lastUsedAt?: number;
|
|
39
|
+
attachment?: AuthenticatorAttachmentName;
|
|
40
|
+
transports?: string[];
|
|
41
|
+
aaguid?: Uint8Array;
|
|
42
|
+
enrolledOs?: EnrolledOs;
|
|
16
43
|
};
|
|
17
44
|
/**
|
|
18
|
-
* Persist
|
|
45
|
+
* Persist the keyring for cold start (e.g. protocol IDB).
|
|
19
46
|
* Called with `undefined` when the store is wiped.
|
|
20
47
|
*/
|
|
21
|
-
export type
|
|
48
|
+
export type PersistKeyring = (ring: Keyring | undefined) => void | Promise<void>;
|
|
22
49
|
export type PrfSeedStoreOpts = PrfRp & {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* Protocol IDB implements this via {@link IDBSeedStore.persistPrfMeta}.
|
|
26
|
-
*/
|
|
27
|
-
persistMeta: PersistPrfSeedMeta;
|
|
28
|
-
/** Previously persisted meta (cold start). Does not re-call persistMeta. */
|
|
29
|
-
meta?: PrfSeedMeta;
|
|
50
|
+
persistKeyring: PersistKeyring;
|
|
51
|
+
keyring?: Keyring;
|
|
30
52
|
};
|
|
31
53
|
/**
|
|
32
|
-
* Session + durable PRF-
|
|
54
|
+
* Session + durable PRF-bound seed. Session secret is always {@link Enclave}.
|
|
33
55
|
*
|
|
34
56
|
* - {@link save} is memory-only (holds enclave).
|
|
35
|
-
* - {@link
|
|
57
|
+
* - {@link bindAndSave} creates a binding and appends/upserts a keyring entry.
|
|
36
58
|
* - {@link unlock} runs PRF UV inside Enclave and returns a new enclave.
|
|
37
|
-
*
|
|
38
|
-
* Fallback when PRF is unavailable (not implemented yet): passphrase-sealed
|
|
39
|
-
* meta with the same layout — no plain seed on disk either way.
|
|
40
59
|
*/
|
|
41
60
|
export declare class PrfSeedStore implements ISeedStore {
|
|
42
61
|
#private;
|
|
43
62
|
constructor(opts: PrfSeedStoreOpts);
|
|
44
|
-
get
|
|
45
|
-
|
|
63
|
+
get keyring(): Keyring | undefined;
|
|
64
|
+
/** Public rows (no sealed master). */
|
|
65
|
+
list(): KeyringRow[];
|
|
46
66
|
save(enclave: Enclave, opts?: SetSeedOpts): Promise<Enclave>;
|
|
47
67
|
/**
|
|
48
|
-
* PRF UV inside Enclave,
|
|
68
|
+
* PRF UV inside Enclave.bind, persist the entry.
|
|
69
|
+
* Always creates a new cred unless `credId` is passed (re-bind that cred).
|
|
70
|
+
* Existing tags are checked inside the enclave (one keyring, one master).
|
|
49
71
|
*/
|
|
50
|
-
|
|
72
|
+
bindAndSave(enclave: Enclave, opts?: Pick<PasskeyPrfOpts, "salt" | "credId" | "createCredIfNeeded" | "authenticatorAttachment" | "hints" | "excludeCredentials"> & {
|
|
73
|
+
nick?: string;
|
|
74
|
+
}): Promise<ValStat<Enclave>>;
|
|
51
75
|
load(): Promise<Enclave | void>;
|
|
52
76
|
/** Passkey UV inside Enclave → new session enclave. */
|
|
53
77
|
unlock(): Promise<ValStat<Enclave>>;
|
|
78
|
+
/** Set a display nick (local only; does not update the binding key). */
|
|
79
|
+
rename(credId: Uint8Array, nick: string): Promise<ValStat<void>>;
|
|
54
80
|
/**
|
|
55
|
-
*
|
|
81
|
+
* Drop a binding from this browser. Does not delete the binding-key cred.
|
|
82
|
+
* Last entry clears the keyring.
|
|
56
83
|
*/
|
|
57
|
-
|
|
84
|
+
remove(credId: Uint8Array): Promise<ValStat<void>>;
|
|
58
85
|
wipe(): Promise<void>;
|
|
59
86
|
}
|
|
87
|
+
export declare function cloneKeyring(r: Keyring): Keyring;
|
|
88
|
+
/** Read a keyring from IDB structured-clone output. */
|
|
89
|
+
export declare function decodeKeyring(raw: unknown): Keyring | undefined;
|
|
90
|
+
/** Cred ids of every keyring entry. */
|
|
91
|
+
export declare function keyringCredIds(ring: Keyring | undefined): Uint8Array[];
|
|
92
|
+
/** Inferred kind for UI. Not attested. */
|
|
93
|
+
export declare function keyringKind(row: KeyringRow): string;
|
|
94
|
+
/** Display label: nick, else inferred kind + short cred id. */
|
|
95
|
+
export declare function keyringLabel(row: KeyringRow): string;
|
|
@@ -5,20 +5,33 @@ import type { ICrypto, PublicKey } from "../types.ts";
|
|
|
5
5
|
import { type ValStat } from "../valstat.ts";
|
|
6
6
|
import { type DHKEReq, type DHKEResp, PairRequest } from "./pairing.ts";
|
|
7
7
|
import { type LargeBlobCreateOpts, type LargeBlobRp } from "../webauthn/largeBlob.ts";
|
|
8
|
-
import { type PrfCreateOpts, type PrfEvalOpts, type PrfRp } from "../webauthn/prf.ts";
|
|
8
|
+
import { type PrfCeremony, type PrfCreateOpts, type PrfEvalOpts, type PrfRp } from "../webauthn/prf.ts";
|
|
9
9
|
export type { LargeBlobCreateOpts, LargeBlobRp };
|
|
10
10
|
export type { PrfCreateOpts, PrfEvalOpts, PrfRp };
|
|
11
11
|
/** Options for PRF seal/unseal ceremonies (no raw PRF bytes). */
|
|
12
12
|
export type PasskeyPrfOpts = PrfCreateOpts & {
|
|
13
|
-
credId?: Uint8Array;
|
|
13
|
+
credId?: Uint8Array | readonly Uint8Array[];
|
|
14
14
|
/** Seal only: create a PRF credential first when no credId is known. */
|
|
15
15
|
createCredIfNeeded?: boolean;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
export type
|
|
17
|
+
/** One binding to try on unseal (cred id + sealed master). */
|
|
18
|
+
export type PrfBinding = {
|
|
19
|
+
sealedMaster: SealedMasterKey;
|
|
20
|
+
credId: Uint8Array;
|
|
21
|
+
};
|
|
22
|
+
/** Durable PRF-sealed master + public ceremony facts (never includes PRF output). */
|
|
23
|
+
export type PrfSealedMaster = PrfCeremony & {
|
|
19
24
|
sealedMaster: SealedMasterKey;
|
|
20
25
|
salt: Uint8Array;
|
|
26
|
+
};
|
|
27
|
+
/** One existing keyring member used to prove this enclave matches the list. */
|
|
28
|
+
export type BindPrior = {
|
|
21
29
|
credId: Uint8Array;
|
|
30
|
+
tag: Uint8Array;
|
|
31
|
+
};
|
|
32
|
+
/** Seal + bind-tag from {@link Enclave.bind} (tag is not a global fingerprint). */
|
|
33
|
+
export type PrfBound = PrfSealedMaster & {
|
|
34
|
+
tag: Uint8Array;
|
|
22
35
|
};
|
|
23
36
|
export type EncryptCipher = {
|
|
24
37
|
encrypt: (data: Uint8Array) => Promise<Uint8Array>;
|
|
@@ -46,7 +59,7 @@ export type Identity = {
|
|
|
46
59
|
/** Per-bag KDM mixed with this identity's private key (see bag seal). */
|
|
47
60
|
kdmFor: (msgHeadEnc: Uint8Array) => Promise<Uint8Array>;
|
|
48
61
|
};
|
|
49
|
-
declare const
|
|
62
|
+
declare const SEAL_BIND_DOMAIN: Uint8Array<ArrayBufferLike>;
|
|
50
63
|
declare const SEAL_KEY_LEN = 32;
|
|
51
64
|
declare const SEAL_PRF_MIN_LEN = 16;
|
|
52
65
|
export declare class Enclave {
|
|
@@ -69,11 +82,16 @@ export declare class Enclave {
|
|
|
69
82
|
*/
|
|
70
83
|
sealWithPasskey(opts?: PasskeyPrfOpts): Promise<ValStat<PrfSealedMaster>>;
|
|
71
84
|
/**
|
|
72
|
-
* UV + PRF
|
|
73
|
-
*
|
|
74
|
-
*
|
|
85
|
+
* UV + PRF seal, plus a per-cred bind tag (blake3(master ‖ domain ‖ credId)).
|
|
86
|
+
* Tag compute and compare stay in the enclave. If `prior` is non-empty, every
|
|
87
|
+
* stored tag must match this master or bind fails (one keyring, one master).
|
|
88
|
+
*/
|
|
89
|
+
bind(opts?: PasskeyPrfOpts, prior?: readonly BindPrior[]): Promise<ValStat<PrfBound>>;
|
|
90
|
+
/**
|
|
91
|
+
* UV + PRF ceremony, then unseal one of `bindings` into a new Enclave.
|
|
92
|
+
* allowCredentials is the union of binding cred ids. Returns the asserted id.
|
|
75
93
|
*/
|
|
76
|
-
static unsealWithPasskey(
|
|
94
|
+
static unsealWithPasskey(bindings: readonly PrfBinding[], opts: PasskeyPrfOpts & {
|
|
77
95
|
salt: Uint8Array;
|
|
78
96
|
}): Promise<ValStat<{
|
|
79
97
|
enclave: Enclave;
|
|
@@ -135,4 +153,4 @@ export declare class Enclave {
|
|
|
135
153
|
}
|
|
136
154
|
/** Derive seal key from PRF output (domain-separated). Used by enclave seal/unseal. */
|
|
137
155
|
export declare function sealKeyFromPrf(crypto: ICrypto, prf: Uint8Array, domain?: Uint8Array): Promise<ValStat<Uint8Array>>;
|
|
138
|
-
export { MASTER_SEED_LEN, SEAL_KEY_LEN, SEAL_PRF_MIN_LEN
|
|
156
|
+
export { MASTER_SEED_LEN, SEAL_BIND_DOMAIN, SEAL_KEY_LEN, SEAL_PRF_MIN_LEN };
|
|
@@ -13,7 +13,7 @@ export type MasterSeed = Uint8Array & {
|
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
15
|
* Master seed sealed under KDF(PRF) (nonce‖ciphertext‖tag).
|
|
16
|
-
* Useless without the PRF output that derives the
|
|
16
|
+
* Useless without the PRF output that derives the binding key.
|
|
17
17
|
*/
|
|
18
18
|
export type SealedMasterKey = Uint8Array & {
|
|
19
19
|
readonly [sealedMasterKeySymbol]: true;
|
|
@@ -5,7 +5,7 @@ export declare const WEBAUTHN_CHAL_LEN = 32;
|
|
|
5
5
|
export declare const COSE_ALG_ES256 = -7;
|
|
6
6
|
export declare const COSE_ALG_EDDSA = -8;
|
|
7
7
|
export declare const COSE_ALG_RS256 = -257;
|
|
8
|
-
/** Prefer modern curves; include RS256 for broader
|
|
8
|
+
/** Prefer modern curves; include RS256 for broader binding-key coverage. */
|
|
9
9
|
export declare const WEBAUTHN_PUB_KEY_PARAMS: PublicKeyCredentialParameters[];
|
|
10
10
|
/** UA preference for the WebAuthn picker. Not exclusive; omit attachment to allow both. */
|
|
11
11
|
export type WebAuthnHint = "security-key" | "client-device" | "hybrid";
|
|
@@ -51,3 +51,13 @@ export declare function asPublicKeyCredential(cred: Credential | null): ValStat<
|
|
|
51
51
|
* `extKey` e.g. `"extension:largeBlob"`, `"extension:prf"`.
|
|
52
52
|
*/
|
|
53
53
|
export declare function webAuthnExtensionCapable(extKey: string): Promise<boolean>;
|
|
54
|
+
export type AuthenticatorAttachmentName = "platform" | "cross-platform";
|
|
55
|
+
export type EnrolledOs = "ios" | "macos" | "android" | "windows" | "linux" | "other";
|
|
56
|
+
/** Maps a WebAuthn attachment string to the two legal values. */
|
|
57
|
+
export declare function readAttachment(v: string | null | undefined): AuthenticatorAttachmentName | undefined;
|
|
58
|
+
/** Coarse OS of this browser (enrollment / display only). */
|
|
59
|
+
export declare function enrolledOsFromUA(ua: string): EnrolledOs;
|
|
60
|
+
/** `getTransports()` on an attestation response, if the UA exposes it. */
|
|
61
|
+
export declare function readTransports(pk: PublicKeyCredential): string[] | undefined;
|
|
62
|
+
/** AAGUID from attested credential data (create). Undefined if AT is absent. */
|
|
63
|
+
export declare function readAaguid(pk: PublicKeyCredential): Uint8Array | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ValStat } from "../valstat.ts";
|
|
2
|
-
import { type WebAuthnRp } from "./common.ts";
|
|
2
|
+
import { type AuthenticatorAttachmentName, type WebAuthnRp } from "./common.ts";
|
|
3
3
|
export type PrfRp = WebAuthnRp;
|
|
4
4
|
export type PrfCreateOpts = PrfRp & {
|
|
5
5
|
userName?: string;
|
|
@@ -7,22 +7,33 @@ export type PrfCreateOpts = PrfRp & {
|
|
|
7
7
|
authenticatorAttachment?: "platform" | "cross-platform";
|
|
8
8
|
/** Seal salt (eval is on get, not create — some UAs throw on create-time eval). */
|
|
9
9
|
salt?: Uint8Array;
|
|
10
|
+
/** Already-bound cred ids so create does not assert an existing key. */
|
|
11
|
+
excludeCredentials?: readonly Uint8Array[];
|
|
10
12
|
};
|
|
11
13
|
/** Default salt for DIPLOMATIC PRF eval (UTF-8). */
|
|
12
14
|
export declare const DEFAULT_PRF_SALT: Uint8Array<ArrayBufferLike>;
|
|
15
|
+
/** Default WebAuthn user.name / displayName when none is supplied. */
|
|
16
|
+
export declare const DEFAULT_PRF_USER_NAME = "diplomatic-prf";
|
|
13
17
|
/** Bytes of PRF output we consume (first N of `results.first`). */
|
|
14
18
|
export declare const PRF_OUTPUT_LEN = 32;
|
|
15
|
-
|
|
19
|
+
/** Public ceremony facts (no PRF bytes). */
|
|
20
|
+
export type PrfCeremony = {
|
|
16
21
|
credId: Uint8Array;
|
|
22
|
+
userId?: Uint8Array;
|
|
23
|
+
attachment?: AuthenticatorAttachmentName;
|
|
24
|
+
transports?: string[];
|
|
25
|
+
aaguid?: Uint8Array;
|
|
26
|
+
};
|
|
27
|
+
export type PrfCreateResult = PrfCeremony & {
|
|
17
28
|
prfEnabled: boolean;
|
|
18
29
|
};
|
|
19
30
|
/** Internal: PRF output + cred id. For Enclave only — do not re-export to apps. */
|
|
20
|
-
export type PrfEvalResult = {
|
|
31
|
+
export type PrfEvalResult = PrfCeremony & {
|
|
21
32
|
prf: Uint8Array;
|
|
22
|
-
credId: Uint8Array;
|
|
23
33
|
};
|
|
24
34
|
export type PrfEvalOpts = PrfRp & {
|
|
25
|
-
|
|
35
|
+
/** One id, or every bound id so a spare key can assert in one get(). */
|
|
36
|
+
credId?: Uint8Array | readonly Uint8Array[];
|
|
26
37
|
salt?: Uint8Array;
|
|
27
38
|
};
|
|
28
39
|
/** Best-effort: client advertises PRF extension. */
|
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
import { Enclave } from "../../shared/crypto/enclave";
|
|
2
2
|
import type { ICrypto } from "../../shared/types";
|
|
3
3
|
import type { ISeedStore, SetSeedOpts } from "../../types";
|
|
4
|
-
import { type
|
|
4
|
+
import { type Keyring, PrfSeedStore, type PrfSeedStoreOpts } from "../../passkey/prf-store";
|
|
5
5
|
export declare class IDBSeedStore implements ISeedStore {
|
|
6
6
|
#private;
|
|
7
7
|
db: IDBDatabase;
|
|
8
8
|
constructor(db: IDBDatabase, crypto: ICrypto);
|
|
9
9
|
/**
|
|
10
|
-
* Hold enclave in memory only. Durable identity is
|
|
11
|
-
* ({@link openPrfStore} / {@link
|
|
10
|
+
* Hold enclave in memory only. Durable identity is the keyring
|
|
11
|
+
* ({@link openPrfStore} / {@link persistKeyring}), never plain seed.
|
|
12
12
|
* Pins identity (nonce+hash) on first save; later saves must match
|
|
13
13
|
* (largeBlob / PRF unlock cannot switch masters under this DB).
|
|
14
14
|
* `opts.persist` is ignored (kept for API compatibility).
|
|
15
15
|
*
|
|
16
16
|
* TODO(passphrase): when PRF is missing, `persist: true` (or a dedicated
|
|
17
|
-
*
|
|
18
|
-
* same shape as
|
|
17
|
+
* bind API) should seal under a passphrase KDF and write sealed meta here —
|
|
18
|
+
* same shape as a keyring entry, not a return of raw seed.
|
|
19
19
|
*/
|
|
20
20
|
save(enclave: Enclave, _opts?: SetSeedOpts): Promise<Enclave>;
|
|
21
21
|
load(): Promise<Enclave | undefined>;
|
|
22
22
|
/**
|
|
23
|
-
* Drop in-memory enclave. Keeps {@link
|
|
23
|
+
* Drop in-memory enclave. Keeps {@link K_KEYRING} and {@link K_ID_PIN}
|
|
24
24
|
* so unlock / largeBlob restore still match this device.
|
|
25
25
|
*/
|
|
26
26
|
clearSession(): Promise<void>;
|
|
27
27
|
wipe(): Promise<void>;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
lastPrfCredId(): Promise<Uint8Array | undefined>;
|
|
33
|
-
openPrfStore(opts: Omit<PrfSeedStoreOpts, "persistMeta" | "meta">): Promise<PrfSeedStore>;
|
|
28
|
+
loadKeyring(): Promise<Keyring | undefined>;
|
|
29
|
+
persistKeyring(ring: Keyring | undefined): Promise<void>;
|
|
30
|
+
hasKeyring(): Promise<boolean>;
|
|
31
|
+
openPrfStore(opts: Omit<PrfSeedStoreOpts, "persistKeyring" | "keyring">): Promise<PrfSeedStore>;
|
|
34
32
|
}
|
package/dist/web/types.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type SetSeedOpts = {
|
|
|
35
35
|
/**
|
|
36
36
|
* When true, request durable storage **if the store supports a non-plaintext
|
|
37
37
|
* durable form** (e.g. passkey largeBlob as IdentityBundle wire).
|
|
38
|
-
* IDB holds the enclave in memory only — durable identity is PRF
|
|
38
|
+
* IDB holds the enclave in memory only — durable identity is the PRF keyring
|
|
39
39
|
* via {@link IDBSeedStore.openPrfStore}, never plain seed.
|
|
40
40
|
* Default false (session enclave only).
|
|
41
41
|
*
|
|
@@ -50,7 +50,7 @@ export type SetSeedOpts = {
|
|
|
50
50
|
* Default `client.wipe()` / `client.wipe({})` clears **everything except seed**:
|
|
51
51
|
* msgs, ents, meta = true; seed = false.
|
|
52
52
|
* That avoids passkey/largeBlob UV when the user only wants to clear app data.
|
|
53
|
-
* Pass `{ seed: true }` to also clear identity (may prompt the
|
|
53
|
+
* Pass `{ seed: true }` to also clear identity (may prompt the binding key).
|
|
54
54
|
*/
|
|
55
55
|
export type WipeOpts = {
|
|
56
56
|
/** Message archive. Default true. */
|