@motebit/crypto-android-keystore 1.1.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 +60 -0
- package/dist/asn1.d.ts +82 -0
- package/dist/asn1.d.ts.map +1 -0
- package/dist/asn1.js +257 -0
- package/dist/asn1.js.map +1 -0
- package/dist/google-roots.d.ts +89 -0
- package/dist/google-roots.d.ts.map +1 -0
- package/dist/google-roots.js +136 -0
- package/dist/google-roots.js.map +1 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/verify.d.ts +179 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +423 -0
- package/dist/verify.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pinned Google Hardware Attestation root certificates.
|
|
3
|
+
*
|
|
4
|
+
* Every `platform: "android_keystore"` hardware-attestation claim must
|
|
5
|
+
* chain to one of the root CAs listed here. Pinning is the
|
|
6
|
+
* self-attesting contract — a verifier that dynamically fetched
|
|
7
|
+
* Google's attestation roots could not be reproduced by a third-party
|
|
8
|
+
* audit. By committing the exact bytes of the CAs we accept, anyone
|
|
9
|
+
* can audit this file, pin the same bytes in their own verifier, and
|
|
10
|
+
* reach the same yes/no answer.
|
|
11
|
+
*
|
|
12
|
+
* Two roots are pinned, covering the in-flight rotation Google
|
|
13
|
+
* announced for early 2026:
|
|
14
|
+
*
|
|
15
|
+
* - **Legacy RSA-4096 root** — covers all factory-provisioned
|
|
16
|
+
* Android devices since 2014. Cannot be rotated on those devices,
|
|
17
|
+
* so it stays valid indefinitely for that fleet.
|
|
18
|
+
*
|
|
19
|
+
* - **ECDSA P-384 root** — issued 2025-07-17. Covers RKP-provisioned
|
|
20
|
+
* devices (Remote Key Provisioning, the modern flow). Devices
|
|
21
|
+
* began emitting chains rooted here on 2026-02-01; switched
|
|
22
|
+
* exclusively after 2026-04-10.
|
|
23
|
+
*
|
|
24
|
+
* Verifiers MUST pin both. Source of truth: `roots.json` in the
|
|
25
|
+
* `android/keyattestation` repository (Google's canonical Kotlin
|
|
26
|
+
* reference verifier), mirrored from Android's published attestation
|
|
27
|
+
* trust anchor list.
|
|
28
|
+
*
|
|
29
|
+
* Operator note — runtime fetching is forbidden. The Android docs are
|
|
30
|
+
* explicit: "Don't query for roots at runtime. Use formal
|
|
31
|
+
* configuration updates." A new Google root cert lands here as an
|
|
32
|
+
* additive constant, never via dynamic fetch — any other path
|
|
33
|
+
* sacrifices the sovereign-verifier story.
|
|
34
|
+
*
|
|
35
|
+
* Each constant ships its real Google-published bytes. Each comment
|
|
36
|
+
* names: source URL, subject DN, SHA-256 fingerprint, public-key
|
|
37
|
+
* algorithm, validity window. The fingerprint is the audit anchor — a
|
|
38
|
+
* third party that fetches the same source and computes its own
|
|
39
|
+
* SHA-256 should reach the byte-identical value below.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Google Hardware Attestation Root — RSA-4096 (legacy / factory-provisioned).
|
|
43
|
+
*
|
|
44
|
+
* Source: https://github.com/android/keyattestation (roots.json[0])
|
|
45
|
+
* Subject: serialNumber=f92009e853b6b045
|
|
46
|
+
* Issuer: same (self-signed)
|
|
47
|
+
* Serial: 0xf1c172a699eaf51d
|
|
48
|
+
* SHA-256: cedb1cb6dc896ae5ec797348bce9286753c2b38ee71ce0fbe34a9a1248800dfc
|
|
49
|
+
* Public key: RSA-4096
|
|
50
|
+
* Validity: 2022-03-20 → 2042-03-15
|
|
51
|
+
*/
|
|
52
|
+
export declare const GOOGLE_ANDROID_KEYSTORE_ROOT_RSA_PEM = "-----BEGIN CERTIFICATE-----\nMIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV\nBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgw\nNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS\nSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7\ntv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj\nnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq\nC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ\noVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O\nJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg\nsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi\nigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M\nRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E\naDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um\nAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud\nIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD\nVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7\n174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGIC\nW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2G\ntkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkx\noSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG\n1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mF\nmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPz\nlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVw\nn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1Eu\nzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHo\nvaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHn\nw1IdYIg2Wxg7yHcQZemFQg==\n-----END CERTIFICATE-----\n";
|
|
53
|
+
/**
|
|
54
|
+
* Google Hardware Attestation Root — ECDSA P-384 (modern / RKP).
|
|
55
|
+
*
|
|
56
|
+
* Source: https://github.com/android/keyattestation (roots.json[1])
|
|
57
|
+
* Subject: CN=Key Attestation CA1, OU=Android, O=Google LLC, C=US
|
|
58
|
+
* Issuer: same (self-signed)
|
|
59
|
+
* Serial: 0x84a9d0297b0eb58ae7ff0e80de760605
|
|
60
|
+
* SHA-256: 6d9db4ce6c5c0b293166d08986e05774a8776ceb525d9e4329520de12ba4bcc0
|
|
61
|
+
* Public key: ECDSA P-384
|
|
62
|
+
* Validity: 2025-07-17 → 2035-07-15
|
|
63
|
+
*
|
|
64
|
+
* Rotation timeline (Google-published, motebit-recorded):
|
|
65
|
+
* - 2026-02-01: RKP-enabled devices begin emitting chains rooted here
|
|
66
|
+
* - 2026-03-31: target deadline for verifiers to pin both anchors
|
|
67
|
+
* - 2026-04-10: RKP-enabled devices switch exclusively to this root
|
|
68
|
+
*/
|
|
69
|
+
export declare const GOOGLE_ANDROID_KEYSTORE_ROOT_ECDSA_PEM = "-----BEGIN CERTIFICATE-----\nMIICIjCCAaigAwIBAgIRAISp0Cl7DrWK5/8OgN52BgUwCgYIKoZIzj0EAwMwUjEc\nMBoGA1UEAwwTS2V5IEF0dGVzdGF0aW9uIENBMTEQMA4GA1UECwwHQW5kcm9pZDET\nMBEGA1UECgwKR29vZ2xlIExMQzELMAkGA1UEBhMCVVMwHhcNMjUwNzE3MjIzMjE4\nWhcNMzUwNzE1MjIzMjE4WjBSMRwwGgYDVQQDDBNLZXkgQXR0ZXN0YXRpb24gQ0Ex\nMRAwDgYDVQQLDAdBbmRyb2lkMRMwEQYDVQQKDApHb29nbGUgTExDMQswCQYDVQQG\nEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABCPaI3FO3z5bBQo8cuiEas4HjqCt\nG/mLFfRT0MsIssPBEEU5Cfbt6sH5yOAxqEi5QagpU1yX4HwnGb7OtBYpDTB57uH5\nEczm34A5FNijV3s0/f0UPl7zbJcTx6xwqMIRq6NCMEAwDwYDVR0TAQH/BAUwAwEB\n/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFFIyuyz7RkOb3NaBqQ5lZuA0QepA\nMAoGCCqGSM49BAMDA2gAMGUCMETfjPO/HwqReR2CS7p0ZWoD/LHs6hDi422opifH\nEUaYLxwGlT9SLdjkVpz0UUOR5wIxAIoGyxGKRHVTpqpGRFiJtQEOOTp/+s1GcxeY\nuR2zh/80lQyu9vAFCj6E4AXc+osmRg==\n-----END CERTIFICATE-----\n";
|
|
70
|
+
/**
|
|
71
|
+
* Default pinned-root set returned when a caller passes no `rootPems`
|
|
72
|
+
* override. Both Google attestation roots — pin order is irrelevant
|
|
73
|
+
* to chain validation but is documented for audit-readability.
|
|
74
|
+
*/
|
|
75
|
+
export declare const DEFAULT_ANDROID_KEYSTORE_TRUST_ANCHORS: readonly string[];
|
|
76
|
+
/**
|
|
77
|
+
* Sentinel value the consumer-facing verifier emits on a mint path
|
|
78
|
+
* where the Kotlin bridge returns a `not_supported` failure envelope.
|
|
79
|
+
* Exposed as a constant so call sites match on a named token rather
|
|
80
|
+
* than a raw string literal.
|
|
81
|
+
*/
|
|
82
|
+
export declare const ANDROID_KEYSTORE_PLATFORM: "android_keystore";
|
|
83
|
+
/**
|
|
84
|
+
* The OID Google uses for the Android Key Attestation extension on
|
|
85
|
+
* the leaf certificate. Defined in the Android security docs:
|
|
86
|
+
* https://source.android.com/docs/security/features/keystore/attestation
|
|
87
|
+
*/
|
|
88
|
+
export declare const ANDROID_KEY_ATTESTATION_OID = "1.3.6.1.4.1.11129.2.1.17";
|
|
89
|
+
//# sourceMappingURL=google-roots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-roots.d.ts","sourceRoot":"","sources":["../src/google-roots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oCAAoC,60DA8BhD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sCAAsC,qzBAclD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,EAAE,SAAS,MAAM,EAGnE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAG,kBAA2B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pinned Google Hardware Attestation root certificates.
|
|
3
|
+
*
|
|
4
|
+
* Every `platform: "android_keystore"` hardware-attestation claim must
|
|
5
|
+
* chain to one of the root CAs listed here. Pinning is the
|
|
6
|
+
* self-attesting contract — a verifier that dynamically fetched
|
|
7
|
+
* Google's attestation roots could not be reproduced by a third-party
|
|
8
|
+
* audit. By committing the exact bytes of the CAs we accept, anyone
|
|
9
|
+
* can audit this file, pin the same bytes in their own verifier, and
|
|
10
|
+
* reach the same yes/no answer.
|
|
11
|
+
*
|
|
12
|
+
* Two roots are pinned, covering the in-flight rotation Google
|
|
13
|
+
* announced for early 2026:
|
|
14
|
+
*
|
|
15
|
+
* - **Legacy RSA-4096 root** — covers all factory-provisioned
|
|
16
|
+
* Android devices since 2014. Cannot be rotated on those devices,
|
|
17
|
+
* so it stays valid indefinitely for that fleet.
|
|
18
|
+
*
|
|
19
|
+
* - **ECDSA P-384 root** — issued 2025-07-17. Covers RKP-provisioned
|
|
20
|
+
* devices (Remote Key Provisioning, the modern flow). Devices
|
|
21
|
+
* began emitting chains rooted here on 2026-02-01; switched
|
|
22
|
+
* exclusively after 2026-04-10.
|
|
23
|
+
*
|
|
24
|
+
* Verifiers MUST pin both. Source of truth: `roots.json` in the
|
|
25
|
+
* `android/keyattestation` repository (Google's canonical Kotlin
|
|
26
|
+
* reference verifier), mirrored from Android's published attestation
|
|
27
|
+
* trust anchor list.
|
|
28
|
+
*
|
|
29
|
+
* Operator note — runtime fetching is forbidden. The Android docs are
|
|
30
|
+
* explicit: "Don't query for roots at runtime. Use formal
|
|
31
|
+
* configuration updates." A new Google root cert lands here as an
|
|
32
|
+
* additive constant, never via dynamic fetch — any other path
|
|
33
|
+
* sacrifices the sovereign-verifier story.
|
|
34
|
+
*
|
|
35
|
+
* Each constant ships its real Google-published bytes. Each comment
|
|
36
|
+
* names: source URL, subject DN, SHA-256 fingerprint, public-key
|
|
37
|
+
* algorithm, validity window. The fingerprint is the audit anchor — a
|
|
38
|
+
* third party that fetches the same source and computes its own
|
|
39
|
+
* SHA-256 should reach the byte-identical value below.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Google Hardware Attestation Root — RSA-4096 (legacy / factory-provisioned).
|
|
43
|
+
*
|
|
44
|
+
* Source: https://github.com/android/keyattestation (roots.json[0])
|
|
45
|
+
* Subject: serialNumber=f92009e853b6b045
|
|
46
|
+
* Issuer: same (self-signed)
|
|
47
|
+
* Serial: 0xf1c172a699eaf51d
|
|
48
|
+
* SHA-256: cedb1cb6dc896ae5ec797348bce9286753c2b38ee71ce0fbe34a9a1248800dfc
|
|
49
|
+
* Public key: RSA-4096
|
|
50
|
+
* Validity: 2022-03-20 → 2042-03-15
|
|
51
|
+
*/
|
|
52
|
+
export const GOOGLE_ANDROID_KEYSTORE_ROOT_RSA_PEM = `-----BEGIN CERTIFICATE-----
|
|
53
|
+
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
|
|
80
|
+
w1IdYIg2Wxg7yHcQZemFQg==
|
|
81
|
+
-----END CERTIFICATE-----
|
|
82
|
+
`;
|
|
83
|
+
/**
|
|
84
|
+
* Google Hardware Attestation Root — ECDSA P-384 (modern / RKP).
|
|
85
|
+
*
|
|
86
|
+
* Source: https://github.com/android/keyattestation (roots.json[1])
|
|
87
|
+
* Subject: CN=Key Attestation CA1, OU=Android, O=Google LLC, C=US
|
|
88
|
+
* Issuer: same (self-signed)
|
|
89
|
+
* Serial: 0x84a9d0297b0eb58ae7ff0e80de760605
|
|
90
|
+
* SHA-256: 6d9db4ce6c5c0b293166d08986e05774a8776ceb525d9e4329520de12ba4bcc0
|
|
91
|
+
* Public key: ECDSA P-384
|
|
92
|
+
* Validity: 2025-07-17 → 2035-07-15
|
|
93
|
+
*
|
|
94
|
+
* Rotation timeline (Google-published, motebit-recorded):
|
|
95
|
+
* - 2026-02-01: RKP-enabled devices begin emitting chains rooted here
|
|
96
|
+
* - 2026-03-31: target deadline for verifiers to pin both anchors
|
|
97
|
+
* - 2026-04-10: RKP-enabled devices switch exclusively to this root
|
|
98
|
+
*/
|
|
99
|
+
export const GOOGLE_ANDROID_KEYSTORE_ROOT_ECDSA_PEM = `-----BEGIN CERTIFICATE-----
|
|
100
|
+
MIICIjCCAaigAwIBAgIRAISp0Cl7DrWK5/8OgN52BgUwCgYIKoZIzj0EAwMwUjEc
|
|
101
|
+
MBoGA1UEAwwTS2V5IEF0dGVzdGF0aW9uIENBMTEQMA4GA1UECwwHQW5kcm9pZDET
|
|
102
|
+
MBEGA1UECgwKR29vZ2xlIExMQzELMAkGA1UEBhMCVVMwHhcNMjUwNzE3MjIzMjE4
|
|
103
|
+
WhcNMzUwNzE1MjIzMjE4WjBSMRwwGgYDVQQDDBNLZXkgQXR0ZXN0YXRpb24gQ0Ex
|
|
104
|
+
MRAwDgYDVQQLDAdBbmRyb2lkMRMwEQYDVQQKDApHb29nbGUgTExDMQswCQYDVQQG
|
|
105
|
+
EwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABCPaI3FO3z5bBQo8cuiEas4HjqCt
|
|
106
|
+
G/mLFfRT0MsIssPBEEU5Cfbt6sH5yOAxqEi5QagpU1yX4HwnGb7OtBYpDTB57uH5
|
|
107
|
+
Eczm34A5FNijV3s0/f0UPl7zbJcTx6xwqMIRq6NCMEAwDwYDVR0TAQH/BAUwAwEB
|
|
108
|
+
/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFFIyuyz7RkOb3NaBqQ5lZuA0QepA
|
|
109
|
+
MAoGCCqGSM49BAMDA2gAMGUCMETfjPO/HwqReR2CS7p0ZWoD/LHs6hDi422opifH
|
|
110
|
+
EUaYLxwGlT9SLdjkVpz0UUOR5wIxAIoGyxGKRHVTpqpGRFiJtQEOOTp/+s1GcxeY
|
|
111
|
+
uR2zh/80lQyu9vAFCj6E4AXc+osmRg==
|
|
112
|
+
-----END CERTIFICATE-----
|
|
113
|
+
`;
|
|
114
|
+
/**
|
|
115
|
+
* Default pinned-root set returned when a caller passes no `rootPems`
|
|
116
|
+
* override. Both Google attestation roots — pin order is irrelevant
|
|
117
|
+
* to chain validation but is documented for audit-readability.
|
|
118
|
+
*/
|
|
119
|
+
export const DEFAULT_ANDROID_KEYSTORE_TRUST_ANCHORS = [
|
|
120
|
+
GOOGLE_ANDROID_KEYSTORE_ROOT_RSA_PEM,
|
|
121
|
+
GOOGLE_ANDROID_KEYSTORE_ROOT_ECDSA_PEM,
|
|
122
|
+
];
|
|
123
|
+
/**
|
|
124
|
+
* Sentinel value the consumer-facing verifier emits on a mint path
|
|
125
|
+
* where the Kotlin bridge returns a `not_supported` failure envelope.
|
|
126
|
+
* Exposed as a constant so call sites match on a named token rather
|
|
127
|
+
* than a raw string literal.
|
|
128
|
+
*/
|
|
129
|
+
export const ANDROID_KEYSTORE_PLATFORM = "android_keystore";
|
|
130
|
+
/**
|
|
131
|
+
* The OID Google uses for the Android Key Attestation extension on
|
|
132
|
+
* the leaf certificate. Defined in the Android security docs:
|
|
133
|
+
* https://source.android.com/docs/security/features/keystore/attestation
|
|
134
|
+
*/
|
|
135
|
+
export const ANDROID_KEY_ATTESTATION_OID = "1.3.6.1.4.1.11129.2.1.17";
|
|
136
|
+
//# sourceMappingURL=google-roots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-roots.js","sourceRoot":"","sources":["../src/google-roots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BnD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG;;;;;;;;;;;;;;CAcrD,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAsB;IACvE,oCAAoC;IACpC,sCAAsC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,kBAA2B,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,0BAA0B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @motebit/crypto-android-keystore — Android Hardware-Backed Keystore
|
|
3
|
+
* Attestation adapter for motebit hardware-attestation claims.
|
|
4
|
+
*
|
|
5
|
+
* The metabolic leaf `@motebit/crypto` delegates to when a
|
|
6
|
+
* `HardwareAttestationClaim` declares `platform: "android_keystore"`.
|
|
7
|
+
* Dep-thin `@motebit/crypto` stays permissive-floor-pure; this
|
|
8
|
+
* package, also on the permissive floor (Apache-2.0), metabolizes
|
|
9
|
+
* `@peculiar/x509` plus a hand-rolled DER walker for the AOSP Key
|
|
10
|
+
* Attestation extension to judge whether a Google-published
|
|
11
|
+
* Hardware Attestation root signed the leaf that the device's
|
|
12
|
+
* Trusted-Environment / StrongBox-backed key signed.
|
|
13
|
+
*
|
|
14
|
+
* Wiring from a consumer:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { verify } from "@motebit/crypto";
|
|
18
|
+
* import { androidKeystoreVerifier } from "@motebit/crypto-android-keystore";
|
|
19
|
+
*
|
|
20
|
+
* const result = await verify(credential, {
|
|
21
|
+
* hardwareAttestation: {
|
|
22
|
+
* android_keystore: androidKeystoreVerifier({
|
|
23
|
+
* expectedAttestationApplicationId,
|
|
24
|
+
* }),
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* This package exports no global state, no side-effect registrations;
|
|
30
|
+
* the injection is call-site only. Pinned Google attestation roots
|
|
31
|
+
* live in `./google-roots.ts` and are the self-attesting audit
|
|
32
|
+
* surface — a third party that fetches the same roots.json and
|
|
33
|
+
* computes its own SHA-256 should reach the byte-identical
|
|
34
|
+
* fingerprints documented inline.
|
|
35
|
+
*/
|
|
36
|
+
import type { HardwareAttestationClaim } from "@motebit/protocol";
|
|
37
|
+
import type { AndroidKeystoreRevocationSnapshot, AndroidKeystoreVerifyResult } from "./verify.js";
|
|
38
|
+
export { parseKeyDescription, SECURITY_LEVEL_SOFTWARE, SECURITY_LEVEL_TRUSTED_ENVIRONMENT, SECURITY_LEVEL_STRONG_BOX, VERIFIED_BOOT_STATE_VERIFIED, VERIFIED_BOOT_STATE_SELF_SIGNED, VERIFIED_BOOT_STATE_UNVERIFIED, VERIFIED_BOOT_STATE_FAILED, type RootOfTrust, type AuthorizationList, type KeyDescription, } from "./asn1.js";
|
|
39
|
+
export { ANDROID_KEYSTORE_PLATFORM, ANDROID_KEY_ATTESTATION_OID, GOOGLE_ANDROID_KEYSTORE_ROOT_RSA_PEM, GOOGLE_ANDROID_KEYSTORE_ROOT_ECDSA_PEM, DEFAULT_ANDROID_KEYSTORE_TRUST_ANCHORS, } from "./google-roots.js";
|
|
40
|
+
export { verifyAndroidKeystoreAttestation, EMPTY_REVOCATION_SNAPSHOT } from "./verify.js";
|
|
41
|
+
export type { AndroidKeystoreVerifyOptions, AndroidKeystoreVerifyResult, AndroidKeystoreVerifyError, AndroidKeystoreRevocationSnapshot, } from "./verify.js";
|
|
42
|
+
/**
|
|
43
|
+
* Shape the optional verifier injected into `@motebit/crypto`'s
|
|
44
|
+
* `HardwareAttestationVerifiers.android_keystore` slot carries.
|
|
45
|
+
* Mirrors the sync-or-async return shape the dispatcher supports
|
|
46
|
+
* and extends the canonical shape with `attestation_detail` so
|
|
47
|
+
* callers can introspect chain / extension / binding independently.
|
|
48
|
+
*/
|
|
49
|
+
export interface AndroidKeystoreVerifierResult {
|
|
50
|
+
readonly valid: boolean;
|
|
51
|
+
readonly platform: "android_keystore";
|
|
52
|
+
readonly errors: ReadonlyArray<{
|
|
53
|
+
readonly message: string;
|
|
54
|
+
}>;
|
|
55
|
+
readonly attestation_detail?: AndroidKeystoreVerifyResult;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* VC-subject fields the dispatcher lifts out of the credential and
|
|
59
|
+
* threads through to the verifier. All three participate in the JCS
|
|
60
|
+
* canonical body that derives the attestation challenge; without them
|
|
61
|
+
* the verifier reports the missing channel explicitly rather than
|
|
62
|
+
* silently passing.
|
|
63
|
+
*/
|
|
64
|
+
export interface AndroidKeystoreVerifierContext {
|
|
65
|
+
readonly expectedMotebitId?: string;
|
|
66
|
+
readonly expectedDeviceId?: string;
|
|
67
|
+
readonly expectedAttestedAt?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface AndroidKeystoreVerifierConfig {
|
|
70
|
+
/**
|
|
71
|
+
* Bound to the registered Android package's
|
|
72
|
+
* `attestationApplicationId` byte representation (raw OCTET STRING
|
|
73
|
+
* captured at registration time). Required at wiring time so the
|
|
74
|
+
* package binding constraint fires on every claim.
|
|
75
|
+
*/
|
|
76
|
+
readonly expectedAttestationApplicationId: Uint8Array;
|
|
77
|
+
/** Optional: override the pinned Google attestation roots. */
|
|
78
|
+
readonly rootPems?: readonly string[];
|
|
79
|
+
/** Optional: caller-supplied revocation snapshot. */
|
|
80
|
+
readonly revocationSnapshot?: AndroidKeystoreRevocationSnapshot;
|
|
81
|
+
/** Optional: allowlist of `verifiedBootState` ENUMERATED values. */
|
|
82
|
+
readonly verifiedBootStateAllowlist?: readonly number[];
|
|
83
|
+
/** Optional: minimum `attestationSecurityLevel` (default TRUSTED_ENVIRONMENT). */
|
|
84
|
+
readonly minSecurityLevel?: number;
|
|
85
|
+
/** Optional: minimum `attestationVersion` (default 3 / Keymaster 3). */
|
|
86
|
+
readonly minAttestationVersion?: number;
|
|
87
|
+
/** Optional: inject a fixed clock for deterministic chain validity. */
|
|
88
|
+
readonly now?: () => number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Factory — build an `android_keystore` verifier bound to a registered
|
|
92
|
+
* package. The returned function matches the
|
|
93
|
+
* `HardwareAttestationVerifiers.android_keystore` three-arg signature
|
|
94
|
+
* the `@motebit/crypto` dispatcher calls.
|
|
95
|
+
*/
|
|
96
|
+
export declare function androidKeystoreVerifier(config: AndroidKeystoreVerifierConfig): (claim: HardwareAttestationClaim, expectedIdentityHex: string, context?: AndroidKeystoreVerifierContext) => Promise<AndroidKeystoreVerifierResult>;
|
|
97
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,KAAK,EACV,iCAAiC,EAEjC,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,kCAAkC,EAClC,yBAAyB,EACzB,4BAA4B,EAC5B,+BAA+B,EAC/B,8BAA8B,EAC9B,0BAA0B,EAC1B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,oCAAoC,EACpC,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gCAAgC,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAC1F,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,EAC1B,iCAAiC,GAClC,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,2BAA2B,CAAC;CAC3D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,8BAA8B;IAC7C,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,6BAA6B;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,gCAAgC,EAAE,UAAU,CAAC;IACtD,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,qDAAqD;IACrD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,iCAAiC,CAAC;IAChE,oEAAoE;IACpE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxD,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,6BAA6B,GACpC,CACD,KAAK,EAAE,wBAAwB,EAC/B,mBAAmB,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE,8BAA8B,KACrC,OAAO,CAAC,6BAA6B,CAAC,CAqC1C"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @motebit/crypto-android-keystore — Android Hardware-Backed Keystore
|
|
3
|
+
* Attestation adapter for motebit hardware-attestation claims.
|
|
4
|
+
*
|
|
5
|
+
* The metabolic leaf `@motebit/crypto` delegates to when a
|
|
6
|
+
* `HardwareAttestationClaim` declares `platform: "android_keystore"`.
|
|
7
|
+
* Dep-thin `@motebit/crypto` stays permissive-floor-pure; this
|
|
8
|
+
* package, also on the permissive floor (Apache-2.0), metabolizes
|
|
9
|
+
* `@peculiar/x509` plus a hand-rolled DER walker for the AOSP Key
|
|
10
|
+
* Attestation extension to judge whether a Google-published
|
|
11
|
+
* Hardware Attestation root signed the leaf that the device's
|
|
12
|
+
* Trusted-Environment / StrongBox-backed key signed.
|
|
13
|
+
*
|
|
14
|
+
* Wiring from a consumer:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { verify } from "@motebit/crypto";
|
|
18
|
+
* import { androidKeystoreVerifier } from "@motebit/crypto-android-keystore";
|
|
19
|
+
*
|
|
20
|
+
* const result = await verify(credential, {
|
|
21
|
+
* hardwareAttestation: {
|
|
22
|
+
* android_keystore: androidKeystoreVerifier({
|
|
23
|
+
* expectedAttestationApplicationId,
|
|
24
|
+
* }),
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* This package exports no global state, no side-effect registrations;
|
|
30
|
+
* the injection is call-site only. Pinned Google attestation roots
|
|
31
|
+
* live in `./google-roots.ts` and are the self-attesting audit
|
|
32
|
+
* surface — a third party that fetches the same roots.json and
|
|
33
|
+
* computes its own SHA-256 should reach the byte-identical
|
|
34
|
+
* fingerprints documented inline.
|
|
35
|
+
*/
|
|
36
|
+
import { verifyAndroidKeystoreAttestation } from "./verify.js";
|
|
37
|
+
export { parseKeyDescription, SECURITY_LEVEL_SOFTWARE, SECURITY_LEVEL_TRUSTED_ENVIRONMENT, SECURITY_LEVEL_STRONG_BOX, VERIFIED_BOOT_STATE_VERIFIED, VERIFIED_BOOT_STATE_SELF_SIGNED, VERIFIED_BOOT_STATE_UNVERIFIED, VERIFIED_BOOT_STATE_FAILED, } from "./asn1.js";
|
|
38
|
+
export { ANDROID_KEYSTORE_PLATFORM, ANDROID_KEY_ATTESTATION_OID, GOOGLE_ANDROID_KEYSTORE_ROOT_RSA_PEM, GOOGLE_ANDROID_KEYSTORE_ROOT_ECDSA_PEM, DEFAULT_ANDROID_KEYSTORE_TRUST_ANCHORS, } from "./google-roots.js";
|
|
39
|
+
export { verifyAndroidKeystoreAttestation, EMPTY_REVOCATION_SNAPSHOT } from "./verify.js";
|
|
40
|
+
/**
|
|
41
|
+
* Factory — build an `android_keystore` verifier bound to a registered
|
|
42
|
+
* package. The returned function matches the
|
|
43
|
+
* `HardwareAttestationVerifiers.android_keystore` three-arg signature
|
|
44
|
+
* the `@motebit/crypto` dispatcher calls.
|
|
45
|
+
*/
|
|
46
|
+
export function androidKeystoreVerifier(config) {
|
|
47
|
+
return async (claim, expectedIdentityHex, context) => {
|
|
48
|
+
const opts = {
|
|
49
|
+
expectedAttestationApplicationId: config.expectedAttestationApplicationId,
|
|
50
|
+
expectedIdentityPublicKeyHex: expectedIdentityHex,
|
|
51
|
+
...(config.rootPems !== undefined ? { rootPems: config.rootPems } : {}),
|
|
52
|
+
...(config.revocationSnapshot !== undefined
|
|
53
|
+
? { revocationSnapshot: config.revocationSnapshot }
|
|
54
|
+
: {}),
|
|
55
|
+
...(config.verifiedBootStateAllowlist !== undefined
|
|
56
|
+
? { verifiedBootStateAllowlist: config.verifiedBootStateAllowlist }
|
|
57
|
+
: {}),
|
|
58
|
+
...(config.minSecurityLevel !== undefined
|
|
59
|
+
? { minSecurityLevel: config.minSecurityLevel }
|
|
60
|
+
: {}),
|
|
61
|
+
...(config.minAttestationVersion !== undefined
|
|
62
|
+
? { minAttestationVersion: config.minAttestationVersion }
|
|
63
|
+
: {}),
|
|
64
|
+
...(config.now !== undefined ? { now: config.now } : {}),
|
|
65
|
+
...(context?.expectedMotebitId !== undefined
|
|
66
|
+
? { expectedMotebitId: context.expectedMotebitId }
|
|
67
|
+
: {}),
|
|
68
|
+
...(context?.expectedDeviceId !== undefined
|
|
69
|
+
? { expectedDeviceId: context.expectedDeviceId }
|
|
70
|
+
: {}),
|
|
71
|
+
...(context?.expectedAttestedAt !== undefined
|
|
72
|
+
? { expectedAttestedAt: context.expectedAttestedAt }
|
|
73
|
+
: {}),
|
|
74
|
+
};
|
|
75
|
+
const detail = await verifyAndroidKeystoreAttestation(claim, opts);
|
|
76
|
+
return {
|
|
77
|
+
valid: detail.valid,
|
|
78
|
+
platform: "android_keystore",
|
|
79
|
+
errors: detail.errors,
|
|
80
|
+
attestation_detail: detail,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAO/D,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,kCAAkC,EAClC,yBAAyB,EACzB,4BAA4B,EAC5B,+BAA+B,EAC/B,8BAA8B,EAC9B,0BAA0B,GAI3B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,oCAAoC,EACpC,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gCAAgC,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAyD1F;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAqC;IAMrC,OAAO,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE;QACnD,MAAM,IAAI,GAAiC;YACzC,gCAAgC,EAAE,MAAM,CAAC,gCAAgC;YACzE,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,kBAAkB,KAAK,SAAS;gBACzC,CAAC,CAAC,EAAE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,EAAE;gBACnD,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,0BAA0B,KAAK,SAAS;gBACjD,CAAC,CAAC,EAAE,0BAA0B,EAAE,MAAM,CAAC,0BAA0B,EAAE;gBACnE,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,SAAS;gBACvC,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE;gBAC/C,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,qBAAqB,KAAK,SAAS;gBAC5C,CAAC,CAAC,EAAE,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE;gBACzD,CAAC,CAAC,EAAE,CAAC;YACP,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,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,gCAAgC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnE,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,kBAAkB;YAC5B,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,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android Hardware-Backed Keystore Attestation verifier — the core
|
|
3
|
+
* judgment function this package exports.
|
|
4
|
+
*
|
|
5
|
+
* Flow (matches Google's published verification recipe at
|
|
6
|
+
* https://source.android.com/docs/security/features/keystore/attestation,
|
|
7
|
+
* plus the motebit-specific identity-key binding step):
|
|
8
|
+
*
|
|
9
|
+
* 1. Split the receipt into the leaf cert plus the rest of the chain
|
|
10
|
+
* (`{leafCertB64}.{intermediatesJoinedB64}` — comma-joined leaf-
|
|
11
|
+
* proximal-first base64url DER blobs in the second segment).
|
|
12
|
+
* 2. Parse the chain as X.509 certificates. Walk the chain leaf →
|
|
13
|
+
* intermediates → terminal anchor with `@peculiar/x509`'s
|
|
14
|
+
* `X509ChainBuilder`. Every non-leaf must carry
|
|
15
|
+
* `basicConstraints.cA === true`. Every signature must verify
|
|
16
|
+
* under its issuer's public key. Every cert must be within its
|
|
17
|
+
* validity window. The terminal cert's DER must equal one of the
|
|
18
|
+
* pinned Google attestation roots.
|
|
19
|
+
* 3. Read the Android Key Attestation extension (OID
|
|
20
|
+
* `1.3.6.1.4.1.11129.2.1.17`) from the LEAF cert only. The AOSP
|
|
21
|
+
* spec is explicit that later occurrences of this extension up
|
|
22
|
+
* the chain MUST be ignored — only the leaf's copy carries
|
|
23
|
+
* trustworthy data, because only the leaf is signed by the
|
|
24
|
+
* device's secure-hardware key.
|
|
25
|
+
* 4. Constrain the parsed `KeyDescription`:
|
|
26
|
+
* - `attestationSecurityLevel ≥ TRUSTED_ENVIRONMENT` (rejects
|
|
27
|
+
* software-only fallback, which is structurally not
|
|
28
|
+
* third-party meaningful)
|
|
29
|
+
* - `attestationVersion ≥ 3` (rejects pre-Android-7 / Keymaster
|
|
30
|
+
* v2; current production is 4 / Keymaster 4 through 400 /
|
|
31
|
+
* KeyMint 4.0)
|
|
32
|
+
* - `hardwareEnforced.rootOfTrust.verifiedBootState` is in the
|
|
33
|
+
* caller's allowlist (default: VERIFIED only)
|
|
34
|
+
* - `hardwareEnforced.attestationApplicationId` byte-equals
|
|
35
|
+
* the caller's expected package binding
|
|
36
|
+
* - leaf's serial number is not in the caller-supplied
|
|
37
|
+
* revocation snapshot
|
|
38
|
+
* 5. Cryptographically bind the leaf's `attestationChallenge` field
|
|
39
|
+
* to the motebit Ed25519 identity: re-derive
|
|
40
|
+
* `SHA-256(canonicalJson({ attested_at, device_id,
|
|
41
|
+
* identity_public_key, motebit_id, platform: "android_keystore",
|
|
42
|
+
* version: "1" }))` and byte-compare against the transmitted
|
|
43
|
+
* challenge. A malicious client that substitutes any other body
|
|
44
|
+
* fails here.
|
|
45
|
+
*
|
|
46
|
+
* Pure. No network. No filesystem. Deterministic given `now()` and
|
|
47
|
+
* the caller-supplied revocation snapshot.
|
|
48
|
+
*/
|
|
49
|
+
import type { HardwareAttestationClaim } from "@motebit/protocol";
|
|
50
|
+
/**
|
|
51
|
+
* Caller-supplied revocation snapshot keyed by lowercase-hex serial
|
|
52
|
+
* number — mirrors Google's published shape at
|
|
53
|
+
* https://android.googleapis.com/attestation/status. The motebit
|
|
54
|
+
* verifier never fetches this at runtime; the canonical CLI in
|
|
55
|
+
* `@motebit/verify` ships an embedded snapshot at release time and
|
|
56
|
+
* accepts an override path. Empty snapshot is the no-revocation-data
|
|
57
|
+
* default and means every leaf passes the revocation check.
|
|
58
|
+
*/
|
|
59
|
+
export interface AndroidKeystoreRevocationSnapshot {
|
|
60
|
+
readonly entries: Readonly<Record<string, {
|
|
61
|
+
readonly status: "REVOKED" | "SUSPENDED";
|
|
62
|
+
readonly reason?: string;
|
|
63
|
+
}>>;
|
|
64
|
+
}
|
|
65
|
+
/** Empty revocation snapshot — every leaf passes the revocation check. */
|
|
66
|
+
export declare const EMPTY_REVOCATION_SNAPSHOT: AndroidKeystoreRevocationSnapshot;
|
|
67
|
+
export interface AndroidKeystoreVerifyOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Android package name + signing-cert SHA-256 hash binding the
|
|
70
|
+
* leaf's `attestationApplicationId` MUST match. The expected value
|
|
71
|
+
* is the byte-identical encoding the Kotlin mint path produces —
|
|
72
|
+
* either a raw OCTET STRING capture or a wrapped representation,
|
|
73
|
+
* depending on the surface. Implementations typically supply the
|
|
74
|
+
* raw bytes captured at registration time.
|
|
75
|
+
*/
|
|
76
|
+
readonly expectedAttestationApplicationId: Uint8Array;
|
|
77
|
+
/**
|
|
78
|
+
* Ed25519 identity key (lowercase hex) the motebit VC claims. The
|
|
79
|
+
* leaf's `attestationChallenge` MUST bind this key (via the
|
|
80
|
+
* canonical-body re-derivation).
|
|
81
|
+
*/
|
|
82
|
+
readonly expectedIdentityPublicKeyHex: string;
|
|
83
|
+
/**
|
|
84
|
+
* motebit_id from the credential subject. Participates in the JCS
|
|
85
|
+
* canonical body re-derived here and byte-compared against the
|
|
86
|
+
* transmitted challenge.
|
|
87
|
+
*/
|
|
88
|
+
readonly expectedMotebitId?: string;
|
|
89
|
+
/** device_id from the credential subject. Same binding role. */
|
|
90
|
+
readonly expectedDeviceId?: string;
|
|
91
|
+
/** `attested_at` (unix ms) from the credential subject. Same binding role. */
|
|
92
|
+
readonly expectedAttestedAt?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Override the pinned trust anchors. Tests fabricate their own root
|
|
95
|
+
* so chain verification exercises the same code path without needing
|
|
96
|
+
* a real device-signed leaf. Defaults to
|
|
97
|
+
* `DEFAULT_ANDROID_KEYSTORE_TRUST_ANCHORS` (RSA + ECDSA P-384).
|
|
98
|
+
*/
|
|
99
|
+
readonly rootPems?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* Allowlist of `verifiedBootState` ENUMERATED values the verifier
|
|
102
|
+
* accepts. Default = `[VERIFIED]` (Google-signed bootloader). Set
|
|
103
|
+
* to `[VERIFIED, SELF_SIGNED]` to allow GrapheneOS-style
|
|
104
|
+
* user-installed roots-of-trust per their published attestation
|
|
105
|
+
* compatibility model. Empty array = accept any state (NOT
|
|
106
|
+
* recommended — leaks the boot-image guarantee).
|
|
107
|
+
*/
|
|
108
|
+
readonly verifiedBootStateAllowlist?: readonly number[];
|
|
109
|
+
/**
|
|
110
|
+
* Minimum `attestationSecurityLevel` the verifier accepts. Default =
|
|
111
|
+
* `TRUSTED_ENVIRONMENT` (1). Software-only attestations (level 0)
|
|
112
|
+
* are structurally not third-party meaningful and are rejected.
|
|
113
|
+
* StrongBox (2) is a higher score in the semiring, not an admission
|
|
114
|
+
* gate — pass `TRUSTED_ENVIRONMENT` here and let the score-side
|
|
115
|
+
* code differentiate.
|
|
116
|
+
*/
|
|
117
|
+
readonly minSecurityLevel?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Minimum `attestationVersion` the verifier accepts. Default = 3
|
|
120
|
+
* (Keymaster 3 / Android 7 — earliest version with the modern chain
|
|
121
|
+
* shape). Anything below this is rejected.
|
|
122
|
+
*/
|
|
123
|
+
readonly minAttestationVersion?: number;
|
|
124
|
+
/**
|
|
125
|
+
* Caller-supplied revocation snapshot. Defaults to empty (no
|
|
126
|
+
* revocation enforcement). Production callers should supply
|
|
127
|
+
* Google's status-list shape; `@motebit/verify` ships an embedded
|
|
128
|
+
* snapshot at release time.
|
|
129
|
+
*/
|
|
130
|
+
readonly revocationSnapshot?: AndroidKeystoreRevocationSnapshot;
|
|
131
|
+
/** Clock for chain-validity checks. Defaults to `Date.now`. */
|
|
132
|
+
readonly now?: () => number;
|
|
133
|
+
}
|
|
134
|
+
export interface AndroidKeystoreVerifyError {
|
|
135
|
+
readonly message: string;
|
|
136
|
+
}
|
|
137
|
+
export interface AndroidKeystoreVerifyResult {
|
|
138
|
+
readonly valid: boolean;
|
|
139
|
+
readonly cert_chain_valid: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* True when the leaf carried a parseable Key Attestation extension
|
|
142
|
+
* AND every constraint check (security level, attestation version,
|
|
143
|
+
* verified-boot state, application-ID match, revocation lookup)
|
|
144
|
+
* passed.
|
|
145
|
+
*/
|
|
146
|
+
readonly attestation_extension_valid: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* True when `attestationChallenge` byte-equals
|
|
149
|
+
* `SHA256(canonical body)` for the caller-supplied identity.
|
|
150
|
+
*/
|
|
151
|
+
readonly identity_bound: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* The `attestationSecurityLevel` parsed off the leaf (or null if
|
|
154
|
+
* the extension wasn't parseable). Exposed so callers can surface
|
|
155
|
+
* `TRUSTED_ENVIRONMENT` vs `STRONG_BOX` in an audit UI alongside
|
|
156
|
+
* the pass/fail verdict — and so a routing semiring can score
|
|
157
|
+
* StrongBox higher than plain TEE.
|
|
158
|
+
*/
|
|
159
|
+
readonly attestation_security_level: number | null;
|
|
160
|
+
/**
|
|
161
|
+
* The `verifiedBootState` parsed off the leaf's `rootOfTrust` (or
|
|
162
|
+
* null if absent / extension unparseable). Same audit-surface
|
|
163
|
+
* rationale as `attestation_security_level`.
|
|
164
|
+
*/
|
|
165
|
+
readonly verified_boot_state: number | null;
|
|
166
|
+
readonly errors: readonly AndroidKeystoreVerifyError[];
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Android Hardware-Backed Keystore Attestation verifier.
|
|
170
|
+
*
|
|
171
|
+
* `claim.attestation_receipt` is the cert chain encoded as
|
|
172
|
+
* `{leafCertB64}.{intermediatesJoinedB64}` — leaf-first DER chain
|
|
173
|
+
* matching the wire format the Kotlin `expo-android-keystore` mint
|
|
174
|
+
* path emits. The intermediates segment is a comma-joined list of
|
|
175
|
+
* base64url-encoded DERs in leaf-proximal-first order; an empty
|
|
176
|
+
* second segment means the leaf chains directly to a pinned root.
|
|
177
|
+
*/
|
|
178
|
+
export declare function verifyAndroidKeystoreAttestation(claim: HardwareAttestationClaim, opts: AndroidKeystoreVerifyOptions): Promise<AndroidKeystoreVerifyResult>;
|
|
179
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAIH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAalE;;;;;;;;GAQG;AACH,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,OAAO,EAAE,QAAQ,CACxB,MAAM,CACJ,MAAM,EACN;QACE,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;QACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KAC1B,CACF,CACF,CAAC;CACH;AAED,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,iCAAmD,CAAC;AAE5F,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,gCAAgC,EAAE,UAAU,CAAC;IACtD;;;;OAIG;IACH,QAAQ,CAAC,4BAA4B,EAAE,MAAM,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,0BAA0B,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxD;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,iCAAiC,CAAC;IAChE,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,SAAS,0BAA0B,EAAE,CAAC;CACxD;AAED;;;;;;;;;GASG;AACH,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,2BAA2B,CAAC,CAiHtC"}
|