Skip to content

Commit

Permalink
[openwallet-foundation#49]fix: generate key binding jwt in present (o…
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasjhan authored Feb 6, 2024
1 parent b43afa7 commit c340de4
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 50 deletions.
12 changes: 6 additions & 6 deletions examples/kb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ export const createKeyPair = () => {
sd_hash: '1234',
};

const encodedSdjwt = await sdjwt.issue(claims, privateKey, disclosureFrame, {
const encodedSdjwt = await sdjwt.issue(claims, privateKey, disclosureFrame);
console.log('encodedSdjwt:', encodedSdjwt);
const sdjwttoken = sdjwt.decode(encodedSdjwt);
console.log(sdjwttoken);

const presentedSdJwt = await sdjwt.present(encodedSdjwt, ['id'], {
kb: {
alg: 'EdDSA',
payload: kbPayload,
privateKey,
},
});
console.log('encodedSdjwt:', encodedSdjwt);
const sdjwttoken = sdjwt.decode(encodedSdjwt);
console.log(sdjwttoken);

const presentedSdJwt = await sdjwt.present(encodedSdjwt, ['id']);

const verified = await sdjwt.verify(
presentedSdJwt,
Expand Down
51 changes: 31 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { KBJwt } from './kbjwt';
import { SDJwt, pack } from './sdjwt';
import {
DisclosureFrame,
KBOptionWithSigner,
KBOptions,
KB_JWT_TYP,
SDJWTCompact,
SDJWTConfig,
SD_JWT_TYP,
Signer,
kbPayload,
} from './type';
import { KeyLike } from 'jose';
Expand Down Expand Up @@ -46,19 +49,33 @@ export class SDJwtInstance {
return new SDJwtInstance(userConfig);
}

private async createKBJwt(
payload: kbPayload,
privateKey: Uint8Array | KeyLike,
alg: string,
): Promise<KBJwt> {
private isKBOptionsWithSigner(
options: KBOptions,
): options is KBOptionWithSigner {
if ((options as KBOptionWithSigner).signer) {
return true;
}
return false;
}

private async createKBJwt(options: KBOptions): Promise<KBJwt> {
const { alg, payload } = options;
const kbJwt = new KBJwt({
header: {
typ: KB_JWT_TYP,
alg,
},
payload,
});
await kbJwt.sign(privateKey);

if (this.isKBOptionsWithSigner(options)) {
const { signer } = options;
await kbJwt.signWithSigner(signer);
} else {
const { privateKey } = options;
await kbJwt.sign(privateKey);
}

return kbJwt;
}

Expand All @@ -70,11 +87,6 @@ export class SDJwtInstance {
header?: object;
sign_alg?: string;
hash_alg?: string;
kb?: {
alg: string;
payload: kbPayload;
privateKey: Uint8Array | KeyLike;
};
},
): Promise<SDJWTCompact> {
const haser =
Expand Down Expand Up @@ -103,18 +115,9 @@ export class SDJwtInstance {
});
await jwt.sign(privateKey);

const kbJwt = options?.kb
? await this.createKBJwt(
options.kb.payload,
options.kb.privateKey,
options.kb.alg,
)
: undefined;

const sdJwt = new SDJwt({
jwt,
disclosures,
kbJwt,
});

return sdJwt.encodeSDJwt();
Expand All @@ -123,9 +126,17 @@ export class SDJwtInstance {
public async present(
encodedSDJwt: string,
presentationKeys?: string[],
options?: {
kb?: KBOptions;
},
): Promise<SDJWTCompact> {
if (!presentationKeys) return encodedSDJwt;
const sdjwt = SDJwt.fromEncode(encodedSDJwt);

const kbJwt = options?.kb ? await this.createKBJwt(options.kb) : undefined;

sdjwt.kbJwt = kbJwt;

return sdjwt.present(presentationKeys.sort());
}

Expand Down
105 changes: 81 additions & 24 deletions src/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sdjwt from '../index';
import sdjwt, { Signer } from '../index';
import Crypto from 'node:crypto';

describe('index', () => {
Expand All @@ -9,7 +9,7 @@ describe('index', () => {
expect(SDJwtInstance).toBeDefined();
});

test('issue kbJwt', async () => {
test('kbJwt', async () => {
const { privateKey } = Crypto.generateKeyPairSync('ed25519');
const credential = await sdjwt.issue(
{
Expand All @@ -19,21 +19,58 @@ describe('index', () => {
{
_sd: ['foo'],
},
{
kb: {
alg: 'EdDSA',
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
},
privateKey,
);

expect(credential).toBeDefined();

const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
alg: 'EdDSA',
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
},
privateKey,
},
});

expect(presentation).toBeDefined();
});

test('kbJwt with custom signer', async () => {
const { privateKey } = Crypto.generateKeyPairSync('ed25519');
const testSigner: Signer = async (data: string) => {
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
return Buffer.from(sig).toString('base64url');
};
const credential = await sdjwt.issue(
{
foo: 'bar',
},
privateKey,
{
_sd: ['foo'],
},
);

expect(credential).toBeDefined();

const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
alg: 'EdDSA',
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
},
signer: testSigner,
},
});

expect(presentation).toBeDefined();
});

test('verify failed', async () => {
Expand All @@ -46,18 +83,6 @@ describe('index', () => {
{
_sd: ['foo'],
},
{
kb: {
alg: 'EdDSA',
payload: {
sd_hash: 'sha-256',
aud: '1',
iat: 1,
nonce: '342',
},
privateKey,
},
},
);

try {
Expand All @@ -69,4 +94,36 @@ describe('index', () => {
expect(e).toBeDefined();
}
});

test('verify failed with kbJwt', async () => {
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
const credential = await sdjwt.issue(
{
foo: 'bar',
},
privateKey,
{
_sd: ['foo'],
},
);

const presentation = await sdjwt.present(credential, ['foo'], {
kb: {
alg: 'EdDSA',
payload: {
sd_hash: '',
aud: '1',
iat: 1,
nonce: '342',
},
privateKey,
},
});

try {
await sdjwt.verify(presentation, publicKey);
} catch (e) {
expect(e).toBeDefined();
}
});
});
15 changes: 15 additions & 0 deletions src/type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { KeyLike } from 'jose';
import { Jwt } from './jwt';

export const SD_SEPARATOR = '~';
Expand Down Expand Up @@ -28,6 +29,20 @@ export type kbPayload = {

export type KeyBinding = Jwt<kbHeader, kbPayload>;

export type KBOptionWithKey = {
alg: string;
payload: kbPayload;
privateKey: Uint8Array | KeyLike;
};

export type KBOptionWithSigner = {
alg: string;
payload: kbPayload;
signer: Signer;
};

export type KBOptions = KBOptionWithKey | KBOptionWithSigner;

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

export type Signer = (data: string) => OrPromise<string>;
Expand Down

0 comments on commit c340de4

Please sign in to comment.