diff --git a/package.json b/package.json index c22a2be..3af1ce1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@docknetwork/crypto-wasm-ts", - "version": "0.56.0", + "version": "0.57.0", "description": "Typescript abstractions over Dock's Rust crypto library's WASM wrapper", "homepage": "https://github.com/docknetwork/crypto-wasm-ts", "main": "lib/index.js", @@ -31,7 +31,7 @@ "@types/flat": "^5.0.2", "@types/lodash": "^4.14.195", "bs58": "5.0.0", - "crypto-wasm-new": "npm:@docknetwork/crypto-wasm@0.25.0", + "crypto-wasm-new": "npm:@docknetwork/crypto-wasm@0.26.0", "crypto-wasm-old": "npm:@docknetwork/crypto-wasm@0.23.0", "flat": "^5.0.2", "json-pointer": "^0.6.2", diff --git a/src/accumulator/IAccumulatorState.ts b/src/accumulator/IAccumulatorState.ts index 54a0448..14f8808 100644 --- a/src/accumulator/IAccumulatorState.ts +++ b/src/accumulator/IAccumulatorState.ts @@ -20,3 +20,17 @@ export interface IAccumulatorState { export interface IUniversalAccumulatorState extends IAccumulatorState { elements(): Promise>; } + +export interface IKBUniversalAccumulatorState extends IAccumulatorState { + /** + * Whether this element is in the domain (could be a member or not) + * @param element + */ + inDomain(element: Uint8Array): Promise; + + /** + * Takes an element not in the domain and adds it. + * @param element + */ + addToDomain(element: Uint8Array): Promise; +} diff --git a/src/accumulator/accumulator.ts b/src/accumulator/accumulator.ts index b06227f..511b2ae 100644 --- a/src/accumulator/accumulator.ts +++ b/src/accumulator/accumulator.ts @@ -30,7 +30,7 @@ import { universalAccumulatorVerifyMembership, universalAccumulatorVerifyNonMembership } from 'crypto-wasm-new'; -import { VBMembershipWitness, VBNonMembershipWitness } from './accumulatorWitness'; +import { AccumulatorWitness, VBMembershipWitness, VBNonMembershipWitness } from './accumulatorWitness'; import { getUint8ArraysFromObject } from '../util'; import { IAccumulatorState, IUniversalAccumulatorState } from './IAccumulatorState'; import { IInitialElementsStore } from './IInitialElementsStore'; @@ -53,7 +53,7 @@ import { * the state object is to check if duplicate elements are not added or already absent elements are not removed or membership witness * for absent elements is not created. If checks in the `state` fail, they throw errors. */ -export abstract class Accumulator { +export abstract class Accumulator { value: Uint8Array | object; secretKey: AccumulatorSecretKey | undefined; params: AccumulatorParams | undefined; @@ -194,7 +194,7 @@ export abstract class Accumulator { /** * Get the accumulated value. */ - abstract get accumulated(): Uint8Array; + abstract get accumulated(): T; // The following functions optionally take secret key as an argument as its better to not store secret key in memory for // long time. @@ -261,7 +261,7 @@ export abstract class Accumulator { element: Uint8Array, secretKey?: AccumulatorSecretKey, state?: IAccumulatorState - ): Promise; + ): Promise>; /** * Calculate the membership witnesses for the given batch of elements @@ -273,7 +273,7 @@ export abstract class Accumulator { elements: Uint8Array[], secretKey?: AccumulatorSecretKey, state?: IAccumulatorState - ): Promise; + ): Promise[]>; /** * Verify the membership witness. @@ -284,7 +284,7 @@ export abstract class Accumulator { */ abstract verifyMembershipWitness( member: Uint8Array, - witness: VBMembershipWitness, + witness: AccumulatorWitness, pk: AccumulatorPublicKey, params?: AccumulatorParams ): boolean; @@ -401,9 +401,9 @@ export abstract class Accumulator { } /** - * Accumulator that supports only membership proofs. + * VB accumulator that supports only membership proofs. */ -export class PositiveAccumulator extends Accumulator { +export class PositiveAccumulator extends Accumulator { // @ts-ignore value: Uint8Array; @@ -441,7 +441,7 @@ export class PositiveAccumulator extends Accumulator { * Remove a single element from the accumulator * @param element * @param secretKey - * @param state- Optional. If provided, checked before removing and element is removed + * @param state Optional. If provided, checked before removing and element is removed */ async remove(element: Uint8Array, secretKey?: AccumulatorSecretKey, state?: IAccumulatorState) { await this.ensurePresence(element, state); @@ -576,12 +576,12 @@ export class PositiveAccumulator extends Accumulator { } /** - * Accumulator that supports both membership proofs and non-membership proofs. For guarding against forgery of + * VB accumulator that supports both membership proofs and non-membership proofs. For guarding against forgery of * non-membership proofs (details in the paper), during initialization, it should generate several accumulator members * and never remove them from accumulator, nor it should allow duplicates of them to be added. Thus, several methods * accept an optional persistent database `IInitialElementsStore` which stores those initial elements. */ -export class UniversalAccumulator extends Accumulator { +export class UniversalAccumulator extends Accumulator { /** * `f_V` is supposed to kept private by the accumulator manager. `V` is the accumulated value. */ diff --git a/src/accumulator/accumulatorWitness.ts b/src/accumulator/accumulatorWitness.ts index 35a9f5a..9f226ac 100644 --- a/src/accumulator/accumulatorWitness.ts +++ b/src/accumulator/accumulatorWitness.ts @@ -1,5 +1,4 @@ import { - publicInfoForWitnessUpdate, updateMembershipWitnessesPostBatchUpdates, updateMembershipWitnessPostAdd, updateMembershipWitnessPostRemove, @@ -11,34 +10,34 @@ import { updateNonMembershipWitnessUsingPublicInfoAfterBatchUpdate, updateNonMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates } from 'crypto-wasm-new'; -import { getUint8ArraysFromObject, jsonObjToUint8Array } from '../util'; +import { getUint8ArraysFromObject } from '../util'; import { AccumulatorSecretKey } from './params-and-keys'; -import { BytearrayWrapper } from '../bytearray-wrapper'; +import { VBWitnessUpdateInfo, WitnessUpdateInfo } from './witness-update-info'; -export abstract class AccumulatorWitness { +export abstract class AccumulatorWitness { value: Uint8Array | object; constructor(value: Uint8Array | object) { this.value = value; } - abstract updatePostAdd(addition: Uint8Array, element: Uint8Array, accumulatorValueBeforeAddition: Uint8Array): void; - abstract updatePostRemove(removal: Uint8Array, element: Uint8Array, accumulatorValueAfterRemoval: Uint8Array): void; + abstract updatePostAdd(addition: Uint8Array, element: Uint8Array, accumulatorValueBeforeAddition: V): void; + abstract updatePostRemove(removal: Uint8Array, element: Uint8Array, accumulatorValueAfterRemoval: V): void; abstract updateUsingPublicInfoPostBatchUpdate( element: Uint8Array, additions: Uint8Array[], removals: Uint8Array[], - publicInfo: VBWitnessUpdatePublicInfo + publicInfo: WitnessUpdateInfo ): void; abstract updateUsingPublicInfoPostMultipleBatchUpdates( element: Uint8Array, additions: Uint8Array[][], removals: Uint8Array[][], - publicInfo: VBWitnessUpdatePublicInfo[] + publicInfo: WitnessUpdateInfo[] ): void; } -export class VBMembershipWitness extends AccumulatorWitness { +export class VBMembershipWitness extends AccumulatorWitness { // @ts-ignore value: Uint8Array; @@ -73,7 +72,7 @@ export class VBMembershipWitness extends AccumulatorWitness { member: Uint8Array, additions: Uint8Array[], removals: Uint8Array[], - publicInfo: VBWitnessUpdatePublicInfo + publicInfo: VBWitnessUpdateInfo ) { this.value = updateMembershipWitnessUsingPublicInfoAfterBatchUpdate( this.value, @@ -96,7 +95,7 @@ export class VBMembershipWitness extends AccumulatorWitness { member: Uint8Array, additions: Uint8Array[][], removals: Uint8Array[][], - publicInfo: VBWitnessUpdatePublicInfo[] + publicInfo: VBWitnessUpdateInfo[] ) { const info = publicInfo.map((i) => i.value); this.value = updateMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates( @@ -149,7 +148,7 @@ export class VBMembershipWitness extends AccumulatorWitness { } } -export class VBNonMembershipWitness extends AccumulatorWitness { +export class VBNonMembershipWitness extends AccumulatorWitness { // @ts-ignore value: { d: Uint8Array; C: Uint8Array }; @@ -184,7 +183,7 @@ export class VBNonMembershipWitness extends AccumulatorWitness { nonMember: Uint8Array, additions: Uint8Array[], removals: Uint8Array[], - publicInfo: VBWitnessUpdatePublicInfo + publicInfo: VBWitnessUpdateInfo ) { this.value = updateNonMembershipWitnessUsingPublicInfoAfterBatchUpdate( this.value, @@ -207,7 +206,7 @@ export class VBNonMembershipWitness extends AccumulatorWitness { nonMember: Uint8Array, additions: Uint8Array[][], removals: Uint8Array[][], - publicInfo: VBWitnessUpdatePublicInfo[] + publicInfo: VBWitnessUpdateInfo[] ) { const info = publicInfo.map((i) => i.value); this.value = updateNonMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates( @@ -259,35 +258,3 @@ export class VBNonMembershipWitness extends AccumulatorWitness { return new VBNonMembershipWitness({ d, C }); } } - -/** - * Public info published by the accumulator manager used to update witnesses after several additions and removals. - */ -export class VBWitnessUpdatePublicInfo extends BytearrayWrapper { - toJSON(): string { - return JSON.stringify({ - value: this.value - }); - } - - fromJSON(json: string): VBWitnessUpdatePublicInfo { - return new VBWitnessUpdatePublicInfo(jsonObjToUint8Array(json)); - } - - /** - * Accumulator manager creates the witness update info corresponding to the additions and removals. - * @param accumulatorValueBeforeUpdates - accumulator value before the additions and removals - * @param additions - * @param removals - * @param sk - */ - static new( - accumulatorValueBeforeUpdates: Uint8Array, - additions: Uint8Array[], - removals: Uint8Array[], - sk: AccumulatorSecretKey - ): VBWitnessUpdatePublicInfo { - const value = publicInfoForWitnessUpdate(accumulatorValueBeforeUpdates, additions, removals, sk.value); - return new VBWitnessUpdatePublicInfo(value); - } -} diff --git a/src/accumulator/in-memory-persistence.ts b/src/accumulator/in-memory-persistence.ts index afb5d58..fabb53d 100644 --- a/src/accumulator/in-memory-persistence.ts +++ b/src/accumulator/in-memory-persistence.ts @@ -1,4 +1,4 @@ -import { IAccumulatorState, IUniversalAccumulatorState } from './IAccumulatorState'; +import { IAccumulatorState, IKBUniversalAccumulatorState, IUniversalAccumulatorState } from './IAccumulatorState'; import { IInitialElementsStore } from './IInitialElementsStore'; /** @@ -11,6 +11,10 @@ export class InMemoryState implements IAccumulatorState { this.state = new Set(); } + get size(): number { + return this.state.size; + } + async add(element: Uint8Array): Promise { const key = InMemoryState.key(element); if (this.state.has(key)) { @@ -75,3 +79,69 @@ export class InMemoryInitialElementsStore implements IInitialElementsStore { return JSON.stringify(Array.from(element)); } } + +/** + * In memory implementation of the state. For testing only + */ +export class InMemoryKBUniversalState implements IKBUniversalAccumulatorState { + memState: Set; + nonMemState: Set; + + constructor() { + this.memState = new Set(); + this.nonMemState = new Set(); + } + + get size(): number { + return this.memState.size; + } + + add(element: Uint8Array): Promise { + const key = InMemoryKBUniversalState.key(element); + if (this.memState.has(key)) { + throw new Error(`${element} already present in mem state`); + } + if (!this.nonMemState.has(key)) { + throw new Error(`${element} not present in non mem state`); + } + this.memState.add(key); + this.nonMemState.delete(key); + return Promise.resolve(); + } + + has(element: Uint8Array): Promise { + const key = InMemoryKBUniversalState.key(element); + // Ideally, something present in `memState` should not be present in `nonMemState` and vice-versa + const b = this.memState.has(key) && !this.nonMemState.has(key); + return Promise.resolve(b); + } + + remove(element: Uint8Array): Promise { + const key = InMemoryKBUniversalState.key(element); + if (!this.memState.has(key)) { + throw new Error(`${element} not present in mem state`); + } + if (this.nonMemState.has(key)) { + throw new Error(`${element} already present in non mem state`); + } + this.memState.delete(key); + this.nonMemState.add(key); + return Promise.resolve(); + } + + static key(element: Uint8Array) { + return JSON.stringify(Array.from(element)); + } + + inDomain(element: Uint8Array): Promise { + const key = InMemoryKBUniversalState.key(element); + const b = this.nonMemState.has(key) || this.memState.has(key); + return Promise.resolve(b); + } + + async addToDomain(element: Uint8Array) { + const key = InMemoryKBUniversalState.key(element); + this.nonMemState.add(key); + return Promise.resolve(); + } +} diff --git a/src/accumulator/index.ts b/src/accumulator/index.ts index 2c0d2ac..affb1e3 100644 --- a/src/accumulator/index.ts +++ b/src/accumulator/index.ts @@ -4,3 +4,4 @@ export * from './accumulatorWitness'; export * from './proof'; export * from './IAccumulatorState'; export * from './IInitialElementsStore'; +export { VBWitnessUpdateInfo } from './witness-update-info'; diff --git a/src/accumulator/kb-acccumulator-witness.ts b/src/accumulator/kb-acccumulator-witness.ts new file mode 100644 index 0000000..76b270b --- /dev/null +++ b/src/accumulator/kb-acccumulator-witness.ts @@ -0,0 +1,185 @@ +import { + kbUniversalUpdateMembershipWitnessPostAdd, + kbUniversalUpdateMembershipWitnessPostRemove, + kbUniversalUpdateNonMembershipWitnessPostRemove, + kbUpdateMembershipWitnessesPostBatchUpdates, + updateKBUniversalMembershipWitnessUsingPublicInfoAfterBatchUpdate, + updateKBUniversalMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates, + kbUniversalUpdateNonMembershipWitnessPostAdd, + kbUpdateNonMembershipWitnessesPostBatchUpdates, + updateKBUniversalNonMembershipWitnessUsingPublicInfoAfterBatchUpdate, + updateKBUniversalNonMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates +} from 'crypto-wasm-new'; +import { getUint8ArraysFromObject } from '../util'; +import { AccumulatorWitness } from './accumulatorWitness'; +import { KBUniversalAccumulatorValue } from './kb-universal-accumulator'; +import { AccumulatorSecretKey } from './params-and-keys'; +import { + KBUniversalMembershipWitnessUpdateInfo, + KBUniversalNonMembershipWitnessUpdateInfo, + WitnessUpdateInfo +} from './witness-update-info'; + +export class KBUniversalMembershipWitness extends AccumulatorWitness { + // @ts-ignore + value: Uint8Array; + + updatePostAdd(addition: Uint8Array, member: Uint8Array, accumulatorValueBeforeAddition: KBUniversalAccumulatorValue) { + this.value = kbUniversalUpdateMembershipWitnessPostAdd( + this.value, + member, + addition, + accumulatorValueBeforeAddition.asInternalType + ); + } + + updatePostRemove(removal: Uint8Array, member: Uint8Array, accumulatorValueAfterRemoval: KBUniversalAccumulatorValue) { + this.value = kbUniversalUpdateMembershipWitnessPostRemove( + this.value, + member, + removal, + accumulatorValueAfterRemoval.asInternalType + ); + } + + static updateMultiplePostBatchUpdates( + witnesses: KBUniversalMembershipWitness[], + members: Uint8Array[], + additions: Uint8Array[], + removals: Uint8Array[], + accumulatorValueBeforeUpdates: KBUniversalAccumulatorValue, + secretKey: AccumulatorSecretKey + ): KBUniversalMembershipWitness[] { + const wits = witnesses.map((m) => m.value); + return kbUpdateMembershipWitnessesPostBatchUpdates( + wits, + members, + additions, + removals, + accumulatorValueBeforeUpdates.asInternalType, + secretKey.value + ).map((m) => new KBUniversalMembershipWitness(m)); + } + + updateUsingPublicInfoPostBatchUpdate( + member: Uint8Array, + additions: Uint8Array[], + removals: Uint8Array[], + publicInfo: KBUniversalMembershipWitnessUpdateInfo + ) { + this.value = updateKBUniversalMembershipWitnessUsingPublicInfoAfterBatchUpdate( + this.value, + member, + additions, + removals, + publicInfo.value + ); + } + + updateUsingPublicInfoPostMultipleBatchUpdates( + member: Uint8Array, + additions: Uint8Array[][], + removals: Uint8Array[][], + publicInfo: KBUniversalMembershipWitnessUpdateInfo[] + ) { + this.value = updateKBUniversalMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates( + this.value, + member, + additions, + removals, + publicInfo.map((i) => i.value) + ); + } + + toJSON(): string { + return JSON.stringify({ + value: Array.from(this.value) + }); + } + + static fromJSON(json: string): KBUniversalMembershipWitness { + const obj = JSON.parse(json); + const [value] = getUint8ArraysFromObject(obj, ['value']); + return new KBUniversalMembershipWitness(value); + } +} + +export class KBUniversalNonMembershipWitness extends AccumulatorWitness { + // @ts-ignore + value: Uint8Array; + + updatePostAdd( + addition: Uint8Array, + nonMember: Uint8Array, + accumulatorValueBeforeAddition: KBUniversalAccumulatorValue + ) { + this.value = kbUniversalUpdateNonMembershipWitnessPostAdd( + this.value, + nonMember, + addition, + accumulatorValueBeforeAddition.asInternalType + ); + } + + updatePostRemove( + removal: Uint8Array, + nonMember: Uint8Array, + accumulatorValueAfterRemoval: KBUniversalAccumulatorValue + ) { + this.value = kbUniversalUpdateNonMembershipWitnessPostRemove( + this.value, + nonMember, + removal, + accumulatorValueAfterRemoval.asInternalType + ); + } + + static updateMultiplePostBatchUpdates( + witnesses: KBUniversalNonMembershipWitness[], + members: Uint8Array[], + additions: Uint8Array[], + removals: Uint8Array[], + accumulatorValueBeforeUpdates: KBUniversalAccumulatorValue, + secretKey: AccumulatorSecretKey + ): KBUniversalNonMembershipWitness[] { + const wits = witnesses.map((m) => m.value); + return kbUpdateNonMembershipWitnessesPostBatchUpdates( + wits, + members, + additions, + removals, + accumulatorValueBeforeUpdates.asInternalType, + secretKey.value + ).map((m) => new KBUniversalNonMembershipWitness(m)); + } + + updateUsingPublicInfoPostBatchUpdate( + nonMember: Uint8Array, + additions: Uint8Array[], + removals: Uint8Array[], + publicInfo: KBUniversalNonMembershipWitnessUpdateInfo + ): void { + this.value = updateKBUniversalNonMembershipWitnessUsingPublicInfoAfterBatchUpdate( + this.value, + nonMember, + additions, + removals, + publicInfo.value + ); + } + + updateUsingPublicInfoPostMultipleBatchUpdates( + nonMember: Uint8Array, + additions: Uint8Array[][], + removals: Uint8Array[][], + publicInfo: KBUniversalNonMembershipWitnessUpdateInfo[] + ): void { + this.value = updateKBUniversalNonMembershipWitnessUsingPublicInfoAfterMultipleBatchUpdates( + this.value, + nonMember, + additions, + removals, + publicInfo.map((i) => i.value) + ); + } +} diff --git a/src/accumulator/kb-universal-accumulator.ts b/src/accumulator/kb-universal-accumulator.ts new file mode 100644 index 0000000..60181cd --- /dev/null +++ b/src/accumulator/kb-universal-accumulator.ts @@ -0,0 +1,304 @@ +import { + IKBUniversalAccumulator, + kbUniversalAccumulatorAdd, + kbUniversalAccumulatorRemove, + kbUniversalAccumulatorAddBatch, + kbUniversalAccumulatorBatchUpdates, + kbUniversalAccumulatorComputeExtended, + kbUniversalAccumulatorInitialise, + kbUniversalAccumulatorMembershipWitness, + kbUniversalAccumulatorRemoveBatch, + kbUniversalAccumulatorVerifyMembership, + kbUniversalAccumulatorMembershipWitnessesForBatch, + kbUniversalAccumulatorNonMembershipWitness, + kbUniversalAccumulatorVerifyNonMembership, + kbUniversalAccumulatorNonMembershipWitnessesForBatch, + kbUpdateBothWitnessesPostBatchUpdates +} from 'crypto-wasm-new'; +import { Accumulator } from './accumulator'; +import { IKBUniversalAccumulatorState } from './IAccumulatorState'; +import { KBUniversalMembershipWitness, KBUniversalNonMembershipWitness } from './kb-acccumulator-witness'; +import { AccumulatorParams, AccumulatorPublicKey, AccumulatorSecretKey } from './params-and-keys'; + +/** + * KB universal accumulator. Its composed of 2 accumulators, one for accumulating elements that are "members" and one + * for "non-members". But this detail is largely abstracted away from. All possible "members" and "non-members" of this + * accumulator are called its domain and during initialization, the domain needs to be decided passed. The domain can be + * extended at any point. + */ +export class KBUniversalAccumulator extends Accumulator { + // @ts-ignore + value: KBUniversalAccumulatorValue; + + /** + * + * @param domain - All possible members or non-members of this accumulator + * @param params + * @param secretKey + * @param state + */ + static async initialize( + domain: Uint8Array[], + params: AccumulatorParams, + secretKey: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ): Promise { + const v = kbUniversalAccumulatorInitialise(domain, secretKey.value, params.value); + const acc = new KBUniversalAccumulator({ + value: new KBUniversalAccumulatorValue(v.mem, v.non_mem), + sk: secretKey, + params + }); + if (state) { + for (const d of domain) { + await state.addToDomain(d); + } + } + return acc; + } + + /** + * Extend the domain + * @param newElements - Add these elements to the domain. These should not be part of the domain + * @param secretKey + * @param state + */ + async extend(newElements: Uint8Array[], secretKey: AccumulatorSecretKey, state?: IKBUniversalAccumulatorState) { + if (state !== undefined) { + for (const e of newElements) { + const r = await state.inDomain(e); + if (r) { + throw new Error(`Element ${e} already part of domain`); + } + } + } + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorComputeExtended(this.value.asInternalType, newElements, secretKey.value) + ); + if (state) { + for (const d of newElements) { + await state.addToDomain(d); + } + } + } + + get accumulated(): KBUniversalAccumulatorValue { + return this.value; + } + + static fromAccumulated(accumulated: KBUniversalAccumulatorValue): KBUniversalAccumulator { + return new KBUniversalAccumulator({ value: accumulated }); + } + + async add(element: Uint8Array, secretKey?: AccumulatorSecretKey, state?: IKBUniversalAccumulatorState) { + await this.checkBeforeAdd(element, state); + const sk = this.getSecretKey(secretKey); + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorAdd(this.value.asInternalType, element, sk.value) + ); + await this.addToState(element, state); + } + + async addBatch(elements: Uint8Array[], secretKey?: AccumulatorSecretKey, state?: IKBUniversalAccumulatorState) { + await this.checkBeforeAddBatch(elements, state); + const sk = this.getSecretKey(secretKey); + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorAddBatch(this.value.asInternalType, elements, sk.value) + ); + await this.addBatchToState(elements, state); + } + + async addRemoveBatches( + additions: Uint8Array[], + removals: Uint8Array[], + secretKey?: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ) { + await this.checkBeforeAddBatch(additions, state); + await this.ensurePresenceOfBatch(removals, state); + const sk = this.getSecretKey(secretKey); + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorBatchUpdates(this.value.asInternalType, additions, removals, sk.value) + ); + await this.addBatchToState(additions, state); + await this.removeBatchFromState(removals, state); + } + + async membershipWitness( + member: Uint8Array, + secretKey?: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ): Promise { + await this.ensurePresence(member, state); + const sk = this.getSecretKey(secretKey); + const wit = kbUniversalAccumulatorMembershipWitness(this.value.asInternalType, member, sk.value); + return new KBUniversalMembershipWitness(wit); + } + + async nonMembershipWitness( + nonMember: Uint8Array, + secretKey?: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ): Promise { + await this.ensureAbsence(nonMember, state); + const sk = this.getSecretKey(secretKey); + const wit = kbUniversalAccumulatorNonMembershipWitness(this.value.asInternalType, nonMember, sk.value); + return new KBUniversalNonMembershipWitness(wit); + } + + async membershipWitnessesForBatch( + members: Uint8Array[], + secretKey?: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ): Promise { + await this.ensurePresenceOfBatch(members, state); + const sk = this.getSecretKey(secretKey); + return kbUniversalAccumulatorMembershipWitnessesForBatch(this.value.asInternalType, members, sk.value).map( + (m) => new KBUniversalMembershipWitness(m) + ); + } + + async nonMembershipWitnessesForBatch( + nonMembers: Uint8Array[], + secretKey?: AccumulatorSecretKey, + state?: IKBUniversalAccumulatorState + ): Promise { + await this.ensureAbsenceOfBatch(nonMembers, state); + const sk = this.getSecretKey(secretKey); + return kbUniversalAccumulatorNonMembershipWitnessesForBatch(this.value.asInternalType, nonMembers, sk.value).map( + (m) => new KBUniversalNonMembershipWitness(m) + ); + } + + async remove(element: Uint8Array, secretKey?: AccumulatorSecretKey, state?: IKBUniversalAccumulatorState) { + await this.ensurePresence(element, state); + const sk = this.getSecretKey(secretKey); + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorRemove(this.value.asInternalType, element, sk.value) + ); + await this.removeFromState(element, state); + } + + async removeBatch(elements: Uint8Array[], secretKey?: AccumulatorSecretKey, state?: IKBUniversalAccumulatorState) { + await this.ensurePresenceOfBatch(elements, state); + const sk = this.getSecretKey(secretKey); + this.value = KBUniversalAccumulatorValue.fromInternalType( + kbUniversalAccumulatorRemoveBatch(this.value.asInternalType, elements, sk.value) + ); + await this.removeBatchFromState(elements, state); + } + + verifyMembershipWitness( + member: Uint8Array, + witness: KBUniversalMembershipWitness, + pk: AccumulatorPublicKey, + params?: AccumulatorParams + ): boolean { + const params_ = this.getParams(params); + return kbUniversalAccumulatorVerifyMembership( + this.value.asInternalType, + member, + witness.value, + pk.value, + params_.value + ); + } + + verifyNonMembershipWitness( + nonMember: Uint8Array, + witness: KBUniversalNonMembershipWitness, + pk: AccumulatorPublicKey, + params?: AccumulatorParams + ): boolean { + const params_ = this.getParams(params); + return kbUniversalAccumulatorVerifyNonMembership( + this.value.asInternalType, + nonMember, + witness.value, + pk.value, + params_.value + ); + } + + updateMultipleMemberAndNonMemberWitnessesPostBatchUpdates( + memWitnesses: KBUniversalMembershipWitness[], + members: Uint8Array[], + nonMemWitnesses: KBUniversalMembershipWitness[], + nonMembers: Uint8Array[], + additions: Uint8Array[], + removals: Uint8Array[], + accumulatorValueBeforeUpdates: KBUniversalAccumulatorValue, + secretKey?: AccumulatorSecretKey + ): [KBUniversalMembershipWitness[], KBUniversalNonMembershipWitness[]] { + const m = memWitnesses.map((m) => m.value); + const nm = nonMemWitnesses.map((m) => m.value); + const sk = this.getSecretKey(secretKey); + const [mw, nmw] = kbUpdateBothWitnessesPostBatchUpdates( + m, + members, + nm, + nonMembers, + additions, + removals, + accumulatorValueBeforeUpdates.asInternalType, + sk.value + ); + return [mw.map((v) => new KBUniversalMembershipWitness(v)), nmw.map((v) => new KBUniversalNonMembershipWitness(v))]; + } + + protected async checkBeforeAdd(element: Uint8Array, state?: IKBUniversalAccumulatorState) { + await this.checkElementAcceptable(element, state); + await this.ensureAbsence(element, state); + } + + protected async checkBeforeAddBatch(elements: Uint8Array[], state?: IKBUniversalAccumulatorState) { + await this.checkElementsAcceptable(elements, state); + await this.ensureAbsenceOfBatch(elements, state); + } + + async checkElementAcceptable(element: Uint8Array, state?: IKBUniversalAccumulatorState): Promise { + if (state !== undefined) { + const valid = await state.inDomain(element); + if (!valid) { + throw new Error(`${element} isn't acceptable`); + } + } + } + + async checkElementsAcceptable(elements: Uint8Array[], state?: IKBUniversalAccumulatorState): Promise { + if (state) { + for (const element of elements) { + await this.checkElementAcceptable(element, state); + } + } + } +} + +/** + * Value of KB universal accumulator + */ +export class KBUniversalAccumulatorValue { + // Value of the accumulator accumulating members + mem: Uint8Array; + // Value of the accumulator accumulating non-members + nonMem: Uint8Array; + + constructor(mem: Uint8Array, nonMem: Uint8Array) { + this.mem = mem; + this.nonMem = nonMem; + } + + /** + * Object expected by wasm. Used when calling wasm functions + */ + get asInternalType(): IKBUniversalAccumulator { + return { + mem: this.mem, + non_mem: this.nonMem + }; + } + + static fromInternalType(o: IKBUniversalAccumulator): KBUniversalAccumulatorValue { + return new KBUniversalAccumulatorValue(o.mem, o.non_mem); + } +} diff --git a/src/accumulator/witness-update-info.ts b/src/accumulator/witness-update-info.ts new file mode 100644 index 0000000..170452b --- /dev/null +++ b/src/accumulator/witness-update-info.ts @@ -0,0 +1,87 @@ +import { + publicInfoForWitnessUpdate, + publicInfoForKBUniversalMemWitnessUpdate, + publicInfoForKBUniversalNonMemWitnessUpdate +} from 'crypto-wasm-new'; +import { BytearrayWrapper } from '../bytearray-wrapper'; +import { jsonObjToUint8Array } from '../util'; +import { KBUniversalAccumulatorValue } from './kb-universal-accumulator'; +import { AccumulatorSecretKey } from './params-and-keys'; + +export class WitnessUpdateInfo extends BytearrayWrapper { + toJSON(): string { + return JSON.stringify({ + value: this.value + }); + } +} + +/** + * Public info published by the accumulator manager used to update witnesses after several additions and removals. + */ +export class VBWitnessUpdateInfo extends WitnessUpdateInfo { + fromJSON(json: string): VBWitnessUpdateInfo { + return new VBWitnessUpdateInfo(jsonObjToUint8Array(json)); + } + + /** + * Accumulator manager creates the witness update info corresponding to the additions and removals. + * @param accumulatorValueBeforeUpdates - accumulator value before the additions and removals + * @param additions + * @param removals + * @param sk + */ + static new( + accumulatorValueBeforeUpdates: Uint8Array, + additions: Uint8Array[], + removals: Uint8Array[], + sk: AccumulatorSecretKey + ): VBWitnessUpdateInfo { + const value = publicInfoForWitnessUpdate(accumulatorValueBeforeUpdates, additions, removals, sk.value); + return new VBWitnessUpdateInfo(value); + } +} + +export class KBUniversalMembershipWitnessUpdateInfo extends WitnessUpdateInfo { + fromJSON(json: string): KBUniversalMembershipWitnessUpdateInfo { + return new KBUniversalMembershipWitnessUpdateInfo(jsonObjToUint8Array(json)); + } + + static new( + accumulatorValueBeforeUpdates: KBUniversalAccumulatorValue, + additions: Uint8Array[], + removals: Uint8Array[], + sk: AccumulatorSecretKey + ): KBUniversalMembershipWitnessUpdateInfo { + return new KBUniversalMembershipWitnessUpdateInfo( + publicInfoForKBUniversalMemWitnessUpdate( + accumulatorValueBeforeUpdates.asInternalType, + additions, + removals, + sk.value + ) + ); + } +} + +export class KBUniversalNonMembershipWitnessUpdateInfo extends WitnessUpdateInfo { + fromJSON(json: string): KBUniversalNonMembershipWitnessUpdateInfo { + return new KBUniversalNonMembershipWitnessUpdateInfo(jsonObjToUint8Array(json)); + } + + static new( + accumulatorValueBeforeUpdates: KBUniversalAccumulatorValue, + additions: Uint8Array[], + removals: Uint8Array[], + sk: AccumulatorSecretKey + ): KBUniversalNonMembershipWitnessUpdateInfo { + return new KBUniversalNonMembershipWitnessUpdateInfo( + publicInfoForKBUniversalNonMemWitnessUpdate( + accumulatorValueBeforeUpdates.asInternalType, + additions, + removals, + sk.value + ) + ); + } +} diff --git a/src/anonymous-credentials/blinded-credential-request-builder.ts b/src/anonymous-credentials/blinded-credential-request-builder.ts index c3c84da..4f480bf 100644 --- a/src/anonymous-credentials/blinded-credential-request-builder.ts +++ b/src/anonymous-credentials/blinded-credential-request-builder.ts @@ -7,6 +7,7 @@ import { import { CredentialSchema } from './schema'; import { BBSCredential, BBSPlusCredential, PSCredential } from './credential'; import { + AccumulatorWitnessType, AttributeEquality, BBS_PLUS_SIGNATURE_PARAMS_LABEL_BYTES, BBS_SIGNATURE_PARAMS_LABEL_BYTES, @@ -58,7 +59,7 @@ type Credential = BBSCredential | BBSPlusCredential | PSCredential; export abstract class BlindedCredentialRequestBuilder extends Versioned { // NOTE: Follows semver and must be updated accordingly when the logic of this class changes or the // underlying crypto changes. - static VERSION = '0.2.0'; + static VERSION = '0.3.0'; // The schema of the whole (unblinded credential). This should include all attributes, i.e. blinded and unblinded _schema?: CredentialSchema; @@ -151,7 +152,7 @@ export abstract class BlindedCredentialRequestBuilder extends Version addAccumInfoForCredStatus( credIdx: number, - accumWitness: AccumulatorWitness, + accumWitness: AccumulatorWitnessType, accumulated: Uint8Array, accumPublicKey: AccumulatorPublicKey, extra: object = {} diff --git a/src/anonymous-credentials/credential-builder-common.ts b/src/anonymous-credentials/credential-builder-common.ts index 2b0df92..f1adfb8 100644 --- a/src/anonymous-credentials/credential-builder-common.ts +++ b/src/anonymous-credentials/credential-builder-common.ts @@ -1,19 +1,20 @@ -import { Versioned } from './versioned'; import { CredentialSchema } from './schema'; import { CRYPTO_VERSION_STR, ID_STR, + MEM_CHECK_KV_STR, MEM_CHECK_STR, + NON_MEM_CHECK_KV_STR, NON_MEM_CHECK_STR, - RevocationStatusProtocol, REV_CHECK_STR, REV_ID_STR, + RevocationStatusProtocol, SCHEMA_STR, STATUS_STR, SUBJECT_STR, - TYPE_STR, - MEM_CHECK_KV_STR + TYPE_STR } from './types-and-consts'; +import { Versioned } from './versioned'; /** * Common fields and methods of `CredentialBuilder` and `BlindedCredentialBuilder` @@ -60,14 +61,29 @@ export abstract class CredentialBuilderCommon extends Versioned { return this._credStatus; } - setCredentialStatus(registryId: string, revCheck: string, memberValue: unknown) { - if (revCheck !== MEM_CHECK_STR && revCheck !== NON_MEM_CHECK_STR && revCheck !== MEM_CHECK_KV_STR) { - throw new Error( - `Revocation check should be either ${MEM_CHECK_STR} or ${NON_MEM_CHECK_STR} or ${MEM_CHECK_KV_STR} but was ${revCheck}` - ); + setCredentialStatus(registryId: string, revCheck: string, memberValue: unknown, revType?: RevocationStatusProtocol) { + const rType = revType ? revType : RevocationStatusProtocol.Vb22; + if (rType == RevocationStatusProtocol.Vb22) { + if (revCheck !== MEM_CHECK_STR && revCheck !== NON_MEM_CHECK_STR && revCheck !== MEM_CHECK_KV_STR) { + throw new Error( + `Revocation check should be either ${MEM_CHECK_STR} or ${NON_MEM_CHECK_STR} or ${MEM_CHECK_KV_STR} but was ${revCheck}` + ); + } + } + if (rType == RevocationStatusProtocol.KbUni24) { + if ( + revCheck !== MEM_CHECK_STR && + revCheck !== NON_MEM_CHECK_STR && + revCheck !== MEM_CHECK_KV_STR && + revCheck !== NON_MEM_CHECK_KV_STR + ) { + throw new Error( + `Revocation check should be either ${MEM_CHECK_STR} or ${NON_MEM_CHECK_STR} or ${MEM_CHECK_KV_STR} or ${NON_MEM_CHECK_KV_STR} but was ${revCheck}` + ); + } } this._credStatus = { - [TYPE_STR]: RevocationStatusProtocol.Vb22, + [TYPE_STR]: rType, [ID_STR]: registryId, [REV_CHECK_STR]: revCheck, [REV_ID_STR]: memberValue diff --git a/src/anonymous-credentials/credential-builder.ts b/src/anonymous-credentials/credential-builder.ts index 975413f..74d9b83 100644 --- a/src/anonymous-credentials/credential-builder.ts +++ b/src/anonymous-credentials/credential-builder.ts @@ -44,7 +44,7 @@ export abstract class CredentialBuilder< > extends CredentialBuilderCommon { // NOTE: Follows semver and must be updated accordingly when the logic of this class changes or the // underlying crypto changes. - static VERSION = '0.4.0'; + static VERSION = '0.5.0'; _encodedAttributes?: { [key: string]: Uint8Array }; _sig?: Signature; diff --git a/src/anonymous-credentials/delegated-proof.ts b/src/anonymous-credentials/delegated-proof.ts index 3a7a6f0..8615cdd 100644 --- a/src/anonymous-credentials/delegated-proof.ts +++ b/src/anonymous-credentials/delegated-proof.ts @@ -2,10 +2,16 @@ import b58 from 'bs58'; import { VerifyResult } from 'crypto-wasm-new'; import { AccumulatorSecretKey } from '../accumulator'; import { BDDT16MacSecretKey } from '../bddt16-mac'; -import { BDDT16DelegatedProof, VBAccumMembershipDelegatedProof } from '../delegated-proofs'; +import { + BDDT16DelegatedProof, + KBUniAccumMembershipDelegatedProof, + KBUniAccumNonMembershipDelegatedProof, + VBAccumMembershipDelegatedProof +} from '../delegated-proofs'; import { ID_STR, MEM_CHECK_KV_STR, + NON_MEM_CHECK_KV_STR, REV_CHECK_STR, RevocationStatusProtocol, SignatureType, @@ -22,7 +28,7 @@ export interface IDelegatedCredentialStatusProof { [ID_STR]: string; [TYPE_STR]: RevocationStatusProtocol; [REV_CHECK_STR]: string; - proof: VBAccumMembershipDelegatedProof; + proof: VBAccumMembershipDelegatedProof | KBUniAccumMembershipDelegatedProof | KBUniAccumNonMembershipDelegatedProof; } /** @@ -36,7 +42,7 @@ export class DelegatedProof extends Versioned { constructor(credential?: IDelegatedCredentialProof, status?: IDelegatedCredentialStatusProof) { if (credential === undefined && status === undefined) { - throw new Error(`At least one of credential or status must be defined`) + throw new Error(`At least one of credential or status must be defined`); } super(DelegatedProof.VERSION); this.credential = credential; @@ -63,15 +69,26 @@ export class DelegatedProof extends Versioned { if (this.status[ID_STR] === undefined) { throw new Error(`${ID_STR} field is required in the delegated proof`); } - if (this.status[TYPE_STR] !== RevocationStatusProtocol.Vb22 || this.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR) { - throw new Error(`Unexpected values for ${TYPE_STR} and ${REV_CHECK_STR}: ${this.status[TYPE_STR]}, ${this.status[REV_CHECK_STR]}`); + if (this.status[TYPE_STR] === RevocationStatusProtocol.Vb22) { + if (this.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR) { + throw new Error( + `Unexpected values for ${TYPE_STR} and ${REV_CHECK_STR}: ${this.status[TYPE_STR]}, ${this.status[REV_CHECK_STR]}` + ); + } + } else if (this.status[TYPE_STR] === RevocationStatusProtocol.KbUni24) { + if (this.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR && this.status[REV_CHECK_STR] !== NON_MEM_CHECK_KV_STR) { + throw new Error( + `Unexpected values for ${TYPE_STR} and ${REV_CHECK_STR}: ${this.status[TYPE_STR]}, ${this.status[REV_CHECK_STR]}` + ); + } + } else { + throw new Error(`Unexpected value for ${TYPE_STR}: ${this.status[TYPE_STR]}`); } const rc = this.status.proof.verify(accumSecretKey); if (!rc.verified) { return rc; } } - return r; } @@ -106,16 +123,33 @@ export class DelegatedProof extends Versioned { }; } if (j['status'] !== undefined) { - if (j['status'][ID_STR] === undefined || j['status'][TYPE_STR] === undefined || j['status'][REV_CHECK_STR] === undefined || j['status'].proof === undefined) { - throw new Error(`Expected fields ${ID_STR}, ${TYPE_STR}, ${REV_CHECK_STR} and proof but found the status object to be ${j['status']}`); + if ( + j['status'][ID_STR] === undefined || + j['status'][TYPE_STR] === undefined || + j['status'][REV_CHECK_STR] === undefined || + j['status'].proof === undefined + ) { + throw new Error( + `Expected fields ${ID_STR}, ${TYPE_STR}, ${REV_CHECK_STR} and proof but found the status object to be ${j['status']}` + ); + } + let cls; + if (j['status'][TYPE_STR] === RevocationStatusProtocol.Vb22) { + cls = VBAccumMembershipDelegatedProof; + } else if (j['status'][TYPE_STR] === RevocationStatusProtocol.KbUni24) { + if (j['status'][REV_CHECK_STR] === MEM_CHECK_KV_STR) { + cls = KBUniAccumMembershipDelegatedProof; + } else if (j['status'][REV_CHECK_STR] === NON_MEM_CHECK_KV_STR) { + cls = KBUniAccumNonMembershipDelegatedProof; + } } status = { [ID_STR]: j['status'][ID_STR], [TYPE_STR]: j['status'][TYPE_STR], [REV_CHECK_STR]: j['status'][REV_CHECK_STR], - proof: new VBAccumMembershipDelegatedProof(b58.decode(j['status'].proof)) + proof: new cls(b58.decode(j['status'].proof)) }; } - return new DelegatedProof(credential, status) + return new DelegatedProof(credential, status); } } diff --git a/src/anonymous-credentials/presentation-builder.ts b/src/anonymous-credentials/presentation-builder.ts index 80ea042..cf67cc8 100644 --- a/src/anonymous-credentials/presentation-builder.ts +++ b/src/anonymous-credentials/presentation-builder.ts @@ -1,3 +1,5 @@ +import { KBUniversalMembershipWitness, KBUniversalNonMembershipWitness } from '../accumulator/kb-acccumulator-witness'; +import { KBUniversalAccumulatorValue } from '../accumulator/kb-universal-accumulator'; import { Versioned } from './versioned'; import { BBSCredential, BBSPlusCredential, BDDT16Credential, PSCredential } from './credential'; import { @@ -17,6 +19,8 @@ import { R1CS } from 'crypto-wasm-new'; import { CredentialSchema, getTransformedMinMax, ValueType } from './schema'; import { getRevealedAndUnrevealed } from '../sign-verify-js-objs'; import { + AccumulatorValueType, + AccumulatorWitnessType, AttributeCiphertexts, AttributeEquality, BoundCheckParamType, @@ -29,6 +33,7 @@ import { InequalityProtocol, MEM_CHECK_KV_STR, MEM_CHECK_STR, + NON_MEM_CHECK_KV_STR, NON_MEM_CHECK_STR, PredicateParamType, PublicKey, @@ -53,9 +58,8 @@ import { PresentationSpecification } from './presentation-specification'; import { buildContextForProof, Presentation } from './presentation'; -import { AccumulatorPublicKey, AccumulatorWitness, VBMembershipWitness, VBNonMembershipWitness } from '../accumulator'; +import { AccumulatorPublicKey, VBMembershipWitness, VBNonMembershipWitness } from '../accumulator'; import { - accumulatorStatement, buildSignatureProverStatementFromParamsRef, buildWitness, createWitEq, @@ -115,7 +119,7 @@ type Credential = BBSCredential | BBSPlusCredential | PSCredential | BDDT16Crede export class PresentationBuilder extends Versioned { // NOTE: Follows semver and must be updated accordingly when the logic of this class changes or the // underlying crypto changes. - static VERSION = '0.6.0'; + static VERSION = '0.7.0'; // This can specify the reason why the proof was created, or date of the proof, or self-attested attributes (as JSON string), etc _context?: string; @@ -146,7 +150,7 @@ export class PresentationBuilder extends Versioned { attributeInequalities: Map>; // Each credential has only one accumulator for status - credStatuses: Map; + credStatuses: Map; // Bounds on attribute. The key of the map is the credential index and for the inner map is the attribute and value of map // denotes min, max, an identifier of the setup parameters for the protocol and the protocol name. @@ -258,8 +262,8 @@ export class PresentationBuilder extends Versioned { */ addAccumInfoForCredStatus( credIdx: number, - accumWitness: AccumulatorWitness, - accumulated: Uint8Array, + accumWitness: AccumulatorWitnessType, + accumulated: AccumulatorValueType, accumPublicKey?: AccumulatorPublicKey, extra: object = {} ) { @@ -508,8 +512,8 @@ export class PresentationBuilder extends Versioned { // Store only needed encoded values of names and their indices. Maps cred index -> attribute index in schema -> encoded attribute const unrevealedMsgsEncoded = new Map>(); - // For credentials with status, i.e. using accumulators, type is [credIndex, revCheckType, encoded (non)member] - const credStatusAux: [number, string, Uint8Array][] = []; + // For credentials with status, i.e. using accumulators, type is [credIndex, protocol, revCheckType, encoded (non)member] + const credStatusAux: [number, string, string, Uint8Array][] = []; const setupParamsTrk = new SetupParamsTracker(); const sigParamsByScheme = new Map(); @@ -541,11 +545,13 @@ export class PresentationBuilder extends Versioned { cred.credentialStatus[ID_STR] === undefined || (cred.credentialStatus[REV_CHECK_STR] !== MEM_CHECK_STR && cred.credentialStatus[REV_CHECK_STR] !== NON_MEM_CHECK_STR && - cred.credentialStatus[REV_CHECK_STR] !== MEM_CHECK_KV_STR) + cred.credentialStatus[REV_CHECK_STR] !== MEM_CHECK_KV_STR && + cred.credentialStatus[REV_CHECK_STR] !== NON_MEM_CHECK_KV_STR) ) { throw new Error(`Credential for ${credIndex} has invalid status ${cred.credentialStatus}`); } revealedNames.add(`${STATUS_STR}.${ID_STR}`); + revealedNames.add(`${STATUS_STR}.${TYPE_STR}`); revealedNames.add(`${STATUS_STR}.${REV_CHECK_STR}`); } @@ -573,13 +579,14 @@ export class PresentationBuilder extends Versioned { } presentedStatus = { [ID_STR]: cred.credentialStatus[ID_STR], - [TYPE_STR]: RevocationStatusProtocol.Vb22, + [TYPE_STR]: cred.credentialStatus[TYPE_STR], [REV_CHECK_STR]: cred.credentialStatus[REV_CHECK_STR], accumulated: s[1], extra: s[3] }; credStatusAux.push([ credIndex, + cred.credentialStatus[TYPE_STR], cred.credentialStatus[REV_CHECK_STR], schema.encoder.encodeMessage(`${STATUS_STR}.${REV_ID_STR}`, cred.credentialStatus[REV_ID_STR]) ]); @@ -734,25 +741,100 @@ export class PresentationBuilder extends Versioned { } // Create statements and witnesses for accumulators used in credential status - credStatusAux.forEach(([i, checkType, value]) => { + credStatusAux.forEach(([i, protocol, checkType, value]) => { const s = this.credStatuses.get(i); if (s === undefined) { throw new Error(`No status details found for credential index ${i}`); } const [wit, acc, pk] = s; - let witness; - if (checkType === MEM_CHECK_STR || checkType === MEM_CHECK_KV_STR) { - if (!(wit instanceof VBMembershipWitness)) { - throw new Error(`Expected membership witness but got non-membership witness for credential index ${i}`); + let statement, witness; + if (protocol === RevocationStatusProtocol.Vb22) { + if (!(Array.isArray(acc) || acc instanceof Uint8Array)) { + throw new Error(`Accumulator value should have been a Uint8Array but was ${acc}`); } - witness = Witness.vbAccumulatorMembership(value, wit); - } else { - if (!(wit instanceof VBNonMembershipWitness)) { - throw new Error(`Expected non-membership witness but got membership witness for credential index ${i}`); + // Create witness + if (checkType === MEM_CHECK_STR || checkType === MEM_CHECK_KV_STR) { + if (!(wit instanceof VBMembershipWitness)) { + throw new Error(`Expected membership witness but got non-membership witness for credential index ${i}`); + } + witness = Witness.vbAccumulatorMembership(value, wit); + } else { + if (!(wit instanceof VBNonMembershipWitness)) { + throw new Error(`Expected non-membership witness but got membership witness for credential index ${i}`); + } + witness = Witness.vbAccumulatorNonMembership(value, wit); } - witness = Witness.vbAccumulatorNonMembership(value, wit); + + // Create statement + let pkSp; + if (checkType === MEM_CHECK_STR || checkType === NON_MEM_CHECK_STR) { + if (!(pk instanceof AccumulatorPublicKey)) { + throw new Error(`Accumulator public key wasn't provided for credential index ${i}`); + } + if (!setupParamsTrk.hasAccumulatorParams()) { + setupParamsTrk.addAccumulatorParams(); + } + pkSp = SetupParam.vbAccumulatorPublicKey(pk); + } + + if (checkType === MEM_CHECK_STR) { + if (!setupParamsTrk.hasAccumulatorMemProvingKey()) { + setupParamsTrk.addAccumulatorMemProvingKey(); + } + statement = Statement.vbAccumulatorMembershipFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + setupParamsTrk.memPrkIdx, + acc as Uint8Array + ); + } else if (checkType === NON_MEM_CHECK_STR) { + if (!setupParamsTrk.hasAccumulatorNonMemProvingKey()) { + setupParamsTrk.addAccumulatorNonMemProvingKey(); + } + statement = Statement.vbAccumulatorNonMembershipFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + setupParamsTrk.memPrkIdx, + acc as Uint8Array + ); + } else if (checkType === MEM_CHECK_KV_STR) { + statement = Statement.vbAccumulatorMembershipKV(acc as Uint8Array); + } else { + throw new Error(`Unknown status check type ${checkType} for credential index ${i}`); + } + } else if (protocol === RevocationStatusProtocol.KbUni24) { + if (!(acc instanceof KBUniversalAccumulatorValue)) { + throw new Error(`Accumulator value should have been a KBUniversalAccumulatorValue object but was ${acc}`); + } + // Create witness + if (checkType === MEM_CHECK_STR || checkType === MEM_CHECK_KV_STR) { + if (!(wit instanceof KBUniversalMembershipWitness)) { + throw new Error(`Expected membership witness but got non-membership witness for credential index ${i}`); + } + witness = Witness.kbUniAccumulatorMembership(value, wit); + } else { + if (!(wit instanceof KBUniversalNonMembershipWitness)) { + throw new Error(`Expected non-membership witness but got membership witness for credential index ${i}`); + } + witness = Witness.kbUniAccumulatorNonMembership(value, wit); + } + + // Create statement + if (checkType === MEM_CHECK_STR) { + statement = Statement.kbUniAccumulatorMembershipProver(acc.mem); + } else if (checkType === NON_MEM_CHECK_STR) { + statement = Statement.kbUniAccumulatorNonMembershipProver(acc.nonMem); + } else if (checkType === MEM_CHECK_KV_STR) { + statement = Statement.kbUniAccumulatorMembershipKV(acc.mem); + } else if (checkType === NON_MEM_CHECK_KV_STR) { + statement = Statement.kbUniAccumulatorNonMembershipKV(acc.nonMem); + } else { + throw new Error(`Unknown status check type ${checkType} for credential index ${i}`); + } + } else { + throw new Error(`Unknown status protocol ${protocol} for credential index ${i}`); } - const statement = accumulatorStatement(i, checkType, acc, setupParamsTrk, pk); + const sIdx = statements.add(statement); witnesses.add(witness); diff --git a/src/anonymous-credentials/presentation-specification.ts b/src/anonymous-credentials/presentation-specification.ts index 48c6e49..3965c83 100644 --- a/src/anonymous-credentials/presentation-specification.ts +++ b/src/anonymous-credentials/presentation-specification.ts @@ -1,3 +1,4 @@ +import { KBUniversalAccumulatorValue } from '../accumulator/kb-universal-accumulator'; import { AttributeEquality, BlindedAttributeEquality, @@ -10,7 +11,8 @@ import { VerifiableEncryptionProtocol, REV_CHECK_STR, TYPE_STR, - InequalityProtocol + InequalityProtocol, + AccumulatorValueType } from './types-and-consts'; import b58 from 'bs58'; import { CredentialSchema } from './schema'; @@ -19,7 +21,7 @@ export interface IPresentedStatus { [ID_STR]: string; [TYPE_STR]: RevocationStatusProtocol; [REV_CHECK_STR]: string; - accumulated: Uint8Array; + accumulated: AccumulatorValueType; extra: object; } @@ -247,7 +249,16 @@ export class PresentationSpecification { } if (pc.status !== undefined) { curJ['status'] = { ...pc.status }; - curJ['status'].accumulated = b58.encode(pc.status.accumulated); + if (pc.status[TYPE_STR] === RevocationStatusProtocol.Vb22) { + curJ['status'].accumulated = b58.encode(pc.status.accumulated as Uint8Array); + } + if (pc.status[TYPE_STR] === RevocationStatusProtocol.KbUni24) { + // @ts-ignore + curJ['status'].accumulated = `${b58.encode(pc.status.accumulated.mem)},${b58.encode( + // @ts-ignore + pc.status.accumulated.nonMem + )}`; + } } if (pc.attributeInequalities !== undefined) { curJ['attributeInequalities'] = pc.attributeInequalities; diff --git a/src/anonymous-credentials/presentation.ts b/src/anonymous-credentials/presentation.ts index 31259b8..ce83755 100644 --- a/src/anonymous-credentials/presentation.ts +++ b/src/anonymous-credentials/presentation.ts @@ -3,6 +3,8 @@ import { VerifyResult } from 'crypto-wasm-new'; import { flatten } from 'flat'; import stringify from 'json-stringify-deterministic'; import semver from 'semver/preload'; +import { AccumulatorPublicKey, AccumulatorSecretKey } from '../accumulator'; +import { KBUniversalAccumulatorValue } from '../accumulator/kb-universal-accumulator'; import { BBSSignatureParams } from '../bbs'; import { BBSPlusSignatureParamsG1 } from '../bbs-plus'; import { BDDT16MacParams } from '../bddt16-mac'; @@ -23,7 +25,12 @@ import { Statements, WitnessEqualityMetaStatement } from '../composite-proof'; -import { BDDT16DelegatedProof, VBAccumMembershipDelegatedProof } from '../delegated-proofs'; +import { + BDDT16DelegatedProof, + KBUniAccumMembershipDelegatedProof, + KBUniAccumNonMembershipDelegatedProof, + VBAccumMembershipDelegatedProof +} from '../delegated-proofs'; import { LegoVerifyingKey, LegoVerifyingKeyUncompressed } from '../legosnark'; import { PederCommKey, PederCommKeyUncompressed } from '../ped-com'; import { PSSignatureParams } from '../ps'; @@ -45,6 +52,7 @@ import { import { CredentialSchema, getTransformedMinMax, ValueType } from './schema'; import { SetupParamsTracker } from './setup-params-tracker'; import { + AccumulatorValueType, AccumulatorVerificationParam, AttributeCiphertexts, BBS_BLINDED_CRED_PROOF_TYPE, @@ -60,6 +68,7 @@ import { InequalityProtocol, MEM_CHECK_KV_STR, MEM_CHECK_STR, + NON_MEM_CHECK_KV_STR, NON_MEM_CHECK_STR, PredicateParamType, PublicKey, @@ -73,7 +82,6 @@ import { VerifiableEncryptionProtocol } from './types-and-consts'; import { - accumulatorStatement, buildSignatureVerifierStatementFromParamsRef, createWitEq, createWitEqForBlindedCred, @@ -188,8 +196,8 @@ export class Presentation extends Versioned { // For the following arrays of pairs, the 1st item of each pair is the credential index - // For credentials with status, i.e. using accumulators, type is [credIndex, revCheckType, accumulator] - const credStatusAux: [number, string, Uint8Array][] = []; + // For credentials with status, i.e. using accumulators, type is [credIndex, protocol, revCheckType, accumulator] + const credStatusAux: [number, string, string, AccumulatorValueType][] = []; // For inequality checks on credential attributes const ineqsAux: [number, { [key: string]: [IPresentedAttributeInequality, Uint8Array][] }][] = []; @@ -207,6 +215,7 @@ export class Presentation extends Versioned { const sigParamsByScheme = new Map(); const versionGt5 = semver.gt(this.version, '0.5.0'); + const versionGt6 = semver.gt(this.version, '0.6.0'); for (let credIndex = 0; credIndex < this.spec.credentials.length; credIndex++) { const presentedCred = this.spec.credentials[credIndex]; @@ -218,7 +227,8 @@ export class Presentation extends Versioned { credIndex, presentedCred, presentedCredSchema, - flattenedSchema[0] + flattenedSchema[0], + versionGt6 ); let sigParamsClass; @@ -265,7 +275,12 @@ export class Presentation extends Versioned { if (presentedCred.status !== undefined) { // The input validation and security checks for these have been done as part of encoding revealed attributes - credStatusAux.push([credIndex, presentedCred.status[REV_CHECK_STR], presentedCred.status.accumulated]); + credStatusAux.push([ + credIndex, + presentedCred.status[TYPE_STR], + presentedCred.status[REV_CHECK_STR], + presentedCred.status.accumulated + ]); } if (presentedCred.attributeInequalities !== undefined) { @@ -292,10 +307,117 @@ export class Presentation extends Versioned { } } - credStatusAux.forEach(([i, checkType, accum]) => { - // let statement; + credStatusAux.forEach(([i, protocol, checkType, acc]) => { + let statement; const pk = accumulatorPublicKeys?.get(i); - const statement = accumulatorStatement(i, checkType, accum, setupParamsTrk, pk); + if (protocol === RevocationStatusProtocol.Vb22) { + if (!(Array.isArray(acc) || acc instanceof Uint8Array)) { + throw new Error(`Accumulator value should have been a Uint8Array but was ${acc}`); + } + let pkSp; + if (checkType === MEM_CHECK_STR || checkType === NON_MEM_CHECK_STR) { + if (!(pk instanceof AccumulatorPublicKey)) { + throw new Error(`Accumulator public key wasn't provided for credential index ${i}`); + } + if (!setupParamsTrk.hasAccumulatorParams()) { + setupParamsTrk.addAccumulatorParams(); + } + pkSp = SetupParam.vbAccumulatorPublicKey(pk); + } + + if (checkType === MEM_CHECK_STR) { + if (!setupParamsTrk.hasAccumulatorMemProvingKey()) { + setupParamsTrk.addAccumulatorMemProvingKey(); + } + statement = Statement.vbAccumulatorMembershipFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + setupParamsTrk.memPrkIdx, + acc as Uint8Array + ); + } else if (checkType === NON_MEM_CHECK_STR) { + if (!setupParamsTrk.hasAccumulatorNonMemProvingKey()) { + setupParamsTrk.addAccumulatorNonMemProvingKey(); + } + statement = Statement.vbAccumulatorNonMembershipFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + setupParamsTrk.nonMemPrkIdx, + acc as Uint8Array + ); + } else if (checkType === MEM_CHECK_KV_STR) { + if (pk === undefined) { + statement = Statement.vbAccumulatorMembershipKV(acc as Uint8Array); + } else { + if (pk instanceof AccumulatorSecretKey) { + statement = Statement.vbAccumulatorMembershipKVFullVerifier(pk, acc as Uint8Array); + } else { + throw new Error( + `Unexpected accumulator verification param ${pk.constructor.name} passed for credential index ${i}` + ); + } + } + } else { + throw new Error(`Unknown status check type ${checkType} for credential index ${i}`); + } + } else if (protocol === RevocationStatusProtocol.KbUni24) { + if (!(acc instanceof KBUniversalAccumulatorValue)) { + throw new Error(`Accumulator value should have been a KBUniversalAccumulatorValue object but was ${acc}`); + } + let pkSp; + if (checkType === MEM_CHECK_STR || checkType === NON_MEM_CHECK_STR) { + if (!(pk instanceof AccumulatorPublicKey)) { + throw new Error(`Accumulator public key wasn't provided for credential index ${i}`); + } + if (!setupParamsTrk.hasAccumulatorParams()) { + setupParamsTrk.addAccumulatorParams(); + } + pkSp = SetupParam.vbAccumulatorPublicKey(pk); + } + + if (checkType === MEM_CHECK_STR) { + statement = Statement.kbUniAccumulatorMembershipVerifierFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + acc.mem + ); + } else if (checkType === NON_MEM_CHECK_STR) { + statement = Statement.kbUniAccumulatorNonMembershipVerifierFromSetupParamRefs( + setupParamsTrk.accumParamsIdx, + setupParamsTrk.add(pkSp), + acc.nonMem + ); + } else if (checkType === MEM_CHECK_KV_STR) { + if (pk === undefined) { + statement = Statement.kbUniAccumulatorMembershipKV(acc.mem); + } else { + if (pk instanceof AccumulatorSecretKey) { + statement = Statement.kbUniAccumulatorMembershipKVFullVerifier(pk, acc.mem); + } else { + throw new Error( + `Unexpected accumulator verification param ${pk.constructor.name} passed for credential index ${i}` + ); + } + } + } else if (checkType === NON_MEM_CHECK_KV_STR) { + if (pk === undefined) { + statement = Statement.kbUniAccumulatorNonMembershipKV(acc.nonMem); + } else { + if (pk instanceof AccumulatorSecretKey) { + statement = Statement.kbUniAccumulatorNonMembershipKVFullVerifier(pk, acc.nonMem); + } else { + throw new Error( + `Unexpected accumulator verification param ${pk.constructor.name} passed for credential index ${i}` + ); + } + } + } else { + throw new Error(`Unknown status check type ${checkType} for credential index ${i}`); + } + } else { + throw new Error(`Unknown status protocol ${protocol} for credential index ${i}`); + } + const sIdx = statements.add(statement); const witnessEq = new WitnessEqualityMetaStatement(); witnessEq.addWitnessRef(i, flattenedSchemas[i][0].indexOf(`${STATUS_STR}.${REV_ID_STR}`)); @@ -661,6 +783,36 @@ export class Presentation extends Versioned { [REV_CHECK_STR]: presentedCred.status[REV_CHECK_STR], proof }; + } else if ( + presentedCred.status[TYPE_STR] === RevocationStatusProtocol.KbUni24 && + (presentedCred.status[REV_CHECK_STR] === MEM_CHECK_KV_STR || + presentedCred.status[REV_CHECK_STR] === NON_MEM_CHECK_KV_STR) + ) { + const proof = delegatedProofs.get(nextCredStatusStatementIdx); + if (proof === undefined) { + throw new Error(`Could not find delegated credential status proof for credential index ${i}`); + } + if (presentedCred.status[REV_CHECK_STR] === MEM_CHECK_KV_STR) { + if (!(proof instanceof KBUniAccumMembershipDelegatedProof)) { + throw new Error( + `Unexpected delegated credential status proof type ${proof.constructor.name} for credential index ${i}` + ); + } + } + if (presentedCred.status[REV_CHECK_STR] === NON_MEM_CHECK_KV_STR) { + if (!(proof instanceof KBUniAccumNonMembershipDelegatedProof)) { + throw new Error( + `Unexpected delegated credential status proof type ${proof.constructor.name} for credential index ${i}` + ); + } + } + statusP = { + [ID_STR]: presentedCred.status[ID_STR], + [TYPE_STR]: presentedCred.status[TYPE_STR], + [REV_CHECK_STR]: presentedCred.status[REV_CHECK_STR], + // @ts-ignore + proof + }; } nextCredStatusStatementIdx++; } @@ -678,12 +830,14 @@ export class Presentation extends Versioned { * @param presentedCred * @param presentedCredSchema * @param flattenedNames + * @param newVersion */ private static encodeRevealed( credIdx: number, presentedCred: IPresentedCredential, presentedCredSchema: CredentialSchema, - flattenedNames: string[] + flattenedNames: string[], + newVersion: boolean ): Map { const revealedRaw = deepClone(presentedCred.revealedAttributes) as object; revealedRaw[CRYPTO_VERSION_STR] = presentedCred.version; @@ -697,7 +851,8 @@ export class Presentation extends Versioned { presentedCred.status[ID_STR] === undefined || (presentedCred.status[REV_CHECK_STR] !== MEM_CHECK_STR && presentedCred.status[REV_CHECK_STR] !== NON_MEM_CHECK_STR && - presentedCred.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR) + presentedCred.status[REV_CHECK_STR] !== MEM_CHECK_KV_STR && + presentedCred.status[REV_CHECK_STR] !== NON_MEM_CHECK_KV_STR) ) { throw new Error(`Presented credential for ${credIdx} has invalid status ${presentedCred.status}`); } @@ -706,6 +861,9 @@ export class Presentation extends Versioned { [ID_STR]: presentedCred.status[ID_STR], [REV_CHECK_STR]: presentedCred.status[REV_CHECK_STR] }; + if (newVersion) { + revealedRaw[STATUS_STR][TYPE_STR] = presentedCred.status[TYPE_STR]; + } } const encoded = new Map(); Object.entries(flatten(revealedRaw) as object).forEach(([k, v]) => { @@ -1028,8 +1186,16 @@ export class Presentation extends Versioned { for (const cred of this.spec.credentials) { const current = deepClone(cred) as object; // Need this deep cloning because structure of revealed attributes or key `extra` isn't fixed if (cred.status !== undefined) { - // @ts-ignore - current.status?.accumulated = b58.encode(cred.status.accumulated); + if (cred.status[TYPE_STR] === RevocationStatusProtocol.Vb22) { + // @ts-ignore + current.status?.accumulated = b58.encode(cred.status.accumulated); + } else if (cred.status[TYPE_STR] === RevocationStatusProtocol.KbUni24) { + // @ts-ignore + current.status?.accumulated = `${b58.encode(cred.status.accumulated.mem)},${b58.encode( + // @ts-ignore + cred.status.accumulated.nonMem + )}`; + } } if (cred.circomPredicates !== undefined) { // @ts-ignore @@ -1340,9 +1506,14 @@ export class Presentation extends Versioned { let status, circomPredicates, sigType; if (cred['status'] !== undefined) { - if (Object.values(RevocationStatusProtocol).includes(cred['status']['type'])) { + if (Object.values(RevocationStatusProtocol).includes(cred['status'][TYPE_STR])) { status = deepClone(cred['status']) as object; - status['accumulated'] = b58.decode(cred['status']['accumulated']); + if (status[TYPE_STR] === RevocationStatusProtocol.Vb22) { + status['accumulated'] = b58.decode(cred['status']['accumulated']); + } else if (status[TYPE_STR] === RevocationStatusProtocol.KbUni24) { + const parts = status['accumulated'].split(','); + status['accumulated'] = new KBUniversalAccumulatorValue(b58.decode(parts[0]), b58.decode(parts[1])); + } } else { throw new Error(`status type should be one of ${RevocationStatusProtocol} but was ${cred['status']['type']}`); } diff --git a/src/anonymous-credentials/schema.ts b/src/anonymous-credentials/schema.ts index 4b4c573..153d134 100644 --- a/src/anonymous-credentials/schema.ts +++ b/src/anonymous-credentials/schema.ts @@ -468,6 +468,7 @@ export class CredentialSchema extends Versioned { CRYPTO_VERSION_STR, SCHEMA_STR, `${STATUS_STR}.${ID_STR}`, + `${STATUS_STR}.${TYPE_STR}`, `${STATUS_STR}.${REV_CHECK_STR}`, `${STATUS_STR}.${REV_ID_STR}` ]); diff --git a/src/anonymous-credentials/types-and-consts.ts b/src/anonymous-credentials/types-and-consts.ts index f39dd35..c989a28 100644 --- a/src/anonymous-credentials/types-and-consts.ts +++ b/src/anonymous-credentials/types-and-consts.ts @@ -1,3 +1,5 @@ +import { KBUniversalMembershipWitness, KBUniversalNonMembershipWitness } from '../accumulator/kb-acccumulator-witness'; +import { KBUniversalAccumulatorValue } from '../accumulator/kb-universal-accumulator'; import { BBSPublicKey, BBSSecretKey, BBSSignature, BBSSignatureParams } from '../bbs'; import { LegoProvingKey, LegoProvingKeyUncompressed } from '../legosnark'; import { @@ -18,7 +20,9 @@ import { AccumulatorPublicKey, AccumulatorSecretKey, MembershipProvingKey, - NonMembershipProvingKey + NonMembershipProvingKey, + VBMembershipWitness, + VBNonMembershipWitness } from '../accumulator'; import { BoundCheckBppParams, @@ -92,6 +96,13 @@ export type CredentialVerificationParam = PublicKey | BDDT16MacSecretKey; // TODO: Find a better name export type AccumulatorVerificationParam = AccumulatorPublicKey | AccumulatorSecretKey; +export type AccumulatorWitnessType = + | VBMembershipWitness + | VBNonMembershipWitness + | KBUniversalMembershipWitness + | KBUniversalNonMembershipWitness; +export type AccumulatorValueType = Uint8Array | KBUniversalAccumulatorValue; + export const VERSION_STR = 'version'; export const CRYPTO_VERSION_STR = 'cryptoVersion'; export const SCHEMA_STR = 'credentialSchema'; @@ -105,12 +116,14 @@ export const SUBJECT_STR = 'credentialSubject'; export const STATUS_STR = 'credentialStatus'; export const TYPE_STR = 'type'; export const VB_ACCUMULATOR_22 = 'DockVBAccumulator2022'; +export const KB_UNI_ACCUMULATOR_24 = 'DockKBUniversalAccumulator2024'; export const ID_STR = 'id'; export const REV_CHECK_STR = 'revocationCheck'; export const REV_ID_STR = 'revocationId'; export const MEM_CHECK_STR = 'membership'; export const NON_MEM_CHECK_STR = 'non-membership'; export const MEM_CHECK_KV_STR = 'membership-kv'; +export const NON_MEM_CHECK_KV_STR = 'non-membership-kv'; export const PROOF_STR = 'proof'; export const BBS_CRED_PROOF_TYPE = 'Bls12381BBSSignatureDock2023'; export const BBS_BLINDED_CRED_PROOF_TYPE = 'Bls12381BlindedBBSSignatureDock2023'; @@ -215,7 +228,8 @@ export enum BlindSignatureType { } export enum RevocationStatusProtocol { - Vb22 = VB_ACCUMULATOR_22 + Vb22 = VB_ACCUMULATOR_22, + KbUni24 = KB_UNI_ACCUMULATOR_24 } export enum BoundCheckProtocol { diff --git a/src/anonymous-credentials/util.ts b/src/anonymous-credentials/util.ts index dbff712..5703c6c 100644 --- a/src/anonymous-credentials/util.ts +++ b/src/anonymous-credentials/util.ts @@ -1,4 +1,21 @@ +import { flatten } from 'flat'; import { AccumulatorPublicKey, AccumulatorSecretKey } from '../accumulator'; +import { BBSPublicKey, BBSSignature, BBSSignatureParams } from '../bbs'; +import { BBSPlusPublicKeyG2, BBSPlusSignatureG1, BBSPlusSignatureParamsG1 } from '../bbs-plus'; +import { BDDT16Mac, BDDT16MacParams, BDDT16MacSecretKey } from '../bddt16-mac'; +import { SetupParam, Statement, Witness, WitnessEqualityMetaStatement } from '../composite-proof'; +import { PSPublicKey, PSSignature, PSSignatureParams } from '../ps'; +import { + SaverChunkedCommitmentKey, + SaverChunkedCommitmentKeyUncompressed, + SaverEncryptionKey, + SaverEncryptionKeyUncompressed, + SaverProvingKey, + SaverProvingKeyUncompressed, + SaverVerifyingKey, + SaverVerifyingKeyUncompressed +} from '../saver'; +import { SetupParamsTracker } from './setup-params-tracker'; import { AccumulatorVerificationParam, AttributeEquality, @@ -10,30 +27,15 @@ import { FlattenedSchema, MEM_CHECK_KV_STR, MEM_CHECK_STR, + NON_MEM_CHECK_KV_STR, PredicateParamType, PS_SIGNATURE_PARAMS_LABEL_BYTES, PublicKey, + RevocationStatusProtocol, Signature, SignatureParams, SignatureParamsClass } from './types-and-consts'; -import { - SaverChunkedCommitmentKey, - SaverChunkedCommitmentKeyUncompressed, - SaverEncryptionKey, - SaverEncryptionKeyUncompressed, - SaverProvingKey, - SaverProvingKeyUncompressed, - SaverVerifyingKey, - SaverVerifyingKeyUncompressed -} from '../saver'; -import { flatten } from 'flat'; -import { BBSPlusPublicKeyG2, BBSPlusSignatureG1, BBSPlusSignatureParamsG1 } from '../bbs-plus'; -import { SetupParam, Statement, Witness, WitnessEqualityMetaStatement } from '../composite-proof'; -import { SetupParamsTracker } from './setup-params-tracker'; -import { BBSPublicKey, BBSSignature, BBSSignatureParams } from '../bbs'; -import { PSPublicKey, PSSignature, PSSignatureParams } from '../ps'; -import { BDDT16Mac, BDDT16MacParams, BDDT16MacSecretKey } from '../bddt16-mac'; export function isValueDate(value: string): boolean { // YYYY-MM-DD @@ -321,10 +323,10 @@ export function buildSignatureProverStatementFromParamsRef( } return setupPK !== undefined - // @ts-ignore - ? buildStatement(setupParamsTrk.add(setupParams), setupParamsTrk.add(setupPK), revealedMessages, false) - // @ts-ignore - : buildStatement(setupParamsTrk.add(setupParams), revealedMessages, false); + ? // @ts-ignore + buildStatement(setupParamsTrk.add(setupParams), setupParamsTrk.add(setupPK), revealedMessages, false) + : // @ts-ignore + buildStatement(setupParamsTrk.add(setupParams), revealedMessages, false); } /** @@ -401,59 +403,6 @@ export const getSignatureParamsForMsgCount = ( return sigParamsEntry.params; }; -export function accumulatorStatement( - credIndex: number, - checkType: string, - accumulated: Uint8Array, - setupParamsTrk: SetupParamsTracker, - vk?: AccumulatorVerificationParam -): Uint8Array { - let statement: Uint8Array; - if (!setupParamsTrk.hasAccumulatorParams()) { - setupParamsTrk.addAccumulatorParams(); - } - if (checkType === MEM_CHECK_KV_STR) { - if (vk === undefined) { - statement = Statement.vbAccumulatorMembershipKV(accumulated); - } else { - if (vk instanceof AccumulatorSecretKey) { - statement = Statement.vbAccumulatorMembershipKVFullVerifier(vk, accumulated); - } else { - throw new Error( - `Unexpected accumulator verification param ${vk.constructor.name} passed for credential index ${credIndex}` - ); - } - } - } else { - if (!(vk instanceof AccumulatorPublicKey)) { - throw new Error(`Accumulator public key wasn't provided for credential index ${credIndex}`); - } - if (checkType === MEM_CHECK_STR) { - if (!setupParamsTrk.hasAccumulatorMemProvingKey()) { - setupParamsTrk.addAccumulatorMemProvingKey(); - } - statement = Statement.vbAccumulatorMembershipFromSetupParamRefs( - setupParamsTrk.accumParamsIdx, - setupParamsTrk.add(SetupParam.vbAccumulatorPublicKey(vk)), - setupParamsTrk.memPrkIdx, - accumulated - ); - } else { - if (!setupParamsTrk.hasAccumulatorNonMemProvingKey()) { - setupParamsTrk.addAccumulatorNonMemProvingKey(); - } - statement = Statement.vbAccumulatorNonMembershipFromSetupParamRefs( - setupParamsTrk.accumParamsIdx, - setupParamsTrk.add(SetupParam.vbAccumulatorPublicKey(vk)), - setupParamsTrk.nonMemPrkIdx, - accumulated - ); - } - } - - return statement; -} - export function saverStatement( forProver: boolean, chunkBitSize: number, diff --git a/src/bddt16-mac/mac.ts b/src/bddt16-mac/mac.ts index 5364a69..d922e84 100644 --- a/src/bddt16-mac/mac.ts +++ b/src/bddt16-mac/mac.ts @@ -96,10 +96,13 @@ export class BDDT16Mac extends MessageEncoder { const sigParams = BDDT16MacParams.getMacParamsOfRequiredSize(encodedMessageList.length, labelOrParams); const signature = BDDT16Mac.generate(encodedMessageList, secretKey, sigParams, false); const proof = new BDDT16MacProofOfValidity(signature, secretKey, publicKey, sigParams); - return [{ - encodedMessages, - signature - }, proof]; + return [ + { + encodedMessages, + signature + }, + proof + ]; } /** diff --git a/src/composite-proof/proof.ts b/src/composite-proof/proof.ts index 20625cf..34d881b 100644 --- a/src/composite-proof/proof.ts +++ b/src/composite-proof/proof.ts @@ -12,7 +12,12 @@ import { verifyCompositeProofG1 as verifyCompositeProofG1Old, verifyCompositeProofG1WithDeconstructedProofSpec as verifyCompositeProofG1WithDeconstructedProofSpecOld } from 'crypto-wasm-old'; -import { BDDT16DelegatedProof, VBAccumMembershipDelegatedProof } from '../delegated-proofs'; +import { + BDDT16DelegatedProof, + KBUniAccumMembershipDelegatedProof, + KBUniAccumNonMembershipDelegatedProof, + VBAccumMembershipDelegatedProof +} from '../delegated-proofs'; import { MetaStatements, Statements } from './statement'; import { Witnesses } from './witness'; import { SetupParam } from './setup-param'; @@ -40,7 +45,6 @@ export class CompositeProof extends BytearrayWrapper { * @param proofSpec * @param witnesses * @param nonce - * @param useNewVersion */ static generateUsingQuasiProofSpec( proofSpec: QuasiProofSpec, @@ -153,8 +157,20 @@ export class CompositeProof extends BytearrayWrapper { * Get delegated proofs from a composite proof. * @returns - The key in the returned map is the statement index */ - getDelegatedProofs(): Map { - const r = new Map(); + getDelegatedProofs(): Map< + number, + | BDDT16DelegatedProof + | VBAccumMembershipDelegatedProof + | KBUniAccumMembershipDelegatedProof + | KBUniAccumNonMembershipDelegatedProof + > { + const r = new Map< + number, + | BDDT16DelegatedProof + | VBAccumMembershipDelegatedProof + | KBUniAccumMembershipDelegatedProof + | KBUniAccumNonMembershipDelegatedProof + >(); const delgProofs = getAllDelegatedSubproofsFromProof(this.value); for (const [i, [t, v]] of delgProofs.entries()) { let cls; @@ -162,6 +178,10 @@ export class CompositeProof extends BytearrayWrapper { cls = BDDT16DelegatedProof; } else if (t === 1) { cls = VBAccumMembershipDelegatedProof; + } else if (t === 2) { + cls = KBUniAccumMembershipDelegatedProof; + } else if (t === 3) { + cls = KBUniAccumNonMembershipDelegatedProof; } else { throw new Error(`Unknown type ${t} of delegated proof for credential index ${i}`); } diff --git a/src/composite-proof/statement.ts b/src/composite-proof/statement.ts index f661701..219d46a 100644 --- a/src/composite-proof/statement.ts +++ b/src/composite-proof/statement.ts @@ -44,7 +44,17 @@ import { generatePoKBDDT16MacFullVerifierStatement, generatePoKBDDT16MacFullVerifierStatementFromParamRefs, generateAccumulatorKVFullVerifierMembershipStatement, - generateAccumulatorKVMembershipStatement + generateAccumulatorKVMembershipStatement, + generateKBUniversalAccumulatorKVNonMembershipStatement, + generateKBUniversalAccumulatorKVFullVerifierNonMembershipStatement, + generateKBUniversalAccumulatorKVFullVerifierMembershipStatement, + generateKBUniversalAccumulatorKVMembershipStatement, + generateKBUniversalAccumulatorMembershipProverStatement, + generateKBUniversalAccumulatorMembershipVerifierStatement, + generateKBUniversalAccumulatorMembershipVerifierStatementFromParamRefs, + generateKBUniversalAccumulatorNonMembershipProverStatement, + generateKBUniversalAccumulatorNonMembershipVerifierStatement, + generateKBUniversalAccumulatorNonMembershipVerifierStatementFromParamRefs } from 'crypto-wasm-new'; // import { generatePoKBBSSignatureStatement, generatePoKBBSPlusSignatureStatement, generatePoKBBSSignatureStatementFromParamRefs, generatePoKBBSPlusSignatureStatementFromParamRefs } from 'crypto-wasm-old/lib/composite_proof_system_wasm'; // @ts-ignore @@ -354,7 +364,7 @@ export class Statement { } /** - * Create statement for proving knowledge of accumulator membership + * Create statement for proving knowledge of VB accumulator membership * @param params * @param publicKey * @param provingKey @@ -387,13 +397,13 @@ export class Statement { } /** - * Create statement for proving knowledge of accumulator non-membership + * Create statement for proving knowledge of VB accumulator non-membership * @param params * @param publicKey * @param provingKey * @param accumulated */ - static vccumulatorNonMembership( + static vbAccumulatorNonMembership( params: AccumulatorParams, publicKey: AccumulatorPublicKey, provingKey: NonMembershipProvingKey, @@ -427,6 +437,102 @@ export class Statement { return generateAccumulatorKVFullVerifierMembershipStatement(secretKey.value, accumulated); } + /** + * Create statement for proving knowledge of KB universal accumulator membership + * @param accumulated + */ + static kbUniAccumulatorMembershipProver(accumulated: Uint8Array): Uint8Array { + return generateKBUniversalAccumulatorMembershipProverStatement(accumulated); + } + + /** + * Create statement for verifying knowledge of KB universal accumulator membership + * @param params + * @param publicKey + * @param accumulated + */ + static kbUniAccumulatorMembershipVerifier( + params: AccumulatorParams, + publicKey: AccumulatorPublicKey, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorMembershipVerifierStatement(params.value, publicKey.value, accumulated); + } + + /** + * Same as `Statement.kbUniAccumulatorMembershipVerifier` but does not take the parameters directly but a reference to them as indices in the + * array of `SetupParam` + * @param params + * @param publicKey + * @param accumulated + */ + static kbUniAccumulatorMembershipVerifierFromSetupParamRefs( + params: number, + publicKey: number, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorMembershipVerifierStatementFromParamRefs(params, publicKey, accumulated); + } + + /** + * Create statement for proving knowledge of ΒΈ non-membership + * @param accumulated + */ + static kbUniAccumulatorNonMembershipProver(accumulated: Uint8Array): Uint8Array { + return generateKBUniversalAccumulatorNonMembershipProverStatement(accumulated); + } + + /** + * Create statement for verifying knowledge of KB universal accumulator non-membership + * @param params + * @param publicKey + * @param accumulated + */ + static kbUniAccumulatorNonMembershipVerifier( + params: AccumulatorParams, + publicKey: AccumulatorPublicKey, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorNonMembershipVerifierStatement(params.value, publicKey.value, accumulated); + } + + /** + * Same as `Statement.kbUniAccumulatorNonMembershipVerifier` but does not take the parameters directly but a reference to them as indices in the + * array of `SetupParam` + * @param params + * @param publicKey + * @param accumulated + */ + static kbUniAccumulatorNonMembershipVerifierFromSetupParamRefs( + params: number, + publicKey: number, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorNonMembershipVerifierStatementFromParamRefs(params, publicKey, accumulated); + } + + static kbUniAccumulatorMembershipKV(accumulated: Uint8Array): Uint8Array { + return generateKBUniversalAccumulatorKVMembershipStatement(accumulated); + } + + static kbUniAccumulatorMembershipKVFullVerifier( + secretKey: AccumulatorSecretKey, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorKVFullVerifierMembershipStatement(secretKey.value, accumulated); + } + + static kbUniAccumulatorNonMembershipKV(accumulated: Uint8Array): Uint8Array { + return generateKBUniversalAccumulatorKVNonMembershipStatement(accumulated); + } + + static kbUniAccumulatorNonMembershipKVFullVerifier( + secretKey: AccumulatorSecretKey, + accumulated: Uint8Array + ): Uint8Array { + return generateKBUniversalAccumulatorKVFullVerifierNonMembershipStatement(secretKey.value, accumulated); + } + /** * Create statement for verifiable encryption of a message using SAVER, for the prover. Accepts the parameters in uncompressed form. * @param encGens diff --git a/src/composite-proof/witness.ts b/src/composite-proof/witness.ts index 04cc830..b40fb95 100644 --- a/src/composite-proof/witness.ts +++ b/src/composite-proof/witness.ts @@ -12,8 +12,11 @@ import { generateBoundCheckSmcWitness, generateBoundCheckSmcWithKVWitness, generatePublicInequalityWitness, - generatePoKBDDT16MacWitness + generatePoKBDDT16MacWitness, + generateKBUniversalAccumulatorNonMembershipWitness, + generateKBUniversalAccumulatorMembershipWitness } from 'crypto-wasm-new'; +import { KBUniversalMembershipWitness, KBUniversalNonMembershipWitness } from '../accumulator/kb-acccumulator-witness'; import { BBSPlusSignatureG1 } from '../bbs-plus'; import { VBMembershipWitness, VBNonMembershipWitness } from '../accumulator'; import { CircomInputs } from '../r1cs'; @@ -75,7 +78,7 @@ export class Witness { } /** - * Accumulator member and its witness + * VB Accumulator member and its witness * @param member * @param accumulatorWitness */ @@ -84,7 +87,7 @@ export class Witness { } /** - * Accumulator non-member and its witness + * VB Accumulator non-member and its witness * @param nonMember * @param accumulatorWitness */ @@ -92,6 +95,27 @@ export class Witness { return generateAccumulatorNonMembershipWitness(nonMember, accumulatorWitness.value); } + /** + * KB universal Accumulator member and its witness + * @param member + * @param accumulatorWitness + */ + static kbUniAccumulatorMembership(member: Uint8Array, accumulatorWitness: KBUniversalMembershipWitness): Uint8Array { + return generateKBUniversalAccumulatorMembershipWitness(member, accumulatorWitness.value); + } + + /** + * KB universal Accumulator non-member and its witness + * @param nonMember + * @param accumulatorWitness + */ + static kbUniAccumulatorNonMembership( + nonMember: Uint8Array, + accumulatorWitness: KBUniversalNonMembershipWitness + ): Uint8Array { + return generateKBUniversalAccumulatorNonMembershipWitness(nonMember, accumulatorWitness.value); + } + /** * Witness for verifiable encryption using SAVER * @param message - Message being encrypted diff --git a/src/delegated-proofs.ts b/src/delegated-proofs.ts index 5552b32..bcc353c 100644 --- a/src/delegated-proofs.ts +++ b/src/delegated-proofs.ts @@ -1,4 +1,10 @@ -import { VerifyResult, verifyBDDT16DelegatedProof, verifyVBAccumMembershipDelegatedProof } from 'crypto-wasm-new'; +import { + VerifyResult, + verifyBDDT16DelegatedProof, + verifyVBAccumMembershipDelegatedProof, + verifyKBUniAccumMembershipDelegatedProof, + verifyKBUniAccumNonMembershipDelegatedProof +} from 'crypto-wasm-new'; import { AccumulatorSecretKey } from './accumulator'; import { BDDT16MacSecretKey } from './bddt16-mac'; import { BytearrayWrapper } from './bytearray-wrapper'; @@ -20,3 +26,21 @@ export class VBAccumMembershipDelegatedProof extends BytearrayWrapper { return verifyVBAccumMembershipDelegatedProof(this.value, secretKey.value); } } + +/** + * Delegated proof of membership in keyed-verification of KB universal accumulator. + */ +export class KBUniAccumMembershipDelegatedProof extends BytearrayWrapper { + verify(secretKey: AccumulatorSecretKey): VerifyResult { + return verifyKBUniAccumMembershipDelegatedProof(this.value, secretKey.value); + } +} + +/** + * Delegated proof of non-membership in keyed-verification of KB universal accumulator. + */ +export class KBUniAccumNonMembershipDelegatedProof extends BytearrayWrapper { + verify(secretKey: AccumulatorSecretKey): VerifyResult { + return verifyKBUniAccumNonMembershipDelegatedProof(this.value, secretKey.value); + } +} diff --git a/tests/accumulator.spec.ts b/tests/accumulator.spec.ts index 945f56d..5a17a4f 100644 --- a/tests/accumulator.spec.ts +++ b/tests/accumulator.spec.ts @@ -6,89 +6,117 @@ import { PositiveAccumulator, UniversalAccumulator, VBMembershipWitness, - VBWitnessUpdatePublicInfo, + VBWitnessUpdateInfo, AccumulatorParams, - AccumulatorKeypair + AccumulatorKeypair, } from '../src'; import { - InMemoryInitialElementsStore, + InMemoryInitialElementsStore, InMemoryKBUniversalState, InMemoryState, InMemoryUniversalState } from '../src/accumulator/in-memory-persistence'; -import { stringToBytes } from './utils'; +import { KBUniversalMembershipWitness } from '../src/accumulator/kb-acccumulator-witness'; +import { KBUniversalAccumulator } from '../src/accumulator/kb-universal-accumulator'; +import { KBUniversalMembershipWitnessUpdateInfo } from '../src/accumulator/witness-update-info'; +import { areUint8ArraysEqual, stringToBytes } from './utils'; -function getAccum(accumulator: any): PositiveAccumulator | UniversalAccumulator { +function getAccum(accumulator: any): PositiveAccumulator | UniversalAccumulator | KBUniversalAccumulator { const accumulated = accumulator.accumulated; let tempAccumulator; if (accumulator instanceof PositiveAccumulator) { tempAccumulator = PositiveAccumulator.fromAccumulated(accumulated); - } else { + } else if (accumulator instanceof UniversalAccumulator) { tempAccumulator = UniversalAccumulator.fromAccumulated(accumulated); + } else { + tempAccumulator = KBUniversalAccumulator.fromAccumulated(accumulated); } return tempAccumulator; } -async function runCommonTests( +async function runCommonTestsForMembership( keypair: AccumulatorKeypair, params: AccumulatorParams, - accumulator: PositiveAccumulator | UniversalAccumulator, - state: InMemoryState, + accumulator: PositiveAccumulator | UniversalAccumulator | KBUniversalAccumulator, + state: InMemoryState | InMemoryKBUniversalState, + members: Uint8Array[], store?: IInitialElementsStore ) { const sk = keypair.sk; const pk = keypair.pk; - const e1 = Accumulator.encodePositiveNumberAsAccumulatorMember(101); - const e2 = Accumulator.encodePositiveNumberAsAccumulatorMember(102); + let witClass, witUpdClass; + if (accumulator instanceof PositiveAccumulator) { + witClass = VBMembershipWitness; + witUpdClass = VBWitnessUpdateInfo; + } else if (accumulator instanceof UniversalAccumulator) { + witClass = VBMembershipWitness; + witUpdClass = VBWitnessUpdateInfo; + } else { + witClass = KBUniversalMembershipWitness; + witUpdClass = KBUniversalMembershipWitnessUpdateInfo; + } + + const e1 = members.pop() as Uint8Array; + const e2 = members.pop() as Uint8Array; - expect(state.state.size).toEqual(0); + expect(state.size).toEqual(0); await expect(state.has(e1)).resolves.toEqual(false); + // @ts-ignore await accumulator.add(e1, sk, state, store); - expect(state.state.size).toEqual(1); + expect(state.size).toEqual(1); await expect(state.has(e1)).resolves.toEqual(true); + // @ts-ignore await expect(accumulator.add(e1, sk, state, store)).rejects.toThrow(); + // @ts-ignore await expect(accumulator.remove(e2, sk, state)).rejects.toThrow(); + // @ts-ignore await accumulator.add(e2, sk, state, store); - expect(state.state.size).toEqual(2); + expect(state.size).toEqual(2); await expect(state.has(e2)).resolves.toEqual(true); + // @ts-ignore await accumulator.remove(e2, sk, state, store); - expect(state.state.size).toEqual(1); + expect(state.size).toEqual(1); await expect(state.has(e2)).resolves.toEqual(false); - const e3 = Accumulator.encodePositiveNumberAsAccumulatorMember(103); - const e4 = Accumulator.encodePositiveNumberAsAccumulatorMember(104); + const e3 = members.pop() as Uint8Array; + const e4 = members.pop() as Uint8Array; + // @ts-ignore await accumulator.addBatch([e3, e4], sk, state, store); - expect(state.state.size).toEqual(3); + expect(state.size).toEqual(3); await expect(state.has(e3)).resolves.toEqual(true); await expect(state.has(e4)).resolves.toEqual(true); + // @ts-ignore await expect(accumulator.addBatch([e3, e4], sk, state, store)).rejects.toThrow(); - expect(state.state.size).toEqual(3); + expect(state.size).toEqual(3); + // @ts-ignore await accumulator.removeBatch([e3, e4], sk, state, store); - expect(state.state.size).toEqual(1); + expect(state.size).toEqual(1); await expect(state.has(e3)).resolves.toEqual(false); await expect(state.has(e4)).resolves.toEqual(false); + // @ts-ignore await expect(accumulator.removeBatch([e3, e4], sk, state, store)).rejects.toThrow(); - expect(state.state.size).toEqual(1); + expect(state.size).toEqual(1); - const e5 = Accumulator.encodePositiveNumberAsAccumulatorMember(105); - const e6 = Accumulator.encodePositiveNumberAsAccumulatorMember(106); + const e5 = members.pop() as Uint8Array; + const e6 = members.pop() as Uint8Array; + // @ts-ignore await accumulator.addRemoveBatches([e5, e6], [e1], sk, state, store); - expect(state.state.size).toEqual(2); + expect(state.size).toEqual(2); await expect(state.has(e5)).resolves.toEqual(true); await expect(state.has(e6)).resolves.toEqual(true); await expect(state.has(e1)).resolves.toEqual(false); @@ -97,51 +125,65 @@ async function runCommonTests( let tempAccumulator = getAccum(accumulator); expect( + // @ts-ignore tempAccumulator.verifyMembershipWitness(e5, await accumulator.membershipWitness(e5, sk, state), pk, params) ).toEqual(true); expect( + // @ts-ignore tempAccumulator.verifyMembershipWitness(e6, await accumulator.membershipWitness(e6, sk, state), pk, params) ).toEqual(true); + // @ts-ignore const wits = await accumulator.membershipWitnessesForBatch([e5, e6], sk, state); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, wits[0], pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, wits[1], pk, params)).toEqual(true); - const e7 = Accumulator.encodePositiveNumberAsAccumulatorMember(107); - const e8 = Accumulator.encodePositiveNumberAsAccumulatorMember(108); + const e7 = members.pop() as Uint8Array; + const e8 = members.pop() as Uint8Array; + // @ts-ignore await accumulator.addBatch([e7, e8], sk, state, store); // Witness updates by accumulator manager using secret key - const newWits = VBMembershipWitness.updateMultiplePostBatchUpdates(wits, [e5, e6], [e7, e8], [], accumulated, sk); + // @ts-ignore + const newWits = witClass.updateMultiplePostBatchUpdates(wits, [e5, e6], [e7, e8], [], accumulated, sk); tempAccumulator = getAccum(accumulator); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, newWits[0], pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, newWits[1], pk, params)).toEqual(true); // Witness update info created by accumulator manager - const witnessUpdInfo = VBWitnessUpdatePublicInfo.new(accumulated, [e7, e8], [], sk); + // @ts-ignore + const witnessUpdInfo = witUpdClass.new(accumulated, [e7, e8], [], sk); // Witness can be updated without secret key using public info wits[0].updateUsingPublicInfoPostBatchUpdate(e5, [e7, e8], [], witnessUpdInfo); wits[1].updateUsingPublicInfoPostBatchUpdate(e6, [e7, e8], [], witnessUpdInfo); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, wits[0], pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, wits[1], pk, params)).toEqual(true); + // @ts-ignore const e5Wit = await accumulator.membershipWitness(e5, sk, state); + // @ts-ignore const e6Wit = await accumulator.membershipWitness(e6, sk, state); - let e5WitTemp = new VBMembershipWitness(e5Wit.value); - let e6WitTemp = new VBMembershipWitness(e6Wit.value); + let e5WitTemp = new witClass(e5Wit.value); + let e6WitTemp = new witClass(e6Wit.value); - const e9 = Accumulator.encodePositiveNumberAsAccumulatorMember(109); - const e10 = Accumulator.encodePositiveNumberAsAccumulatorMember(110); - const e11 = Accumulator.encodePositiveNumberAsAccumulatorMember(111); - const e12 = Accumulator.encodePositiveNumberAsAccumulatorMember(112); - const e13 = Accumulator.encodePositiveNumberAsAccumulatorMember(113); - const e14 = Accumulator.encodePositiveNumberAsAccumulatorMember(114); - const e15 = Accumulator.encodePositiveNumberAsAccumulatorMember(115); + const e9 = members.pop() as Uint8Array; + const e10 = members.pop() as Uint8Array; + const e11 = members.pop() as Uint8Array; + const e12 = members.pop() as Uint8Array; + const e13 = members.pop() as Uint8Array; + const e14 = members.pop() as Uint8Array; + const e15 = members.pop() as Uint8Array; const additions = [ [e9, e10], @@ -150,31 +192,43 @@ async function runCommonTests( ]; const removals = [[e7, e8], [e9], []]; - const witUpd1 = VBWitnessUpdatePublicInfo.new(accumulator.accumulated, additions[0], removals[0], sk); + // @ts-ignore + const witUpd1 = witUpdClass.new(accumulator.accumulated, additions[0], removals[0], sk); + // @ts-ignore await accumulator.addRemoveBatches(additions[0], removals[0], sk, state); tempAccumulator = getAccum(accumulator); e5WitTemp.updateUsingPublicInfoPostBatchUpdate(e5, additions[0], removals[0], witUpd1); e6WitTemp.updateUsingPublicInfoPostBatchUpdate(e6, additions[0], removals[0], witUpd1); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, e5WitTemp, pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, e6WitTemp, pk, params)).toEqual(true); - const witUpd2 = VBWitnessUpdatePublicInfo.new(accumulator.accumulated, additions[1], removals[1], sk); + // @ts-ignore + const witUpd2 = witUpdClass.new(accumulator.accumulated, additions[1], removals[1], sk); + // @ts-ignore await accumulator.addRemoveBatches(additions[1], removals[1], sk, state); tempAccumulator = getAccum(accumulator); e5WitTemp.updateUsingPublicInfoPostBatchUpdate(e5, additions[1], removals[1], witUpd2); e6WitTemp.updateUsingPublicInfoPostBatchUpdate(e6, additions[1], removals[1], witUpd2); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, e5WitTemp, pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, e6WitTemp, pk, params)).toEqual(true); - const witUpd3 = VBWitnessUpdatePublicInfo.new(accumulator.accumulated, additions[2], removals[2], sk); + // @ts-ignore + const witUpd3 = witUpdClass.new(accumulator.accumulated, additions[2], removals[2], sk); + // @ts-ignore await accumulator.addRemoveBatches(additions[2], removals[2], sk, state); tempAccumulator = getAccum(accumulator); e5WitTemp.updateUsingPublicInfoPostBatchUpdate(e5, additions[2], removals[2], witUpd3); e6WitTemp.updateUsingPublicInfoPostBatchUpdate(e6, additions[2], removals[2], witUpd3); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, e5WitTemp, pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, e6WitTemp, pk, params)).toEqual(true); const witUpds = [witUpd1, witUpd2, witUpd3]; @@ -184,11 +238,14 @@ async function runCommonTests( tempAccumulator = getAccum(accumulator); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e5, e5Wit, pk, params)).toEqual(true); + // @ts-ignore expect(tempAccumulator.verifyMembershipWitness(e6, e6Wit, pk, params)).toEqual(true); } describe('Accumulators type', () => { + beforeAll(async () => { await initializeWasm(); }); @@ -196,14 +253,71 @@ describe('Accumulators type', () => { it('State update', async () => { const params = PositiveAccumulator.generateParams(); const keypair = PositiveAccumulator.generateKeypair(params); + const posAccumulator = PositiveAccumulator.initialize(params); - const state = new InMemoryState(); + const posState = new InMemoryState(); + + const uniState = new InMemoryKBUniversalState(); + const domain = [generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement()]; + const uniAccumulator = await KBUniversalAccumulator.initialize(domain, params, keypair.secretKey, uniState); + + for (const d of domain) { + expect(await uniState.inDomain(d)).toEqual(true); + } - const members1 = [generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement()]; - await posAccumulator.addBatch(members1, keypair.secretKey, state); + async function check(accum: PositiveAccumulator | KBUniversalAccumulator, state, members1: Uint8Array[], members2: Uint8Array[]) { + for (let i = 0; i < members1.length; i++) { + expect(await state.has(members1[i])).toEqual(false); + } + await accum.addBatch(members1, keypair.secretKey, state); + for (let i = 0; i < members1.length; i++) { + expect(await state.has(members1[i])).toEqual(true); + } + + for (let i = 0; i < members2.length; i++) { + expect(await state.has(members2[i])).toEqual(false); + } + await accum.addRemoveBatches(members2, members1, keypair.secretKey, state); + for (let i = 0; i < members2.length; i++) { + expect(await state.has(members2[i])).toEqual(true); + } + for (let i = 0; i < members1.length; i++) { + expect(await state.has(members1[i])).toEqual(false); + } + } + + await check(posAccumulator, posState, [generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement()], [generateRandomFieldElement(), generateRandomFieldElement()]); + await check(uniAccumulator, uniState, domain.slice(0, 3), domain.slice(3)); + + // Cannot add element not part of the domain + const oldSize = uniState.size; + const newElement = generateRandomFieldElement(); + expect(uniAccumulator.add(newElement, keypair.secretKey, uniState)).rejects.toThrow(); + expect(await uniState.inDomain(newElement)).toEqual(false); + expect(await uniState.has(newElement)).toEqual(false); + expect(uniState.size).toEqual(oldSize); + // Add to domain + await uniAccumulator.extend([newElement], keypair.secretKey, uniState); + // Now it works + await uniAccumulator.add(newElement, keypair.secretKey, uniState); + expect(await uniState.inDomain(newElement)).toEqual(true); + expect(await uniState.has(newElement)).toEqual(true); + expect(uniState.size).toEqual(oldSize + 1); + + // Cannot extend the domain with an existing element + expect(() => uniAccumulator.extend([newElement], keypair.secretKey, uniState)).rejects.toThrow(); + + const newElements = [generateRandomFieldElement(), generateRandomFieldElement(), generateRandomFieldElement()]; + for (const e of newElements) { + expect(await uniState.inDomain(e)).toEqual(false); + } + await uniAccumulator.extend(newElements, keypair.secretKey, uniState); + for (const e of newElements) { + expect(await uniState.inDomain(e)).toEqual(true); + } - const members2 = [generateRandomFieldElement(), generateRandomFieldElement()]; - await posAccumulator.addRemoveBatches(members2, members1, keypair.secretKey, state); + // Cannot extend the domain with existing elements + expect(() => uniAccumulator.extend(newElements, keypair.secretKey, uniState)).rejects.toThrow(); }); it('Positive accumulator', async () => { @@ -212,10 +326,11 @@ describe('Accumulators type', () => { const keypair = PositiveAccumulator.generateKeypair(params); const accumulator = PositiveAccumulator.initialize(params); const state = new InMemoryState(); - await runCommonTests(keypair, params, accumulator, state); + const domain = Array.from(Array(20).keys()).map((i) => Accumulator.encodePositiveNumberAsAccumulatorMember(100 + i)); + await runCommonTestsForMembership(keypair, params, accumulator, state, domain); }); - it('Universal accumulator', async () => { + it('VB universal accumulator', async () => { const params = UniversalAccumulator.generateParams(); const keypair = UniversalAccumulator.generateKeypair(params); const store = new InMemoryInitialElementsStore(); @@ -228,7 +343,8 @@ describe('Accumulators type', () => { await expect(store.has(i)).resolves.toEqual(true); } const state1 = new InMemoryUniversalState(); - await runCommonTests(keypair, params, accumulator1, state1, store); + const domain = Array.from(Array(20).keys()).map((i) => Accumulator.encodePositiveNumberAsAccumulatorMember(100 + i)); + await runCommonTestsForMembership(keypair, params, accumulator1, state1, domain, store); const nm1 = Accumulator.encodePositiveNumberAsAccumulatorMember(500); const nm1Wit = await accumulator1.nonMembershipWitness(nm1, state1, keypair.secretKey, params, store, 2); @@ -250,4 +366,36 @@ describe('Accumulators type', () => { expect(tempAccumulator.verifyNonMembershipWitness(nm2, nm2Wit, keypair.publicKey, params)).toEqual(true); expect(tempAccumulator.verifyNonMembershipWitness(nm3, nm3Wit, keypair.publicKey, params)).toEqual(true); }); + + it('KB universal accumulator', async () => { + const label = stringToBytes('Accumulator params'); + const params = KBUniversalAccumulator.generateParams(label); + const keypair = KBUniversalAccumulator.generateKeypair(params); + const state = new InMemoryKBUniversalState(); + const domain = Array.from(Array(20).keys()).map((i) => Accumulator.encodePositiveNumberAsAccumulatorMember(100 + i)); + const accumulator = await KBUniversalAccumulator.initialize(domain, params, keypair.secretKey, state); + await runCommonTestsForMembership(keypair, params, accumulator, state, domain); + + async function check(nonMember: Uint8Array) { + expect(await state.has(nonMember)).toEqual(false); + expect(await state.inDomain(nonMember)).toEqual(true); + const wit = await accumulator.nonMembershipWitness(nonMember, keypair.secretKey, state); + let tempAccumulator = getAccum(accumulator) as KBUniversalAccumulator; + expect(tempAccumulator.verifyNonMembershipWitness(nonMember, wit, keypair.publicKey, params)).toEqual(true); + return wit + } + + const nm1Wit = await check(domain[0]); + const nm2Wit = await check(domain[1]); + const nm3Wit = await check(domain[2]); + + const [nm1Wit_, nm2Wit_, nm3Wit_] = await accumulator.nonMembershipWitnessesForBatch( + [domain[0], domain[1], domain[2]], + keypair.secretKey, + state, + ); + expect(areUint8ArraysEqual(nm1Wit.value, nm1Wit_.value)).toEqual(true); + expect(areUint8ArraysEqual(nm2Wit.value, nm2Wit_.value)).toEqual(true); + expect(areUint8ArraysEqual(nm3Wit.value, nm3Wit_.value)).toEqual(true); + }); }); diff --git a/tests/anonymous-credentials/blind-issuance.spec.ts b/tests/anonymous-credentials/blind-issuance.spec.ts index d171269..411d428 100644 --- a/tests/anonymous-credentials/blind-issuance.spec.ts +++ b/tests/anonymous-credentials/blind-issuance.spec.ts @@ -264,7 +264,7 @@ skipIfPS.each([true, false])(`${Scheme} Blind issuance of credentials with withS accumulator1Sk = accumKeypair1.secretKey; accumulator1 = PositiveAccumulator.initialize(dockAccumulatorParams()); accumulator1State = new InMemoryState(); - accumulator1Members = await prefillAccumulator( + accumulator1Members = await prefillAccumulator( accumulator1, accumKeypair1.secretKey, accumulator1State, diff --git a/tests/anonymous-credentials/delegated-proofs.spec.ts b/tests/anonymous-credentials/delegated-proofs.spec.ts index e961781..203332c 100644 --- a/tests/anonymous-credentials/delegated-proofs.spec.ts +++ b/tests/anonymous-credentials/delegated-proofs.spec.ts @@ -1,20 +1,42 @@ import { VerifyResult } from 'crypto-wasm-new'; import { AccumulatorPublicKey, - AccumulatorSecretKey, BDDT16Credential, BDDT16CredentialBuilder, CredentialSchema, DelegatedProof, ID_STR, - initializeWasm, MEM_CHECK_KV_STR, MEM_CHECK_STR, - PositiveAccumulator, Presentation, + AccumulatorSecretKey, + BDDT16Credential, + BDDT16CredentialBuilder, + BDDT16MacSecretKey, + CredentialSchema, + DelegatedProof, + dockAccumulatorParams, + ID_STR, + initializeWasm, + MEM_CHECK_KV_STR, + MEM_CHECK_STR, + NON_MEM_CHECK_KV_STR, + PositiveAccumulator, + Presentation, PresentationBuilder, REV_CHECK_STR, RevocationStatusProtocol, SignatureType, TYPE_STR, - VBMembershipWitness, - BDDT16MacSecretKey + VBMembershipWitness } from '../../src'; +import { + KBUniversalMembershipWitness, + KBUniversalNonMembershipWitness +} from '../../src/accumulator/kb-acccumulator-witness'; +import { KBUniversalAccumulator } from '../../src/accumulator/kb-universal-accumulator'; import { Credential, CredentialBuilder, isKvac, isPS, PublicKey, Scheme, SecretKey } from '../scheme'; import { checkResult } from '../utils'; -import { checkPresentationJson, getExampleSchema, getKeys, setupPrefilledAccum, verifyCred } from './utils'; +import { + checkPresentationJson, + getExampleSchema, + getKeys, + setupKBUniAccumulator, + setupPrefilledAccum, + verifyCred +} from './utils'; describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures`, () => { let sk: SecretKey, pk: PublicKey; @@ -25,6 +47,8 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` let credential3: Credential; let credential4: BDDT16Credential; let credential5: BDDT16Credential; + let credential6: Credential; + let credential7: Credential; // Accumulator where membership is publicly verifiable let accumulator1: PositiveAccumulator; @@ -36,6 +60,12 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` let accumulator2Sk: AccumulatorSecretKey; let accumulator2Witness: VBMembershipWitness; + // Accumulator where membership and non-membership verification needs secret key + let accumulator3: KBUniversalAccumulator; + let accumulator3Sk: AccumulatorSecretKey; + let accumulator3MemWitness: KBUniversalMembershipWitness; + let accumulator3NonMemWitness: KBUniversalNonMembershipWitness; + beforeAll(async () => { await initializeWasm(); @@ -128,11 +158,33 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` // @ts-ignore [accumulator2Sk, , accumulator2, accumulator2Witness] = await setupPrefilledAccum(200, 123, 'user:A-', schema); + + let others; + // @ts-ignore + [accumulator3Sk, , accumulator3, ...others] = await setupKBUniAccumulator(100, 'user:A-', schema); + const [domain, state] = others; + + const builder6 = new CredentialBuilder(); + builder6.schema = schema; + builder6.subject = subject; + builder6.setCredentialStatus('dock:accumulator:accumId125', MEM_CHECK_KV_STR, 'user:A-25', RevocationStatusProtocol.KbUni24); + credential6 = builder6.sign(sk); + await accumulator3.add(domain[24], accumulator3Sk, state); + accumulator3MemWitness = await accumulator3.membershipWitness(domain[24], accumulator3Sk, state); + verifyCred(credential6, pk, sk); + + const builder7 = new CredentialBuilder(); + builder7.schema = schema; + builder7.subject = subject; + builder7.setCredentialStatus('dock:accumulator:accumId125', NON_MEM_CHECK_KV_STR, 'user:A-26', RevocationStatusProtocol.KbUni24); + credential7 = builder7.sign(sk); + accumulator3NonMemWitness = await accumulator3.nonMembershipWitness(domain[25], accumulator3Sk, state); + verifyCred(credential6, pk, sk); }); it('works', () => { - // Describes a test with 5 credentials. Credentials 1, 2, and 3 are non-KVAC and 4 and 5 is KVAC. - // Status verification of credential 1 and credential 4 requires public key but for credential 2 and credential 5 requires secret key + // Describes a test with 7 credentials. Credentials 1, 2, 3, 6, 7 are non-KVAC and 4 and 5 is KVAC. + // Status verification of credential 1 and credential 4 requires public key but for credential 2, 5, 6, 7 requires secret key const builder = new PresentationBuilder(); @@ -141,6 +193,8 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` expect(builder.addCredential(credential3, isPS() ? pk : undefined)).toEqual(2); expect(builder.addCredential(credential4)).toEqual(3); expect(builder.addCredential(credential5)).toEqual(4); + expect(builder.addCredential(credential6, isPS() ? pk : undefined)).toEqual(5); + expect(builder.addCredential(credential7, isPS() ? pk : undefined)).toEqual(6); builder.addAccumInfoForCredStatus(0, accumulator1Witness, accumulator1.accumulated, accumulator1Pk, { blockNo: 2010334 @@ -154,6 +208,12 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` builder.addAccumInfoForCredStatus(4, accumulator2Witness, accumulator2.accumulated, undefined, { blockNo: 2010337 }); + builder.addAccumInfoForCredStatus(5, accumulator3MemWitness, accumulator3.accumulated, undefined, { + blockNo: 2010338 + }); + builder.addAccumInfoForCredStatus(6, accumulator3NonMemWitness, accumulator3.accumulated, undefined, { + blockNo: 2010339 + }); const pres = builder.finalize(); @@ -161,6 +221,8 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` pks.set(0, pk); pks.set(1, pk); pks.set(2, pk); + pks.set(5, pk); + pks.set(6, pk); const accumPks = new Map(); accumPks.set(0, accumulator1Pk); accumPks.set(3, accumulator1Pk); @@ -173,6 +235,8 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` pks.set(4, skKvac); accumPks.set(1, accumulator2Sk); accumPks.set(4, accumulator2Sk); + accumPks.set(5, accumulator3Sk); + accumPks.set(6, accumulator3Sk); checkResult(pres.verify(pks, accumPks)); let recreatedPres = checkPresentationJson(pres, pks, accumPks); @@ -232,8 +296,32 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` checkResult(delgCredProof?.status?.proof.verify(accumulator2Sk) as VerifyResult); } + function check5(delgCredProof?: DelegatedProof) { + if (!isKvac()) { + expect(delgCredProof?.credential).not.toBeDefined(); + } + expect(delgCredProof?.status).toMatchObject({ + [ID_STR]: 'dock:accumulator:accumId125', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + [REV_CHECK_STR]: MEM_CHECK_KV_STR + }); + checkResult(delgCredProof?.status?.proof.verify(accumulator3Sk) as VerifyResult); + } + + function check6(delgCredProof?: DelegatedProof) { + if (!isKvac()) { + expect(delgCredProof?.credential).not.toBeDefined(); + } + expect(delgCredProof?.status).toMatchObject({ + [ID_STR]: 'dock:accumulator:accumId125', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + [REV_CHECK_STR]: NON_MEM_CHECK_KV_STR + }); + checkResult(delgCredProof?.status?.proof.verify(accumulator3Sk) as VerifyResult); + } + const delegatedProofs = presentation.getDelegatedProofs(); - expect(delegatedProofs.size).toEqual(isKvac() ? 5 : 3); + expect(delegatedProofs.size).toEqual(isKvac() ? 7 : 5); if (isKvac()) { for (let i = 0; i < 3; i++) { @@ -254,6 +342,14 @@ describe(`Delegated proof verification with BDDT16 MAC and ${Scheme} signatures` const delgCredProof5 = delegatedProofs.get(4); check4(delgCredProof5); checkSerialized(check4, delgCredProof5); + + const delgCredProof6 = delegatedProofs.get(5); + check5(delgCredProof6); + checkSerialized(check5, delgCredProof6); + + const delgCredProof7 = delegatedProofs.get(6); + check6(delgCredProof7); + checkSerialized(check6, delgCredProof7); } }) }) \ No newline at end of file diff --git a/tests/anonymous-credentials/presentation-status-kb-accumulator.spec.ts b/tests/anonymous-credentials/presentation-status-kb-accumulator.spec.ts new file mode 100644 index 0000000..d64e5c7 --- /dev/null +++ b/tests/anonymous-credentials/presentation-status-kb-accumulator.spec.ts @@ -0,0 +1,206 @@ +import { + AccumulatorPublicKey, + AccumulatorSecretKey, + CredentialSchema, dockAccumulatorParams, + initializeWasm, + MEM_CHECK_STR, NON_MEM_CHECK_STR, + PresentationBuilder, + RevocationStatusProtocol, TYPE_STR +} from '../../src'; +import { + KBUniversalMembershipWitness, + KBUniversalNonMembershipWitness +} from '../../src/accumulator/kb-acccumulator-witness'; +import { KBUniversalAccumulator } from '../../src/accumulator/kb-universal-accumulator'; +import { Credential, CredentialBuilder, isPS, PublicKey, Scheme, SecretKey } from '../scheme'; +import { checkResult } from '../utils'; +import { + checkPresentationJson, + checkSchemaFromJson, + getExampleSchema, + getKeys, + setupKBUniAccumulator, + verifyCred +} from './utils'; + +describe(`Presentation of ${Scheme} credential with KB universal accumulator`, () => { + let sk1: SecretKey, pk1: PublicKey; + let credential1: Credential; + let credential2: Credential; + let accumulator: KBUniversalAccumulator; + let accumulatorSk: AccumulatorSecretKey; + let accumulatorPk: AccumulatorPublicKey; + let memWitness: KBUniversalMembershipWitness; + let nonMemWitness: KBUniversalNonMembershipWitness; + + beforeAll(async () => { + await initializeWasm(); + + [sk1, pk1] = getKeys('seed1'); + const schema1 = new CredentialSchema(getExampleSchema(5)); + + let others; + // @ts-ignore + [accumulatorSk, accumulatorPk, accumulator, ...others] = await setupKBUniAccumulator(100, 'user:A-', schema1); + const [domain, state] = others; + + const subject = { + fname: 'John', + lname: 'Smith', + sensitive: { + very: { + secret: 'my-secret-that-wont-tell-anyone' + }, + email: 'john.smith@acme.com', + phone: '801009801', + SSN: '123-456789-0' + }, + lessSensitive: { + location: { + country: 'USA', + city: 'New York' + }, + department: { + name: 'Random', + location: { + name: 'Somewhere', + geo: { + lat: -23.658, + long: 2.556 + } + } + } + }, + rank: 6 + }; + + const builder1 = new CredentialBuilder(); + builder1.schema = schema1; + builder1.subject = subject; + builder1.setCredentialStatus('dock:accumulator:accumId123', MEM_CHECK_STR, 'user:A-23', RevocationStatusProtocol.KbUni24); + credential1 = builder1.sign(sk1); + await accumulator.add(domain[22], accumulatorSk, state); + memWitness = await accumulator.membershipWitness(domain[22], accumulatorSk, state); + verifyCred(credential1, pk1, sk1); + expect( + accumulator.verifyMembershipWitness( + domain[22], + memWitness, + accumulatorPk, + dockAccumulatorParams() + ) + ).toEqual(true); + + const builder2 = new CredentialBuilder(); + builder2.schema = schema1; + builder2.subject = subject; + builder2.setCredentialStatus('dock:accumulator:accumId123', NON_MEM_CHECK_STR, 'user:A-30', RevocationStatusProtocol.KbUni24); + credential2 = builder2.sign(sk1); + + nonMemWitness = await accumulator.nonMembershipWitness(domain[29], accumulatorSk, state); + verifyCred(credential2, pk1, sk1); + expect( + accumulator.verifyNonMembershipWitness( + domain[29], + nonMemWitness, + accumulatorPk, + dockAccumulatorParams() + ) + ).toEqual(true); + }) + + it('from 1 credential having credential status with a membership proof', () => { + const builder = new PresentationBuilder(); + builder.addCredential(credential1, isPS() ? pk1 : undefined); + builder.addAccumInfoForCredStatus(0, memWitness, accumulator.accumulated, accumulatorPk, { + blockNo: 100 + }); + const pres = builder.finalize(); + // This check is made by the verifier, i.e. verifier checks that the accumulator id, type, value and timestamp (`blockNo`) + // are as expected + expect(pres.spec.getStatus(0)).toEqual({ + id: 'dock:accumulator:accumId123', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + revocationCheck: MEM_CHECK_STR, + accumulated: accumulator.accumulated, + extra: { blockNo: 100 } + }); + + // Verifier passes the accumulator public key for verification + const acc = new Map(); + acc.set(0, accumulatorPk); + checkResult(pres.verify([pk1], acc)); + + const presJson = pres.toJSON(); + + // The schema of the credential in the presentation matches the JSON-schema + // @ts-ignore + checkSchemaFromJson(presJson.spec.credentials[0].schema, credential1.schema); + + checkPresentationJson(pres, [pk1], acc); + }) + + it('from 1 credential having credential status with a non-membership proof', () => { + const builder = new PresentationBuilder(); + builder.addCredential(credential2, isPS() ? pk1 : undefined); + builder.addAccumInfoForCredStatus(0, nonMemWitness, accumulator.accumulated, accumulatorPk, { + blockNo: 100 + }); + const pres = builder.finalize(); + // This check is made by the verifier, i.e. verifier checks that the accumulator id, type, value and timestamp (`blockNo`) + // are as expected + expect(pres.spec.getStatus(0)).toEqual({ + id: 'dock:accumulator:accumId123', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + revocationCheck: NON_MEM_CHECK_STR, + accumulated: accumulator.accumulated, + extra: { blockNo: 100 } + }); + + // Verifier passes the accumulator public key for verification + const acc = new Map(); + acc.set(0, accumulatorPk); + checkResult(pres.verify([pk1], acc)); + + checkPresentationJson(pres, [pk1], acc); + }) + + it('from 2 credentials, one with status with a membership proof and another status with a non-membership proof', () => { + const builder = new PresentationBuilder(); + builder.addCredential(credential1, isPS() ? pk1 : undefined); + builder.addAccumInfoForCredStatus(0, memWitness, accumulator.accumulated, accumulatorPk, { + blockNo: 100 + }); + builder.addCredential(credential2, isPS() ? pk1 : undefined); + builder.addAccumInfoForCredStatus(1, nonMemWitness, accumulator.accumulated, accumulatorPk, { + blockNo: 200 + }); + const pres = builder.finalize(); + + // This check is made by the verifier, i.e. verifier checks that the accumulator id, type, value and timestamp (`blockNo`) + // are as expected + expect(pres.spec.getStatus(0)).toEqual({ + id: 'dock:accumulator:accumId123', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + revocationCheck: MEM_CHECK_STR, + accumulated: accumulator.accumulated, + extra: { blockNo: 100 } + }); + expect(pres.spec.getStatus(1)).toEqual({ + id: 'dock:accumulator:accumId123', + [TYPE_STR]: RevocationStatusProtocol.KbUni24, + revocationCheck: NON_MEM_CHECK_STR, + accumulated: accumulator.accumulated, + extra: { blockNo: 200 } + }); + + // Verifier passes the accumulator public key for verification + const acc = new Map(); + acc.set(0, accumulatorPk); + acc.set(1, accumulatorPk); + + checkResult(pres.verify([pk1, pk1], acc)); + + checkPresentationJson(pres, [pk1, pk1], acc); + }) +}) \ No newline at end of file diff --git a/tests/anonymous-credentials/utils.ts b/tests/anonymous-credentials/utils.ts index 2ed0e05..9a41d34 100644 --- a/tests/anonymous-credentials/utils.ts +++ b/tests/anonymous-credentials/utils.ts @@ -17,6 +17,7 @@ import { STATUS_STR, SUBJECT_STR } from '../../src'; +import { KBUniversalAccumulator } from '../../src/accumulator/kb-universal-accumulator'; import { Credential, CredentialBuilder, @@ -33,7 +34,7 @@ import { checkResult, stringToBytes } from '../utils'; import fs from 'fs'; import { BytearrayWrapper } from '../../src/bytearray-wrapper'; import { BDDT16MacParams, BDDT16MacSecretKey } from '../../src/bddt16-mac'; -import { InMemoryState } from '../../src/accumulator/in-memory-persistence'; +import { InMemoryKBUniversalState, InMemoryState } from '../../src/accumulator/in-memory-persistence'; export function getExampleSchema(num): IEmbeddedJsonSchema { const schema = CredentialSchema.essential(); @@ -559,8 +560,8 @@ export function checkSchemaFromJson(schemaJson: string, schema: CredentialSchema } // Prefill the given accumulator with `totalMembers` members. The members are creates in a certain way for these tests -export async function prefillAccumulator( - accumulator: Accumulator, +export async function prefillAccumulator( + accumulator: Accumulator, secretKey: AccumulatorSecretKey, state: IAccumulatorState, credSchema: CredentialSchema, @@ -763,7 +764,7 @@ export async function setupPrefilledAccum(totalMembers: number, memberIdx: numbe const pk = kp.publicKey; const accumulator = PositiveAccumulator.initialize(dockAccumulatorParams()); const state = new InMemoryState(); - const allMembers = await prefillAccumulator( + const allMembers = await prefillAccumulator( accumulator, sk, state, @@ -787,4 +788,20 @@ export async function setupPrefilledAccum(totalMembers: number, memberIdx: numbe ) ).toEqual(true); return [sk, pk, accumulator, witness] -} \ No newline at end of file +} + +export async function setupKBUniAccumulator( + totalMembers: number, memberValPrefix: string, schema: CredentialSchema, seed?: Uint8Array +) { + const kp = KBUniversalAccumulator.generateKeypair(dockAccumulatorParams(), seed); + const sk = kp.secretKey; + const pk = kp.publicKey; + const state = new InMemoryKBUniversalState(); + const domain: Uint8Array[] = []; + for (let i = 1; i <= totalMembers; i++) { + const userId = `${memberValPrefix}${i}`; + domain.push(schema.encoder.encodeMessage(`${STATUS_STR}.${REV_ID_STR}`, userId)); + } + const accumulator = await KBUniversalAccumulator.initialize(domain, dockAccumulatorParams(), sk, state); + return [sk, pk, accumulator, domain, state] +} diff --git a/tests/composite-proofs/blind-signature.spec.ts b/tests/composite-proofs/blind-signature.spec.ts index 3fa63f0..049bf22 100644 --- a/tests/composite-proofs/blind-signature.spec.ts +++ b/tests/composite-proofs/blind-signature.spec.ts @@ -1,18 +1,17 @@ -import { checkResult, getParamsAndKeys, stringToBytes } from '../utils'; -import { initializeWasm, CompositeProof, MetaStatements, ProofSpec, Statements, Witnesses } from '../../src'; +import { generateRandomG1Element } from 'crypto-wasm-new'; +import { CompositeProof, initializeWasm, MetaStatements, ProofSpec, Statements, Witnesses } from '../../src'; import { BlindSignature, - KeyPair, - SignatureParams, + encodeMessageForSigningIfNotPS, + encodeMessageForSigningIfPS, getStatementForBlindSigRequest, getWitnessForBlindSigRequest, - isBBSPlus, - encodeMessageForSigningIfPS, + isBBS, + isKvac, isPS, - encodeMessageForSigningIfNotPS, - Scheme, isBBS, isKvac + Scheme } from '../scheme'; -import { generateRandomG1Element } from 'crypto-wasm-new'; +import { checkResult, getParamsAndKeys, stringToBytes } from '../utils'; describe(`${Scheme} Getting a blind signature, i.e. signature where signer is not aware of certain attributes of the user`, () => { it('works', async () => { diff --git a/tests/composite-proofs/bound-check.spec.ts b/tests/composite-proofs/bound-check.spec.ts index 08dabcb..42efe8b 100644 --- a/tests/composite-proofs/bound-check.spec.ts +++ b/tests/composite-proofs/bound-check.spec.ts @@ -20,24 +20,17 @@ import { WitnessEqualityMetaStatement, Witnesses } from '../../src'; +import { buildWitness, PublicKey, Scheme, SecretKey, Signature, SignatureParams } from '../scheme'; import { checkResult, - getRevealedUnrevealed, - stringToBytes, getBoundCheckSnarkKeys, getParamsAndKeys, - signAndVerify, proverStmt, verifierStmt + getRevealedUnrevealed, + proverStmt, + signAndVerify, + stringToBytes, + verifierStmt } from '../utils'; -import { - KeyPair, - SecretKey, - PublicKey, - Signature, - SignatureParams, - buildWitness, - buildVerifierStatement, - Scheme, isPS, buildProverStatement -} from '../scheme'; describe(`Bound check of ${Scheme} signed messages`, () => { const messageCount = 5; diff --git a/tests/composite-proofs/many-signatures.spec.ts b/tests/composite-proofs/many-signatures.spec.ts index e8b6b11..bcd67a5 100644 --- a/tests/composite-proofs/many-signatures.spec.ts +++ b/tests/composite-proofs/many-signatures.spec.ts @@ -10,13 +10,9 @@ import { Witnesses, Statement } from '../../src'; import { - KeyPair, Scheme, - Signature, - SignatureParams, - buildVerifierStatement, buildWitness, - encodeMessageForSigningIfPS, isPS, buildProverStatement, isKvac + encodeMessageForSigningIfPS, isKvac } from '../scheme'; describe(`Proving knowledge of 2 ${Scheme} signatures over attributes and equality of a specific attribute`, () => { diff --git a/tests/composite-proofs/msg-js-obj/accumulator.spec.ts b/tests/composite-proofs/msg-js-obj/accumulator.spec.ts index 1508e7d..21617ff 100644 --- a/tests/composite-proofs/msg-js-obj/accumulator.spec.ts +++ b/tests/composite-proofs/msg-js-obj/accumulator.spec.ts @@ -16,7 +16,7 @@ import { ProofSpec, Statement, Statements, - VBWitnessUpdatePublicInfo, + VBWitnessUpdateInfo, Witness, WitnessEqualityMetaStatement, Witnesses @@ -36,7 +36,7 @@ describe(`${Scheme} Accumulator`, () => { // Prefill the given accumulator with `totalMembers` members. The members are creates in a certain way for these tests async function prefillAccumulator( - accumulator: Accumulator, + accumulator: PositiveAccumulator, secretKey: AccumulatorSecretKey, state: IAccumulatorState, totalMembers: number @@ -307,13 +307,13 @@ describe(`${Scheme} Accumulator`, () => { // Remove members from accumulator // Prepare witness update info that needs to be shared with the members - const witnessUpdInfo1 = VBWitnessUpdatePublicInfo.new( + const witnessUpdInfo1 = VBWitnessUpdateInfo.new( accumulator1.accumulated, [], [allMembers1[5]], accumKeypair1.secretKey ); - const witnessUpdInfo2 = VBWitnessUpdatePublicInfo.new( + const witnessUpdInfo2 = VBWitnessUpdateInfo.new( accumulator2.accumulated, [], [allMembers1[20]], diff --git a/tests/prefilled-positive-accumulator.spec.ts b/tests/prefilled-positive-accumulator.spec.ts index 95addd9..99cab02 100644 --- a/tests/prefilled-positive-accumulator.spec.ts +++ b/tests/prefilled-positive-accumulator.spec.ts @@ -5,7 +5,7 @@ import { initializeWasm, PositiveAccumulator, VBMembershipWitness, - VBWitnessUpdatePublicInfo + VBWitnessUpdateInfo } from '../src'; import { InMemoryState } from '../src/accumulator/in-memory-persistence'; import { stringToBytes } from './utils'; @@ -69,7 +69,7 @@ describe('Prefilled positive accumulator', () => { expect(verifAccumulator.verifyMembershipWitness(member2, witness2, keypair.publicKey, params)).toEqual(true); // Manager decides to remove a member, the new accumulated value will be published along with witness update info - const witnessUpdInfo = VBWitnessUpdatePublicInfo.new(accumulator.accumulated, [], [member2], keypair.secretKey); + const witnessUpdInfo = VBWitnessUpdateInfo.new(accumulator.accumulated, [], [member2], keypair.secretKey); await accumulator.remove(member2, keypair.secretKey, state); verifAccumulator = PositiveAccumulator.fromAccumulated(accumulator.accumulated); diff --git a/yarn.lock b/yarn.lock index 903de37..c361eeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1509,10 +1509,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -"crypto-wasm-new@npm:@docknetwork/crypto-wasm@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@docknetwork/crypto-wasm/-/crypto-wasm-0.25.0.tgz#679e38d7656f11c9417b602fc6fccf239c9d116e" - integrity sha512-iKoM390iG2ohwbmaMNlYDh7hYtwoGKjj8zikU+Oh5DkFaPpPofhq1yzoPiaU2DRZiYZox/38MRattdXcaCi0Kw== +"crypto-wasm-new@npm:@docknetwork/crypto-wasm@0.26.0": + version "0.26.0" + resolved "https://registry.yarnpkg.com/@docknetwork/crypto-wasm/-/crypto-wasm-0.26.0.tgz#4f1dc3c89db518b14e6a0eee9996ac55c03f8821" + integrity sha512-f9YMdPftYIBO5vPWZuh3ckPdmw8yvE3pLPPzt+9orA7EgOtU2zfu/Zp88JBxB6X/J2unQWB0zGE3jTxL45JIEw== dependencies: buffer "^6.0.3"