diff --git a/protocol/did/src/utils.ts b/protocol/did/src/utils.ts index 542a5bc..90bdd56 100644 --- a/protocol/did/src/utils.ts +++ b/protocol/did/src/utils.ts @@ -4,7 +4,7 @@ import type { DidUrl, VerificationMethodType } from '@zcloak/did-resolver/types'; import type { KeypairType } from '@zcloak/keyring/types'; -import { stringToU8a, u8aConcat } from '@polkadot/util'; +import { isArray, stringToU8a, u8aConcat } from '@polkadot/util'; import { parseDid } from '@zcloak/did-resolver/parseDid'; @@ -19,6 +19,20 @@ export function isSameUri(didUrl1: DidUrl, didUrl2: DidUrl): boolean { return did1 === did2; } +export function isAttester(value: unknown, version: string): boolean { + if (typeof value === 'string' && (version === '0' || version === '1')) { + return isDidUrl(value) + } else if (isArray(value) && value.length !== 0 && version === '2'){ + let check = true; + for (const item of value) { + check = isDidUrl(item) && check; + } + return check; + } else { + throw new Error(`The attester is not valid`); + } +} + export function isDidUrl(value: unknown): value is DidUrl { if (typeof value !== 'string') return false; diff --git a/protocol/vc/src/credential/index.spec.ts b/protocol/vc/src/credential/index.spec.ts index 7c433cc..949259e 100644 --- a/protocol/vc/src/credential/index.spec.ts +++ b/protocol/vc/src/credential/index.spec.ts @@ -110,8 +110,8 @@ describe('VerifiableCredential', (): void => { digestHashType: 'Keccak256' }); - const vc = await vcBuilder.build(issuer); - + const vc = await vcBuilder.build(issuer, false); + console.log(vc) expect(isPrivateVC(vc)).toBe(true); expect(vc).toMatchObject({ @@ -120,7 +120,7 @@ describe('VerifiableCredential', (): void => { ctype: ctype.$id, issuanceDate: now, credentialSubject: CONTENTS, - issuer: issuer.id, + issuer: [issuer.id], holder: holder.id, hasher: ['RescuePrime', 'Keccak256'], proof: [ @@ -153,7 +153,7 @@ describe('VerifiableCredential', (): void => { digestHashType: 'Keccak256' }); - const vc = await vcBuilder.build(issuer); + const vc = await vcBuilder.build(issuer, false); expect(isPrivateVC(vc)).toBe(true); @@ -163,7 +163,7 @@ describe('VerifiableCredential', (): void => { ctype: ctype.$id, issuanceDate: now, credentialSubject: CONTENTS, - issuer: issuer.id, + issuer: [issuer.id], holder: holder.id, hasher: ['RescuePrime', 'Keccak256'], proof: [ @@ -196,9 +196,9 @@ describe('VerifiableCredential', (): void => { digestHashType: 'Keccak256' }); - const vc = await vcBuilder.build(issuer); - - expect(isPrivateVC(vc)).toBe(true); + const vc = await vcBuilder.build(issuer, false,["did:zk:0xFeDE01Ff4402e35c6f6d20De9821d64bDF4Ba563"]); + console.log(vc) + expect(isPrivateVC(vc)).toBe(false); expect(vc).toMatchObject({ '@context': DEFAULT_CONTEXT, @@ -206,7 +206,7 @@ describe('VerifiableCredential', (): void => { ctype: ctype.$id, issuanceDate: now, credentialSubject: CONTENTS, - issuer: issuer.id, + issuer: [issuer.id, "did:zk:0xFeDE01Ff4402e35c6f6d20De9821d64bDF4Ba563"], holder: holder.id, hasher: ['RescuePrimeOptimized', 'Keccak256'], proof: [ @@ -256,7 +256,7 @@ describe('VerifiableCredential', (): void => { ctype: ctype.$id, issuanceDate: now, credentialSubject: CONTENTS, - issuer: issuer.id, + issuer: [issuer.id], holder: holder.id, hasher: ['RescuePrime', 'Keccak256'], proof: [ diff --git a/protocol/vc/src/credential/vc.ts b/protocol/vc/src/credential/vc.ts index 8d3e852..a2b5a73 100644 --- a/protocol/vc/src/credential/vc.ts +++ b/protocol/vc/src/credential/vc.ts @@ -114,7 +114,7 @@ export class VerifiableCredentialBuilder { if (isPublic) { rootHashResult = calcRoothash(this.raw.contents, this.raw.hashType, this.version); } else { - rootHashResult = calcRoothash(this.raw.contents, this.raw.hashType, this.version,{}); + rootHashResult = calcRoothash(this.raw.contents, this.raw.hashType, this.version, {}); } const digestPayload: DigestPayload = { diff --git a/protocol/vc/src/is.ts b/protocol/vc/src/is.ts index 678a5d2..5a49cf3 100644 --- a/protocol/vc/src/is.ts +++ b/protocol/vc/src/is.ts @@ -14,9 +14,10 @@ import type { import { isArray, isHex, isJsonObject, isNull, isNumber, isString, isUndefined } from '@polkadot/util'; import { isBase32, isBase58, isBase64 } from '@zcloak/crypto'; -import { isDidUrl } from '@zcloak/did/utils'; +import { isAttester, isDidUrl } from '@zcloak/did/utils'; import { ALL_HASH_TYPES, ALL_SIG_TYPES, ALL_VP_TYPES } from './defaults'; +import { parseDid } from '@zcloak/did-resolver/parseDid'; /** * @name isHashType @@ -61,6 +62,37 @@ export function isProof(input: unknown): input is Proof { ); } +/** + * @name isAttesterMapping + * @description + * check the `attester` is the same in [[Proof]] + */ +export function isAttesterMapping(issuer: unknown, proof: unknown): boolean { + if (isProof(proof)) { + const issuerInProof = parseDid(proof.verificationMethod).did; + return issuer === issuerInProof; + } else return false; +} + +/** + * @name isAttesterProof + * @description + * check the Proof is qualified or not + */ +export function isAttesterProof(issuer: unknown, proof: unknown): boolean { + // version 0 & version 1, only one attester and one proof + if (typeof issuer === 'string' && isArray(proof) && proof.length === 1) { + return isAttesterMapping(issuer, proof); + } else if (isArray(issuer) && isArray(proof) && issuer.length === proof.length) { + let check = true; + for (let i = 0; i < issuer.length; i++) { + check = isAttesterMapping(issuer[i], proof[i]) && check; + } + return check; + } else { + return false; + } +} /** * @name isRawCredential * @description @@ -118,10 +150,10 @@ export function isVC(input: unknown): input is VerifiableCredential { isString(input.version) && isNumber(input.issuanceDate) && (isUndefined(input.expirationDate) || isNull(input.expirationDate) || isNumber(input.expirationDate)) && - isDidUrl(input.issuer) && + isAttester(input.issuer, input.version as string) && isHex(input.digest) && isArray(input.proof) && - !input.proof.map((p) => isProof(p)).includes(false) && + isAttesterProof(input.issuer, input.proof) && isHex(input.ctype) && (isJsonObject(input.credentialSubject) || isHex(input.credentialSubject)) && isDidUrl(input.holder) && diff --git a/protocol/vc/src/rootHash.spec.ts b/protocol/vc/src/rootHash.spec.ts index 4118ba7..25ce193 100644 --- a/protocol/vc/src/rootHash.spec.ts +++ b/protocol/vc/src/rootHash.spec.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { HexString } from '@polkadot/util/types'; -import { initCrypto } from '@zcloak/crypto'; +import { initCrypto, keccak256AsHex } from '@zcloak/crypto'; import { calcRoothash } from './rootHash'; import { encodeAsSol } from './utils'; @@ -122,6 +122,7 @@ describe('calcRoothash', (): void => { expect(rootHash).toEqual('0x2cbd72a75cf5797fb6893b222a45de60eaa2ddf2e5d435d67e5ed8067efb76e3'); }); }); + describe('calcContentAsSolidity', () => { it('calcRoothash', (): void => { const input = { @@ -138,4 +139,30 @@ describe('calcRoothash', (): void => { ]); }); }); + + describe('calc Roothash with version 2', () => { + it('calcRoothash', (): void => { + const input = { + name: 'zCloak', + age: 111, + number_array: [11, 12, 13], + isUser: true, + string_array: ["zCloak", "database"] + }; + const values = Object.values(input); + const encodedClaimHashes: HexString[] = values.map((values) => encodeAsSol(values)); + + expect(encodedClaimHashes).toEqual(["0x28cb5b00333a3266fa3d92f3426ad4ef1d20018b44dd64913578d43438b4051a", "0x39f2babe526038520877fc7c33d81accf578af4a06c5fa6b0d038cae36e12711", "0x8d20824cd86ad1c5673537a4b5d1ee68bd9265b7d357e82bc76483ae879322c2", "0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2", "0x69ab0c29dcf9256886435d72272bceee1f0a0b2cee41865d3b56f9726e7fe0a3", + ]); + + const { rootHash } = calcRoothash(input, 'Keccak256', '2', {'0x28cb5b00333a3266fa3d92f3426ad4ef1d20018b44dd64913578d43438b4051a': '0xcd769c703ecfdc6798d56711af8b3a7918973524db6c6c0901209fd396f3a61b', + '0x39f2babe526038520877fc7c33d81accf578af4a06c5fa6b0d038cae36e12711': '0xc5bd9417c2dd820e8c074086acb0e1a785c5f9aa59022ca74606f4f6f32338bb', + '0x8d20824cd86ad1c5673537a4b5d1ee68bd9265b7d357e82bc76483ae879322c2': '0x9895d1010a3c7ae796bfdb2bff52febcfb21d8c71a0fb465a02d8a2a0ad857bd', + '0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2': '0xddb361f768d83f2f8eabd4f51517adb8d92aef33ce627127311bad01bac41710', + '0x69ab0c29dcf9256886435d72272bceee1f0a0b2cee41865d3b56f9726e7fe0a3': '0x51c09e0512047698103565fc9c2aca963eb3af6cf99b9201fc6be1331e4649e5' + }); + console.log(rootHash) + expect(rootHash).toEqual('0x8e94d639398a25f0484ba8f40feff5e694b084164118af84ffece25c68be4342'); + }); + }); }); diff --git a/protocol/vc/src/rootHash.ts b/protocol/vc/src/rootHash.ts index 63a42d0..1ea25e2 100644 --- a/protocol/vc/src/rootHash.ts +++ b/protocol/vc/src/rootHash.ts @@ -53,7 +53,6 @@ export function rootHashFromMerkle( } const leave = HASHER[hashType](nonceMap ? u8aConcat(encode, nonceMap[encode]) : encode); - leaves.push(leave); } diff --git a/protocol/vc/src/utils.ts b/protocol/vc/src/utils.ts index fc51c21..b40f507 100644 --- a/protocol/vc/src/utils.ts +++ b/protocol/vc/src/utils.ts @@ -49,8 +49,7 @@ export function encodeAsSol(input: NativeType | NativeTypeWithOutNull[]): HexStr return web3.utils.soliditySha3({ type: 'int256[]', value: input }); } else throw new Error(`This object can not be encoded ${input}`); default: - const check: never = input; - return check; + throw new Error(`Can not encode this type: ${input}`) } }