-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: PatStLouis <[email protected]>
- Loading branch information
1 parent
cab6b26
commit d8b6034
Showing
4 changed files
with
211 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/*! | ||
* Copyright 2024 Digital Bazaar, Inc. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
import { | ||
addProof, | ||
createVc, | ||
} from './vc-issuer/index.js'; | ||
import { | ||
setupReportableTestSuite, | ||
setupRow, | ||
} from './helpers.js'; | ||
import {endpoints} from 'vc-test-suite-implementations'; | ||
|
||
const cryptosuites = [ | ||
'ecdsa-rdfc-2019', | ||
]; | ||
|
||
const {match: issuers} = endpoints.filterByTag({ | ||
tags: cryptosuites, | ||
property: 'issuers' | ||
}); | ||
|
||
const {match: verifiers} = endpoints.filterByTag({ | ||
tags: cryptosuites, | ||
property: 'verifiers' | ||
}); | ||
|
||
describe('Proof Chains', function() { | ||
setupReportableTestSuite(this); | ||
for(const [columnId, {endpoints}] of verifiers) { | ||
describe(columnId, function() { | ||
const [verifier] = endpoints; | ||
let issuedCredential; | ||
let issuedProofSet; | ||
let issuedProofChain; | ||
before(async function() { | ||
// signedCredential = await addProof( | ||
// generateCredential()); | ||
issuedCredential = await createVc(); | ||
issuedProofSet = await addProof( | ||
structuredClone(issuedCredential)); | ||
issuedProofChain = await addProof( | ||
structuredClone(issuedProofSet), issuedCredential.proof[0].id); | ||
}); | ||
beforeEach(setupRow); | ||
it('If a proof with id value equal to the value of previousProof ' + | ||
'does not exist in allProofs, an error MUST be raised and SHOULD ' + | ||
'convey an error type of PROOF_VERIFICATION_ERROR.', | ||
async function() { | ||
this.test.link = 'https://www.w3.org/TR/vc-data-integrity/#verify-proof-sets-and-chains'; | ||
}); | ||
it('If any element of previousProof list has an id attribute ' + | ||
'value that does not match the id attribute value of any ' + | ||
'element of allProofs, an error MUST be raised and SHOULD ' + | ||
'convey an error type of PROOF_VERIFICATION_ERROR.', | ||
async function() { | ||
this.test.link = 'https://www.w3.org/TR/vc-data-integrity/#verify-proof-sets-and-chains'; | ||
}); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/*! | ||
* Copyright (c) 2023-2024 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import dataIntegrityContext from '@digitalbazaar/data-integrity-context'; | ||
import multikeyContext from '@digitalbazaar/multikey-context'; | ||
import {named} from '@digitalbazaar/credentials-context'; | ||
import {securityLoader} from '@digitalbazaar/security-document-loader'; | ||
|
||
export const loader = securityLoader(); | ||
|
||
loader.addStatic( | ||
named.get('v2').id, | ||
named.get('v2').context | ||
); | ||
|
||
loader.addStatic( | ||
dataIntegrityContext.constants.CONTEXT_URL, | ||
dataIntegrityContext.contexts.get(dataIntegrityContext.constants.CONTEXT_URL) | ||
); | ||
|
||
loader.addStatic( | ||
multikeyContext.constants.CONTEXT_URL, | ||
multikeyContext.contexts.get(multikeyContext.constants.CONTEXT_URL) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/*! | ||
* Copyright 2024 Digital Bazaar, Inc. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
import * as base58 from 'base58-universal'; | ||
import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey'; | ||
import * as rdfCanonize from 'rdf-canonize'; | ||
import crypto from 'crypto'; | ||
// import {DataIntegrityProof} from '@digitalbazaar/data-integrity'; | ||
// import {cryptosuite as ecdsaRdfc2019Cryptosuite} from | ||
// '@digitalbazaar/ecdsa-rdfc-2019-cryptosuite'; | ||
// import jsigs from 'jsonld-signatures'; | ||
import jsonld from 'jsonld'; | ||
// const {purposes: {AssertionProofPurpose}} = jsigs; | ||
import {loader} from './documentLoader.js'; | ||
|
||
const documentLoader = loader.build(); | ||
const publicKeyMultibase = 'zDnaekGZTbQBerwcehBSXLqAg6s55hVEBms1zFy89VHXtJSa9'; | ||
const secretKeyMultibase = 'z42tqZ5smVag3DtDhjY9YfVwTMyVHW6SCHJi2ZMrD23DGYS3'; | ||
const controller = `did:key:${publicKeyMultibase}`; | ||
|
||
function generateProofId() { | ||
return `urn:uuid:${crypto.randomUUID()}`; | ||
} | ||
|
||
const dataIntegrityProof = { | ||
type: 'DataIntegrityProof', | ||
cryptosuite: 'ecdsa-rdfc-2019', | ||
proofPurpose: 'assertionMethod', | ||
verificationMethod: `${controller}#${publicKeyMultibase}`, | ||
}; | ||
|
||
// create the keypair to use when signing | ||
const keyPair = await EcdsaMultikey.from({ | ||
'@context': 'https://w3id.org/security/multikey/v1', | ||
id: `${controller}#${publicKeyMultibase}`, | ||
type: 'Multikey', | ||
controller, | ||
publicKeyMultibase, | ||
secretKeyMultibase | ||
}); | ||
|
||
// // create suite | ||
// const suite = new DataIntegrityProof({ | ||
// signer: keyPair.signer(), cryptosuite: ecdsaRdfc2019Cryptosuite | ||
// }); | ||
|
||
// create the unsigned credential | ||
const unsignedCredential = { | ||
'@context': ['https://www.w3.org/ns/credentials/v2'], | ||
type: ['VerifiableCredential'], | ||
issuer: controller, | ||
credentialSubject: {name: 'Alice'} | ||
}; | ||
|
||
export async function createVc() { | ||
return addProof(unsignedCredential); | ||
} | ||
|
||
export async function addProof(credential, previousProof = null) { | ||
// const unsecuredDocument = structuredClone(unsignedCredential); | ||
// const signedDocument = await jsigs.sign(credential, { | ||
// suite, | ||
// purpose: new AssertionProofPurpose(), | ||
// documentLoader | ||
// }); | ||
const proofSet = credential?.proof || []; | ||
const unsecuredDocument = structuredClone(credential); | ||
delete unsecuredDocument.proof; | ||
const proofOptions = structuredClone(dataIntegrityProof); | ||
if(previousProof) { | ||
// const allProofs = []; | ||
const matchingProofs = proofSet.filter(entry => entry.id === previousProof); | ||
unsecuredDocument.proof = matchingProofs; | ||
proofOptions.previousProof = previousProof; | ||
} | ||
|
||
const securedDocument = structuredClone(unsecuredDocument); | ||
const proof = await createProof(unsecuredDocument, proofOptions); | ||
proofSet.push(proof); | ||
securedDocument.proof = proofSet; | ||
|
||
return securedDocument; | ||
} | ||
|
||
export async function createProof(unsecuredDocument, options) { | ||
// https://www.w3.org/TR/vc-di-ecdsa/#create-proof-ecdsa-rdfc-2019 | ||
options.id = generateProofId(); | ||
const proof = structuredClone(options); | ||
|
||
options['@context'] = unsecuredDocument['@context']; | ||
|
||
const proofConfig = await canonize(options); | ||
const proofConfigHash = | ||
crypto.createHash('sha256').update(proofConfig).digest(); | ||
|
||
const transformedData = await canonize(unsecuredDocument); | ||
const transformedDataHash = | ||
crypto.createHash('sha256').update(transformedData).digest(); | ||
|
||
const hashData = Buffer.concat([proofConfigHash, transformedDataHash]); | ||
|
||
const proofbytes = await keyPair.signer().sign({data: hashData}); | ||
|
||
proof.proofValue = `z${base58.encode(proofbytes)}`; | ||
|
||
return proof; | ||
} | ||
|
||
async function canonize(input) { | ||
const options = { | ||
algorithm: 'RDFC-1.0', | ||
base: null, | ||
// format: 'application/n-quads', | ||
documentLoader, | ||
safe: true, | ||
skipExpansion: false, | ||
produceGeneralizedRdf: false, | ||
rdfDirection: 'i18n-datatype', | ||
messageDigestAlgorithm: 'SHA-256', | ||
}; | ||
const dataset = await jsonld.toRDF(input, options); | ||
return rdfCanonize.canonize(dataset, options); | ||
} |