Skip to content

Commit

Permalink
flatten verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed Aug 10, 2024
1 parent 8e321a0 commit 18bc6db
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 145 deletions.
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@



import Verifier from './lib/Verifier'
import JWK from './lib/JWK'
import JWS from './lib/JWS'
import Parse from './lib/Parse'
Expand All @@ -12,6 +11,6 @@ import v2 from './v2'

export * from './types'

const sd = { ...v2, YAML, JWK, JWS, Verifier, Parse }
const sd = { ...v2, YAML, JWK, JWS, Parse }

export default sd
119 changes: 0 additions & 119 deletions src/lib/Verifier.ts

This file was deleted.

102 changes: 102 additions & 0 deletions src/lib/_verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

import { DIGEST_ALG_KEY } from "./constants";

import { VerifiedSdJwt } from '../types'

import JWS from './JWS';
import Parse from './Parse';


import _unpack_disclosed_claims from './_unpack_disclosed_claims'
import { decodeProtectedHeader } from "jose";

import { validate_public_claims } from './validate_public_claims'
import { validate_sd_hash } from './validate_sd_hash'


export const _verify = async ({ presentation, aud, nonce, debug, verifier, resolver, digester }: any) => {
const { jwt, kbt } = Parse.compact(presentation)
const decodedHeader = decodeProtectedHeader(jwt)
let verifiedIssuanceToken
if (verifier) {
verifiedIssuanceToken = await verifier.verify(presentation)
} else if (resolver) {
if (!decodedHeader.kid) {
throw new Error('kid is required when resolver is used to obtain public keys')
}
const issuerPublicKey = await resolver.resolve(decodedHeader.kid)
const compactJwsVerifier = await JWS.verifier(issuerPublicKey)
verifiedIssuanceToken = await compactJwsVerifier.verify(jwt)
} else {
throw new Error('a verifier or resolver is required, but not present.')
}
if (verifiedIssuanceToken.claimset[DIGEST_ALG_KEY] !== digester.name) {
throw new Error('Invalid hash algorithm')
}
// here we are verifying the "Issuer Signed JWT"
// aud and nonce, are expected to be checked in the KBT, not the "Issuer Signed JWT".
// See: https://github.com/oauth-wg/oauth-selective-disclosure-jwt/issues/395
validate_public_claims('Issuer-signed JWT', verifiedIssuanceToken.claimset, {
debug,
reference_audience: verifiedIssuanceToken.claimset.aud,
reference_nonce: verifiedIssuanceToken.claimset.nonce
})
if (debug) {
console.info('Verified Issuer-signed JWT: ', JSON.stringify(verifiedIssuanceToken, null, 2))
}
const { cnf } = verifiedIssuanceToken.claimset
if (cnf) {
if (!kbt) {
throw new Error('Verification of this credential requires proof of posession from the holder. Key binding token is expected based on claims, but was not found.')
}
try {
let kid;
let jwk;
let confirmationPublicKey
let verified;
const { cnf } = verifiedIssuanceToken.claimset
if (cnf.jwk) {
({ cnf: { jwk } } = verifiedIssuanceToken.claimset)
confirmationPublicKey = jwk
if (debug) {
console.info('Issued JWT has JWK confirmation method.')
}
}
if ((verifiedIssuanceToken.claimset.cnf).kid) {
({ cnf: { kid } } = verifiedIssuanceToken.claimset)
if (debug) {
console.info('Issued JWT has kid confirmation method.')
}
if (!resolver) {
throw new Error('Resolver is required for kid confirmation method')
}
confirmationPublicKey = await resolver?.resolve(kid)
}
const compactJwsVerifier = await JWS.verifier(confirmationPublicKey)
verified = await compactJwsVerifier.verify(kbt)
if (!verified) {
throw new Error('Failed to verify key binding token')
}
await validate_sd_hash(presentation, verified.claimset.sd_hash, debug)
validate_public_claims('Key Binding Token', verified.claimset, {
debug,
reference_audience: aud,
reference_nonce: nonce
})
if (debug) {
console.info('Verified Key Binding Token: ', JSON.stringify(verified, null, 2))
}
} catch (e) {
console.error(e)
throw new Error('Failed to validate key binding token.')
}
} else {
if (debug) {
console.info('Issued JWT has no confirmation method.')
}
}
const { disclosureMap, hashToEncodedDisclosureMap } = await Parse.expload(presentation, { digester: digester })
const state = { _hash_to_disclosure: hashToEncodedDisclosureMap, _hash_to_decoded_disclosure: disclosureMap }
const output = _unpack_disclosed_claims(verifiedIssuanceToken.claimset, state)
return JSON.parse(JSON.stringify({ protectedHeader: verifiedIssuanceToken.protectedHeader, claimset: output })) as VerifiedSdJwt
}
10 changes: 5 additions & 5 deletions src/v2/verifier.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@

import Verifier from "../lib/Verifier"

import digester from "./digester"
import JWS from "../lib/JWS"

import Parse from "../lib/Parse"

import { PublicKeyJwk, RequestVerifier, VerifierCtx, VerifiedSdJwt } from '../types'
import { PublicKeyJwk, RequestVerifier, VerifiedSdJwt } from '../types'

import { _verify } from "../lib/_verify"

export default function verifier<T=VerifiedSdJwt>(options: RequestVerifier){
if (!options.digester){
Expand All @@ -28,8 +28,8 @@ export default function verifier<T=VerifiedSdJwt>(options: RequestVerifier){
}
return {
verify: async ({ token, audience, nonce }: { token: string, audience ?: string, nonce?: string }): Promise<T> => {
const role = new Verifier(options as VerifierCtx)
const verified = await role.verify({
const verified = await _verify({
...options,
presentation: token,
aud: audience,
nonce: nonce
Expand Down
9 changes: 4 additions & 5 deletions test/headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ credentialSubject:
entryNumber: True
`,
})
const verifier = new SD.Verifier({
const verified = await SD.verifier({
alg,
digester,
verifier: {
Expand All @@ -84,11 +84,10 @@ credentialSubject:
return verifier.verify(parsed.jwt)
}
}
})
const verified = await verifier.verify({
presentation: vp,
}).verify({
token: vp,
nonce,
aud: audience
audience: audience
})
expect(verified.claimset.issuer.location).toBeUndefined()
expect(verified.claimset.credentialSubject.entryNumber).toBe('12345123456')
Expand Down
28 changes: 14 additions & 14 deletions test/vc-jose-cose-test/vc-jose-cose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,7 @@ it('W3C VC JOSE COSE Test', async () => {
salter
})

const verifier = new SD.Verifier({
alg,
digester,
verifier: {
verify: async (token: string) => {
const parsed = SD.Parse.compact(token)
const verifier = await SD.JWS.verifier(issuerKeyPair.publicKeyJwk)
return verifier.verify(parsed.jwt)
}
}
})

const claimsYaml = fs.readFileSync(`test/vc-jose-cose-test/payload.yaml`).toString()
const vc = await issuer.issue({
jwk: holderKeyPair.publicKeyJwk,
Expand All @@ -50,10 +40,20 @@ it('W3C VC JOSE COSE Test', async () => {
disclosure: claimsDisclosureYaml,
})

const verified = await verifier.verify({
presentation: vp,
const verified = await SD.verifier({
alg,
digester,
verifier: {
verify: async (token: string) => {
const parsed = SD.Parse.compact(token)
const verifier = await SD.JWS.verifier(issuerKeyPair.publicKeyJwk)
return verifier.verify(parsed.jwt)
}
}
}).verify({
token: vp,
nonce,
aud: audience
audience: audience
})
expect(verified.claimset.proof.created).toBe('2023-06-18T21:19:10Z')
});

0 comments on commit 18bc6db

Please sign in to comment.