first commit
This commit is contained in:
@@ -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.
|
||||
84
docs/security-stack/crypto/crypto-rsa/src/jwk/thumbprint.md
Normal file
84
docs/security-stack/crypto/crypto-rsa/src/jwk/thumbprint.md
Normal 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.
|
||||
Reference in New Issue
Block a user