Skip to content

Commit

Permalink
feat: calcuate sd_hash in kb JWT (#117)
Browse files Browse the repository at this point in the history
Signed-off-by: Lukas <Lukas@hopae.io>
Signed-off-by: Lukas.J.Han <lukas.j.han@gmail.com>
Signed-off-by: Mirko Mollik <mirko.mollik@fit.fraunhofer.de>
  • Loading branch information
lukasjhan authored and cre8 committed Mar 8, 2024
1 parent dcaf1f6 commit 3415550
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 15 deletions.
1 change: 0 additions & 1 deletion examples/sd-jwt-vc-example/kb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { createSignerVerifier, digest, generateSalt } from './utils';
aud: 'https://example.com',
nonce: '1234',
custom: 'data',
sd_hash: '1234',
};

const encodedSdjwt = await sdjwt.issue(
Expand Down
64 changes: 58 additions & 6 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { SDJWTException } from '@sd-jwt/utils';
import { SDJWTException, Uint8ArrayToBase64Url } from '@sd-jwt/utils';
import { Jwt } from './jwt';
import { KBJwt } from './kbjwt';
import { SDJwt, pack } from './sdjwt';
import {
DisclosureFrame,
Hasher,
KBOptions,
KB_JWT_TYP,
SDJWTCompact,
SDJWTConfig,
} from '@sd-jwt/types';
import { getSDAlgAndPayload } from '@sd-jwt/decode';

export * from './sdjwt';
export * from './kbjwt';
Expand All @@ -31,20 +33,24 @@ export abstract class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
}
}

private async createKBJwt(options: KBOptions): Promise<KBJwt> {
private async createKBJwt(
options: KBOptions,
sdHash: string,
): Promise<KBJwt> {
if (!this.userConfig.kbSigner) {
throw new SDJWTException('Key Binding Signer not found');
}
if (!this.userConfig.kbSignAlg) {
throw new SDJWTException('Key Binding sign algorithm not specified');
}

const { payload } = options;
const kbJwt = new KBJwt({
header: {
typ: KB_JWT_TYP,
alg: this.userConfig.kbSignAlg,
},
payload,
payload: { ...payload, sd_hash: sdHash },
});

await kbJwt.sign(this.userConfig.kbSigner);
Expand Down Expand Up @@ -139,9 +145,24 @@ export abstract class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
const hasher = this.userConfig.hasher;

const sdjwt = await SDJwt.fromEncode(encodedSDJwt, hasher);
const kbJwt = options?.kb ? await this.createKBJwt(options.kb) : undefined;
sdjwt.kbJwt = kbJwt;

if (!sdjwt.jwt?.payload) throw new SDJWTException('Payload not found');
const presentSdJwtWithoutKb = await sdjwt.present(
presentationKeys.sort(),
hasher,
);

if (!options?.kb) {
return presentSdJwtWithoutKb;
}

const sdHashStr = await this.calculateSDHash(
presentSdJwtWithoutKb,
sdjwt,
hasher,
);

sdjwt.kbJwt = await this.createKBJwt(options.kb, sdHashStr);
return sdjwt.present(presentationKeys.sort(), hasher);
}

Expand All @@ -159,7 +180,7 @@ export abstract class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
const hasher = this.userConfig.hasher;

const sdjwt = await SDJwt.fromEncode(encodedSDJwt, hasher);
if (!sdjwt.jwt) {
if (!sdjwt.jwt || !sdjwt.jwt.payload) {
throw new SDJWTException('Invalid SD JWT');
}
const { payload, header } = await this.validate(encodedSDJwt);
Expand All @@ -185,9 +206,40 @@ export abstract class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
throw new SDJWTException('Key Binding Verifier not found');
}
const kb = await sdjwt.kbJwt.verify(this.userConfig.kbVerifier);
const sdHashfromKb = kb.payload.sd_hash;
const sdjwtWithoutKb = new SDJwt({
jwt: sdjwt.jwt,
disclosures: sdjwt.disclosures,
});

const presentSdJwtWithoutKb = sdjwtWithoutKb.encodeSDJwt();
const sdHashStr = await this.calculateSDHash(
presentSdJwtWithoutKb,
sdjwt,
hasher,
);

if (sdHashStr !== sdHashfromKb) {
throw new SDJWTException('Invalid sd_hash in Key Binding JWT');
}

return { payload, header, kb };
}

private async calculateSDHash(
presentSdJwtWithoutKb: string,
sdjwt: SDJwt,
hasher: Hasher,
) {
if (!sdjwt.jwt || !sdjwt.jwt.payload) {
throw new SDJWTException('Invalid SD JWT');
}
const { _sd_alg } = getSDAlgAndPayload(sdjwt.jwt.payload);
const sdHash = await hasher(presentSdJwtWithoutKb, _sd_alg);
const sdHashStr = Uint8ArrayToBase64Url(sdHash);
return sdHashStr;
}

// This function is for validating the SD JWT
// Just checking signature and return its the claims
public async validate(encodedSDJwt: string) {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/sdjwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import {
DisclosureFrame,
Hasher,
HasherAndAlg,
KBOptions,
KB_JWT_TYP,
SDJWTCompact,
SD_DECOY,
SD_DIGEST,
SD_LIST_KEY,
SD_SEPARATOR,
SaltGenerator,
Signer,
kbHeader,
kbPayload,
} from '@sd-jwt/types';
Expand Down
8 changes: 1 addition & 7 deletions packages/core/src/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
Expand Down Expand Up @@ -176,8 +175,7 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: '',
aud: '1',
aud: '',
iat: 1,
nonce: '342',
},
Expand Down Expand Up @@ -219,7 +217,6 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
Expand Down Expand Up @@ -326,7 +323,6 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
Expand Down Expand Up @@ -367,7 +363,6 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
Expand Down Expand Up @@ -406,7 +401,6 @@ describe('index', () => {
const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type kbPayload = {
};

export type KBOptions = {
payload: kbPayload;
payload: Omit<kbPayload, 'sd_hash'>;
};

export type OrPromise<T> = T | Promise<T>;
Expand Down

0 comments on commit 3415550

Please sign in to comment.