Skip to content

Commit

Permalink
Add sync functions for decode/present (#72)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas.J.Han <[email protected]>
  • Loading branch information
lukasjhan authored Feb 21, 2024
1 parent e95cac5 commit 5ed88bf
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 6 deletions.
69 changes: 69 additions & 0 deletions packages/decode/src/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SD_LIST_KEY,
SD_SEPARATOR,
} from '@hopae/sd-jwt-type';
import { HasherAndAlgSync, HasherSync } from '@hopae/sd-jwt-type/src/type';

export const decodeJwt = <
H extends Record<string, any>,
Expand Down Expand Up @@ -93,6 +94,40 @@ export const decodeSdJwt = async (
};
};

export const decodeSdJwtSync = (
sdjwt: string,
hasher: HasherSync,
): DecodedSDJwt => {
const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
const jwt = decodeJwt(encodedJwt);

if (encodedDisclosures.length === 0) {
// if input is just jwt, then return here.
// This is for compatibility with jwt
return {
jwt,
disclosures: [],
};
}

const encodedKeyBindingJwt = encodedDisclosures.pop();
const kbJwt = encodedKeyBindingJwt
? decodeJwt(encodedKeyBindingJwt)
: undefined;

const { _sd_alg } = getSDAlgAndPayload(jwt.payload);

const disclosures = encodedDisclosures.map((ed) =>
Disclosure.fromEncodeSync(ed, { alg: _sd_alg, hasher }),
);

return {
jwt,
disclosures,
kbJwt,
};
};

// Get the claims from jwt and disclosures
// The digested values are matched with the disclosures and the claims are extracted
export const getClaims = async <T>(
Expand All @@ -104,6 +139,15 @@ export const getClaims = async <T>(
return unpackedObj as T;
};

export const getClaimsSync = <T>(
rawPayload: any,
disclosures: Array<Disclosure<any>>,
hasher: HasherSync,
): T => {
const { unpackedObj } = unpackSync(rawPayload, disclosures, hasher);
return unpackedObj as T;
};

export const unpackArray = (
arr: Array<any>,
map: Record<string, Disclosure<any>>,
Expand Down Expand Up @@ -215,6 +259,19 @@ export const createHashMapping = async (
return map;
};

export const createHashMappingSync = (
disclosures: Array<Disclosure<any>>,
hash: HasherAndAlgSync,
) => {
const map: Record<string, Disclosure<any>> = {};
for (let i = 0; i < disclosures.length; i++) {
const disclosure = disclosures[i];
const digest = disclosure.digestSync(hash);
map[digest] = disclosure;
}
return map;
};

// Extract _sd_alg. If it is not present, it is assumed to be sha-256
export const getSDAlgAndPayload = (sdjwtPayload: any) => {
const { _sd_alg, ...payload } = sdjwtPayload;
Expand All @@ -239,6 +296,18 @@ export const unpack = async (
return unpackObj(payload, map);
};

export const unpackSync = (
sdjwtPayload: any,
disclosures: Array<Disclosure<any>>,
hasher: HasherSync,
) => {
const { _sd_alg, payload } = getSDAlgAndPayload(sdjwtPayload);
const hash = { hasher, alg: _sd_alg };
const map = createHashMappingSync(disclosures, hash);

return unpackObj(payload, map);
};

// This is the type of the object that is returned by the decodeSdJwt function
// It is a combination of the decoded jwt, the disclosures and the keybinding jwt
export type DecodedSDJwt = {
Expand Down
61 changes: 60 additions & 1 deletion packages/decode/src/test/decode.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { describe, expect, test } from 'vitest';
import { decodeJwt, decodeSdJwt, getClaims, splitSdJwt } from '../decode';
import {
decodeJwt,
decodeSdJwt,
decodeSdJwtSync,
getClaims,
getClaimsSync,
splitSdJwt,
} from '../decode';
import { digest } from '@hopae/sd-jwt-node-crypto';

describe('decode tests', () => {
Expand Down Expand Up @@ -57,6 +64,26 @@ describe('decode tests', () => {
expect(decodedSdJwt.jwt).toBeDefined();
});

test('decode sdjwt sync', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJfc2QiOlsiaWQ1azZ1ZVplVTY4bExaMlU2YjJJbF9QR3ZKb1RDMlpkMkpwY0RwMzFIWSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.GiLF_HhacrstqCJ223VvWOoJJWU8qk4dYQHklSMwxv36pPF_7ER53Wbty1qYRlQ6NeMUdBRRdj9JQLLCzz1gCQ~WyI2NTMxNDA2ZmVhZmU0YjBmIiwiZm9vIiwiYmFyIl0~';
const decodedSdJwt = decodeSdJwtSync(sdjwt, digest);
expect(decodedSdJwt).toBeDefined();
expect(decodedSdJwt.kbJwt).toBeUndefined();
expect(decodedSdJwt.disclosures.length).toEqual(1);
expect(decodedSdJwt.jwt).toBeDefined();
});

test('decode jwt sync', () => {
const jwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6InNkK2p3dCJ9.eyJsYXN0bmFtZSI6IkRvZSIsInNzbiI6IjEyMy00NS02Nzg5IiwiX3NkIjpbIk4yUXhZV1UxTlRnME1qQmpOR1JpWVRCaU1tRmtaamN5WXpSbFpXUmhaRGd5WkRCbE1qaGhZVGcwTnpJMU9XSXpZek5qWkdNNE1qZG1NVGN6TmpZd05RIiwiWlRSalkyUTVOemRoWkRVM05tWTFZV0UyTmpka01XVmpNRE16WXpOak5qQmtNak5pT0dZelpHSTBOelV4TURsak9EWTRNREEzWm1JeFpUY3daREZqTmciXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.mX14Sw86xy8NFQta7tCfNmhVCqzfaJ_K3VEIhTjbLDY';
const decodedSdJwt = decodeSdJwtSync(jwt, digest);
expect(decodedSdJwt).toBeDefined();
expect(decodedSdJwt.kbJwt).toBeUndefined();
expect(decodedSdJwt.disclosures.length).toEqual(0);
expect(decodedSdJwt.jwt).toBeDefined();
});

test('get claims', async () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJfc2QiOlsiaWQ1azZ1ZVplVTY4bExaMlU2YjJJbF9QR3ZKb1RDMlpkMkpwY0RwMzFIWSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.GiLF_HhacrstqCJ223VvWOoJJWU8qk4dYQHklSMwxv36pPF_7ER53Wbty1qYRlQ6NeMUdBRRdj9JQLLCzz1gCQ~WyI2NTMxNDA2ZmVhZmU0YjBmIiwiZm9vIiwiYmFyIl0~';
Expand Down Expand Up @@ -88,4 +115,36 @@ describe('decode tests', () => {
},
});
});

test('get claims sync', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJfc2QiOlsiaWQ1azZ1ZVplVTY4bExaMlU2YjJJbF9QR3ZKb1RDMlpkMkpwY0RwMzFIWSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.GiLF_HhacrstqCJ223VvWOoJJWU8qk4dYQHklSMwxv36pPF_7ER53Wbty1qYRlQ6NeMUdBRRdj9JQLLCzz1gCQ~WyI2NTMxNDA2ZmVhZmU0YjBmIiwiZm9vIiwiYmFyIl0~';
const decodedSdJwt = decodeSdJwtSync(sdjwt, digest);
const claims = getClaimsSync(
decodedSdJwt.jwt.payload,
decodedSdJwt.disclosures,
digest,
);
expect(claims).toStrictEqual({
foo: 'bar',
});
});

test('getClaims sync #2', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
const decodedSdJwt = decodeSdJwtSync(sdjwt, digest);
const claims = getClaimsSync(
decodedSdJwt.jwt.payload,
decodedSdJwt.disclosures,
digest,
);
expect(claims).toStrictEqual({
foo: 'bar',
arr: ['1', '2', { a: '1' }],
test: {
zzz: 'xxx',
},
});
});
});
4 changes: 2 additions & 2 deletions packages/node-crypto/src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export const generateSalt = (length: number): string => {
return salt.substring(0, length);
};

export const digest = async (
export const digest = (
data: string,
algorithm: string = 'SHA-256',
): Promise<Uint8Array> => {
): Uint8Array => {
const nodeAlg = toNodeCryptoAlg(algorithm);
const hash = createHash(nodeAlg);
hash.update(data);
Expand Down
49 changes: 49 additions & 0 deletions packages/present/src/present.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import {
getSDAlgAndPayload,
splitSdJwt,
unpack,
createHashMappingSync,
decodeSdJwtSync,
unpackSync,
} from '@hopae/sd-jwt-decode';
import { HasherSync } from '@hopae/sd-jwt-type/src/type';

// Presentable keys
// The presentable keys are the path of JSON object that are presentable in the SD JWT
Expand Down Expand Up @@ -34,6 +38,15 @@ export const presentableKeys = async (
return Object.keys(disclosureKeymap).sort();
};

export const presentableKeysSync = (
rawPayload: any,
disclosures: Array<Disclosure<any>>,
hasher: HasherSync,
): string[] => {
const { disclosureKeymap } = unpackSync(rawPayload, disclosures, hasher);
return Object.keys(disclosureKeymap).sort();
};

export const present = async (
sdJwt: string,
keys: string[],
Expand Down Expand Up @@ -69,3 +82,39 @@ export const present = async (
kbJwt ?? '',
].join(SD_SEPARATOR);
};

export const presentSync = (
sdJwt: string,
keys: string[],
hasher: HasherSync,
): string => {
const { jwt, kbJwt } = splitSdJwt(sdJwt);
const {
jwt: { payload },
disclosures,
} = decodeSdJwtSync(sdJwt, hasher);

const { _sd_alg: alg } = getSDAlgAndPayload(payload);
const hash = { alg, hasher };

// hashmap: <digest> => <disclosure>
// to match the digest with the disclosure
const hashmap = createHashMappingSync(disclosures, hash);
const { disclosureKeymap } = unpackSync(payload, disclosures, hasher);
const presentableKeys = Object.keys(disclosureKeymap);

const missingKeys = keys.filter((k) => !presentableKeys.includes(k));
if (missingKeys.length > 0) {
throw new SDJWTException(
`Invalid sd-jwt: invalid present keys: ${missingKeys.join(', ')}`,
);
}

const presentedDisclosures = keys.map((k) => hashmap[disclosureKeymap[k]]);

return [
jwt,
...presentedDisclosures.map((d) => d.encode()),
kbJwt ?? '',
].join(SD_SEPARATOR);
};
44 changes: 42 additions & 2 deletions packages/present/src/test/present.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { describe, expect, test } from 'vitest';
import { digest } from '@hopae/sd-jwt-node-crypto';
import { present, presentableKeys } from '../present';
import { decodeSdJwt } from '@hopae/sd-jwt-decode';
import {
present,
presentSync,
presentableKeys,
presentableKeysSync,
} from '../present';
import { decodeSdJwt, decodeSdJwtSync } from '@hopae/sd-jwt-decode';

describe('Present tests', () => {
test('presentableKeys', async () => {
Expand All @@ -16,6 +21,18 @@ describe('Present tests', () => {
expect(keys).toStrictEqual(['arr', 'arr.0', 'arr.2.a', 'foo', 'test.zzz']);
});

test('presentableKeys sync', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
const decodedSdJwt = decodeSdJwtSync(sdjwt, digest);
const keys = presentableKeysSync(
decodedSdJwt.jwt.payload,
decodedSdJwt.disclosures,
digest,
);
expect(keys).toStrictEqual(['arr', 'arr.0', 'arr.2.a', 'foo', 'test.zzz']);
});

test('present', async () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
Expand All @@ -29,6 +46,19 @@ describe('Present tests', () => {
);
});

test('present sync', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
const presentedSdJwt = presentSync(
sdjwt,
['foo', 'arr.0', 'test.zzz'],
digest,
);
expect(presentedSdJwt).toStrictEqual(
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~',
);
});

test('present error', async () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
Expand All @@ -38,4 +68,14 @@ describe('Present tests', () => {
expect(e).toBeDefined();
}
});

test('present error sync', () => {
const sdjwt =
'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
try {
presentSync(sdjwt, ['notthekey'], digest);
} catch (e) {
expect(e).toBeDefined();
}
});
});
10 changes: 10 additions & 0 deletions packages/types/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ export type HasherAndAlg = {
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];
};
Expand Down
24 changes: 23 additions & 1 deletion packages/utils/src/disclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import {
Base64urlEncode,
} from './base64url';
import { SDJWTException } from './error';
import { HasherAndAlg, DisclosureData } from '@hopae/sd-jwt-type';
import {
HasherAndAlg,
DisclosureData,
HasherAndAlgSync,
} from '@hopae/sd-jwt-type';

export class Disclosure<T> {
public salt: string;
Expand Down Expand Up @@ -46,6 +50,14 @@ export class Disclosure<T> {
return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
}

public static fromEncodeSync<T>(s: string, hash: HasherAndAlgSync) {
const { hasher, alg } = hash;
const digest = hasher(s, alg);
const digestStr = Uint8ArrayToBase64Url(digest);
const item = JSON.parse(Base64urlDecode(s)) as DisclosureData<T>;
return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
}

public static fromArray<T>(
item: DisclosureData<T>,
_meta?: { digest: string; encoded: string },
Expand Down Expand Up @@ -77,4 +89,14 @@ export class Disclosure<T> {

return this._digest;
}

public digestSync(hash: HasherAndAlgSync): string {
const { hasher, alg } = hash;
if (!this._digest) {
const hash = hasher(this.encode(), alg);
this._digest = Uint8ArrayToBase64Url(hash);
}

return this._digest;
}
}

0 comments on commit 5ed88bf

Please sign in to comment.