Skip to content

Commit

Permalink
add error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jchartrand committed Jan 7, 2025
1 parent 1545645 commit dfc8c27
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 61 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
node-version: [16.x]
node-version: [18.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
node-version: [18.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
32 changes: 28 additions & 4 deletions src/Verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const suite = new Ed25519Signature2020();

export interface VerificationError {
"message": string,
"isFatal": boolean
"isFatal": boolean,
"name"?: string,
stackTrace?: string
}

export interface Step {
Expand All @@ -38,11 +40,15 @@ export interface VerificationResponse {
"log"?: Step[]
}


export async function verifyCredential({credential, reloadIssuerRegistry = true}:{credential: Credential, reloadIssuerRegistry: boolean}): Promise<VerificationResponse> {


const fatalErrorMessage = checkForFatalErrors(credential)

if (fatalErrorMessage) return {credential, isFatal: true, verified: false, errors: [{message: fatalErrorMessage, isFatal: true}]}
if (fatalErrorMessage) {
return buildFatalErrorObject(fatalErrorMessage, "fatalError", credential, null)
}

const verificationResponse = await vc.verifyCredential({
credential,
Expand All @@ -51,18 +57,36 @@ export async function verifyCredential({credential, reloadIssuerRegistry = true}
checkStatus: getCredentialStatusChecker(credential)
});

verificationResponse.isFatal = false

if (verificationResponse.error) {
if (verificationResponse.error.log) {
verificationResponse.log = verificationResponse.error.log
delete verificationResponse.error
} else if (verificationResponse?.error?.name === 'VerificationError') {
const fatalErrorMessage = 'The signature is not valid.'
const stackTrace = verificationResponse?.error?.errors?.stack
return buildFatalErrorObject(fatalErrorMessage, "invalidSignature", credential, stackTrace)
}
}
console.log('results from the verify call:')
console.log(JSON.stringify(verificationResponse))

delete verificationResponse.results
delete verificationResponse.statusResult

const { issuer } = credential
await addTrustedIssuersToVerificationResponse({verificationResponse, reloadIssuerRegistry, issuer})
verificationResponse.isFatal = false


return verificationResponse;
}

function checkForFatalErrors(credential: Credential) : string | null {
function buildFatalErrorObject(fatalErrorMessage: string, name: string, credential: Credential, stackTrace: string | null) : VerificationResponse {
return {credential, isFatal: true, verified: false, errors: [{name, message: fatalErrorMessage, isFatal: true, ...stackTrace?{stackTrace}:null}]}
}

function checkForFatalErrors(credential: Credential) : string | null {
try {
// eslint-disable-next-line no-new
new URL(credential.id as string);
Expand Down
46 changes: 46 additions & 0 deletions src/test-fixtures/expiredV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const expiredV2 = {
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.com/credentials/3527",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "did:key:z6MknNQD1WHLGGraFi6zcbGevuAgkVfdyCdtZnQTGWVVvR5Q",
"type": [
"Profile"
],
"name": "Example Corp"
},
"validFrom": "2010-01-01T00:00:00Z",
"validUntil": "2011-01-01T00:00:00Z",
"name": "Teamwork Badge",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
],
"achievement": {
"id": "https://example.com/achievements/21st-century-skills/teamwork",
"type": [
"Achievement"
],
"criteria": {
"narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management."
},
"description": "This badge recognizes the development of the capacity to collaborate within a group environment.",
"name": "Teamwork"
}
},
"proof": {
"type": "Ed25519Signature2020",
"created": "2025-01-07T22:14:25Z",
"verificationMethod": "did:key:z6MknNQD1WHLGGraFi6zcbGevuAgkVfdyCdtZnQTGWVVvR5Q#z6MknNQD1WHLGGraFi6zcbGevuAgkVfdyCdtZnQTGWVVvR5Q",
"proofPurpose": "assertionMethod",
"proofValue": "z2B4R2hwrcgR8ag39SZEgm2hAJSPnoWae7zdRr8RUTkcbMPNHu7tedk1x3D29J3CmiU5Wb1e7zew82nQAKYCQuRBo"
}
}
84 changes: 31 additions & 53 deletions src/test-fixtures/vc.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { expiredV2 } from "./expiredV2"
const signedVC1Unrevoked = {
"type": [
"VerifiableCredential",
Expand Down Expand Up @@ -142,7 +143,7 @@ const usignedVCv2 = {
name: 'Jane Doe'
}
}
const unsignedVC = {
const unsignedVCv1 = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
Expand Down Expand Up @@ -183,71 +184,48 @@ const unsignedVC = {
}
}

// "credentialStatus":
const credentialStatus = {
id: 'https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4#16',
type: 'StatusList2021Entry',
statusPurpose: 'revocation',
statusListIndex: 16,
statusListCredential:
'https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4'
}

const credentialStatusBitString = {
id: 'https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4#16',
type: 'BitstringStatusListEntry',
statusPurpose: 'revocation',
statusListIndex: 16,
statusListCredential:
'https://digitalcredentials.github.io/credential-status-jc-test/XA5AAK1PV4'
}

const getUnsignedVC = (): any => JSON.parse(JSON.stringify(unsignedVC))


const getUnsignedVC1 = (): any => JSON.parse(JSON.stringify(unsignedVCv1))
const getUnsignedVCv2 = (): any => JSON.parse(JSON.stringify(usignedVCv2))

const getUnsignedVCWithoutSuiteContext = (): any => {
const vcCopy = JSON.parse(JSON.stringify(unsignedVC))
const index = vcCopy['@context'].indexOf(ed25519SuiteContext)
if (index > -1) {
vcCopy['@context'].splice(index, 1)
}
return vcCopy
}
const getCredentialStatus = (): any => JSON.parse(JSON.stringify(credentialStatus))
const getCredentialStatusBitString = (): any =>
JSON.parse(JSON.stringify(credentialStatusBitString))

const getUnsignedVCWithStatus = (): any => {
const unsignedVCWithStatus = getUnsignedVC()
unsignedVCWithStatus.credentialStatus = getCredentialStatus()
return unsignedVCWithStatus
}

const getUnsignedVC2WithStatus = (): any => {
const unsignedVC2WithStatus = getUnsignedVCv2()
unsignedVC2WithStatus.credentialStatus = getCredentialStatusBitString()
return unsignedVC2WithStatus
const getSignedVC1 = (): any => {
return JSON.parse(JSON.stringify(signedVC1))
}

const ed25519SuiteContext =
'https://w3id.org/security/suites/ed25519-2020/v1'

const getSignedVC = (): any => {
return signedVC1
}

const getSignedUnrevokedVC2 = (): any => {
return signedVC1Unrevoked
}

const getTamperedVC1 = (): any => {
const signedVC1 = getSignedVC1()
signedVC1.name = 'Introduction to Tampering'
return signedVC1
}

const getExpiredVC1 = (): any => {
return null
}
const getExpiredVC2 = (): any => {
return JSON.parse(JSON.stringify(expiredV2))
}

const getExpiredAndTamperedVC2 = (): any => {
const cred = getExpiredVC2()
cred.name = 'tampered!'
return cred
}

export {
getExpiredVC2,
getExpiredAndTamperedVC2,
getTamperedVC1,
getSignedUnrevokedVC2,
getSignedVC,
getUnsignedVC,
getUnsignedVCWithoutSuiteContext,
getCredentialStatus,
getCredentialStatusBitString,
getUnsignedVCWithStatus,
getUnsignedVC2WithStatus,
ed25519SuiteContext
getSignedVC1,
getUnsignedVC1
}
23 changes: 21 additions & 2 deletions test/Verify.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//import { expect } from 'chai'
import { strict as assert } from 'assert';
import { verifyCredential } from '../src/Verify'
import { getSignedUnrevokedVC2 } from '../src/test-fixtures/vc'
import { getSignedUnrevokedVC2, getTamperedVC1, getExpiredVC2 } from '../src/test-fixtures/vc'

describe('Verify', () => {
it('calls function', async () => {
it('verifies with valid status', async () => {
//const signedVC : any = getSignedVC()
const signedVC2Unrevoked : any = getSignedUnrevokedVC2()
const result = await verifyCredential({credential: signedVC2Unrevoked, reloadIssuerRegistry: true})
Expand All @@ -13,4 +13,23 @@ describe('Verify', () => {
assert.ok(result.verified);
//expect(result.verified).to.be.true
})

it.only('returns fatal error when tampered', async () => {
const tamperedVC1 : any = getTamperedVC1()
const result = await verifyCredential({credential: tamperedVC1, reloadIssuerRegistry: true})
console.log("result returned from verifyCredential call:")
console.log(JSON.stringify(result,null,2))
assert.ok(result.verified === false);
//expect(result.verified).to.be.true
})

it('returns fatal error when tampered', async () => {
const expiredVC2 : any = getExpiredVC2()
const result = await verifyCredential({credential: expiredVC2, reloadIssuerRegistry: true})
console.log("result returned from verifyCredential call:")
console.log(JSON.stringify(result,null,2))
assert.ok(result.verified === false);
//expect(result.verified).to.be.true
})

})

0 comments on commit dfc8c27

Please sign in to comment.