first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
# jwk/thumbprint-uri
## Purpose
Compute the JWK Thumbprint URI per RFC 9278 §3 — the canonical
`urn:ietf:params:oauth:jwk-thumbprint:<hash-alg>:<base64url(thumbprint)>`
form used by OAuth 2.0 Demonstrating Proof-of-Possession (DPoP),
draft-ietf-oauth-dpop, and other OAuth/OIDC binding protocols. Wraps
`jwkThumbprint` with the hash-algorithm label and URN prefix.
## Features
- RFC 9278 §3 conformant URN format
- Default SHA-256 hash; SHA-1 / SHA-384 / SHA-512 supported
- Hash labels match RFC 9278 §3.1 registry: `sha-1`, `sha-256`, `sha-384`,
`sha-512`
- Returns a deterministic `string` ready to embed in OAuth tokens, DPoP
proofs, or `iss`/`sub` claims
## Usage: Basic Example
```ts
import { jwkThumbprintUri } from "@suaveplan/crypto-rsa/jwk";
const uri = jwkThumbprintUri(publicKey);
// "urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8u..."
```
## Usage: Advanced Example
For protocols that pin a non-default hash (e.g. SHA-512 for
high-assurance bindings), pass the `hash` option:
```ts
import { jwkThumbprintUri } from "@suaveplan/crypto-rsa/jwk";
const sha512Uri = jwkThumbprintUri(publicKey, { hash: "SHA-512" });
// "urn:ietf:params:oauth:jwk-thumbprint:sha-512:..."
```
## API Reference
### `jwkThumbprintUri(key, options?)`
- `key: RsaPublicKey | RsaJwk` — public key or JWK
- `options.hash?: RsaHashAlgorithm` — defaults to `"SHA-256"`
- **returns** `string` — full URN
- **throws** `CryptoRsaError(CRYPTO_RSA.UNSUPPORTED_HASH)` /
`CRYPTO_RSA.INVALID_KEY` (propagated from `jwkThumbprint`)
## Implementation Notes
- The hash-label mapping is a frozen lookup table mirroring the
IANA-registered names in RFC 9278 §3.1. Adding new RSA hash
algorithms (e.g. SHA-3) requires updating both this table and the
`RsaHashAlgorithm` union in `keys.types.ts`.
- This helper is a thin wrapper over `jwkThumbprint` + `toBase64Url`;
it adds no cryptographic complexity, only the URN framing. Tests
cover the framing for every supported hash.

View File

@@ -0,0 +1,84 @@
# jwk/thumbprint
## Purpose
Compute the JWK Thumbprint of an RSA public key per RFC 7638 §3.2. The
thumbprint is a deterministic hash over the canonical JSON form of the
public-key members (`e`, `kty`, `n` in alphabetical order with no
whitespace), giving every RSA key a stable identifier independent of
its wire format. Used downstream for `kid` derivation, key rotation
diffing, and key-pinning protocols.
## Features
- RFC 7638 §3.2 conformant — RSA member ordering `e`, `kty`, `n`
- Default SHA-256 hash; SHA-1 / SHA-384 / SHA-512 supported via `hash` option
- Accepts both canonical `RsaPublicKey` and `RsaJwk` shapes
- Hand-built canonical JSON byte string (does NOT use `JSON.stringify` —
member order is implementation-defined for that API)
- Worked-example matched against the RFC 7638 §3.1 reference value
`"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"`
- `jwkThumbprintBase64Url` convenience returns the URL-safe base64 encoding directly
- All thrown errors are `CryptoRsaError` with `INVALID_KEY` /
`UNSUPPORTED_HASH`
## Usage: Basic Example
```ts
import { jwkThumbprint } from "@suaveplan/crypto-rsa/jwk";
const fingerprint = jwkThumbprint(publicKey);
// 32 bytes (SHA-256 default)
```
## Usage: Advanced Example
The `kid` JWK member is conventionally set to the base64url-encoded
SHA-256 thumbprint. Combine `jwkThumbprintBase64Url` with
`serializeRsaPublicKeyJwk` to populate it on emit:
```ts
import {
jwkThumbprintBase64Url,
serializeRsaPublicKeyJwk,
} from "@suaveplan/crypto-rsa";
const baseJwk = serializeRsaPublicKeyJwk(publicKey);
const jwkWithKid = {
...baseJwk,
kid: jwkThumbprintBase64Url(publicKey),
};
```
## API Reference
### `jwkThumbprint(key, options?)`
- `key: RsaPublicKey | RsaJwk` — public key or JWK
- `options.hash?: RsaHashAlgorithm` — defaults to `"SHA-256"`
- **returns** `Uint8Array` — digest bytes
- **throws** `CryptoRsaError(CRYPTO_RSA.UNSUPPORTED_HASH)` on bad hash;
`CRYPTO_RSA.INVALID_KEY` on missing `e` or `n`
### `jwkThumbprintBase64Url(key, options?)`
Convenience: returns `toBase64Url(jwkThumbprint(key, options))`.
### `JwkThumbprintOptions`
- `hash?: RsaHashAlgorithm`
## Implementation Notes
- The canonical JSON byte string is `'{"e":"<b64u(e)>","kty":"RSA","n":"<b64u(n)>"}'`.
Members are alphabetically ordered per RFC 7638 §3.2; only the three
required members participate in the digest.
- `JSON.stringify` is NOT used because member-order in object iteration
is implementation-defined for non-integer keys; constructing the byte
string by hand is the only correctness-stable approach.
- When the input is a `RsaPublicKey` (canonical bigint form), the
implementation routes through `serializeRsaPublicKeyJwk` to obtain
the base64url members, then concatenates the canonical JSON shape.
- Hash algorithm validation runs BEFORE member-validity checks so
consumers see `UNSUPPORTED_HASH` on bad hashes regardless of key
validity — useful for fail-fast configuration validators.