Skip to content

Commit

Permalink
Merge pull request #116 from blockchain-certificates/feat/proof-chall…
Browse files Browse the repository at this point in the history
…enge

feat(DataIntegrity): support challenge verification
  • Loading branch information
lemoustachiste authored Dec 5, 2024
2 parents fb26dc2 + b92cd0e commit 09a28a4
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/data/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"assertProofValidityPurposeVerifier": "Invalid proof purpose. Expected ${expectedProofPurpose} but received ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "The verification method ${proof.verificationMethod} is not allowed for the proof purpose ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "The proof is not authorized for this domain",
"assertProofValidityInvalidChallenge":"The proof's challenge does not match the verifier's challenge",
"ensureHashesEqual": "Computed hash does not match remote hash",
"ensureMerkleRootEqual": "Merkle root does not match remote hash.",
"ensureValidReceipt": "The receipt is malformed. There was a problem navigating the merkle tree in the receipt.",
Expand All @@ -23,6 +24,7 @@
"assertProofValidityPurposeVerifier": "Objectif de la signature invalide. Attendu: ${expectedProofPurpose} mais la signature a un objectif de ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "La méthode de vérification ${proof.verificationMethod} n'est pas autorisée pour l'objectif de verification: ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "La signature n'est pas autorisée pour ce domaine",
"assertProofValidityInvalidChallenge": "Le challenge de la signature ne correspond pas au challenge de vérification",
"ensureHashesEqual": "Calcul du hash local différent du hash distant",
"ensureMerkleRootEqual": "Le Merkle root ne correspond pas au hash distant",
"ensureValidReceipt": "Erreur d'écriture du reçu. Un problème est survenu lors de la navigation de l'arbre Merkle du reçu.",
Expand All @@ -42,6 +44,7 @@
"assertProofValidityPurposeVerifier": "Objectivo de verificación invalido. Se esperaba ${expectedProofPurpose} pero recibió ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "El método de verificación ${proof.verificationMethod} no está permitido para el objectivo de verificación ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "La prueba no está autorizada para este dominio",
"assertProofValidityInvalidChallenge": "El reto de la prueba no coincide con el reto de verificación",
"ensureHashesEqual": "La cadena binaria calculada no corresponde con la cadena binaria remota",
"ensureMerkleRootEqual": "La raíz Merkle no corresponde con la cadena binaria remota",
"ensureValidReceipt": "El recibo está malformado. Hubo un problema navegando el árbol Merkle en el recibo",
Expand All @@ -61,6 +64,7 @@
"assertProofValidityPurposeVerifier": "Invalid proof purpose. Expected ${expectedProofPurpose} but received ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "The verification method ${proof.verificationMethod} is not allowed for the proof purpose ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "The proof is not authorized for this domain",
"assertProofValidityInvalidChallenge": "The proof's challenge does not match the verifier's challenge",
"ensureHashesEqual": "Il-hash ikkalkulat ma jikkorrispondix mar-remote hash",
"ensureMerkleRootEqual": "Merkle root ma taqbilx mar-remote hash",
"ensureValidReceipt": "L-irċevuta hija malformata. Kien hemm problema fin-navigazzjoni tal-merkle tree fl-irċevuta",
Expand All @@ -80,6 +84,7 @@
"assertProofValidityPurposeVerifier": "Invalid proof purpose. Expected ${expectedProofPurpose} but received ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "The verification method ${proof.verificationMethod} is not allowed for the proof purpose ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "The proof is not authorized for this domain",
"assertProofValidityInvalidChallenge": "The proof's challenge does not match the verifier's challenge",
"ensureHashesEqual": "L'hash calcolato non corrisponde all'hash remoto",
"ensureMerkleRootEqual": "La radice di Merkle non corrisponde all'hash remoto.",
"ensureValidReceipt": "La ricevuta è malformata. C'è stato un problema nella navigazione dell'albero di Merkle nella ricevuta.",
Expand All @@ -99,6 +104,7 @@
"assertProofValidityPurposeVerifier": "Invalid proof purpose. Expected ${expectedProofPurpose} but received ${proof.proofPurpose}",
"assertProofValidityPurposeIssuerKey": "The verification method ${proof.verificationMethod} is not allowed for the proof purpose ${expectedProofPurpose}",
"assertProofValidityDomainVerifier": "The proof is not authorized for this domain",
"assertProofValidityInvalidChallenge": "The proof's challenge does not match the verifier's challenge",
"ensureHashesEqual": "算出されたハッシュがリモートハッシュと一致しませんでした",
"ensureMerkleRootEqual": "Merkle rootがリモートハッシュと一致しませんでした",
"ensureValidReceipt": "レシートが異常です。レシート内のMerkle treeを辿る際に問題が発生しました。",
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface MerkleProof2019API {
// the purpose of proof that the verifier will be used for, defaults to assertionMethod
proofPurpose?: string;
domain?: string | string[];
challenge?: string;
}

export interface MerkleProof2019VerificationResult {
Expand All @@ -60,6 +61,7 @@ export class LDMerkleProof2019 extends LinkedDataProof {
* using a context different from security-v2).
* @param [document] {document} document used and signed by the MerkleProof2019 signature
*/
public challenge: string;
public domain: string[];
public type: string = 'MerkleProof2019';
public issuer: any = null; // TODO: define issuer type
Expand Down Expand Up @@ -98,7 +100,8 @@ export class LDMerkleProof2019 extends LinkedDataProof {
proof = null,
options = {},
proofPurpose = 'assertionMethod',
domain = []
domain = [],
challenge = ''
}: MerkleProof2019API) {
super({ type: 'MerkleProof2019' });

Expand All @@ -111,6 +114,7 @@ export class LDMerkleProof2019 extends LinkedDataProof {
this.document = document;
this.proofPurpose = proofPurpose;
this.domain = Array.isArray(domain) ? domain : [domain];
this.challenge = challenge;
this.setProof(proof);
this.setOptions(options);
this.getChain();
Expand Down Expand Up @@ -245,6 +249,7 @@ export class LDMerkleProof2019 extends LinkedDataProof {
() => assertProofValidity({
expectedProofPurpose: this.proofPurpose,
expectedDomain: this.domain,
expectedChallenge: this.challenge,
proof: this.proof,
issuer: this.issuer
}),
Expand Down
21 changes: 19 additions & 2 deletions src/inspectors/assertProofValidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ function assertProofPurposeValidity ({ expectedProofPurpose, proof, issuer }): v
.replace('${expectedProofPurpose}', expectedProofPurpose)
);
}

if (expectedProofPurpose === 'authentication' && !proof.domain) {
// TODO: return actual warning
console.warn('No domain found in proof, but it is recommended for authentication purposes');
}
}

function assertProofDomain ({ expectedDomain, proof }): void {
function assertProofDomain ({ expectedDomain, proof, expectedChallenge }): void {
if (!expectedDomain.includes(proof.domain)) {
throw new VerifierError('assertProofValidity',
getText('errors', 'assertProofValidityDomainVerifier')
Expand All @@ -34,18 +39,30 @@ function assertProofDomain ({ expectedDomain, proof }): void {
.replace('${proof.domain}', proof.domain)
);
}

if (proof.domain && !proof.challenge) {
// TODO: return actual warning
console.warn('No challenge found in proof, but it is recommended for domain verification');
}

if (proof.challenge && proof.challenge !== expectedChallenge) {
throw new VerifierError('assertProofValidity',
getText('errors', 'assertProofValidityInvalidChallenge'));
}
}

interface AssertProofValidityAPI {
expectedProofPurpose: string;
expectedDomain: string[];
expectedChallenge: string;
proof: VCProof;
issuer: any;
}

export default function assertProofValidity ({
expectedProofPurpose,
expectedDomain,
expectedChallenge,
proof,
issuer
}: AssertProofValidityAPI): boolean {
Expand All @@ -54,7 +71,7 @@ export default function assertProofValidity ({
}

if (proof.domain) {
assertProofDomain({ expectedDomain, proof });
assertProofDomain({ expectedDomain, proof, expectedChallenge });
}

return true;
Expand Down
24 changes: 24 additions & 0 deletions tests/verification/data-integrity-proof-support.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,28 @@ describe('given the document is signed following the DataIntegrityProof spec', f
});
});
});

describe('given the challenge provided by the proof does not match the verifier\'s challenge', function () {
it('should throw an error', async function () {
const instance = new LDMerkleProof2019({
document: fixture,
proof: {
...fixture.proof,
domain: 'blockcerts.org',
challenge: 'another-challenge'
},
proofPurpose: 'assertionMethod',
issuer: fixtureIssuerProfile,
domain: 'blockcerts.org',
challenge: 'challenge'
});

const result = await instance.verifyProof();
expect(result).toEqual({
verified: false,
verificationMethod: null,
error: 'The proof\'s challenge does not match the verifier\'s challenge'
});
});
});
});

0 comments on commit 09a28a4

Please sign in to comment.