Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# `android-safetynet` attestation verifier
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Implements the W3C WebAuthn L3 §8.5 `android-safetynet` attestation format. Google's SafetyNet API issues a JWS attestation token whose payload binds the credential creation challenge (`nonce = base64(sha256(authData || clientDataHash))`) to a Google-signed claim that the device passed the SafetyNet integrity checks. The verifier delegates JWS structural parsing to `@suaveplan/jws`, confirms the nonce, and surfaces the x5c chain for the caller to bind to the Google attestation root.
## Features
- JWS parsing via `@suaveplan/jws` — no inline JWT/JOSE decoding.
- `nonce` claim cross-bind via constant-time compare against `base64(sha256(authData || clientDataHash))`.
- `x5c` chain extracted from the JWS header, decoded into raw DER bytes, and surfaced via `trustPath`.
- Constant-time `rpIdHash` compare.
- Reports `attestationType: "basic"`.
## Usage: Basic Example
```ts
import { verifyAndroidSafetynetAttestation } from "@suaveplan/webauthn/attestation/android-safetynet";
const result = await verifyAndroidSafetynetAttestation({
attestationObject,
clientDataHash,
expectedRpIdHash,
});
```
## Usage: Advanced Example
```ts
import {
verifyAndroidSafetynetAttestation,
type AttestationFormatRegistry,
} from "@suaveplan/webauthn";
function strictSafetynetRegistry(): AttestationFormatRegistry {
return new Map([
[
"android-safetynet",
async (input) => {
const r = await verifyAndroidSafetynetAttestation(input);
await assertGoogleAttestationRoot(r.trustPath);
return r;
},
],
]);
}
```
## API Reference
- `verifyAndroidSafetynetAttestation: AttestationFormatVerifier` — the format verifier.
## Implementation Notes
- Google has deprecated SafetyNet in favour of the Play Integrity API. The format is retained because Android devices still emit it during the transition window; new deployments SHOULD NOT depend on it for new policy.
- Full JWS signature verification against the Google attestation root is intentionally NOT performed here — the root cert pool changes and the caller's metadata layer is the right place for it. The verifier surfaces the x5c chain via `trustPath` and confirms the nonce binding so the caller can do the full chain check with the correct trust anchor.
- The `nonce` is base64 (not base64url) per the SafetyNet spec; the verifier explicitly encodes via `btoa` rather than via `toBase64Url` to match.