Skip to content

Commit

Permalink
Merge pull request #1920 from blockchain-certificates/feat/proof-domain
Browse files Browse the repository at this point in the history
Feat/proof domain
  • Loading branch information
lemoustachiste authored Dec 6, 2024
2 parents b16e5a1 + c142e3e commit 9a83901
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 36 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"jsonld": "^8.3.2",
"jsonld-checker": "npm:@blockcerts/jsonld-checker@^0.1.9",
"jsonld-signatures": "^11.2.1",
"jsonld-signatures-merkleproof2019": "^2.9.0",
"jsonld-signatures-merkleproof2019": "^2.10.0",
"lodash.clonedeep": "^4.5.0",
"secp256k1": "^5.0.0",
"sha256": "^0.2.0"
Expand Down
8 changes: 7 additions & 1 deletion src/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface CertificateOptions {
// allows to define a specific purpose verification for the verifier (authentication, assertionMethod, etc)
// https://www.w3.org/TR/vc-data-integrity/#proof-purposes
proofPurpose?: string;
// restricts the verification to (a) specific domain(s) - useful for authentication, should match the domain property in the proof
// https://www.w3.org/TR/vc-data-integrity/#defn-domain
domain?: string | string[];
}

export interface Signers {
Expand All @@ -53,6 +56,7 @@ export default class Certificate {
public certificateJson: Blockcerts;
public description?: string; // v1, v3.2
public display?: BlockcertsV3Display;
public proofDomain?: string | string[];
public expires: string;
public validFrom: string;
public explorerAPIs: ExplorerAPI[] = [];
Expand Down Expand Up @@ -128,7 +132,8 @@ export default class Certificate {
hashlinkVerifier: this.hashlinkVerifier,
revocationKey: this.revocationKey,
explorerAPIs: deepCopy<ExplorerAPI[]>(this.explorerAPIs),
proofPurpose: this.proofPurpose
proofPurpose: this.proofPurpose,
proofDomain: this.proofDomain
});
await this.verifier.init();
this.verificationSteps = this.verifier.getVerificationSteps();
Expand Down Expand Up @@ -183,6 +188,7 @@ export default class Certificate {
this.locale = domain.i18n.ensureIsSupported(this.options.locale === 'auto' ? domain.i18n.detectLocale() : this.options.locale);
this.explorerAPIs = this.options.explorerAPIs ?? [];
this.proofPurpose = this.options.proofPurpose;
this.proofDomain = this.options.domain;

if (options.didResolverUrl) {
domain.did.didResolver.url = options.didResolverUrl;
Expand Down
24 changes: 12 additions & 12 deletions src/data/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"statusCheckLabelPending": "Checking record status"
},
"subSteps": {
"assertProofPurposeLabel": "Assert proof purpose",
"assertProofPurposeLabelPending": "Asserting proof purpose",
"assertProofValidityLabel": "Assert proof purpose",
"assertProofValidityLabelPending": "Asserting proof purpose",
"getTransactionIdLabel": "Get transaction ID",
"getTransactionIdLabelPending": "Getting transaction ID",
"computeLocalHashLabel": "Compute local hash",
Expand Down Expand Up @@ -131,8 +131,8 @@
"statusCheckLabelPending": "Vérification du status"
},
"subSteps": {
"assertProofPurposeLabel": "Vérification de l'objectif de la signature",
"assertProofPurposeLabelPending": "Vérification de l'objectif de la signature",
"assertProofValidityLabel": "Vérification de l'objectif de la signature",
"assertProofValidityLabelPending": "Vérification de l'objectif de la signature",
"getTransactionIdLabel": "Obtention de l'identifiant de transaction",
"getTransactionIdLabelPending": "Obtention de l'identifiant de transaction",
"computeLocalHashLabel": "Calcul du hash local",
Expand Down Expand Up @@ -251,8 +251,8 @@
"statusCheckLabelPending": "Verificando Estado de Grabación"
},
"subSteps": {
"assertProofPurposeLabel": "Afirmar propósito de la prueba",
"assertProofPurposeLabelPending": "Afirmando propósito de la prueba",
"assertProofValidityLabel": "Afirmar propósito de la prueba",
"assertProofValidityLabelPending": "Afirmando propósito de la prueba",
"getTransactionIdLabel": "Obtener Identificación de Transacción",
"getTransactionIdLabelPending": "Obteniendo Identificación de Transacción",
"computeLocalHashLabel": "Calcular cadena binaria local",
Expand Down Expand Up @@ -371,8 +371,8 @@
"statusCheckLabelPending": "Ir-record status qed jiġi ċċekkjat"
},
"subSteps": {
"assertProofPurposeLabel": "Assert proof purpose",
"assertProofPurposeLabelPending": "Asserting proof purpose",
"assertProofValidityLabel": "Assert proof purpose",
"assertProofValidityLabelPending": "Asserting proof purpose",
"getTransactionIdLabel": "Ikseb l-ID ta' tranżazzjoni",
"getTransactionIdLabelPending": "L-ID ta' tranżazzjoni qed tiġi mniżżla",
"computeLocalHashLabel": "Ikkalkula l-hash lokali",
Expand Down Expand Up @@ -491,8 +491,8 @@
"statusCheckLabelPending": "Verifica stato del record"
},
"subSteps": {
"assertProofPurposeLabel": "Assert proof purpose",
"assertProofPurposeLabelPending": "Asserting proof purpose",
"assertProofValidityLabel": "Assert proof purpose",
"assertProofValidityLabelPending": "Asserting proof purpose",
"getTransactionIdLabel": "Ottenere ID transazione",
"getTransactionIdLabelPending": "Ottieni ID transazione",
"computeLocalHashLabel": "Calcolare hash locale",
Expand Down Expand Up @@ -611,8 +611,8 @@
"statusCheckLabelPending": "ステータスを確認"
},
"subSteps": {
"assertProofPurposeLabel": "証明の目的を主弭する",
"assertProofPurposeLabelPending": "証明の目的を主弭中",
"assertProofValidityLabel": "証明の目的を主弭する",
"assertProofValidityLabelPending": "証明の目的を主弭中",
"getTransactionIdLabel": "取引IDの取得",
"getTransactionIdLabelPending": "取引IDを取得",
"computeLocalHashLabel": "ローカルハッシュの算出",
Expand Down
1 change: 1 addition & 0 deletions src/models/Suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface SuiteAPI {
proof: VCProof | MerkleProof2017;
issuer: Issuer;
proofPurpose?: string;
proofDomain?: string | string[];
}

export abstract class Suite {
Expand Down
3 changes: 3 additions & 0 deletions src/suites/MerkleProof2019.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export default class MerkleProof2019 extends Suite {
public cryptosuite = 'merkle-proof-2019';
public suite: LDMerkleProof2019;
public proofPurpose: string;
public proofDomain: string | string[];

constructor (props: SuiteAPI) {
super(props);
Expand All @@ -66,6 +67,7 @@ export default class MerkleProof2019 extends Suite {
this.proof = props.proof as VCProof;
this.issuer = props.issuer;
this.proofPurpose = props.proofPurpose;
this.proofDomain = props.proofDomain;
this.validateProofType();
this.receipt = parseReceipt(this.proof);
this.transactionId = domain.certificates.getTransactionId(this.receipt);
Expand Down Expand Up @@ -172,6 +174,7 @@ export default class MerkleProof2019 extends Suite {
proof: this.proof,
verificationMethod: this.verificationMethodPublicKey,
proofPurpose: this.proofPurpose,
domain: this.proofDomain,
options: {
explorerAPIs: this.explorerAPIs,
executeStepMethod: this.executeStep
Expand Down
8 changes: 6 additions & 2 deletions src/verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export enum SupportedVerificationSuites {
export default class Verifier {
public expires: string;
public validFrom: string;
public proofDomain: string | string[];
public id: string;
public issuer: Issuer;
public revocationKey: string;
Expand All @@ -77,7 +78,7 @@ export default class Verifier {
public proofPurpose: string;

constructor (
{ certificateJson, expires, hashlinkVerifier, id, issuer, revocationKey, explorerAPIs, validFrom, proofPurpose }: {
{ certificateJson, expires, hashlinkVerifier, id, issuer, revocationKey, explorerAPIs, validFrom, proofPurpose, proofDomain }: {
certificateJson: Blockcerts;
expires: string;
validFrom?: string;
Expand All @@ -87,6 +88,7 @@ export default class Verifier {
revocationKey: string;
explorerAPIs?: ExplorerAPI[];
proofPurpose?: string;
proofDomain?: string | string[];
}
) {
this.expires = expires;
Expand All @@ -97,6 +99,7 @@ export default class Verifier {
this.revocationKey = revocationKey;
this.explorerAPIs = explorerAPIs;
this.proofPurpose = proofPurpose;
this.proofDomain = proofDomain;

this.documentToVerify = Object.assign<any, Blockcerts>({}, certificateJson);
}
Expand Down Expand Up @@ -220,7 +223,8 @@ export default class Verifier {
proof,
explorerAPIs: this.explorerAPIs,
issuer: this.issuer,
proofPurpose: this.proofPurpose
proofPurpose: this.proofPurpose,
proofDomain: this.proofDomain
};

this.proofVerifiers.push(new this.supportedVerificationSuites[proofTypes[index]](suiteOptions));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export default [
proofType: 'MerkleProof2019',
subSteps: [
{
code: 'assertProofPurpose',
label: defaultLanguageSet.subSteps.assertProofPurposeLabel,
labelPending: defaultLanguageSet.subSteps.assertProofPurposeLabelPending,
code: 'assertProofValidity',
label: defaultLanguageSet.subSteps.assertProofValidityLabel,
labelPending: defaultLanguageSet.subSteps.assertProofValidityLabelPending,
parentStep: 'proofVerification',
status: VERIFICATION_STATUSES.DEFAULT
},
Expand Down
6 changes: 3 additions & 3 deletions test/assertions/verification-steps-v3-hashlink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export default [
proofType: 'MerkleProof2019',
subSteps: [
{
code: 'assertProofPurpose',
label: defaultLanguageSet.subSteps.assertProofPurposeLabel,
labelPending: defaultLanguageSet.subSteps.assertProofPurposeLabelPending,
code: 'assertProofValidity',
label: defaultLanguageSet.subSteps.assertProofValidityLabel,
labelPending: defaultLanguageSet.subSteps.assertProofValidityLabelPending,
parentStep: 'proofVerification',
status: VERIFICATION_STATUSES.DEFAULT
},
Expand Down
2 changes: 1 addition & 1 deletion test/assertions/verification-steps-v3-multiple-proofs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const defaultLanguageSet = i18n[currentLocale.locale];

const merkleProof2019VerificationSubsteps = [
{
code: 'assertProofPurpose',
code: 'assertProofValidity',
label: 'Assert proof purpose',
labelPending: 'Asserting proof purpose',
parentStep: 'proofVerification',
Expand Down
6 changes: 3 additions & 3 deletions test/assertions/verification-steps-v3-no-did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default [
proofType: 'MerkleProof2019',
subSteps: [
{
code: 'assertProofPurpose',
label: defaultLanguageSet.subSteps.assertProofPurposeLabel,
labelPending: defaultLanguageSet.subSteps.assertProofPurposeLabelPending,
code: 'assertProofValidity',
label: defaultLanguageSet.subSteps.assertProofValidityLabel,
labelPending: defaultLanguageSet.subSteps.assertProofValidityLabelPending,
parentStep: 'proofVerification',
status: VERIFICATION_STATUSES.DEFAULT
},
Expand Down
6 changes: 3 additions & 3 deletions test/assertions/verification-steps-v3-validFrom-mocknet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export default [
proofType: 'MerkleProof2019',
subSteps: [
{
code: 'assertProofPurpose',
label: defaultLanguageSet.subSteps.assertProofPurposeLabel,
labelPending: defaultLanguageSet.subSteps.assertProofPurposeLabelPending,
code: 'assertProofValidity',
label: defaultLanguageSet.subSteps.assertProofValidityLabel,
labelPending: defaultLanguageSet.subSteps.assertProofValidityLabelPending,
parentStep: 'proofVerification',
status: VERIFICATION_STATUSES.DEFAULT
},
Expand Down
6 changes: 3 additions & 3 deletions test/assertions/verification-steps-v3-with-did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default [
proofType: 'MerkleProof2019',
subSteps: [
{
code: 'assertProofPurpose',
label: defaultLanguageSet.subSteps.assertProofPurposeLabel,
labelPending: defaultLanguageSet.subSteps.assertProofPurposeLabelPending,
code: 'assertProofValidity',
label: defaultLanguageSet.subSteps.assertProofValidityLabel,
labelPending: defaultLanguageSet.subSteps.assertProofValidityLabelPending,
parentStep: 'proofVerification',
status: VERIFICATION_STATUSES.DEFAULT
},
Expand Down
36 changes: 36 additions & 0 deletions test/e2e/verifier/mocknet-vc-v2-proofDomain-invalid.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, it, expect, vi } from 'vitest';
import { Certificate, VERIFICATION_STATUSES } from '../../../src';
import MocknetVCV2ValidFromValid from '../../fixtures/v3/mocknet-vc-v2-validFrom-valid.json';
import fixtureBlockcertsIssuerProfile from '../../fixtures/issuer-blockcerts.json';

describe('given the proofPurpose of the certificate\'s proof does not match the verifier\'s purpose', function () {
// this test will expire in 2039
it('should fail verification', async function () {
vi.mock('@blockcerts/explorer-lookup', async (importOriginal) => {
const explorerLookup = await importOriginal();
return {
...explorerLookup,
request: async function ({ url }) {
if (url === 'https://www.blockcerts.org/samples/3.0/issuer-blockcerts.json') {
return JSON.stringify(fixtureBlockcertsIssuerProfile);
}
}
};
});

const fixture = {
...MocknetVCV2ValidFromValid,
proof: {
...MocknetVCV2ValidFromValid.proof,
domain: 'blockcerts.org'
}
};

const certificate = new Certificate(fixture, { domain: ['example.org', 'another-example.org'] });
await certificate.init();
const result = await certificate.verify();
expect(result.status).toBe(VERIFICATION_STATUSES.FAILURE);
expect(result.message).toBe('The proof is not authorized for this domain');
vi.restoreAllMocks();
});
});

0 comments on commit 9a83901

Please sign in to comment.