Skip to content

Commit

Permalink
Merge pull request #334 from docknetwork/feat/derived-bbs-credentials
Browse files Browse the repository at this point in the history
Allow to derive BBS+ W3C credentials for presenting
  • Loading branch information
osinakayah authored Dec 16, 2022
2 parents 94bfa1f + 0ef563f commit b6b8359
Show file tree
Hide file tree
Showing 13 changed files with 513 additions and 71 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@docknetwork/sdk",
"version": "2.9.0",
"version": "2.9.1",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -85,7 +85,7 @@
"type-check": "tsc --allowJs --checkJs --noEmit --moduleResolution node --resolveJsonModule --target ES6 --skipLibCheck true --allowSyntheticDefaultImports true"
},
"dependencies": {
"@docknetwork/crypto-wasm-ts": "0.31.0",
"@docknetwork/crypto-wasm-ts": "0.31.1",
"@docknetwork/node-types": "^0.13.0",
"@juanelas/base64": "^1.0.5",
"@polkadot/api": "9.7.1",
Expand Down
57 changes: 44 additions & 13 deletions src/bbs-plus-presentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@ import {
PresentationBuilder,
Credential,
} from '@docknetwork/crypto-wasm-ts/lib/anonymous-credentials';
import { ensureArray, ensureURI, isObject } from './utils/type-helpers';
import { ensureArray } from './utils/type-helpers';

import Bls12381BBSSignatureDock2022 from './utils/vc/crypto/Bls12381BBSSignatureDock2022';
import { Bls12381BBSSigProofDockSigName } from './utils/vc/crypto/constants';
import CustomLinkedDataSignature from './utils/vc/crypto/custom-linkeddatasignature';
import defaultDocumentLoader from './utils/vc/document-loader';

const DEFAULT_CONTEXT = 'https://ld.dock.io/security/bbs/v1';
export default class BbsPlusPresentation {
/**
* Create a new BbsPlusPresentation instance.
* @constructor
*/
constructor() {
this.presBuilder = new PresentationBuilder();
this.setPresentationContext([DEFAULT_CONTEXT]);
}

/**
Expand All @@ -43,7 +42,7 @@ export default class BbsPlusPresentation {
this.presBuilder.nonce = stringToU8a(nonce);
}
if (context) {
this.setPresentationContext(context);
this.presBuilder.context = context;
}
const pres = this.presBuilder.finalize();
return pres.toJSON();
Expand Down Expand Up @@ -83,22 +82,54 @@ export default class BbsPlusPresentation {
document: json,
});

const idx = await this.presBuilder.addCredential(Credential.fromJSON(credential, CustomLinkedDataSignature.fromJsigProofValue(credentialLD.proof.proofValue)), pk);
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
this.addAttributeToReveal(idx, ['proof.type']);
this.addAttributeToReveal(idx, ['proof.verificationMethod']);

// We also require context and type for JSON-LD
this.addAttributeToReveal(idx, ['@context']);
this.addAttributeToReveal(idx, ['type']);
return idx;
}

/**
*
* @param context
*/
setPresentationContext(context) {
if (!isObject(context) && !Array.isArray(context)) {
ensureURI(context);
deriveCredentials(options) {
const presentation = this.createPresentation(options);
const { credentials } = presentation.spec;
if (credentials.length > 1) {
throw new Error('Cannot derive from multiple credentials in a presentation');
}
this.presBuilder.context = context;

return credentials.map((credential) => {
if (!credential.revealedAttributes.proof) {
throw new Error('Credential proof is not revealed, it should be');
}

const date = new Date().toISOString();

return {
...credential.revealedAttributes,
'@context': JSON.parse(credential.revealedAttributes['@context']),
type: JSON.parse(credential.revealedAttributes.type),
credentialSchema: JSON.parse(credential.schema),
issuer: credential.revealedAttributes.issuer || credential.revealedAttributes.proof.verificationMethod.split('#')[0],
issuanceDate: credential.revealedAttributes.issuanceDate || date,
proof: {
proofPurpose: 'assertionMethod',
created: date,
...credential.revealedAttributes.proof,
type: Bls12381BBSSigProofDockSigName,
proofValue: presentation.proof,
nonce: presentation.nonce,
context: presentation.context,
attributeCiphertexts: presentation.attributeCiphertexts,
attributeEqualities: presentation.spec.attributeEqualities,
version: credential.version,
},
};
});
}
}
70 changes: 70 additions & 0 deletions src/utils/vc/contexts/dock-bbs-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,76 @@
"@version": 1.1,
"id": "@id",
"type": "@type",
"dockBBS": "https://ld.dock.io/dock-bbs",
"parsingOptions": {
"@id": "dockBBS:parsingOptions",
"@type": "@id",
"@container": "@graph"
},
"defaultDecimalPlaces": {
"@id": "dockBBS:defaultDecimalPlaces",
"@type": "@id",
"@container": "@graph"
},
"defaultMinimumInteger": {
"@id": "dockBBS:defaultMinimumInteger",
"@type": "@id",
"@container": "@graph"
},
"useDefaults": {
"@id": "dockBBS:useDefaults",
"@type": "@id",
"@container": "@graph"
},
"version": {
"@id": "dockBBS:useDefaults",
"@type": "@id",
"@container": "@graph"
},
"Bls12381BBS+SignatureProofDock2022": {
"@id": "https://ld.dock.io/security#Bls12381BBS+SignatureDock2022",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"challenge": "https://ld.dock.io/security#challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"domain": "https://ld.dock.io/security#domain",
"context": "https://ld.dock.io/security#context",
"attributeCiphertexts": "https://ld.dock.io/security#attributeCiphertexts",
"attributeEqualities": "https://ld.dock.io/security#attributeEqualities",
"proofValue": "https://ld.dock.io/security#proofValue",
"nonce": "https://ld.dock.io/security#nonce",
"proofPurpose": {
"@id": "https://ld.dock.io/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"assertionMethod": {
"@id": "https://ld.dock.io/security#assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "https://ld.dock.io/security#authenticationMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"verificationMethod": {
"@id": "https://ld.dock.io/security#verificationMethod",
"@type": "@id"
}
}
},
"Bls12381BBS+SignatureDock2022": {
"@id": "https://ld.dock.io/security#Bls12381BBS+SignatureDock2022",
"@context": {
Expand Down
5 changes: 3 additions & 2 deletions src/utils/vc/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { DEFAULT_CONTEXT_V1_URL, credentialContextField } from './constants';
import { ensureValidDatetime } from '../type-helpers';

import {
EcdsaSepc256k1Signature2019, Ed25519Signature2018, Sr25519Signature2020, Bls12381BBSSignatureDock2022,
EcdsaSepc256k1Signature2019, Ed25519Signature2018, Sr25519Signature2020,
Bls12381BBSSignatureDock2022, Bls12381BBSSignatureProofDock2022,
} from './custom_crypto';

/**
Expand Down Expand Up @@ -218,7 +219,7 @@ export async function verifyCredential(credential, {
controller,
}),
// TODO: support more key types, see digitalbazaar github
suite: [new Ed25519Signature2018(), new EcdsaSepc256k1Signature2019(), new Sr25519Signature2020(), new Bls12381BBSSignatureDock2022(), ...suite],
suite: [new Ed25519Signature2018(), new EcdsaSepc256k1Signature2019(), new Sr25519Signature2020(), new Bls12381BBSSignatureDock2022(), new Bls12381BBSSignatureProofDock2022(), ...suite],
documentLoader: docLoader,
compactProof,
});
Expand Down
3 changes: 3 additions & 0 deletions src/utils/vc/crypto/Bls12381BBSSignatureDock2022.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export default class Bls12381BBSSignatureDock2022 extends CustomLinkedDataSignat
credBuilder.setTopLevelField(k, custom[k]);
});

credBuilder.setTopLevelField('@context', JSON.stringify(document['@context']));
credBuilder.setTopLevelField('type', JSON.stringify(document.type));

const retval = credBuilder.updateSchemaIfNeeded(signingOptions);
return [retval, credBuilder.schema];
}
Expand Down
141 changes: 141 additions & 0 deletions src/utils/vc/crypto/Bls12381BBSSignatureProofDock2022.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {
BBSPlusPublicKeyG2,
} from '@docknetwork/crypto-wasm-ts';
import { Presentation } from '@docknetwork/crypto-wasm-ts/lib/anonymous-credentials/presentation';
import b58 from 'bs58';

import Bls12381BBSSignatureDock2022 from './Bls12381BBSSignatureDock2022';
import { Bls12381BBSSigDockSigName, Bls12381BBSSigProofDockSigName } from './constants';

import Bls12381G2KeyPairDock2022 from './Bls12381G2KeyPairDock2022';
import CustomLinkedDataSignature from './custom-linkeddatasignature';

const SUITE_CONTEXT_URL = 'https://www.w3.org/2018/credentials/v1';

/*
* Converts a derived BBS+ proof credential to the native presentation format
*/
export function convertToPresentation(document) {
const {
'@context': context,
type,
credentialSchema,
issuer,
issuanceDate,
proof,
...revealedAttributes
} = document;

return {
version: '0.0.1',
nonce: proof.nonce,
context: proof.context,
spec: {
credentials: [
{
version: proof.version,
schema: JSON.stringify(credentialSchema),
revealedAttributes: {
proof: {
type: Bls12381BBSSigDockSigName,
verificationMethod: proof.verificationMethod,
},
'@context': JSON.stringify(context),
type: JSON.stringify(type),
...revealedAttributes,
},
},
],
attributeEqualities: proof.attributeEqualities,
},
attributeCiphertexts: proof.attributeCiphertexts,
proof: proof.proofValue,
};
}

/**
* A BBS+ signature suite for use with derived BBS+ credentials aka BBS+ presentations
*/
export default class Bls12381BBSSignatureProofDock2022 extends CustomLinkedDataSignature {
/**
* Default constructor
* @param options {SignatureSuiteOptions} options for constructing the signature suite
*/
constructor(options = {}) {
const {
verificationMethod,
} = options;

super({
type: Bls12381BBSSigProofDockSigName,
LDKeyClass: Bls12381G2KeyPairDock2022,
contextUrl: SUITE_CONTEXT_URL,
alg: Bls12381BBSSigProofDockSigName,
});

this.proof = {
'@context': [
{
sec: 'https://w3id.org/security#',
proof: {
'@id': 'sec:proof',
'@type': '@id',
'@container': '@graph',
},
},
'https://ld.dock.io/security/bbs/v1',
],
type: Bls12381BBSSigProofDockSigName,
};

this.verificationMethod = verificationMethod;
}

async verifyProof({
proof, document, documentLoader, expansionMap,
}) {
try {
const verificationMethod = await this.getVerificationMethod(
{
proof, document, documentLoader, expansionMap,
},
);

const presentationJSON = convertToPresentation({ ...document, proof });
const recreatedPres = Presentation.fromJSON(presentationJSON);

const pks = [verificationMethod].map((keyDocument) => {
const pkRaw = b58.decode(keyDocument.publicKeyBase58);
return new BBSPlusPublicKeyG2(pkRaw);
});

if (!recreatedPres.verify(pks)) {
throw new Error('Invalid signature');
}

return { verified: true, verificationMethod };
} catch (error) {
return { verified: false, error };
}
}

/**
* @param document {object} to be signed.
* @param proof {object}
* @param documentLoader {function}
* @param expansionMap {function}
*/
async getVerificationMethod({ proof, documentLoader }) {
return Bls12381BBSSignatureDock2022.getVerificationMethod({ proof, documentLoader });
}

ensureSuiteContext() {
// no-op
}
}

Bls12381BBSSignatureProofDock2022.proofType = [
Bls12381BBSSigProofDockSigName,
`sec:${Bls12381BBSSigProofDockSigName}`,
`https://w3id.org/security#${Bls12381BBSSigProofDockSigName}`,
];
1 change: 1 addition & 0 deletions src/utils/vc/crypto/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export const Ed25519SigName = 'Ed25519Signature2018';
export const Sr25519VerKeyName = 'Sr25519VerificationKey2020';
export const Sr25519SigName = 'Sr25519Signature2020';
export const Bls12381BBSSigDockSigName = 'Bls12381BBS+SignatureDock2022';
export const Bls12381BBSSigProofDockSigName = 'Bls12381BBS+SignatureProofDock2022';
export const Bls12381BBSDockVerKeyName = 'Bls12381G2VerificationKeyDock2022';
6 changes: 5 additions & 1 deletion src/utils/vc/crypto/custom-linkeddatasignature.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,14 @@ export default class CustomLinkedDataSignature extends jsigs.suites.LinkedDataSi

return {
...proof,
proofValue: MULTIBASE_BASE58BTC_HEADER + base58btc.encode(signatureBytes),
proofValue: CustomLinkedDataSignature.encodeProofValue(signatureBytes),
};
}

static encodeProofValue(signatureBytes) {
return MULTIBASE_BASE58BTC_HEADER + base58btc.encode(signatureBytes);
}

/**
* Json-ld signs prefix signatures with a specific character. Removes that character
* @param proofValue
Expand Down
Loading

0 comments on commit b6b8359

Please sign in to comment.