Skip to content

Commit

Permalink
fix: implement kbverify function (#127)
Browse files Browse the repository at this point in the history
Signed-off-by: Mirko Mollik <mirko.mollik@fit.fraunhofer.de>
Signed-off-by: Lukas.J.Han <lukas.j.han@gmail.com>
Co-authored-by: Lukas.J.Han <lukas.j.han@gmail.com>
Signed-off-by: Mirko Mollik <mirko.mollik@fit.fraunhofer.de>
  • Loading branch information
cre8 and lukasjhan committed Mar 8, 2024
1 parent c4069a7 commit e5609f2
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 61 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"lint": "biome lint ./packages",
"format": "biome format . --write",
"test": "vitest run --coverage.enabled=true --coverage.include=packages/*",
"test:watch": "vitest",
"clean": "lerna clean -y",
"publish:latest": "lerna publish --no-private --conventional-commits --include-merged-tags --create-release github --yes --dist-tag latest",
"publish:next": "lerna publish --no-private --conventional-prerelease --force-publish --canary --no-git-tag-version --include-merged-tags --preid next --pre-dist-tag next --yes",
Expand Down Expand Up @@ -34,6 +35,7 @@
"@biomejs/biome": "1.5.3",
"@types/node": "^20.10.2",
"@vitest/coverage-v8": "^1.2.2",
"jose": "^5.2.2",
"jsdom": "^24.0.0",
"lerna": "^8.1.2",
"ts-node": "^10.9.1",
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SDJWTConfig,
} from '@sd-jwt/types';
import { getSDAlgAndPayload } from '@sd-jwt/decode';
import { JwtPayload } from '@sd-jwt/types';

export * from './sdjwt';
export * from './kbjwt';
Expand Down Expand Up @@ -212,7 +213,13 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload> {
if (!this.userConfig.kbVerifier) {
throw new SDJWTException('Key Binding Verifier not found');
}
const kb = await sdjwt.kbJwt.verify(this.userConfig.kbVerifier);
const kb = await sdjwt.kbJwt.verifyKB({
verifier: this.userConfig.kbVerifier,
payload: payload as JwtPayload,
});
if (!kb) {
throw new Error('signature is not valid');
}
const sdHashfromKb = kb.payload.sd_hash;
const sdjwtWithoutKb = new SDJwt({
jwt: sdjwt.jwt,
Expand Down
24 changes: 20 additions & 4 deletions packages/core/src/kbjwt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { SDJWTException } from '@sd-jwt/utils';
import { Base64urlEncode, SDJWTException } from '@sd-jwt/utils';
import { Jwt } from './jwt';
import { Verifier, kbHeader, kbPayload } from '@sd-jwt/types';
import { JwtPayload, kbHeader, kbPayload, KbVerifier } from '@sd-jwt/types';

export class KBJwt<
Header extends kbHeader = kbHeader,
Payload extends kbPayload = kbPayload,
> extends Jwt<Header, Payload> {
// Checking the validity of the key binding jwt
public async verify(verifier: Verifier) {
// the type unknown is not good, but we don't know at this point how to get the public key of the signer, this is defined in the kbVerifier
public async verifyKB(values: { verifier: KbVerifier; payload: JwtPayload }) {
if (
!this.header?.alg ||
!this.header.typ ||
Expand All @@ -22,7 +23,22 @@ export class KBJwt<
) {
throw new SDJWTException('Invalid Key Binding Jwt');
}
return await super.verify(verifier);
if (!this.header || !this.payload || !this.signature) {
throw new SDJWTException('Verify Error: Invalid JWT');
}

const header = Base64urlEncode(JSON.stringify(this.header));
const payload = Base64urlEncode(JSON.stringify(this.payload));
const data = `${header}.${payload}`;
const verified = await values.verifier(
data,
this.signature,
values.payload,
);
if (!verified) {
throw new SDJWTException('Verify Error: Invalid JWT Signature');
}
return { payload: this.payload, header: this.header };
}

// This function is for creating KBJwt object for verify properly
Expand Down
44 changes: 38 additions & 6 deletions packages/core/src/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { SDJwtInstance, SdJwtPayload } from '../index';
import { Signer, Verifier } from '@sd-jwt/types';
import Crypto from 'node:crypto';
import Crypto, { KeyLike } from 'node:crypto';
import { describe, expect, test } from 'vitest';
import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
import { KbVerifier, JwtPayload } from '@sd-jwt/types';
import { importJWK, exportJWK, JWK } from 'jose';

export const createSignerVerifier = () => {
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
Expand Down Expand Up @@ -181,23 +183,53 @@ describe('index', () => {

test('verify with kbJwt', async () => {
const { signer, verifier } = createSignerVerifier();

const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');

//TODO: maybe we can pass a minial class of the jwt to pass the token
const kbVerifier: KbVerifier = async (
data: string,
sig: string,
payload: JwtPayload,
) => {
let publicKey: JsonWebKey;
if (payload.cnf) {
// use the key from the cnf
publicKey = payload.cnf.jwk;
} else {
throw Error('key binding not supported');
}
// get the key of the holder to verify the signature
return Crypto.verify(
null,
Buffer.from(data),
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
Buffer.from(sig, 'base64url'),
);
};

const kbSigner = (data: string) => {
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
return Buffer.from(sig).toString('base64url');
};

const sdjwt = new SDJwtInstance<SdJwtPayload>({
signer,
signAlg: 'EdDSA',
verifier,
hasher: digest,
saltGenerator: generateSalt,
kbSigner: signer,
kbVerifier: verifier,
kbSigner: kbSigner,
kbVerifier: kbVerifier,
kbSignAlg: 'EdDSA',
});

const credential = await sdjwt.issue(
{
foo: 'bar',
iss: 'Issuer',
iat: new Date().getTime(),
vct: '',
cnf: {
jwk: await exportJWK(publicKey),
},
},
{
_sd: ['foo'],
Expand Down
Loading

0 comments on commit e5609f2

Please sign in to comment.