Skip to content

Commit

Permalink
Change schema format from data-uri to object
Browse files Browse the repository at this point in the history
Signed-off-by: lovesh <[email protected]>
  • Loading branch information
lovesh committed Jun 29, 2024
1 parent 5a46b12 commit db5b9e0
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 103 deletions.
7 changes: 5 additions & 2 deletions example/bbs-dock.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { initializeWasm } from '@docknetwork/crypto-wasm-ts';
import { DIDResolver } from '../src/resolver';
import Bls12381G2KeyPairDock2022 from '../src/utils/vc/crypto/Bls12381G2KeyPairDock2022';
import { issueCredential, verifyCredential } from '../src/utils/vc';
import stringify from 'json-stringify-deterministic';

const keypairOpts = {
id: 'did:example:489398593#keys-1',
Expand Down Expand Up @@ -34,9 +35,10 @@ class ExampleDIDResolver extends DIDResolver {

const resolver = new ExampleDIDResolver();

const id = 'https://ld.dock.io/examples/resident-card-schema.json';
const residentCardSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'https://ld.dock.io/examples/resident-card-schema.json',
$id: id,
title: 'Resident Card Example',
type: 'object',
properties: {
Expand All @@ -63,8 +65,9 @@ const residentCardSchema = {
};

const embeddedSchema = {
id: `data:application/json;charset=utf-8,${encodeURIComponent(JSON.stringify(residentCardSchema))}`,
id,
type: 'JsonSchemaValidator2018',
details: stringify({ jsonSchema: residentCardSchema }),
};

// Defining schema allows to specify custom encoding
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@docknetwork/sdk",
"version": "8.4.0",
"version": "8.5.0",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -103,7 +103,7 @@
},
"dependencies": {
"@digitalcredentials/vc-status-list": "^8.0.0",
"@docknetwork/crypto-wasm-ts": "0.60.0",
"@docknetwork/crypto-wasm-ts": "0.62.0",
"@docknetwork/node-types": "^0.17.0",
"@juanelas/base64": "^1.0.5",
"@polkadot/api": "10.12.4",
Expand All @@ -127,6 +127,7 @@
"multiformats": "^9.5.4",
"n3": "1.16.2",
"pako": "^2.1.0",
"rify": "^0.7.1"
"rify": "^0.7.1",
"semver": "^7.6.0"
}
}
3 changes: 2 additions & 1 deletion src/presentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@docknetwork/crypto-wasm-ts';
import b58 from 'bs58';
import { stringToU8a } from '@polkadot/util';
import semver from 'semver/preload';
import { ensureArray } from './utils/type-helpers';

import Bls12381BBSSignatureDock2022 from './utils/vc/crypto/Bls12381BBSSignatureDock2022';
Expand Down Expand Up @@ -197,7 +198,7 @@ export default class Presentation {
id: credential.revealedAttributes.id || DOCK_ANON_CREDENTIAL_ID,
'@context': JSON.parse(credential.revealedAttributes['@context']),
type: JSON.parse(credential.revealedAttributes.type),
credentialSchema: JSON.parse(credential.schema),
credentialSchema: semver.gte(credential.version, '0.6.0') ? credential.schema : JSON.parse(credential.schema),
issuer:
credential.revealedAttributes.issuer
|| credential.revealedAttributes.proof.verificationMethod.split('#')[0],
Expand Down
5 changes: 5 additions & 0 deletions src/utils/vc/contexts/dock-bbs-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
"@type": "@id",
"@container": "@graph"
},
"details": {
"@id": "dockBBS:details",
"@type": "@id",
"@container": "@graph"
},
"DockVBAccumulator2022": {
"@id": "https://ld.dock.io/security#DockVBAccumulator2022",
"@context": {
Expand Down
5 changes: 5 additions & 0 deletions src/utils/vc/contexts/dock-bbs23-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
"@type": "@id",
"@container": "@graph"
},
"details": {
"@id": "dockBBS23:details",
"@type": "@id",
"@container": "@graph"
},
"DockVBAccumulator2022": {
"@id": "https://ld.dock.io/security#DockVBAccumulator2022",
"@context": {
Expand Down
5 changes: 5 additions & 0 deletions src/utils/vc/contexts/dock-bddt16-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
"@type": "@id",
"@container": "@graph"
},
"details": {
"@id": "dockBDDT16:details",
"@type": "@id",
"@container": "@graph"
},
"DockVBAccumulator2022": {
"@id": "https://ld.dock.io/security#DockVBAccumulator2022",
"@context": {
Expand Down
5 changes: 5 additions & 0 deletions src/utils/vc/contexts/dock-ps-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
"@type": "@id",
"@container": "@graph"
},
"details": {
"@id": "dockPS:details",
"@type": "@id",
"@container": "@graph"
},
"DockVBAccumulator2022": {
"@id": "https://ld.dock.io/security#DockVBAccumulator2022",
"@context": {
Expand Down
5 changes: 2 additions & 3 deletions src/utils/vc/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,8 @@ export function getJsonSchemaFromCredential(credential, full = false) {
if (typeof credential.credentialSchema.id !== 'string') {
throw new Error(`credentialSchema was expected to be string but was ${typeof credential.credentialSchema}`);
}
// eslint-disable-next-line no-nested-ternary
const key = full ? (credential.credentialSchema.fullJsonSchema !== undefined ? 'fullJsonSchema' : 'id') : 'id';
return CredentialSchema.convertFromDataUri(credential.credentialSchema[key]);
const schema = CredentialSchema.fromJSON(credential.credentialSchema);
return full ? schema.getEmbeddedJsonSchema() : schema.jsonSchema;
}

/**
Expand Down
66 changes: 50 additions & 16 deletions src/utils/vc/crypto/common/DockCryptoSignature.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { SECURITY_CONTEXT_URL } from 'jsonld-signatures';

import { u8aToU8a } from '@polkadot/util';
import stringify from 'json-stringify-deterministic';
import semver from 'semver/preload';
import { withExtendedStaticProperties } from '../../../inheritance';
import CustomLinkedDataSignature from './CustomLinkedDataSignature';
import { deepClone } from '../../../common';

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

Expand Down Expand Up @@ -91,7 +93,7 @@ export default withExtendedStaticProperties(
} else {
// Legacy. Cannot use `convertCredentialForVerification` because JSON.stringify is not deterministic
// and credentialSchema string becomes different. See https://stackoverflow.com/a/43049877
[serializedCredential, credSchema] = this.constructor.convertCredential(options);
[serializedCredential, credSchema] = this.constructor.convertLegacyCredential(options);
}
} else {
// Serialize the data for signing
Expand All @@ -113,7 +115,7 @@ export default withExtendedStaticProperties(
* @param signingOptions
* @returns {Array} - Returns [serialized cred object, cred schema]
*/
static convertCredential({
static convertLegacyCredential({
document,
proof: explicitProof /* documentLoader */,
signingOptions = { requireAllFieldsFromSchema: false },
Expand Down Expand Up @@ -157,7 +159,7 @@ export default withExtendedStaticProperties(
credBuilder.setTopLevelField('type', JSON.stringify(document.type));

// Allow for relaxed schema generation, then embed the generated schema directly into the credential
const builtAnoncreds = credBuilder.updateSchemaIfNeeded(signingOptions);
const builtAnoncreds = credBuilder.updateSchemaIfNeededLegacy(signingOptions);

// Re-assign the embedded schema to the document schema object
// this is a bit hacky, but its the only way right now
Expand Down Expand Up @@ -217,24 +219,28 @@ export default withExtendedStaticProperties(

const s = this.extractSchema(document);
let credSchema = s[0];
const wasLegacySchema = s[1];
const wasExactSchema = s[1];

const credJson = {
...document,
proof: trimmedProof,
};
this.formatMandatoryFields(credJson, document);
if (!wasLegacySchema) {
if (!wasExactSchema) {
// Older credentials didn't include the version field in the final credential but they did while signing
credJson.cryptoVersion = '0.2.0';
if (credJson.credentialSchema === undefined) {
credJson.credentialSchema = JSON.stringify(credSchema.toJSON());
}
credSchema = CredentialSchema.generateAppropriateSchema(credJson, credSchema);
}
const schemaJson = credSchema.toJSON();
// Older versions used JSON.stringify but newer use deterministic conversion
credJson.credentialSchema = wasLegacySchema ? stringify(schemaJson) : JSON.stringify(schemaJson);
const schemaJson = semver.gte(credSchema.version, '0.4.0') ? credSchema.toJSON() : credSchema.toJSONOlder();
if (credJson.cryptoVersion && semver.gte(credJson.cryptoVersion, '0.6.0')) {
credJson.credentialSchema = schemaJson;
} else {
// Older versions used JSON.stringify but newer use deterministic conversion
credJson.credentialSchema = wasExactSchema ? stringify(schemaJson) : JSON.stringify(schemaJson);
}

if (document.credentialSchema) {
Object.assign(document.credentialSchema, schemaJson);
Expand Down Expand Up @@ -283,10 +289,13 @@ export default withExtendedStaticProperties(

// To work with JSON-LD credentials/presentations, we must always reveal the context and type
// NOTE: that they are encoded as JSON strings here to reduce message count and so its *always known*
// TODO: `stringify` should be used which is deterministic since @context can be an object
credBuilder.setTopLevelField(
'@context',
JSON.stringify(document['@context']),
stringify(document['@context']),
);
// TODO: type is never an object? Or use `stringify` to be safe
// Not adding the TODOs here since verification code also uses JSON.stringify and will need to check versions and then use the appropriate call.
credBuilder.setTopLevelField('type', JSON.stringify(document.type));

const serializedCred = credBuilder.serializeForSigning();
Expand All @@ -295,7 +304,7 @@ export default withExtendedStaticProperties(

// Update `document` so that generated credential has `credentialSchema` and `cryptoVersion` set
const updatedSchemaJson = credBuilder.schema.toJSON();
serializedCred.credentialSchema = stringify(updatedSchemaJson);
serializedCred.credentialSchema = updatedSchemaJson;
Object.assign(document.credentialSchema, updatedSchemaJson);
// eslint-disable-next-line no-param-reassign
document.cryptoVersion = serializedCred.cryptoVersion;
Expand Down Expand Up @@ -370,12 +379,17 @@ export default withExtendedStaticProperties(
return schema;
}

const schemaJson = deepClone(document.credentialSchema);
// Passing all the default parsing options. Ideally `document.credentialSchema` should contain these
if (schemaJson.details === undefined) {
schemaJson.details = stringify({ parsingOptions: DefaultSchemaParsingOpts });
} else {
const details = JSON.parse(schemaJson.details);
details.parsingOptions = { ...DefaultSchemaParsingOpts, ...details.parsingOptions };
schemaJson.details = stringify(details);
}
// If we already have a schema to use, add that first and then generate relaxed values later on
credSchema = await CredentialSchema.fromJSONWithPotentiallyExternalSchema({
// Passing all the default parsing options. Ideally `document.credentialSchema` should contain these
parsingOptions: DefaultSchemaParsingOpts,
...document.credentialSchema,
}, getSchema);
credSchema = await CredentialSchema.fromJSONWithPotentiallyExternalSchema(schemaJson, getSchema);
} else {
credSchema = this.extractSchemaWhenIdNotSet(document);
wasExactSchema = false;
Expand Down Expand Up @@ -406,7 +420,7 @@ export default withExtendedStaticProperties(
let credSchema;
if (document.credentialSchema) {
// schema object exists but no ID means the SDK is signalling for the suite to generate a schema
credSchema = new CredentialSchema(CredentialSchema.essential(), document.credentialSchema.parsingOptions);
credSchema = new CredentialSchema(CredentialSchema.essential(), this.getParsingOptions(document));
} else {
// Else, no schema was found so just use the essentials and v0.0.1 schema version
// NOTE: version is important here and MUST be 0.0.1 otherwise it will invalidate BBS+ credentials
Expand All @@ -428,6 +442,26 @@ export default withExtendedStaticProperties(
return credSchema;
}

static getParsingOptions(document) {
if (document.credentialSchema && document.credentialSchema.details !== undefined) {
const details = JSON.parse(document.credentialSchema.details);
return details.parsingOptions;
}
return DefaultSchemaParsingOpts;
}

static withUpdatedParsingOptions(schemaJson, parsingOptions) {
const newSchemaJson = deepClone(schemaJson);
if (schemaJson.details === undefined) {
newSchemaJson.details = stringify({ parsingOptions });
} else {
const details = JSON.parse(schemaJson.details);
details.parsingOptions = { ...details.parsingOptions, ...parsingOptions };
newSchemaJson.details = stringify(details);
}
return newSchemaJson;
}

/**
* @param document {object} to be signed.
* @param proof {object}
Expand Down
7 changes: 5 additions & 2 deletions src/utils/vc/crypto/common/DockCryptoSignatureProof.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Presentation } from '@docknetwork/crypto-wasm-ts';
import b58 from 'bs58';
import semver from 'semver/preload';
import { withExtendedStaticProperties } from '../../../inheritance';

import CustomLinkedDataSignature from './CustomLinkedDataSignature';
Expand Down Expand Up @@ -121,12 +122,14 @@ export default withExtendedStaticProperties(
delete revealedAttributes.id;
}

const credVersion = proof.version;

// TODO: This is wrong. This won't work with presentation from 2 or more credentials
const c = {
sigType: proof.sigType,
version: proof.version,
version: credVersion,
bounds: proof.bounds,
schema: JSON.stringify(credentialSchema),
schema: semver.gte(credVersion, '0.6.0') ? credentialSchema : JSON.stringify(credentialSchema),
revealedAttributes: {
proof: {
type: this.sigName,
Expand Down
7 changes: 3 additions & 4 deletions src/utils/vc/presentations.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CredentialSchema,
} from '@docknetwork/crypto-wasm-ts';
import b58 from 'bs58';
import semver from 'semver/preload';
import { getPrivateStatus, verifyCredential } from './credentials';
import DIDResolver from "../../resolver/did/did-resolver"; // eslint-disable-line

Expand Down Expand Up @@ -374,10 +375,8 @@ export function getKeyedProofsFromVerifiedPresentation(presentation) {
*/
export function getJsonSchemasFromPresentation(presentation, full = false) {
return presentation.spec.credentials.map((cred) => {
const schema = CredentialSchema.fromJSON(JSON.parse(cred.schema));
// eslint-disable-next-line no-nested-ternary
const key = full ? (schema.fullJsonSchema !== undefined ? 'fullJsonSchema' : 'jsonSchema') : 'jsonSchema';
return schema[key];
const schema = semver.gte(cred.version, '0.6.0') ? CredentialSchema.fromJSON(cred.schema) : CredentialSchema.fromJSON(JSON.parse(cred.schema));
return full ? schema.getEmbeddedJsonSchema() : schema.jsonSchema;
});
}

Expand Down
10 changes: 6 additions & 4 deletions tests/integration/anoncreds/derived-credentials.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
RevocationStatusProtocol,
} from '@docknetwork/crypto-wasm-ts';
import { InMemoryState, InMemoryKBUniversalState } from '@docknetwork/crypto-wasm-ts/lib/accumulator/in-memory-persistence';
import stringify from 'json-stringify-deterministic';
import { DockAPI } from '../../../src';
import {
FullNodeEndpoint,
Expand All @@ -35,9 +36,10 @@ import { getKeyedProofsFromVerifiedPresentation } from '../../../src/utils/vc/pr
import { deepClone } from '../../../src/utils/common';

// TODO: move to fixtures
const id = 'https://ld.dock.io/examples/resident-card-schema.json';
const residentCardSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'https://ld.dock.io/examples/resident-card-schema.json',
$id: id,
title: 'Resident Card Example',
type: 'object',
properties: {
Expand Down Expand Up @@ -73,10 +75,9 @@ const residentCardSchema = {
};

const embeddedSchema = {
id: `data:application/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(residentCardSchema),
)}`,
id,
type: 'JsonSchemaValidator2018',
details: stringify({ jsonSchema: residentCardSchema }),
};

describe.each(Schemes)('Derived Credentials', ({
Expand Down Expand Up @@ -467,6 +468,7 @@ describe.each(Schemes)('Derived Credentials', ({
}),
);
expect(credentials[0].issuer).toEqual(credential.issuer);
expect(credentials[0].credentialSchema.id).toEqual(residentCardSchema.$id);

// Ensure reconstructing presentation from credential matches
// NOTE: ignoring proof here as itll differ when signed twice as above
Expand Down
Loading

0 comments on commit db5b9e0

Please sign in to comment.