@sd-jwt/types
Version:
sd-jwt draft 7 implementation in typescript
244 lines (222 loc) • 5.47 kB
text/typescript
export const SD_SEPARATOR = '~';
export const SD_LIST_KEY = '...';
export const SD_DIGEST = '_sd';
export const SD_DECOY = '_sd_decoy';
export const KB_JWT_TYP = 'kb+jwt';
export type SDJWTCompact = string;
export type Base64urlString = string;
export type DisclosureData<T> = [string, string, T] | [string, T];
// based on https://www.iana.org/assignments/named-information/named-information.xhtml
export const IANA_HASH_ALGORITHMS = [
'sha-256',
'sha-256-128',
'sha-256-120',
'sha-256-96',
'sha-256-64',
'sha-256-32',
'sha-384',
'sha-512',
'sha3-224',
'sha3-256',
'sha3-384',
'sha3-512',
'blake2s-256',
'blake2b-256',
'blake2b-512',
'k12-256',
'k12-512',
] as const;
export type HashAlgorithm = (typeof IANA_HASH_ALGORITHMS)[number];
export type SDJWTConfig = {
omitTyp?: boolean;
hasher?: Hasher;
hashAlg?: HashAlgorithm;
saltGenerator?: SaltGenerator;
signer?: Signer;
signAlg?: string;
verifier?: Verifier;
kbSigner?: Signer;
kbSignAlg?: string;
kbVerifier?: KbVerifier;
};
export type kbHeader = { typ: 'kb+jwt'; alg: string };
export type kbPayload = {
iat: number;
aud: string;
nonce: string;
sd_hash: string;
};
export type KBOptions = {
payload: Omit<kbPayload, 'sd_hash'>;
};
// This type declaration is from lib.dom.ts
interface RsaOtherPrimesInfo {
d?: string;
r?: string;
t?: string;
}
interface JsonWebKey {
alg?: string;
crv?: string;
d?: string;
dp?: string;
dq?: string;
e?: string;
ext?: boolean;
k?: string;
key_ops?: string[];
kty?: string;
n?: string;
oth?: RsaOtherPrimesInfo[];
p?: string;
q?: string;
qi?: string;
use?: string;
x?: string;
y?: string;
}
export interface JwtPayload {
cnf?: {
jwk: JsonWebKey;
};
exp?: number;
[key: string]: unknown;
}
export type OrPromise<T> = T | Promise<T>;
export type Signer = (data: string) => OrPromise<string>;
export type Verifier = (data: string, sig: string) => OrPromise<boolean>;
export type KbVerifier = (
data: string,
sig: string,
payload: JwtPayload,
) => OrPromise<boolean>;
export type Hasher = (
data: string | ArrayBuffer,
alg: string,
) => OrPromise<Uint8Array>;
export type SaltGenerator = (length: number) => OrPromise<string>;
export type HasherAndAlg = {
hasher: Hasher;
alg: string;
};
// This functions are sync versions
export type SignerSync = (data: string) => string;
export type VerifierSync = (data: string, sig: string) => boolean;
export type HasherSync = (data: string, alg: string) => Uint8Array;
export type SaltGeneratorSync = (length: number) => string;
export type HasherAndAlgSync = {
hasher: HasherSync;
alg: string;
};
type NonNever<T> = {
[P in keyof T as T[P] extends never ? never : P]: T[P];
};
export type SD<Payload> = { [SD_DIGEST]?: Array<keyof Payload> };
export type DECOY = { [SD_DECOY]?: number };
/**
* This is a disclosureFrame type that is used to represent the structure of what is being disclosed.
* DisclosureFrame is made from the payload type.
*
* For example, if the payload is
* {
* foo: 'bar',
* test: {
* zzz: 'yyy',
* }
* arr: ['1', '2', {a: 'b'}]
* }
*
* The disclosureFrame can be subset of:
* {
* _sd: ["foo", "test", "arr"],
* test: {
* _sd: ["zzz"],
* },
* arr: {
* _sd: ["0", "1", "2"],
* "2": {
* _sd: ["a"],
* }
* }
* }
*
* The disclosureFrame can be used with decoy.
* Decoy can be used like this:
* {
* ...
* _sd: ...
* _sd_decoy: 1 // number of decoy in this layer
* }
*
*/
type Frame<Payload> = Payload extends Array<infer U>
? U extends object
? Record<number, Frame<U>> & SD<Payload> & DECOY
: SD<Payload> & DECOY
: Payload extends Record<string, unknown>
? NonNever<
{
[K in keyof Payload]?: NonNullable<Payload[K]> extends object
? Frame<Payload[K]>
: never;
} & SD<Payload> &
DECOY
>
: SD<Payload> & DECOY;
/**
* This is a disclosureFrame type that is used to represent the structure of what is being disclosed.
*/
export type Extensible = Record<string, unknown | boolean>;
export type DisclosureFrame<T extends Extensible> = Frame<T>;
/**
* This is a presentationFrame type that is used to represent the structure of what is being presented.
* PresentationFrame is made from the payload type.
* const claims = {
firstname: 'John',
lastname: 'Doe',
ssn: '123-45-6789',
id: '1234',
data: {
firstname: 'John',
lastname: 'Doe',
ssn: '123-45-6789',
list: [{ r: 'd' }, 'b', 'c'],
list2: ['1', '2', '3'],
list3: ['1', null, 2],
},
data2: {
hi: 'bye',
},
};
Example of a presentationFrame:
const presentationFrame: PresentationFrame<typeof claims> = {
firstname: true,
lastname: true,
ssn: true,
id: 'true',
data: {
firstname: true,
list: {
1: true,
0: {
r: true,
},
},
list2: {
1: true,
},
list3: true,
},
data2: true,
};
*/
type PFrame<Payload> = Payload extends Array<infer U>
? U extends object
? Record<number, PFrame<U> | boolean> | boolean
: Record<number, boolean> | boolean
: {
[K in keyof Payload]?: NonNullable<Payload[K]> extends object
? PFrame<Payload[K]> | boolean
: boolean;
};
export type PresentationFrame<T extends Extensible> = PFrame<T>;