Skip to content

Commit

Permalink
Merge pull request #343 from docknetwork/feat/jws-issuance
Browse files Browse the repository at this point in the history
JWS issuance by default
  • Loading branch information
cykoder authored Feb 24, 2023
2 parents f01bc76 + 318f2d2 commit cdd210f
Show file tree
Hide file tree
Showing 13 changed files with 51 additions and 108 deletions.
81 changes: 0 additions & 81 deletions example/didweb.js

This file was deleted.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@docknetwork/sdk",
"version": "2.10.0",
"version": "3.0.0",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -55,15 +55,14 @@
"staking_payouts": "npx babel-node scripts/staking_payouts.js",
"did-resolver-example": "npx babel-node example/resolver.js",
"revocation-example": "npx babel-node example/revocation.js",
"didweb-example": "npx babel-node example/didweb.js",
"vcdm-example": "npx babel-node example/vcdm.js",
"standard-schemas-example": "npx babel-node example/standard_schemas.js",
"schema-example": "npx babel-node example/schema.js",
"schema-validation-example": "npx babel-node example/schema-validation.js",
"blob-example": "npx babel-node example/blob.js",
"claim-deduction-example": "npx babel-node example/claim-deduction.js",
"anchor-example": "npx babel-node example/anchor.js",
"test-examples": "yarn didweb-example && yarn dock-did-example && yarn did-resolver-example && yarn revocation-example && yarn vcdm-example && yarn standard-schemas-example && yarn schema-example && yarn schema-validation-example && yarn blob-example && yarn claim-deduction-example && yarn anchor-example",
"test-examples": "yarn dock-did-example && yarn did-resolver-example && yarn revocation-example && yarn vcdm-example && yarn standard-schemas-example && yarn schema-example && yarn schema-validation-example && yarn blob-example && yarn claim-deduction-example && yarn anchor-example",
"get-did": "npx babel-node scripts/get_did_doc.js",
"upgrade_with_sudo": "npx babel-node scripts/runtime_upgrade_with_sudo.js",
"onboard_with_sudo": "npx babel-node scripts/onboard_validator_with_sudo.js",
Expand Down
1 change: 0 additions & 1 deletion src/bbs-plus-presentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export default class BbsPlusPresentation {
});

const convertedCredential = Credential.fromJSON(credential, CustomLinkedDataSignature.fromJsigProofValue(credentialLD.proof.proofValue));
console.log('convertedCredential', convertedCredential.toJSON());
const idx = await this.presBuilder.addCredential(convertedCredential, pk);

// Enforce revealing of verificationMethod and type
Expand Down
4 changes: 2 additions & 2 deletions src/utils/vc/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,9 @@ export async function verifyCredential(credential, {
* `@context` (if it is not present, it's added to the context list).
* @return {Promise<object>} The signed credential object.
*/
export async function issueCredential(keyDoc, credential, compactProof = true, documentLoader = null, purpose = null, expansionMap = null, issuerObject = null, addSuiteContext = false) {
export async function issueCredential(keyDoc, credential, compactProof = true, documentLoader = null, purpose = null, expansionMap = null, issuerObject = null, addSuiteContext = false, useProofValue = false) {
// Get suite from keyDoc parameter
const suite = getSuiteFromKeyDoc(keyDoc);
const suite = getSuiteFromKeyDoc(keyDoc, useProofValue);
if (!suite.verificationMethod) {
throw new TypeError('"suite.verificationMethod" property is required.');
}
Expand Down
1 change: 1 addition & 0 deletions src/utils/vc/crypto/Bls12381BBSSignatureDock2022.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default class Bls12381BBSSignatureDock2022 extends CustomLinkedDataSignat
alg: 'Bls12381BBS+SignatureDock2022',
signer: signer || Bls12381BBSSignatureDock2022.signerFactory(keypair, verificationMethod),
verifier,
useProofValue: true,
});

this.proof = {
Expand Down
1 change: 1 addition & 0 deletions src/utils/vc/crypto/Bls12381BBSSignatureProofDock2022.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default class Bls12381BBSSignatureProofDock2022 extends CustomLinkedDataS
LDKeyClass: Bls12381G2KeyPairDock2022,
contextUrl: SUITE_CONTEXT_URL,
alg: Bls12381BBSSigProofDockSigName,
useProofValue: true,
});

this.proof = {
Expand Down
3 changes: 2 additions & 1 deletion src/utils/vc/crypto/EcdsaSepc256k1Signature2019.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class EcdsaSepc256k1Signature2019 extends CustomLinkedDataSignatu
* @param {object} config - Configuration options
*/
constructor({
keypair, verificationMethod, verifier, signer,
keypair, verificationMethod, verifier, signer, useProofValue,
} = {}) {
super({
type: EcdsaSecp256k1SigName,
Expand All @@ -21,6 +21,7 @@ export default class EcdsaSepc256k1Signature2019 extends CustomLinkedDataSignatu
alg: 'ES256K',
signer: signer || EcdsaSepc256k1Signature2019.signerFactory(keypair, verificationMethod),
verifier,
useProofValue,
});
this.requiredKeyType = EcdsaSecp256k1VerKeyName;
}
Expand Down
3 changes: 2 additions & 1 deletion src/utils/vc/crypto/Sr25519Signature2020.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class Sr25519Signature2020 extends CustomLinkedDataSignature {
* @param {object} config - Configuration options
*/
constructor({
keypair, verificationMethod, verifier, signer,
keypair, verificationMethod, verifier, signer, useProofValue,
} = {}) {
super({
type: Sr25519SigName,
Expand All @@ -20,6 +20,7 @@ export default class Sr25519Signature2020 extends CustomLinkedDataSignature {
alg: 'EdDSA',
signer: signer || Sr25519Signature2020.signerFactory(keypair, verificationMethod),
verifier,
useProofValue,
});
this.requiredKeyType = Sr25519VerKeyName;
}
Expand Down
39 changes: 26 additions & 13 deletions src/utils/vc/crypto/custom-linkeddatasignature.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default class CustomLinkedDataSignature extends jsigs.suites.LinkedDataSi
constructor(config) {
super(config);
this.alg = config.alg;
this.useProofValue = config.useProofValue || false;
}

/**
Expand Down Expand Up @@ -101,22 +102,34 @@ export default class CustomLinkedDataSignature extends jsigs.suites.LinkedDataSi
throw new Error('A signer API has not been specified.');
}

let signatureBytes;
const signature = await this.signer.sign({ data: verifyData });
if (typeof signature === 'string') {
// Some signers will return a string like: header..signature
// split apart those strings to get the signature in bytes
const signatureSplit = signature.split('.');
const signatureEncoded = signatureSplit[signatureSplit.length - 1];
signatureBytes = decodeBase64Url(signatureEncoded);
const getSigBytes = async (data) => {
let signatureBytes;
const signature = await this.signer.sign({ data });
if (typeof signature === 'string') {
// Some signers will return a string like: header..signature
// split apart those strings to get the signature in bytes
const signatureSplit = signature.split('.');
const signatureEncoded = signatureSplit[signatureSplit.length - 1];
signatureBytes = decodeBase64Url(signatureEncoded);
} else {
signatureBytes = signature;
}
return signatureBytes;
};

const finalProof = { ...proof };
if (this.useProofValue) {
const signatureBytes = await getSigBytes(verifyData);
finalProof.proofValue = CustomLinkedDataSignature.encodeProofValue(signatureBytes);
} else {
signatureBytes = signature;
const header = { alg: this.alg, b64: false, crit: ['b64'] };
const encodedHeader = base64url.encode(JSON.stringify(header));
const jwsData = createJws({ encodedHeader, verifyData });
const signatureBytesJWS = await getSigBytes(jwsData);
finalProof.jws = `${encodedHeader}..${base64url.encode(signatureBytesJWS)}`;
}

return {
...proof,
proofValue: CustomLinkedDataSignature.encodeProofValue(signatureBytes),
};
return finalProof;
}

static encodeProofValue(signatureBytes) {
Expand Down
4 changes: 3 additions & 1 deletion src/utils/vc/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function getKeyDoc(did, keypair, type, id) {
* @param {object} keyDoc - key document containing `id`, `controller`, `type`, `privateKeyBase58` and `publicKeyBase58`
* @returns {object} - signature suite.
*/
export function getSuiteFromKeyDoc(keyDoc) {
export function getSuiteFromKeyDoc(keyDoc, useProofValue) {
// Check if passing suite directly
if (keyDoc.verificationMethod) {
return keyDoc;
Expand All @@ -62,9 +62,11 @@ export function getSuiteFromKeyDoc(keyDoc) {
default:
throw new Error(`Unknown key type ${keyDoc.type}.`);
}

return new Cls({
...keyDoc,
verificationMethod: keyDoc.id,
useProofValue,
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/verifiable-credential.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,15 @@ class VerifiableCredential {
* `@context` (if it is not present, it's added to the context list).
* @returns {Promise<VerifiableCredential>}
*/
async sign(keyDoc, compactProof = true, issuerObject = null, addSuiteContext = false) {
async sign(keyDoc, compactProof = true, issuerObject = null, addSuiteContext = false, useProofValue = false) {
const signedVC = await issueCredential(
keyDoc,
this.toJSON(),
compactProof,
null, null, null,
issuerObject,
addSuiteContext,
useProofValue,
);
this.setProof(signedVC.proof);
this.issuer = signedVC.issuer;
Expand Down
1 change: 0 additions & 1 deletion tests/integration/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export function getCredMatcherDoc(cred, issuer, issuerKeyId, sigType) {
issuer,
proof: expect.objectContaining({
type: sigType,
proofValue: expect.anything(),
proofPurpose: 'assertionMethod',
verificationMethod: issuerKeyId,
}),
Expand Down
13 changes: 10 additions & 3 deletions tests/unit/issuing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function getSamplePres(signed = false) {
type: 'EcdsaSecp256k1Signature2019',
challenge: 'some_challenge',
domain: 'some_domain',
proofValue: expect.anything(),
jws: expect.anything(),
proofPurpose: 'authentication',
verificationMethod: keyUrl,
},
Expand Down Expand Up @@ -118,6 +118,13 @@ describe('Verifiable Credential Issuing', () => {
expect(result.results[0].verified).toBe(true);
}, 30000);

test('Issuing should return an object with a proof, and it must pass validation (using proofValue).', async () => {
const credential = await issueCredential(getSampleKey(), getSampleCredential(), true, null, null, null, null, false, true);
expect(credential.proof.proofValue).toBeDefined();
const result = await verifyCredential(credential);
expect(result.verified).toBe(true);
}, 30000);

test('Tampered Credential should not pass validation.', async () => {
const credential = await issueCredential(getSampleKey(), getSampleCredential());
credential.issuanceDate = '9020-04-15T09:05:35Z';
Expand Down Expand Up @@ -422,7 +429,7 @@ describe('Verifiable Presentation incremental creation', () => {
expect(vp.proof).toMatchObject({ created: expect.anything() });
expect(vp.proof).toMatchObject({ challenge: 'some_challenge' });
expect(vp.proof).toMatchObject({ domain: 'some_domain' });
expect(vp.proof).toMatchObject({ proofValue: expect.anything() });
expect(vp.proof).toMatchObject({ jws: expect.anything() });
expect(vp.proof).toMatchObject({ proofPurpose: 'authentication' });
expect(vp.proof).toMatchObject({ verificationMethod: expect.anything() });

Expand Down Expand Up @@ -462,7 +469,7 @@ describe('Verifiable Presentation incremental creation', () => {
expect(vp.proof).toMatchObject({ created: expect.anything() });
expect(vp.proof).toMatchObject({ challenge: 'some_challenge' });
expect(vp.proof).toMatchObject({ domain: 'some_domain' });
expect(vp.proof).toMatchObject({ proofValue: expect.anything() });
expect(vp.proof).toMatchObject({ jws: expect.anything() });
expect(vp.proof).toMatchObject({ proofPurpose: 'authentication' });
expect(vp.proof).toMatchObject({ verificationMethod: expect.anything() });

Expand Down

0 comments on commit cdd210f

Please sign in to comment.