@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.
- package/LICENSE +206 -0
- package/NOTICE +19 -0
- package/README.md +49 -0
- package/dist/cbor.d.ts +48 -0
- package/dist/cbor.d.ts.map +1 -0
- package/dist/cbor.js +99 -0
- package/dist/cbor.js.map +1 -0
- package/dist/fido-roots.d.ts +81 -0
- package/dist/fido-roots.d.ts.map +1 -0
- package/dist/fido-roots.js +152 -0
- package/dist/fido-roots.js.map +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/verify.d.ts +120 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +693 -0
- package/dist/verify.js.map +1 -0
- package/package.json +79 -0
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/verify.d.ts
ADDED
|
@@ -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"}
|