@motebit/crypto-webauthn 1.0.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.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Pinned FIDO-vendor root certificates the `packed`-attestation full-
3
+ * attestation verifier accepts as trust anchors.
4
+ *
5
+ * This is the self-attesting contract: a verifier that dynamically
6
+ * fetched FIDO Metadata Service roots would have no sovereign story —
7
+ * third parties auditing our output could never reproduce the decision
8
+ * without trusting our fetch path. By committing the exact bytes of the
9
+ * CAs we accept, anyone can audit this file, pin the same bytes in their
10
+ * own verifier, and reach the same yes/no answer for any WebAuthn
11
+ * attestation they receive.
12
+ *
13
+ * Starter set (v1):
14
+ * - Apple WebAuthn Anonymous Attestation CA — the platform-authenticator
15
+ * root iOS/macOS `Touch ID` / `Face ID` / `Passkey` attestations
16
+ * chain to when a site requests `attestation: "direct"`.
17
+ * Source: https://www.apple.com/certificateauthority/Apple_WebAuthn_Root_CA.pem
18
+ *
19
+ * - Yubico FIDO Root CA Serial 457200631 — the Yubico attestation
20
+ * root every modern YubiKey (FIDO2 / Security Key) leaf certifies
21
+ * under. Source: https://developers.yubico.com/PKI/yubico-ca-certs.txt
22
+ *
23
+ * - Microsoft TPM Root CA 2014 — the root Microsoft TPM-backed
24
+ * platform-authenticator leaves chain to on Windows Hello setups.
25
+ * Source: https://www.microsoft.com/pkiops/certs/Microsoft%20TPM%20Root%20Certificate%20Authority%202014.crt
26
+ *
27
+ * Rotations land as additive constants and a dispatch arm on the
28
+ * accept-set here. Removing a root is a wire-format break and MUST be
29
+ * coordinated with a spec version bump.
30
+ *
31
+ * Tests override the accept-set via `WebAuthnVerifyOptions.rootPems` so
32
+ * chain-validation exercises the same code path without needing a real
33
+ * vendor-signed leaf.
34
+ */
35
+ /**
36
+ * Apple WebAuthn Anonymous Attestation Root CA.
37
+ *
38
+ * Published by Apple as the root for WebAuthn packed-attestation leaves
39
+ * minted by iOS / macOS platform authenticators. Byte-for-byte match of
40
+ * Apple's published certificate at
41
+ * https://www.apple.com/certificateauthority/Apple_WebAuthn_Root_CA.pem.
42
+ */
43
+ export const APPLE_WEBAUTHN_ROOT_PEM = `-----BEGIN CERTIFICATE-----
44
+ MIICEjCCAZmgAwIBAgIQaB0BbHo84wIlpQGUKEdXcTAKBggqhkjOPQQDAzBLMR8w
45
+ HQYDVQQDDBZBcHBsZSBXZWJBdXRobiBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ
46
+ bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTIwMDMxODE4MjEzMloXDTQ1MDMx
47
+ NTAwMDAwMFowSzEfMB0GA1UEAwwWQXBwbGUgV2ViQXV0aG4gUm9vdCBDQTETMBEG
48
+ A1UECgwKQXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTB2MBAGByqGSM49
49
+ AgEGBSuBBAAiA2IABCJCQ2pTVhzjl4Wo6IhHtMSAzO2cv+H9DQKev3//fG59G11k
50
+ xu9eI0/7o6V5uShBpe1u6l6mS19S1FEh6yGljnZAJ+2GNP1mi/YK2kSXIuTHjxA/
51
+ pcoRf7XkOtO4o1qlcaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJtdk
52
+ 2cV4wlpn0afeaxLQG2PxxtcwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA
53
+ MGQCMFrZ+9DsJ1PW9hfNdBywZDsWDbWFp28it1d/5w2RPkRX3Bbn/UbDTNLx7Jr3
54
+ jAGGiQIwHFj+dJZYUJR786osByBelJYsVZd2GbHQu209b5RCmGQ21gWSw2PdMsSn
55
+ 1LabATR4H7iIgXPxz8m8KiS1hXiz
56
+ -----END CERTIFICATE-----
57
+ `;
58
+ /**
59
+ * Yubico FIDO Root CA — Serial 457200631.
60
+ *
61
+ * Root of trust for YubiKey FIDO2 / Security Key attestation leaves.
62
+ * Byte-for-byte match of Yubico's published certificate at
63
+ * https://developers.yubico.com/PKI/yubico-ca-certs.txt.
64
+ */
65
+ export const YUBICO_FIDO_ROOT_PEM = `-----BEGIN CERTIFICATE-----
66
+ MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ
67
+ dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw
68
+ MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290
69
+ IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
70
+ AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk
71
+ 5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep
72
+ 8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw
73
+ nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT
74
+ 9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw
75
+ LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ
76
+ hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN
77
+ BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4
78
+ MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt
79
+ hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k
80
+ LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U
81
+ sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc
82
+ U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==
83
+ -----END CERTIFICATE-----
84
+ `;
85
+ /**
86
+ * Microsoft TPM Root Certificate Authority 2014.
87
+ *
88
+ * Root of trust for Windows-Hello TPM-backed platform-authenticator
89
+ * attestation leaves. Byte-for-byte match of Microsoft's published
90
+ * certificate at
91
+ * https://www.microsoft.com/pkiops/certs/Microsoft%20TPM%20Root%20Certificate%20Authority%202014.crt.
92
+ *
93
+ * Note: Microsoft's Windows Hello attestation commonly uses `fmt: "tpm"`
94
+ * rather than `fmt: "packed"`; this root is pinned for forward-compatibility
95
+ * with Microsoft-signed `packed` leaves and for the TPM fmt's additive
96
+ * arm (non-goal in v1).
97
+ */
98
+ export const MICROSOFT_TPM_ROOT_PEM = `-----BEGIN CERTIFICATE-----
99
+ MIIF9TCCA92gAwIBAgIQXbYwTgy/J79JuMhpUB5dyzANBgkqhkiG9w0BAQsFADCB
100
+ jDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
101
+ ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE2MDQGA1UEAxMt
102
+ TWljcm9zb2Z0IFRQTSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDE0MB4X
103
+ DTE0MTIxMDIxMzExOVoXDTM5MTIxMDIxMzkyOFowgYwxCzAJBgNVBAYTAlVTMRMw
104
+ EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
105
+ aWNyb3NvZnQgQ29ycG9yYXRpb24xNjA0BgNVBAMTLU1pY3Jvc29mdCBUUE0gUm9v
106
+ dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxNDCCAiIwDQYJKoZIhvcNAQEBBQAD
107
+ ggIPADCCAgoCggIBAJ+n+bnKt/JHIRC/oI/xgkgsYdPzP0gpvduDA2GbRtth+L4W
108
+ UyoZKGBw7uz5bjjP8Aql4YExyjR3EZQ4LqnZChMpoCofbeDR4MjCE1TGwWghGpS0
109
+ mM3GtWD9XiME4rE2K0VW3pdN0CLzkYbvZbs2wQTFfE62yNQiDjyHFWAZ4BQH4eWa
110
+ 8wrDMUxIAneUCpU6zCwM+l6Qh4ohX063BHzXlTSTc1fDsiPaKuMMjWjK9vp5UHFP
111
+ a+dMAWr6OljQZPFIg3aZ4cUfzS9y+n77Hs1NXPBn6E4Db679z4DThIXyoKeZTv1a
112
+ aWOWl/exsDLGt2mTMTyykVV8uD1eRjYriFpLQBFT0EfijJB0WzrVeJExDVBtH74E
113
+ 1vV0zGlKn3IdmkoXMq72wBWmldvnWqMkYmG+/TT4VqsmxwYQKsW8pAmO2ouCXCq7
114
+ x8XFs2otDi4QZIc6HBxGe0GR8yxI/XxdFR3jGq0GmgcwBKJSNXOvbTS0Ql3TjfuJ
115
+ HO3+SM8ioybJMeAexjRPOGd9mZZdkns2awTW8lzxE2pi0iMsPALLmWekmQTxAkyo
116
+ h+mXxIdjICnsA+J5Nc2vB/YfM1v8Of8jlLSrkVZ+HjAJKaA3TwfnuL9yPalajgSs
117
+ AonA/aGPrpbFTJKnYsX6TAKpxvlLrNs/XZBERPFfygBJTffBMVoerIJQa+W5AgMB
118
+ AAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR6
119
+ jArOL0hiF+KU0a5VwVLscXSkVjAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0B
120
+ AQsFAAOCAgEAW4ioo1+J9VWC0UntSBXcXRm1ePTVamtsxVy/GpP4EmJd3Ub53JzN
121
+ BfYdgfUL51CppS3ZY6BoagB+DqoA2GbSL+7sFGHBl5ka6FNelrwsH6VVw4xV/8kl
122
+ IjmqOyfatPYsz0sUdZev+reeiGpKVoXrK6BDnUU27/mgPtem5YKWvHB/soofUrLK
123
+ zZV3WfGdx9zBr8V0xb6n9YUTHRp/nmn5F4VWLBwyjjlRofFJuFZbM0S91aOu6hit
124
+ UOHsgEl6vTlc1v5ymG2tTRSZ+hVbazQWGF6cnKE0NYZy7UCjl2ZWN+FYHUX3tzrK
125
+ UXDy3jl6wmRy+R0O7CJjEgQsaa4Rkz/L+0mdCIdhTNijxPRTaPcLGn1l5ZxtqWl3
126
+ OdcbVjQGMnOWrkaxpbI99/C8tBiYuUCiPkCeuK+0wLMjP3b3zlPGjZnTVSgq52bK
127
+ 7JIB70R8dnC6PIPjY9QeY5bOTQJ1LJ/h8Hcn4Vam0aZMIpWDjMOXK48rLNG1+mXB
128
+ oDpoa1jAD+hxi54GSJHgGtVHG/HEiBdpHumOBYDLv5UZ9T2nHhPbmkpTA5JvzaCT
129
+ Wb0B4htaAlVCbQ0Tn4mNHhASa/rsSpl0C5bpFggsDdaaRmcRJ3UPy+GmSZPT9Xsh
130
+ Hv7yoFFKi50iNfc59NhT2Qh/6V/8gK7U+rfcKk+KlW/2k+JSX+4Dyh8=
131
+ -----END CERTIFICATE-----
132
+ `;
133
+ /**
134
+ * Default FIDO root accept-set. Full attestation (x5c present) must
135
+ * chain to exactly one of these. Tests override by passing
136
+ * `WebAuthnVerifyOptions.rootPems` — the runtime-injected set replaces
137
+ * the pinned default so fabricated chains can exercise the same code
138
+ * path.
139
+ */
140
+ export const DEFAULT_FIDO_ROOTS = [
141
+ APPLE_WEBAUTHN_ROOT_PEM,
142
+ YUBICO_FIDO_ROOT_PEM,
143
+ MICROSOFT_TPM_ROOT_PEM,
144
+ ];
145
+ /**
146
+ * WebAuthn attestation format discriminator the verifier accepts in v1.
147
+ * `packed` is the broadest-coverage format and the only one v1 handles.
148
+ * `tpm`, `android-key`, `android-safetynet`, `fido-u2f`, `apple`, and
149
+ * `none` are rejected with a structured `fmt-not-supported` error.
150
+ */
151
+ export const WEBAUTHN_FMT_PACKED = "packed";
152
+ //# sourceMappingURL=fido-roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fido-roots.js","sourceRoot":"","sources":["../src/fido-roots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;CActC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;CAmBnC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCrC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA0B;IACvD,uBAAuB;IACvB,oBAAoB;IACpB,sBAAsB;CACvB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @motebit/crypto-webauthn — W3C WebAuthn platform-authenticator
3
+ * attestation adapter for motebit hardware-attestation claims.
4
+ *
5
+ * The metabolic leaf `@motebit/crypto` delegates to when a
6
+ * `HardwareAttestationClaim` declares `platform: "webauthn"`. Dep-thin
7
+ * `@motebit/crypto` stays permissive-floor-pure; this package, also on
8
+ * the permissive floor (Apache-2.0), metabolizes `@peculiar/x509` + `cbor2`
9
+ * to judge whether the browser's platform authenticator rooted the leaf
10
+ * that signed the caller's attestation (full attestation), or whether the
11
+ * credential's own key witnessed the challenge (self attestation).
12
+ *
13
+ * Wiring from a consumer:
14
+ *
15
+ * ```ts
16
+ * import { verify } from "@motebit/crypto";
17
+ * import { webauthnVerifier } from "@motebit/crypto-webauthn";
18
+ *
19
+ * const result = await verify(credential, {
20
+ * hardwareAttestation: { webauthn: webauthnVerifier({ expectedRpId: "motebit.com" }) },
21
+ * });
22
+ * ```
23
+ *
24
+ * This package exports no global state, no side-effect registrations;
25
+ * the injection is call-site only.
26
+ */
27
+ import type { HardwareAttestationClaim } from "@motebit/protocol";
28
+ import type { WebAuthnVerifyResult } from "./verify.js";
29
+ export { parseWebAuthnAttestationObjectCbor } from "./cbor.js";
30
+ export type { WebAuthnAttestationObjectCbor } from "./cbor.js";
31
+ export { APPLE_WEBAUTHN_ROOT_PEM, YUBICO_FIDO_ROOT_PEM, MICROSOFT_TPM_ROOT_PEM, DEFAULT_FIDO_ROOTS, WEBAUTHN_FMT_PACKED, } from "./fido-roots.js";
32
+ export { verifyWebAuthnAttestation } from "./verify.js";
33
+ export type { WebAuthnVerifyOptions, WebAuthnVerifyResult, WebAuthnVerifyError } from "./verify.js";
34
+ /**
35
+ * Shape the optional verifier injected into `@motebit/crypto`'s
36
+ * `HardwareAttestationVerifiers.webauthn` slot carries. Mirrors the
37
+ * sync-or-async return shape the dispatcher supports, and extends the
38
+ * canonical shape with the structured `attestation_detail` so callers
39
+ * can introspect chain / signature / binding independently.
40
+ */
41
+ export interface WebAuthnVerifyDispatchResult {
42
+ readonly valid: boolean;
43
+ readonly platform: "webauthn";
44
+ readonly errors: ReadonlyArray<{
45
+ readonly message: string;
46
+ }>;
47
+ readonly attestation_detail?: WebAuthnVerifyResult;
48
+ }
49
+ /**
50
+ * Context fields the dispatcher lifts out of the credential subject and
51
+ * threads through to the verifier. All three participate in the
52
+ * canonical body the web mint path composes at WebAuthn challenge
53
+ * time — the verifier re-derives the body from them and byte-compares
54
+ * against the `challenge` field of clientDataJSON.
55
+ *
56
+ * All fields optional: a caller that omits one loses the identity-
57
+ * binding channel but still gets chain / signature / rp binding
58
+ * results — the verifier reports the missing channel explicitly rather
59
+ * than silently passing.
60
+ */
61
+ export interface WebAuthnVerifierContext {
62
+ readonly expectedMotebitId?: string;
63
+ readonly expectedDeviceId?: string;
64
+ readonly expectedAttestedAt?: number;
65
+ }
66
+ export interface WebAuthnVerifierConfig {
67
+ /** WebAuthn Relying Party ID the credential was minted for (e.g. "motebit.com"). */
68
+ readonly expectedRpId: string;
69
+ /** Optional: override the pinned FIDO root accept-set (tests fabricate their own). */
70
+ readonly rootPems?: ReadonlyArray<string>;
71
+ /** Optional: inject a fixed clock for deterministic chain-validity checks. */
72
+ readonly now?: () => number;
73
+ /** Optional: override the accepted attestation-format string. */
74
+ readonly expectedFmt?: string;
75
+ }
76
+ /**
77
+ * Factory — build a `webauthn` verifier bound to a specific RP ID and
78
+ * (optionally) a test root set / clock / fmt. The returned function
79
+ * matches the `HardwareAttestationVerifiers.webauthn` signature the
80
+ * `@motebit/crypto` dispatcher expects.
81
+ *
82
+ * Third-parameter `context` carries motebit_id / device_id / attested_at
83
+ * so the verifier can re-derive the canonical body the browser signed
84
+ * over. Callers that omit it receive `identity_bound: false` in the
85
+ * result (fail-closed).
86
+ *
87
+ * Kept as a factory rather than a free function so the consumer's RP
88
+ * ID is captured once at wiring time, not repeated at every call site.
89
+ */
90
+ export declare function webauthnVerifier(config: WebAuthnVerifierConfig): (claim: HardwareAttestationClaim, expectedIdentityHex: string, context?: WebAuthnVerifierContext) => Promise<WebAuthnVerifyDispatchResult>;
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,KAAK,EAAyB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EAAE,kCAAkC,EAAE,MAAM,WAAW,CAAC;AAC/D,YAAY,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEpG;;;;;;GAMG;AACH,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;CACpD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,GAC7B,CACD,KAAK,EAAE,wBAAwB,EAC/B,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE,uBAAuB,KAC9B,OAAO,CAAC,4BAA4B,CAAC,CA0BzC"}
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @motebit/crypto-webauthn — W3C WebAuthn platform-authenticator
3
+ * attestation adapter for motebit hardware-attestation claims.
4
+ *
5
+ * The metabolic leaf `@motebit/crypto` delegates to when a
6
+ * `HardwareAttestationClaim` declares `platform: "webauthn"`. Dep-thin
7
+ * `@motebit/crypto` stays permissive-floor-pure; this package, also on
8
+ * the permissive floor (Apache-2.0), metabolizes `@peculiar/x509` + `cbor2`
9
+ * to judge whether the browser's platform authenticator rooted the leaf
10
+ * that signed the caller's attestation (full attestation), or whether the
11
+ * credential's own key witnessed the challenge (self attestation).
12
+ *
13
+ * Wiring from a consumer:
14
+ *
15
+ * ```ts
16
+ * import { verify } from "@motebit/crypto";
17
+ * import { webauthnVerifier } from "@motebit/crypto-webauthn";
18
+ *
19
+ * const result = await verify(credential, {
20
+ * hardwareAttestation: { webauthn: webauthnVerifier({ expectedRpId: "motebit.com" }) },
21
+ * });
22
+ * ```
23
+ *
24
+ * This package exports no global state, no side-effect registrations;
25
+ * the injection is call-site only.
26
+ */
27
+ import { verifyWebAuthnAttestation } from "./verify.js";
28
+ export { parseWebAuthnAttestationObjectCbor } from "./cbor.js";
29
+ export { APPLE_WEBAUTHN_ROOT_PEM, YUBICO_FIDO_ROOT_PEM, MICROSOFT_TPM_ROOT_PEM, DEFAULT_FIDO_ROOTS, WEBAUTHN_FMT_PACKED, } from "./fido-roots.js";
30
+ export { verifyWebAuthnAttestation } from "./verify.js";
31
+ /**
32
+ * Factory — build a `webauthn` verifier bound to a specific RP ID and
33
+ * (optionally) a test root set / clock / fmt. The returned function
34
+ * matches the `HardwareAttestationVerifiers.webauthn` signature the
35
+ * `@motebit/crypto` dispatcher expects.
36
+ *
37
+ * Third-parameter `context` carries motebit_id / device_id / attested_at
38
+ * so the verifier can re-derive the canonical body the browser signed
39
+ * over. Callers that omit it receive `identity_bound: false` in the
40
+ * result (fail-closed).
41
+ *
42
+ * Kept as a factory rather than a free function so the consumer's RP
43
+ * ID is captured once at wiring time, not repeated at every call site.
44
+ */
45
+ export function webauthnVerifier(config) {
46
+ return async (claim, expectedIdentityHex, context) => {
47
+ const opts = {
48
+ expectedRpId: config.expectedRpId,
49
+ expectedIdentityPublicKeyHex: expectedIdentityHex,
50
+ ...(config.rootPems !== undefined ? { rootPems: config.rootPems } : {}),
51
+ ...(config.now !== undefined ? { now: config.now } : {}),
52
+ ...(config.expectedFmt !== undefined ? { expectedFmt: config.expectedFmt } : {}),
53
+ ...(context?.expectedMotebitId !== undefined
54
+ ? { expectedMotebitId: context.expectedMotebitId }
55
+ : {}),
56
+ ...(context?.expectedDeviceId !== undefined
57
+ ? { expectedDeviceId: context.expectedDeviceId }
58
+ : {}),
59
+ ...(context?.expectedAttestedAt !== undefined
60
+ ? { expectedAttestedAt: context.expectedAttestedAt }
61
+ : {}),
62
+ };
63
+ const detail = await verifyWebAuthnAttestation(claim, opts);
64
+ return {
65
+ valid: detail.valid,
66
+ platform: "webauthn",
67
+ errors: detail.errors,
68
+ attestation_detail: detail,
69
+ };
70
+ };
71
+ }
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAKH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,kCAAkC,EAAE,MAAM,WAAW,CAAC;AAE/D,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AA8CxD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAA8B;IAM9B,OAAO,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE;QACnD,MAAM,IAAI,GAA0B;YAClC,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,4BAA4B,EAAE,mBAAmB;YACjD,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,SAAS;gBAC1C,CAAC,CAAC,EAAE,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,EAAE;gBAClD,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,SAAS;gBACzC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE;gBAChD,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,kBAAkB,KAAK,SAAS;gBAC3C,CAAC,CAAC,EAAE,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,EAAE;gBACpD,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC5D,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,kBAAkB,EAAE,MAAM;SAC3B,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * WebAuthn platform-authenticator attestation verifier — the core
3
+ * judgment function this package exports.
4
+ *
5
+ * Flow (matches the W3C WebAuthn `packed` attestation verification
6
+ * recipe, plus the motebit-specific identity-key binding step):
7
+ *
8
+ * 1. Split the receipt into (attestationObjectBase64, clientDataJSONBase64).
9
+ * 2. CBOR-decode the attestation object to {fmt, attStmt:{alg, sig, x5c?},
10
+ * authData}. Assert fmt === "packed" in v1. Other fmts are named-not-
11
+ * supported errors (tpm / android-key / android-safetynet / fido-u2f /
12
+ * apple / none) — each is a separate additive arm future passes can add.
13
+ * 3a. Full attestation (x5c present): parse leaf, walk the chain against
14
+ * the pinned FIDO roots. Every non-leaf must carry basicConstraints.cA
15
+ * === true; every signature must verify; every cert must be within
16
+ * its validity window; the terminal cert's DER must equal one of the
17
+ * pinned roots byte-for-byte. Then verify `attStmt.sig` over
18
+ * `authData || clientDataHash` using the leaf's public key and alg.
19
+ * 3b. Self attestation (no x5c): extract the credential public key from
20
+ * `authData.attestedCredentialData`. Verify `attStmt.sig` over
21
+ * `authData || clientDataHash` using that key. Self-attested
22
+ * credentials carry no vendor chain and score as hardware-exported-
23
+ * equivalent (0.5) in the semiring — still better than software
24
+ * because the binding is still hardware-held, just not chain-proven.
25
+ * 4. Parse `clientDataJSON` (UTF-8 JSON): assert its `challenge` field
26
+ * (base64url-decoded) byte-equals the reconstructed
27
+ * SHA256(canonical body) the caller threads in via
28
+ * (motebit_id, device_id, identity_public_key, attested_at). The
29
+ * identity-binding step — byte-identical contract to App Attest.
30
+ * 5. Parse `authData` minimally: `rpIdHash` is the first 32 bytes; the
31
+ * caller's RP ID (e.g. "motebit.com") hashed with SHA-256 must match
32
+ * byte-for-byte. Bundle/RP binding.
33
+ *
34
+ * FIDO Metadata Service (MDS) dynamic fetch is explicitly out of scope —
35
+ * the pinned-roots model keeps the verifier sovereign and offline.
36
+ * Rotations land as additive constants in `fido-roots.ts`.
37
+ */
38
+ import type { HardwareAttestationClaim } from "@motebit/protocol";
39
+ export interface WebAuthnVerifyOptions {
40
+ /**
41
+ * Relying Party ID the credential was minted for (e.g. "motebit.com").
42
+ * Hashed with SHA-256 and byte-compared against authData.rpIdHash.
43
+ */
44
+ readonly expectedRpId: string;
45
+ /**
46
+ * Ed25519 identity key (lowercase hex) the motebit VC claims. The
47
+ * reconstructed attestation body MUST name this key.
48
+ */
49
+ readonly expectedIdentityPublicKeyHex: string;
50
+ /**
51
+ * motebit_id from the credential subject. Participates in the canonical
52
+ * body the web mint path signs; re-derived here and byte-compared
53
+ * against clientDataJSON.challenge so a malicious client cannot
54
+ * substitute a different body.
55
+ */
56
+ readonly expectedMotebitId?: string;
57
+ /** device_id from the credential subject. */
58
+ readonly expectedDeviceId?: string;
59
+ /** attested_at (unix ms) from the credential subject. */
60
+ readonly expectedAttestedAt?: number;
61
+ /**
62
+ * Override the pinned FIDO-root accept-set. Tests fabricate their own
63
+ * chain and supply its root PEM here so chain verification exercises
64
+ * the same code path without a real vendor-signed leaf.
65
+ */
66
+ readonly rootPems?: ReadonlyArray<string>;
67
+ /** Clock for chain-validity checks. Defaults to `Date.now`. */
68
+ readonly now?: () => number;
69
+ /**
70
+ * Override the accepted attestation-format string. Defaults to
71
+ * `"packed"`. Exposed only so test fabrications that exercise chain-
72
+ * validation edge cases can still reach the code path without
73
+ * colliding with a production fmt.
74
+ */
75
+ readonly expectedFmt?: string;
76
+ }
77
+ export interface WebAuthnVerifyError {
78
+ readonly message: string;
79
+ }
80
+ export interface WebAuthnVerifyResult {
81
+ readonly valid: boolean;
82
+ readonly cert_chain_valid: boolean;
83
+ /**
84
+ * True when the signature verifies AND the signer is the same key
85
+ * the credential subject claims (chain-rooted for full attestation;
86
+ * the credential public key itself for self attestation).
87
+ */
88
+ readonly signature_valid: boolean;
89
+ /** True when `authData.rpIdHash === SHA256(expectedRpId)`. */
90
+ readonly rp_bound: boolean;
91
+ /**
92
+ * True when `clientDataJSON.challenge` (base64url-decoded) equals
93
+ * SHA256(reconstructed canonical body naming the caller's identity).
94
+ */
95
+ readonly identity_bound: boolean;
96
+ /**
97
+ * Attestation kind chosen by the presence / absence of x5c in the
98
+ * attestation object. `"self"` scores lower in the semiring (0.5 —
99
+ * treated as hardware-exported equivalent) because the binding is not
100
+ * chain-rooted; `"full"` scores 1.0.
101
+ */
102
+ readonly attestation_kind: "full" | "self" | null;
103
+ readonly errors: readonly WebAuthnVerifyError[];
104
+ }
105
+ /**
106
+ * WebAuthn platform-authenticator attestation verifier.
107
+ *
108
+ * Pure. No network. No filesystem. Deterministic given `now()`.
109
+ *
110
+ * `claim` is the `HardwareAttestationClaim` as carried inside the motebit
111
+ * AgentTrustCredential. For WebAuthn, the `attestation_receipt` field is
112
+ * two base64url segments separated by `.`:
113
+ *
114
+ * `{attestationObjectB64}.{clientDataJSONB64}`
115
+ *
116
+ * The web mint path constructs this shape; see
117
+ * `apps/web/src/mint-hardware-credential.ts`.
118
+ */
119
+ export declare function verifyWebAuthnAttestation(claim: HardwareAttestationClaim, opts: WebAuthnVerifyOptions): Promise<WebAuthnVerifyResult>;
120
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAKlE,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,4BAA4B,EAAE,MAAM,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,yDAAyD;IACzD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CAyT/B"}