Skip to content

Commit

Permalink
refactor verifier service
Browse files Browse the repository at this point in the history
Signed-off-by: F-Node-Karlsruhe <christian.fries@eecc.de>
  • Loading branch information
F-Node-Karlsruhe committed Sep 12, 2023
1 parent cc6447a commit 0f47aee
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 149 deletions.
4 changes: 2 additions & 2 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

152 changes: 152 additions & 0 deletions api/src/services/verifier/dataintegrity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// @ts-ignore
import { verifyCredential, verify } from '@digitalbazaar/vc';
// @ts-ignore
import { Ed25519Signature2018 } from '@digitalbazaar/ed25519-signature-2018';
// @ts-ignore
import { Ed25519Signature2020 } from '@digitalbazaar/ed25519-signature-2020';
// @ts-ignore
import { checkStatus as checkStatus2020 } from '@digitalbazaar/vc-revocation-list';
// @ts-ignore
import { checkStatus as checkStatus2021 } from '@digitalbazaar/vc-status-list';
// @ts-ignore
import * as ecdsaSd2023Cryptosuite from '@digitalbazaar/ecdsa-sd-2023-cryptosuite';
// @ts-ignore
import { DataIntegrityProof } from '@digitalbazaar/data-integrity';
// @ts-ignore
import jsigs from 'jsonld-signatures';

import { documentLoader } from '../documentLoader/index.js';

const { createVerifyCryptosuite } = ecdsaSd2023Cryptosuite;
const { purposes: { AssertionProofPurpose } } = jsigs;


function getSuite(proof: Proof): unknown[] {

switch (proof.type) {

case 'Ed25519Signature2018': return new Ed25519Signature2018();

case 'Ed25519Signature2020': return new Ed25519Signature2020();

case 'DataIntegrityProof': return new DataIntegrityProof({
cryptosuite: createVerifyCryptosuite()
});

default: throw new Error(`${proof.type} not implemented`);
}

}

function getSuites(proof: Proof | Proof[]): unknown[] {

var suites: unknown[] = []

if (Array.isArray(proof)) {
proof.forEach((proof: Proof) => suites.push(getSuite(proof)));
} else {
suites = [getSuite(proof)]
}

return suites;

}

function getPresentationStatus(presentation: VerifiablePresentation): CredentialStatus[] | CredentialStatus | undefined {

const credentials = (
Array.isArray(presentation.verifiableCredential)
? presentation.verifiableCredential
: [presentation.verifiableCredential]
)
.filter((credential: VerifiableCredential) => credential.credentialStatus);

if (credentials.length == 0) return undefined;

if (credentials.length == 1) return credentials[0].credentialStatus;

const statusTypes = credentials.map((credential: VerifiableCredential) => {
return Array.isArray(credential.credentialStatus)
? credential.credentialStatus.map((credentialStatus: CredentialStatus) => credentialStatus.type)
: credential.credentialStatus.type
});

// disallow multiple status types
if (new Set(statusTypes.flat(1)).size > 1) throw new Error('Currently only on status type is allowed within one presentation!');

return credentials[0].credentialStatus;

}

function getCheckStatus(credentialStatus?: CredentialStatus[] | CredentialStatus): any | undefined {
// no status provided
if (!credentialStatus) return undefined;

let statusTypes = [];

if (Array.isArray(credentialStatus)) {
statusTypes = credentialStatus.map(cs => cs.type);
}
else statusTypes = [credentialStatus.type]

if (statusTypes.includes('StatusList2021Entry')) return checkStatus2021;

if (statusTypes.includes('RevocationList2020Status')) return checkStatus2020;

throw new Error(`${statusTypes} not implemented`);

}

export async function verifyDataIntegrityProof(verifiable: Verifiable, challenge?: string, domain?: string): Promise<VerificationResult> {

const suite = getSuites(verifiable.proof);

let result;

if (verifiable.type.includes('VerifiableCredential')) {

const credential = verifiable as VerifiableCredential;

const checkStatus = getCheckStatus(credential.credentialStatus);

if ((Array.isArray(credential.proof) ? credential.proof[0].type : credential.proof.type) == 'DataIntegrityProof') {

result = await jsigs.verify(credential, {
suite,
purpose: new AssertionProofPurpose(),
documentLoader,
// give it to jsigs anyway - does not get verified
checkStatus
});

} else {

result = await verifyCredential({ credential, suite, documentLoader, checkStatus });

}
}

if (verifiable.type.includes('VerifiablePresentation')) {

const presentation = verifiable as VerifiablePresentation;

// try to use challenge in proof if not provided in case no exchange protocol is used
if (!challenge) challenge = (Array.isArray(presentation.proof) ? presentation.proof[0].challenge : presentation.proof.challenge);


const checkStatus = getCheckStatus(
getPresentationStatus(presentation)
);

result = await verify({ presentation, suite, documentLoader, challenge, domain, checkStatus });

}

if (!result) throw Error('Provided verifiable object is of unknown type!');

// make non enumeratable errors enumeratable for the respsonse
if (result.error && !result.error.errors) result.error.name = result.error.message;

return result;

}
156 changes: 9 additions & 147 deletions api/src/services/verifier/index.ts
Original file line number Diff line number Diff line change
@@ -1,155 +1,17 @@
// @ts-ignore
import { verifyCredential, verify } from '@digitalbazaar/vc';
// @ts-ignore
import { Ed25519Signature2018 } from '@digitalbazaar/ed25519-signature-2018';
// @ts-ignore
import { Ed25519Signature2020 } from '@digitalbazaar/ed25519-signature-2020';
// @ts-ignore
import { checkStatus as checkStatus2020 } from '@digitalbazaar/vc-revocation-list';
// @ts-ignore
import { checkStatus as checkStatus2021 } from '@digitalbazaar/vc-status-list';
// @ts-ignore
import * as ecdsaSd2023Cryptosuite from '@digitalbazaar/ecdsa-sd-2023-cryptosuite';
// @ts-ignore
import { DataIntegrityProof } from '@digitalbazaar/data-integrity';
// @ts-ignore
import jsigs from 'jsonld-signatures';

import { documentLoader } from '../documentLoader/index.js';

const { createVerifyCryptosuite } = ecdsaSd2023Cryptosuite;
const { purposes: { AssertionProofPurpose } } = jsigs;

function getSuite(proof: Proof): unknown[] {

switch (proof.type) {

case 'Ed25519Signature2018': return new Ed25519Signature2018();

case 'Ed25519Signature2020': return new Ed25519Signature2020();

case 'DataIntegrityProof': return new DataIntegrityProof({
cryptosuite: createVerifyCryptosuite()
});

default: throw new Error(`${proof.type} not implemented`);
}

}

function getSuites(proof: Proof | Proof[]): unknown[] {

var suites: unknown[] = []

if (Array.isArray(proof)) {
proof.forEach((proof: Proof) => suites.push(getSuite(proof)));
} else {
suites = [getSuite(proof)]
}

return suites;

}

function getPresentationStatus(presentation: VerifiablePresentation): CredentialStatus[] | CredentialStatus | undefined {

const credentials = (
Array.isArray(presentation.verifiableCredential)
? presentation.verifiableCredential
: [presentation.verifiableCredential]
)
.filter((credential: VerifiableCredential) => credential.credentialStatus);

if (credentials.length == 0) return undefined;

if (credentials.length == 1) return credentials[0].credentialStatus;

const statusTypes = credentials.map((credential: VerifiableCredential) => {
return Array.isArray(credential.credentialStatus)
? credential.credentialStatus.map((credentialStatus: CredentialStatus) => credentialStatus.type)
: credential.credentialStatus.type
});

// disallow multiple status types
if (new Set(statusTypes.flat(1)).size > 1) throw new Error('Currently only on status type is allowed within one presentation!');

return credentials[0].credentialStatus;

}

function getCheckStatus(credentialStatus?: CredentialStatus[] | CredentialStatus): any | undefined {
// no status provided
if (!credentialStatus) return undefined;

let statusTypes = [];

if (Array.isArray(credentialStatus)) {
statusTypes = credentialStatus.map(cs => cs.type);
}
else statusTypes = [credentialStatus.type]

if (statusTypes.includes('StatusList2021Entry')) return checkStatus2021;

if (statusTypes.includes('RevocationList2020Status')) return checkStatus2020;

throw new Error(`${statusTypes} not implemented`);

}
import { verifyDataIntegrityProof } from './dataintegrity.js';
import { verifySDJWT } from './sdjwt.js';


export class Verifier {

static async verify(verifiable: Verifiable, challenge?: string, domain?: string): Promise<any> {

const suite = getSuites(verifiable.proof);

let result;

if (verifiable.type.includes('VerifiableCredential')) {

const credential = verifiable as VerifiableCredential;

const checkStatus = getCheckStatus(credential.credentialStatus);

if ((Array.isArray(credential.proof) ? credential.proof[0].type : credential.proof.type) == 'DataIntegrityProof') {

result = await jsigs.verify(credential, {
suite,
purpose: new AssertionProofPurpose(),
documentLoader,
// give it to jsigs anyway - does not get verified
checkStatus
});

} else {

result = await verifyCredential({ credential, suite, documentLoader, checkStatus });

}
}

if (verifiable.type.includes('VerifiablePresentation')) {

const presentation = verifiable as VerifiablePresentation;

// try to use challenge in proof if not provided in case no exchange protocol is used
if (!challenge) challenge = (Array.isArray(presentation.proof) ? presentation.proof[0].challenge : presentation.proof.challenge);


const checkStatus = getCheckStatus(
getPresentationStatus(presentation)
);

result = await verify({ presentation, suite, documentLoader, challenge, domain, checkStatus });

}

if (!result) throw Error('Provided verifiable object is of unknown type!');

// make non enumeratable errors enumeratable for the respsonse
if (result.error && !result.error.errors) result.error.name = result.error.message;
static async verify(verifiable: Verifiable | string, challenge?: string, domain?: string): Promise<VerificationResult> {

return result;
// vc-jwt or sd-jwt
if (typeof verifiable == 'string') return await verifySDJWT(verifiable, challenge, domain)

// DataIntegrityProof
return await verifyDataIntegrityProof(verifiable, challenge, domain);

}

}
7 changes: 7 additions & 0 deletions api/src/services/verifier/sdjwt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


export async function verifySDJWT(verifiable: string, nonce?: string, aud?: string): Promise<VerificationResult> {

return {verified: false};

}
4 changes: 4 additions & 0 deletions api/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ type VerifiablePresentation = Verifiable & {
id: string | URL;
holder: string | any;
verifiableCredential: VerifiableCredential | VerifiableCredential[];
}

type VerificationResult = {
verified: boolean;
}

0 comments on commit 0f47aee

Please sign in to comment.