diff --git a/docs/design/w3c/w3c-representation.md b/docs/design/w3c/w3c-representation.md index 8a0ca795..c6e97642 100644 --- a/docs/design/w3c/w3c-representation.md +++ b/docs/design/w3c/w3c-representation.md @@ -104,52 +104,6 @@ The reasons for adding duplication methods: - only credential conversion method to do migration for previously issued credentials ```rust -/// Create Credential Offer according to the AnonCreds specification -/// Note that Credential Offer still will be legacy styled (the same as result of anoncreds_create_credential_offer) -/// -/// # Params -/// schema_id: id of schema future credential refers to -/// cred_def_id: id of credential definition future credential refers to -/// key_proof: object handle pointing to credential definition key correctness proof -/// cred_offer_p: reference that will contain created credential offer (in legacy form) instance pointer -/// -/// # Returns -/// Error code -#[no_mangle] -pub extern "C" fn anoncreds_create_w3c_credential_offer( - schema_id: FfiStr, - cred_def_id: FfiStr, - key_proof: ObjectHandle, - cred_offer_p: *mut ObjectHandle, -) -> ErrorCode {} - -/// Create Credential Request according to the AnonCreds specification -/// Note that Credential Request still will be legacy styled (the same as result of anoncreds_create_credential_request) -/// -/// # Params -/// entropy: entropy string to use for request creation -/// prover_did: DID of the credential holder -/// cred_def: object handle pointing to credential definition -/// link_secret: holder link secret -/// link_secret_id: id of holder's link secret -/// credential_offer: object handle pointing to credential offer -/// cred_req_p: Reference that will contain created credential request (in legacy form) instance pointer. -/// cred_req_meta_p: Reference that will contain created credential request metadata (in legacy form) instance pointer. -/// -/// # Returns -/// Error code -#[no_mangle] -pub extern "C" fn anoncreds_create_w3c_credential_request( - entropy: FfiStr, - prover_did: FfiStr, - cred_def: ObjectHandle, - link_secret: FfiStr, - link_secret_id: FfiStr, - cred_offer: ObjectHandle, - cred_req_p: *mut ObjectHandle, - cred_req_meta_p: *mut ObjectHandle, -) -> ErrorCode {} - /// Create Credential in W3C form according to the specification. /// /// # Params @@ -343,297 +297,3 @@ w3c_presentation_request = Verifier.w3c_create_presentation_request() w3c_presentation = Holder.anoncreds_w3c_create_presentation(w3c_presentation_request, w3c_credential) Verifier.anoncreds_w3c_verify_presentation(w3c_presentation) ``` - -#### Issue W3C Credential, set RSA Identity Proof signature, and present W3C Presentation using RSA Identity Proof - -``` -/// Issue W3C credential using new flow methods -w3c_credential_offer = Issuer.anoncreds_w3c_create_credential_offer(...) -w3c_credential_request = Holder.anoncreds_w3c_create_credential_request(w3c_credential_offer,...) -w3c_credential = Issuer.anoncreds_w3c_create_credential(w3c_credential_request,...) -w3c_credential = Holder.anoncreds_w3c_process_credential(w3c_credential,...) - -/// Add RSA Identity Proof signature to credential -integrity_proof = extartnal_library.create_rsa_integrity_proof(w3c_credential) -w3c_credential = anoncreds_w3c_credential_add_non_anoncreds_integrity_proof(w3c_credential, integrity_proof) - -/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests? -Wallet.store_w3c_credential(w3c_credential) - -/// Verifiy W3C presenttion using RSA Identity Proof signature -w3c_presentation_request = Verifier.w3c_create_presentation_request() -rsa_integrity_proof_presentation = extartnal_library.create_presentation_using_rsa_integrity_proof(w3c_presentation_request, w3c_credential) -extartnal_verifier.verify_rsa_integrity_proof_presentation(rsa_integrity_proof_presentation) -``` - -### Presentation validation - -**Request** -``` -{ - "name":"pres_req_1", - "non_revoked":null, - "nonce":"358493544514389191968232", - "requested_attributes":{ - "attr1_referent":{ - "name":"first_name", - "non_revoked":null, - "restrictions":null - }, - "attr2_referent":{ - "name":"sex", - "non_revoked":null, - "restrictions":null - }, - "attr3_referent":{ - "names":[ - "last_name", - "height" - ], - "non_revoked":null, - "restrictions":null - } - }, - "requested_predicates":{ - "predicate1_referent":{ - "name":"age", - "non_revoked":null, - "p_type":">=", - "p_value":18, - "restrictions":null - } - }, - "ver":"1.0", - "version":"0.1" -} -``` - -**Presentation** -``` -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://raw.githubusercontent.com/DSRCorporation/anoncreds-rs/design/w3c-support/docs/design/w3c/context.json" - ], - "type": [ - "VerifiablePresentation", - "AnonCredsPresentation" - ], - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://raw.githubusercontent.com/DSRCorporation/anoncreds-rs/design/w3c-support/docs/design/w3c/context.json" - ], - "type": [ - "VerifiableCredential", - "AnonCredsPresentation" - ], - "issuer": "did:sov:3avoBCqDMFHFaKUHug9s8W", - "issuanceDate": "2023-10-26T01:17:32Z", - "credentialSchema": { - "type": "AnonCredsDefinition", - "definition": "did:sov:3avoBCqDMFHFaKUHug9s8W:3:CL:13:default", - "schema": "did:sov:3avoBCqDMFHFaKUHug9s8W:2:basic_person:0.1.0" - }, - "credentialSubject": { - "first_name": "Alice" - "lastt_name": "Jons" - "height": "185" - }, - "proof": { - "type": "AnonCredsPresentationProof2023", - "mapping": { - "revealedAttributes": ["attr1_referent"], - "unrevealedAttributes": ["attr2_referent"], - "revealedAttributeGroups": ["attr3_referent"], - "requestedPredicates": ["predicate1_referent"] - }, - "proofValue": "AAEBAnr2Ql...0UhJ-bIIdWFKVWxjU3ePxv_7HoY5pUw" - } - } - ], - "proof": { - "type": "AnonCredsPresentationProof2023", - "challenge": "182453895158932070575246", - "proofValue": "AAAgtMR4....J19l-agSA" - } -} -``` - -**Verifier validation steps is we keep mapping**: -``` -// validate requested attributes -for (referent, requested) in presentation_request.requested_attributes { - credential = presentation.verifiableCredential.find((verifiableCredential) => - verifiableCredential.proof.mapping.revealedAttributes.includes(referent) || - verifiableCredential.proof.mapping.unrevealedAttributes.includes(referent) || - verifiableCredential.proof.mapping.revealedAttributeGroups.includes(referent)) - - credential.checkRestrictions(requested.restrictions) - - if !credential { - error - } - if requested.name { - assert(credential.credentialSubject[requested.name]) - } - if requested.names { - names.forEach((name) => assert(credential.credentialSubject[name])) - } -} - -// validate requested predicates -for (referent, requested) in presentation_request.requested_predicates { - credential = presentation.verifiableCredential.find((verifiableCredential) => - verifiableCredential.proof.mapping.requestedPredicates.includes(referent)) - credential.checkRestrictions(requested.restrictions) - assert(credential.credentialSubject[requested.name]) // if we include derived predicate into subject -} -``` - -**Verifier validation steps is we drop mapping**: -``` -// validate requested attributes -for (referent, requested) in presentation_request.requested_attributes { - if requested.name { - // or filter if requted same attribute multiple times? - credential = presentation.verifiableCredential.find((verifiableCredential) => - credentialSubject.contains(requested[name]) - ) - if credential { - credential.checkRestrictions(requested.restrictions) - assert(credential.credentialSubject[requested.name]) - } - if !credential { - // consider attribute as unrevealed - // If we need to support and validate unrevealed attributes - credential_with_attribute = presentation.verifiableCredential.find((verifiableCredential) => - schema = get_schema(verifiableCredential.schema_id) // all schemas already passed into verification function - schema.attributes.includes(requested.name) - verifiableCredential.matches(restrictions) - ) - if !credential_with_attribute { - error - } - } - } - if requested.names { - for (referent, requested) in requested.names { - // do same as for single attribute above - // make sure that all come from single credential - } - } -} - -// validate requested predicates - we put predicate derived string or object into credentialSubject -// { -// "age" ">= 18" -// } -for (referent, requested) in presentation_request.requested_predicates { - // or filter if requted same attribute multiple times? - credential = presentation.verifiableCredential.find((verifiableCredential) => - credentialSubject.contains(requested[name]) - ) - if !credential { - error - } - credential.checkRestrictions(requested.restrictions) - assert(credential.credentialSubject[requested.name]) -} -``` - -### Examples - -Example of an AnonCreds W3C credential: - -```json -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://raw.githubusercontent.com/hyperledger/anoncreds-spec/main/data/anoncreds-w3c-context.json" - ], - "type": [ - "VerifiableCredential", - "AnonCredsCredential" - ], - "issuer": "did:sov:3avoBCqDMFHFaKUHug9s8W", - "issuanceDate": "2023-11-15T10:00:00.036203Z", - "credentialSchema": { - "type": "AnonCredsDefinition", - "definition": "did:sov:3avoBCqDMFHFaKUHug9s8W:3:CL:13:default", - "schema": "did:sov:3avoBCqDMFHFaKUHug9s8W:2:basic_person:0.1.0" - }, - "credentialSubject": { - "firstName": "Alice", - "lastName": "Jones", - "age": "18" - }, - "proof": [ - { - "type": "AnonCredsProof2023", - "signature": "AAAgf9w5.....8Z_x3FqdwRHoWruiF0FlM" - }, - { - "type": "Ed25519Signature2020", - "created": "2021-11-13T18:19:39Z", - "verificationMethod": "did:sov:3avoBCqDMFHFaKUHug9s8W#key-1", - "proofPurpose": "assertionMethod", - "proofValue": "z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz" - } - ] -} -``` - -Example of an AnonCreds W3C presentation: - -```json -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://raw.githubusercontent.com/hyperledger/anoncreds-spec/main/data/anoncreds-w3c-context.json" - ], - "type": [ - "VerifiablePresentation", - "AnonCredsPresentation" - ], - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://raw.githubusercontent.com/hyperledger/anoncreds-spec/main/data/anoncreds-w3c-context.json" - ], - "type": [ - "VerifiableCredential", - "AnonCredsCredential" - ], - "credentialSchema": { - "type": "AnonCredsDefinition", - "definition": "did:sov:3avoBCqDMFHFaKUHug9s8W:3:CL:13:default", - "schema": "did:sov:3avoBCqDMFHFaKUHug9s8W:2:basic_person:0.1.0" - }, - "credentialSubject": { - "firstName": "Alice", - "age": [ - { - "type": "AnonCredsPredicate", - "predicate": ">=", - "value": 18 - } - ] - }, - "issuanceDate": "2023-11-15T10:59:48.036203Z", - "issuer": "did:sov:3avoBCqDMFHFaKUHug9s8W", - "proof": { - "type": "AnonCredsPresentationProof2023", - "proofValue": "eyJzdWJfcHJvb2Yi...zMTc1NzU0NDAzNDQ0ODUifX1dfX19" - } - } - ], - "proof": { - "type": "AnonCredsPresentationProof2023", - "challenge": "413296376279822794586260", - "proofValue": "eyJhZ2dyZWdhdGVkIjp7ImNfaGFzaCI6IjEwMT...IsMzAsMTM1LDE4MywxMDcsMTYwXV19fQ==" - } -} -``` \ No newline at end of file diff --git a/wrappers/javascript/packages/anoncreds-nodejs/src/NodeJSAnoncreds.ts b/wrappers/javascript/packages/anoncreds-nodejs/src/NodeJSAnoncreds.ts index f4dc05e7..fe7c76a9 100644 --- a/wrappers/javascript/packages/anoncreds-nodejs/src/NodeJSAnoncreds.ts +++ b/wrappers/javascript/packages/anoncreds-nodejs/src/NodeJSAnoncreds.ts @@ -152,35 +152,10 @@ export class NodeJSAnoncreds implements Anoncreds { const { credentialDefinition, credentialDefinitionPrivate, credentialOffer, credentialRequest } = serializeArguments(options) - const attributeNames = StringListStruct({ - count: Object.keys(options.attributeRawValues).length, - data: Object.keys(options.attributeRawValues) as unknown as TypedArray - }) - - const attributeRawValues = StringListStruct({ - count: Object.keys(options.attributeRawValues).length, - data: Object.values(options.attributeRawValues) as unknown as TypedArray - }) - - const attributeEncodedValues = options.attributeEncodedValues - ? StringListStruct({ - count: Object.keys(options.attributeEncodedValues).length, - data: Object.values(options.attributeEncodedValues) as unknown as TypedArray - }) - : undefined - - let revocationConfiguration - if (options.revocationConfiguration) { - const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, revocationStatusList, registryIndex } = - serializeArguments(options.revocationConfiguration) - - revocationConfiguration = CredRevInfoStruct({ - reg_def: revocationRegistryDefinition, - reg_def_private: revocationRegistryDefinitionPrivate, - status_list: revocationStatusList, - reg_idx: registryIndex - }) - } + const attributeNames = this.convertAttributeNames(options.attributeRawValues) + const attributeRawValues = this.convertAttributeRawValues(options.attributeRawValues) + const attributeEncodedValues = this.convertAttributeEncodedValues(options.attributeEncodedValues) + const revocationConfiguration = this.convertRevocationConfiguration(options.revocationConfiguration) const credentialPtr = allocatePointer() this.nativeAnoncreds.anoncreds_create_credential( @@ -188,8 +163,8 @@ export class NodeJSAnoncreds implements Anoncreds { credentialDefinitionPrivate, credentialOffer, credentialRequest, - attributeNames as unknown as Buffer, - attributeRawValues as unknown as Buffer, + attributeNames, + attributeRawValues, attributeEncodedValues as unknown as Buffer, revocationConfiguration?.ref().address() ?? 0, credentialPtr @@ -302,42 +277,6 @@ export class NodeJSAnoncreds implements Anoncreds { }): ObjectHandle { const { presentationRequest, linkSecret } = serializeArguments(options) - const credentialEntries = options.credentials.map((value) => - CredentialEntryStruct({ - credential: value.credential.handle, - timestamp: value.timestamp ?? -1, - rev_state: value.revocationState?.handle ?? 0 - }) - ) - - const credentialEntryList = CredentialEntryListStruct({ - count: credentialEntries.length, - data: credentialEntries as unknown as TypedArray< - StructObject<{ - credential: number - timestamp: number - rev_state: number - }> - > - }) - - const credentialProves = options.credentialsProve.map((value) => { - const { entryIndex: entry_idx, isPredicate: is_predicate, reveal, referent } = serializeArguments(value) - return CredentialProveStruct({ entry_idx, referent, is_predicate, reveal }) - }) - - const credentialProveList = CredentialProveListStruct({ - count: credentialProves.length, - data: credentialProves as unknown as TypedArray< - StructObject<{ - entry_idx: string | number - referent: string - is_predicate: number - reveal: number - }> - > - }) - const selfAttestNames = StringListStruct({ count: Object.keys(options.selfAttest).length, data: Object.keys(options.selfAttest) as unknown as TypedArray @@ -348,43 +287,24 @@ export class NodeJSAnoncreds implements Anoncreds { data: Object.values(options.selfAttest) as unknown as TypedArray }) - const schemaKeys = Object.keys(options.schemas) - const schemaIds = StringListStruct({ - count: schemaKeys.length, - data: schemaKeys as unknown as TypedArray - }) - - const schemaValues = Object.values(options.schemas) - const schemas = ObjectHandleListStruct({ - count: schemaValues.length, - data: ObjectHandleArray(schemaValues.map((o) => o.handle)) - }) - - const credentialDefinitionKeys = Object.keys(options.credentialDefinitions) - const credentialDefinitionIds = StringListStruct({ - count: credentialDefinitionKeys.length, - data: credentialDefinitionKeys as unknown as TypedArray - }) - - const credentialDefinitionValues = Object.values(options.credentialDefinitions) - const credentialDefinitions = ObjectHandleListStruct({ - count: credentialDefinitionValues.length, - data: ObjectHandleArray(credentialDefinitionValues.map((o) => o.handle)) - }) + const credentialEntryList = this.convertCredentialList(options.credentials) + const credentialProveList = this.convertCredentialProves(options.credentialsProve) + const { schemaIds, schemas } = this.convertSchemas(options.schemas) + const { credentialDefinitionIds, credentialDefinitions } = this.convertCredDefs(options.credentialDefinitions) const ret = allocatePointer() this.nativeAnoncreds.anoncreds_create_presentation( presentationRequest, - credentialEntryList as unknown as Buffer, - credentialProveList as unknown as Buffer, + credentialEntryList, + credentialProveList, selfAttestNames as unknown as Buffer, selfAttestValues as unknown as Buffer, linkSecret, - schemas as unknown as Buffer, - schemaIds as unknown as Buffer, - credentialDefinitions as unknown as Buffer, - credentialDefinitionIds as unknown as Buffer, + schemas, + schemaIds, + credentialDefinitions, + credentialDefinitionIds, ret ) this.handleError() @@ -416,26 +336,7 @@ export class NodeJSAnoncreds implements Anoncreds { credentialDefinitionIds } = serializeArguments(options) - const nativeNonRevokedIntervalOverrides = options.nonRevokedIntervalOverrides?.map((value) => { - const { requestedFromTimestamp, revocationRegistryDefinitionId, overrideRevocationStatusListTimestamp } = - serializeArguments(value) - return NonRevokedIntervalOverrideStruct({ - rev_reg_def_id: revocationRegistryDefinitionId, - requested_from_ts: requestedFromTimestamp, - override_rev_status_list_ts: overrideRevocationStatusListTimestamp - }) - }) - - const nonRevokedIntervalOverrideList = NonRevokedIntervalOverrideListStruct({ - count: options.nonRevokedIntervalOverrides?.length ?? 0, - data: nativeNonRevokedIntervalOverrides as unknown as TypedArray< - StructObject<{ - rev_reg_def_id: string - requested_from_ts: number - override_rev_status_list_ts: number - }> - > - }) + const nonRevokedIntervalOverrideList = this.convertNonRevokedIntervalOverrides(options.nonRevokedIntervalOverrides) const ret = allocateInt8Buffer() @@ -449,7 +350,7 @@ export class NodeJSAnoncreds implements Anoncreds { revocationRegistryDefinitions, revocationRegistryDefinitionIds, revocationStatusLists, - nonRevokedIntervalOverrideList as unknown as Buffer, + nonRevokedIntervalOverrideList, ret ) this.handleError() @@ -617,6 +518,198 @@ export class NodeJSAnoncreds implements Anoncreds { return new ObjectHandle(handleReturnPointer(ret)) } + public createW3cCredential(options: { + credentialDefinition: ObjectHandle + credentialDefinitionPrivate: ObjectHandle + credentialOffer: ObjectHandle + credentialRequest: ObjectHandle + attributeRawValues: Record + revocationConfiguration?: NativeCredentialRevocationConfig + w3cVersion?: string + }): ObjectHandle { + const { credentialDefinition, credentialDefinitionPrivate, credentialOffer, credentialRequest, w3cVersion } = + serializeArguments(options) + + const attributeNames = this.convertAttributeNames(options.attributeRawValues) + const attributeRawValues = this.convertAttributeRawValues(options.attributeRawValues) + const revocationConfiguration = this.convertRevocationConfiguration(options.revocationConfiguration) + + const credentialPtr = allocatePointer() + this.nativeAnoncreds.anoncreds_create_w3c_credential( + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + attributeNames, + attributeRawValues, + revocationConfiguration?.ref().address() ?? 0, + w3cVersion, + credentialPtr + ) + this.handleError() + + return new ObjectHandle(handleReturnPointer(credentialPtr)) + } + + public processW3cCredential(options: { + credential: ObjectHandle + credentialRequestMetadata: ObjectHandle + linkSecret: string + credentialDefinition: ObjectHandle + revocationRegistryDefinition?: ObjectHandle | undefined + }): ObjectHandle { + const { credential, credentialRequestMetadata, linkSecret, credentialDefinition } = serializeArguments(options) + + const ret = allocatePointer() + + this.nativeAnoncreds.anoncreds_process_w3c_credential( + credential, + credentialRequestMetadata, + linkSecret, + credentialDefinition, + options.revocationRegistryDefinition?.handle ?? 0, + ret + ) + this.handleError() + + return new ObjectHandle(handleReturnPointer(ret)) + } + + public createW3cPresentation(options: { + presentationRequest: ObjectHandle + credentials: NativeCredentialEntry[] + credentialsProve: NativeCredentialProve[] + linkSecret: string + schemas: Record + credentialDefinitions: Record + w3cVersion?: string + }): ObjectHandle { + const { presentationRequest, linkSecret, w3cVersion } = serializeArguments(options) + + const credentialEntryList = this.convertCredentialList(options.credentials) + const credentialProveList = this.convertCredentialProves(options.credentialsProve) + const { schemaIds, schemas } = this.convertSchemas(options.schemas) + const { credentialDefinitionIds, credentialDefinitions } = this.convertCredDefs(options.credentialDefinitions) + + const ret = allocatePointer() + + this.nativeAnoncreds.anoncreds_create_w3c_presentation( + presentationRequest, + credentialEntryList, + credentialProveList, + linkSecret, + schemas, + schemaIds, + credentialDefinitions, + credentialDefinitionIds, + w3cVersion, + ret + ) + this.handleError() + + return new ObjectHandle(handleReturnPointer(ret)) + } + + public verifyW3cPresentation(options: { + presentation: ObjectHandle + presentationRequest: ObjectHandle + schemas: ObjectHandle[] + schemaIds: string[] + credentialDefinitions: ObjectHandle[] + credentialDefinitionIds: string[] + revocationRegistryDefinitions?: ObjectHandle[] + revocationRegistryDefinitionIds?: string[] + revocationStatusLists?: ObjectHandle[] + nonRevokedIntervalOverrides?: NativeNonRevokedIntervalOverride[] + }): boolean { + const { + presentation, + presentationRequest, + schemas, + credentialDefinitions, + revocationRegistryDefinitions, + revocationStatusLists, + revocationRegistryDefinitionIds, + schemaIds, + credentialDefinitionIds + } = serializeArguments(options) + + const nonRevokedIntervalOverrideList = this.convertNonRevokedIntervalOverrides(options.nonRevokedIntervalOverrides) + + const ret = allocateInt8Buffer() + + this.nativeAnoncreds.anoncreds_verify_w3c_presentation( + presentation, + presentationRequest, + schemas, + schemaIds, + credentialDefinitions, + credentialDefinitionIds, + revocationRegistryDefinitions, + revocationRegistryDefinitionIds, + revocationStatusLists, + nonRevokedIntervalOverrideList, + ret + ) + this.handleError() + + return Boolean(handleReturnPointer(ret)) + } + + public credentialToW3c(options: { + objectHandle: ObjectHandle + credentialDefinition: ObjectHandle + w3cVersion?: string + }): ObjectHandle { + const { objectHandle, credentialDefinition, w3cVersion } = serializeArguments(options) + + const ret = allocatePointer() + + this.nativeAnoncreds.anoncreds_credential_to_w3c(objectHandle, credentialDefinition, w3cVersion, ret) + this.handleError() + + return new ObjectHandle(handleReturnPointer(ret)) + } + + public credentialFromW3c(options: { objectHandle: ObjectHandle }): ObjectHandle { + const { objectHandle } = serializeArguments(options) + + const ret = allocatePointer() + + this.nativeAnoncreds.anoncreds_credential_from_w3c(objectHandle, ret) + this.handleError() + + return new ObjectHandle(handleReturnPointer(ret)) + } + + public w3cCredentialGetIntegrityProofDetails(options: { objectHandle: ObjectHandle }) { + const { objectHandle } = serializeArguments(options) + + const ret = allocatePointer() + this.nativeAnoncreds.anoncreds_w3c_credential_get_integrity_proof_details(objectHandle, ret) + this.handleError() + + return new ObjectHandle(handleReturnPointer(ret)) + } + + public w3cCredentialProofGetAttribute(options: { objectHandle: ObjectHandle; name: string }) { + const { objectHandle, name } = serializeArguments(options) + + const ret = allocateStringBuffer() + this.nativeAnoncreds.anoncreds_w3c_credential_proof_get_attribute(objectHandle, name, ret) + this.handleError() + + return handleReturnPointer(ret) + } + + public w3cCredentialFromJson(options: { json: string }): ObjectHandle { + return this.objectFromJson(this.nativeAnoncreds.anoncreds_w3c_credential_from_json, options) + } + + public w3cPresentationFromJson(options: { json: string }): ObjectHandle { + return this.objectFromJson(this.nativeAnoncreds.anoncreds_w3c_presentation_from_json, options) + } + public version(): string { return this.nativeAnoncreds.anoncreds_version() } @@ -738,4 +831,142 @@ export class NodeJSAnoncreds implements Anoncreds { this.nativeAnoncreds.anoncreds_object_free(options.objectHandle.handle) this.handleError() } + + private convertCredentialList(credentials: NativeCredentialEntry[]) { + const credentialEntries = credentials.map((value) => + CredentialEntryStruct({ + credential: value.credential.handle, + timestamp: value.timestamp ?? -1, + rev_state: value.revocationState?.handle ?? 0 + }) + ) + + return CredentialEntryListStruct({ + count: credentialEntries.length, + data: credentialEntries as unknown as TypedArray< + StructObject<{ + credential: number + timestamp: number + rev_state: number + }> + > + }) as unknown as Buffer + } + + private convertCredentialProves(credentialsProve: NativeCredentialProve[]) { + const credentialProves = credentialsProve.map((value) => { + const { entryIndex: entry_idx, isPredicate: is_predicate, reveal, referent } = serializeArguments(value) + return CredentialProveStruct({ entry_idx, referent, is_predicate, reveal }) + }) + + return CredentialProveListStruct({ + count: credentialProves.length, + data: credentialProves as unknown as TypedArray< + StructObject<{ + entry_idx: string | number + referent: string + is_predicate: number + reveal: number + }> + > + }) as unknown as Buffer + } + + private convertSchemas(schemas: Record) { + const schemaKeys = Object.keys(schemas) + const schemaIds = StringListStruct({ + count: schemaKeys.length, + data: schemaKeys as unknown as TypedArray + }) as unknown as Buffer + + const schemaValues = Object.values(schemas) + const schemasList = ObjectHandleListStruct({ + count: schemaValues.length, + data: ObjectHandleArray(schemaValues.map((o) => o.handle)) + }) as unknown as Buffer + return { + schemaIds, + schemas: schemasList + } + } + + private convertCredDefs(credentialDefinitions: Record) { + const credentialDefinitionKeys = Object.keys(credentialDefinitions) + const credentialDefinitionIds = StringListStruct({ + count: credentialDefinitionKeys.length, + data: credentialDefinitionKeys as unknown as TypedArray + }) as unknown as Buffer + + const credentialDefinitionValues = Object.values(credentialDefinitions) + const credentialDefinitionsList = ObjectHandleListStruct({ + count: credentialDefinitionValues.length, + data: ObjectHandleArray(credentialDefinitionValues.map((o) => o.handle)) + }) as unknown as Buffer + return { + credentialDefinitionIds, + credentialDefinitions: credentialDefinitionsList + } + } + + private convertNonRevokedIntervalOverrides(nonRevokedIntervalOverrides?: NativeNonRevokedIntervalOverride[]) { + const nativeNonRevokedIntervalOverrides = nonRevokedIntervalOverrides?.map((value) => { + const { requestedFromTimestamp, revocationRegistryDefinitionId, overrideRevocationStatusListTimestamp } = + serializeArguments(value) + return NonRevokedIntervalOverrideStruct({ + rev_reg_def_id: revocationRegistryDefinitionId, + requested_from_ts: requestedFromTimestamp, + override_rev_status_list_ts: overrideRevocationStatusListTimestamp + }) + }) + + return NonRevokedIntervalOverrideListStruct({ + count: nonRevokedIntervalOverrides?.length ?? 0, + data: nativeNonRevokedIntervalOverrides as unknown as TypedArray< + StructObject<{ + rev_reg_def_id: string + requested_from_ts: number + override_rev_status_list_ts: number + }> + > + }) as unknown as Buffer + } + + private convertAttributeNames(attributeRawValues: Record) { + return StringListStruct({ + count: Object.keys(attributeRawValues).length, + data: Object.keys(attributeRawValues) as unknown as TypedArray + }) as unknown as Buffer + } + + private convertAttributeRawValues(attributeRawValues: Record) { + return StringListStruct({ + count: Object.keys(attributeRawValues).length, + data: Object.values(attributeRawValues) as unknown as TypedArray + }) as unknown as Buffer + } + + private convertAttributeEncodedValues(attributeEncodedValues?: Record) { + return attributeEncodedValues + ? StringListStruct({ + count: Object.keys(attributeEncodedValues).length, + data: Object.values(attributeEncodedValues) as unknown as TypedArray + }) + : undefined + } + + private convertRevocationConfiguration(revocationConfiguration?: NativeCredentialRevocationConfig) { + let credRevInfo + if (revocationConfiguration) { + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate, revocationStatusList, registryIndex } = + serializeArguments(revocationConfiguration) + + credRevInfo = CredRevInfoStruct({ + reg_def: revocationRegistryDefinition, + reg_def_private: revocationRegistryDefinitionPrivate, + status_list: revocationStatusList, + reg_idx: registryIndex + }) + } + return credRevInfo + } } diff --git a/wrappers/javascript/packages/anoncreds-nodejs/src/library/bindings.ts b/wrappers/javascript/packages/anoncreds-nodejs/src/library/bindings.ts index a8ffc7ab..9f99754d 100644 --- a/wrappers/javascript/packages/anoncreds-nodejs/src/library/bindings.ts +++ b/wrappers/javascript/packages/anoncreds-nodejs/src/library/bindings.ts @@ -147,7 +147,6 @@ export const nativeBindings = { FFI_INT8_PTR ] ], - anoncreds_create_revocation_status_list: [ FFI_ERRORCODE, [ @@ -192,5 +191,63 @@ export const nativeBindings = { anoncreds_credential_definition_private_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]], anoncreds_revocation_registry_definition_private_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]], anoncreds_key_correctness_proof_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]], - anoncreds_schema_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]] + anoncreds_schema_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]], + anoncreds_create_w3c_credential: [ + FFI_ERRORCODE, + [ + FFI_OBJECT_HANDLE, + FFI_OBJECT_HANDLE, + FFI_OBJECT_HANDLE, + FFI_OBJECT_HANDLE, + StringListStruct, + StringListStruct, + FFI_OBJECT_HANDLE, + FFI_STRING, + FFI_OBJECT_HANDLE_PTR + ] + ], + anoncreds_process_w3c_credential: [ + FFI_ERRORCODE, + [FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE, FFI_STRING, FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE_PTR] + ], + anoncreds_create_w3c_presentation: [ + FFI_ERRORCODE, + [ + FFI_OBJECT_HANDLE, + CredentialEntryListStruct, + CredentialProveListStruct, + FFI_STRING, + ObjectHandleListStruct, + StringListStruct, + ObjectHandleListStruct, + StringListStruct, + FFI_STRING, + FFI_OBJECT_HANDLE_PTR + ] + ], + anoncreds_verify_w3c_presentation: [ + FFI_ERRORCODE, + [ + FFI_OBJECT_HANDLE, + FFI_OBJECT_HANDLE, + ObjectHandleListStruct, + StringListStruct, + ObjectHandleListStruct, + StringListStruct, + ObjectHandleListStruct, + StringListStruct, + ObjectHandleListStruct, + NonRevokedIntervalOverrideListStruct, + FFI_INT8_PTR + ] + ], + anoncreds_credential_to_w3c: [ + FFI_ERRORCODE, + [FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE, FFI_STRING, FFI_OBJECT_HANDLE_PTR] + ], + anoncreds_credential_from_w3c: [FFI_ERRORCODE, [FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE_PTR]], + anoncreds_w3c_credential_get_integrity_proof_details: [FFI_ERRORCODE, [FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE_PTR]], + anoncreds_w3c_credential_proof_get_attribute: [FFI_ERRORCODE, [FFI_OBJECT_HANDLE, FFI_STRING, FFI_STRING_PTR]], + anoncreds_w3c_presentation_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]], + anoncreds_w3c_credential_from_json: [FFI_ERRORCODE, [ByteBufferStruct, FFI_STRING_PTR]] } as const diff --git a/wrappers/javascript/packages/anoncreds-nodejs/tests/api.test.ts b/wrappers/javascript/packages/anoncreds-nodejs/tests/api.test.ts index 6968eedb..58da5ca2 100644 --- a/wrappers/javascript/packages/anoncreds-nodejs/tests/api.test.ts +++ b/wrappers/javascript/packages/anoncreds-nodejs/tests/api.test.ts @@ -11,7 +11,11 @@ import { PresentationRequest, RevocationRegistryDefinition, RevocationStatusList, - Schema + Schema, + W3cCredential, + CredentialOffer, + CredentialRequest, + W3cPresentation } from '@hyperledger/anoncreds-shared' import { setup } from './utils' @@ -540,3 +544,306 @@ test('create and verify presentation passing only JSON objects as parameters', ( expect(verify).toBeTruthy() }) + +describe('API W3C', () => { + beforeAll(() => setup()) + + test('create and verify w3c presentation', () => { + const nonce = anoncreds.generateNonce() + + const presentationRequest = PresentationRequest.fromJson({ + nonce, + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + issuer: 'mock:uri' + }, + attr2_referent: { + names: ['name', 'height'] + } + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=', p_value: 18 } + }, + non_revoked: { from: 13, to: 200 } + }) + + const schema = Schema.create({ + name: 'schema-1', + issuerId: 'mock:uri', + version: '1', + attributeNames: ['name', 'age', 'sex', 'height'] + }) + + const { credentialDefinition, keyCorrectnessProof, credentialDefinitionPrivate } = CredentialDefinition.create({ + schemaId: 'mock:uri', + issuerId: 'mock:uri', + schema, + signatureType: 'CL', + supportRevocation: true, + tag: 'TAG' + }) + + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = RevocationRegistryDefinition.create({ + credentialDefinitionId: 'mock:uri', + credentialDefinition, + issuerId: 'mock:uri', + tag: 'some_tag', + revocationRegistryType: 'CL_ACCUM', + maximumCredentialNumber: 10 + }) + + const tailsPath = revocationRegistryDefinition.getTailsLocation() + + const timeCreateRevStatusList = 12 + const revocationStatusList = RevocationStatusList.create({ + credentialDefinition, + revocationRegistryDefinitionId: 'mock:uri', + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + issuerId: 'mock:uri', + issuanceByDefault: true, + timestamp: timeCreateRevStatusList + }) + + const credentialOffer = CredentialOffer.create({ + schemaId: 'mock:uri', + credentialDefinitionId: 'mock:uri', + keyCorrectnessProof + }) + + const linkSecret = LinkSecret.create() + const linkSecretId = 'link secret id' + + const { credentialRequestMetadata, credentialRequest } = CredentialRequest.create({ + entropy: 'entropy', + credentialDefinition, + linkSecret, + linkSecretId, + credentialOffer + }) + + const credential = W3cCredential.create({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + attributeRawValues: { name: 'Alex', height: '175', age: '28', sex: 'male' }, + revocationConfiguration: new CredentialRevocationConfig({ + registryDefinition: revocationRegistryDefinition, + registryDefinitionPrivate: revocationRegistryDefinitionPrivate, + statusList: revocationStatusList, + registryIndex: 9 + }) + }) + + const legacyCredential = credential.toLegacy() + expect('mock:uri').toEqual(legacyCredential.schemaId) + expect('mock:uri').toEqual(legacyCredential.credentialDefinitionId) + + const legacyCredentialFrom = Credential.fromW3c({ credential }) + expect('mock:uri').toEqual(legacyCredentialFrom.schemaId) + expect('mock:uri').toEqual(legacyCredentialFrom.credentialDefinitionId) + + const w3cCredential = W3cCredential.fromLegacy({ credential: legacyCredential, credentialDefinition }) + expect('mock:uri').toEqual(w3cCredential.schemaId) + expect('mock:uri').toEqual(w3cCredential.credentialDefinitionId) + + const convertedW3cCredential = legacyCredential.toW3c({ credentialDefinition }) + expect('mock:uri').toEqual(convertedW3cCredential.schemaId) + expect('mock:uri').toEqual(convertedW3cCredential.credentialDefinitionId) + + const credentialReceived = credential.process({ + credentialDefinition, + credentialRequestMetadata, + linkSecret, + revocationRegistryDefinition + }) + + const revocationRegistryIndex = credentialReceived.revocationRegistryIndex ?? 0 + + const revocationState = CredentialRevocationState.create({ + revocationRegistryDefinition, + revocationStatusList, + revocationRegistryIndex, + tailsPath + }) + + const presentation = W3cPresentation.create({ + presentationRequest, + credentials: [ + { + credential: credentialReceived, + revocationState, + timestamp: timeCreateRevStatusList + } + ], + credentialDefinitions: { 'mock:uri': credentialDefinition }, + credentialsProve: [ + { + entryIndex: 0, + isPredicate: false, + referent: 'attr1_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: false, + referent: 'attr2_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: true, + referent: 'predicate1_referent', + reveal: true + } + ], + linkSecret, + schemas: { 'mock:uri': schema } + }) + + expect(presentation.handle.handle).toStrictEqual(expect.any(Number)) + + // Without revocation timestamp override, it shall fail + expect(() => { + presentation.verify({ + presentationRequest, + schemas: { 'mock:uri': schema }, + credentialDefinitions: { 'mock:uri': credentialDefinition }, + revocationRegistryDefinitions: { 'mock:uri': revocationRegistryDefinition }, + revocationStatusLists: [revocationStatusList] + }) + }).toThrowError('Invalid timestamp') + + const verify = presentation.verify({ + presentationRequest, + schemas: { 'mock:uri': schema }, + credentialDefinitions: { 'mock:uri': credentialDefinition }, + revocationRegistryDefinitions: { 'mock:uri': revocationRegistryDefinition }, + revocationStatusLists: [revocationStatusList], + nonRevokedIntervalOverrides: [ + { + overrideRevocationStatusListTimestamp: 12, + requestedFromTimestamp: 13, + revocationRegistryDefinitionId: 'mock:uri' + } + ] + }) + + expect(verify).toBeTruthy() + }) + + test('create and verify w3c presentation (no revocation use case)', () => { + const schema = Schema.create({ + name: 'schema-1', + issuerId: 'mock:uri', + version: '1', + attributeNames: ['name', 'age', 'sex', 'height'] + }) + + const { credentialDefinition, keyCorrectnessProof, credentialDefinitionPrivate } = CredentialDefinition.create({ + schemaId: 'mock:uri', + issuerId: 'mock:uri', + schema, + signatureType: 'CL', + supportRevocation: false, + tag: 'TAG' + }) + + const credentialOffer = CredentialOffer.create({ + schemaId: 'mock:uri', + credentialDefinitionId: 'mock:uri', + keyCorrectnessProof + }) + + const linkSecret = LinkSecret.create() + const linkSecretId = 'link secret id' + + const { credentialRequestMetadata, credentialRequest } = CredentialRequest.create({ + entropy: 'entropy', + credentialDefinition, + linkSecret, + linkSecretId, + credentialOffer + }) + + const credential = W3cCredential.create({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + attributeRawValues: { name: 'Alex', height: '175', age: '28', sex: 'male' } + }) + + const credReceived = credential.process({ + credentialDefinition, + credentialRequestMetadata, + linkSecret + }) + + const nonce = anoncreds.generateNonce() + + const presentationRequest = PresentationRequest.fromJson({ + nonce, + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + issuer: 'mock:uri' + }, + attr2_referent: { + names: ['name', 'height'] + } + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=', p_value: 18 } + } + }) + + const presentation = W3cPresentation.create({ + presentationRequest, + credentials: [ + { + credential: credReceived + } + ], + credentialDefinitions: { 'mock:uri': credentialDefinition }, + credentialsProve: [ + { + entryIndex: 0, + isPredicate: false, + referent: 'attr1_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: false, + referent: 'attr2_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: true, + referent: 'predicate1_referent', + reveal: true + } + ], + linkSecret, + schemas: { 'mock:uri': schema } + }) + + expect(presentation.handle.handle).toStrictEqual(expect.any(Number)) + + const verify = presentation.verify({ + presentationRequest, + schemas: { 'mock:uri': schema }, + credentialDefinitions: { 'mock:uri': credentialDefinition } + }) + + expect(verify).toBeTruthy() + }) +}) diff --git a/wrappers/javascript/packages/anoncreds-nodejs/tests/bindings.test.ts b/wrappers/javascript/packages/anoncreds-nodejs/tests/bindings.test.ts index 0d190790..7e3e8966 100644 --- a/wrappers/javascript/packages/anoncreds-nodejs/tests/bindings.test.ts +++ b/wrappers/javascript/packages/anoncreds-nodejs/tests/bindings.test.ts @@ -329,6 +329,94 @@ describe('bindings', () => { expect(JSON.parse(credReceivedJson)).toHaveProperty('witness') }) + test('create and receive w3c credential', () => { + const schemaObj = anoncreds.createSchema({ + name: 'schema-1', + issuerId: 'mock:uri', + version: '1', + attributeNames: ['attr-1'] + }) + + const { credentialDefinition, keyCorrectnessProof, credentialDefinitionPrivate } = + anoncreds.createCredentialDefinition({ + schemaId: 'mock:uri', + issuerId: 'mock:uri', + schema: schemaObj, + signatureType: 'CL', + supportRevocation: true, + tag: 'TAG' + }) + + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + anoncreds.createRevocationRegistryDefinition({ + credentialDefinitionId: 'mock:uri', + credentialDefinition, + issuerId: 'mock:uri', + tag: 'some_tag', + revocationRegistryType: 'CL_ACCUM', + maximumCredentialNumber: 10 + }) + + const tailsPath = anoncreds.revocationRegistryDefinitionGetAttribute({ + objectHandle: revocationRegistryDefinition, + name: 'tails_location' + }) + expect(tailsPath).toBeTruthy() + + const timeCreateRevStatusList = 12 + const revocationStatusList = anoncreds.createRevocationStatusList({ + credentialDefinition, + revocationRegistryDefinitionId: 'mock:uri', + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + issuerId: 'mock:uri', + issuanceByDefault: true, + timestamp: timeCreateRevStatusList + }) + + const credentialOffer = anoncreds.createCredentialOffer({ + schemaId: 'mock:uri', + credentialDefinitionId: 'mock:uri', + keyCorrectnessProof + }) + + const linkSecret = anoncreds.createLinkSecret() + const linkSecretId = 'link secret id' + + const { credentialRequestMetadata, credentialRequest } = anoncreds.createCredentialRequest({ + entropy: ENTROPY, + credentialDefinition, + linkSecret, + linkSecretId, + credentialOffer + }) + + const credential = anoncreds.createW3cCredential({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + revocationConfiguration: { + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + revocationStatusList, + registryIndex: 9 + }, + attributeRawValues: { 'attr-1': 'test' }, + encoding: undefined + }) + + const credReceived = anoncreds.processW3cCredential({ + credential, + credentialDefinition, + credentialRequestMetadata, + linkSecret, + revocationRegistryDefinition + }) + + anoncreds.getJson({ objectHandle: credReceived }) + }) + test('create and verify presentation', () => { const nonce = anoncreds.generateNonce() @@ -515,6 +603,184 @@ describe('bindings', () => { expect(verify).toBeTruthy() }) + test('create and verify w3c presentation', () => { + const nonce = anoncreds.generateNonce() + + const presentationRequest = anoncreds.presentationRequestFromJson({ + json: JSON.stringify({ + nonce, + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + issuer: 'mock:uri' + }, + attr2_referent: { + names: ['name', 'height'] + } + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=', p_value: 18 } + }, + non_revoked: { from: 10, to: 200 } + }) + }) + + expect(anoncreds.getTypeName({ objectHandle: presentationRequest })).toEqual('PresentationRequest') + + const schemaObj = anoncreds.createSchema({ + name: 'schema-1', + issuerId: 'mock:uri', + version: '1', + attributeNames: ['name', 'age', 'sex', 'height'] + }) + + const { credentialDefinition, keyCorrectnessProof, credentialDefinitionPrivate } = + anoncreds.createCredentialDefinition({ + schemaId: 'mock:uri', + issuerId: 'mock:uri', + schema: schemaObj, + signatureType: 'CL', + supportRevocation: true, + tag: 'TAG' + }) + + const { revocationRegistryDefinition, revocationRegistryDefinitionPrivate } = + anoncreds.createRevocationRegistryDefinition({ + credentialDefinitionId: 'mock:uri', + credentialDefinition, + issuerId: 'mock:uri', + tag: 'some_tag', + revocationRegistryType: 'CL_ACCUM', + maximumCredentialNumber: 10 + }) + + const tailsPath = anoncreds.revocationRegistryDefinitionGetAttribute({ + objectHandle: revocationRegistryDefinition, + name: 'tails_location' + }) + + const timeCreateRevStatusList = 12 + const revocationStatusList = anoncreds.createRevocationStatusList({ + credentialDefinition, + revocationRegistryDefinitionId: 'mock:uri', + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + issuerId: 'mock:uri', + issuanceByDefault: true, + timestamp: timeCreateRevStatusList + }) + + const credentialOffer = anoncreds.createCredentialOffer({ + schemaId: 'mock:uri', + credentialDefinitionId: 'mock:uri', + keyCorrectnessProof + }) + + const linkSecret = anoncreds.createLinkSecret() + const linkSecretId = 'link secret id' + + const { credentialRequestMetadata, credentialRequest } = anoncreds.createCredentialRequest({ + entropy: ENTROPY, + credentialDefinition, + linkSecret, + linkSecretId, + credentialOffer + }) + + const credential = anoncreds.createW3cCredential({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + revocationConfiguration: { + revocationRegistryDefinition, + revocationRegistryDefinitionPrivate, + revocationStatusList, + registryIndex: 9 + }, + attributeRawValues: { name: 'Alex', height: '175', age: '28', sex: 'male' }, + encoding: undefined + }) + + const credentialReceived = anoncreds.processW3cCredential({ + credential, + credentialDefinition, + credentialRequestMetadata, + linkSecret, + revocationRegistryDefinition + }) + + const credentialProofDetails = anoncreds.w3cCredentialGetIntegrityProofDetails({ + objectHandle: credentialReceived + }) + + const revRegIndex = anoncreds.w3cCredentialProofGetAttribute({ + objectHandle: credentialProofDetails, + name: 'rev_reg_index' + }) + + const revocationRegistryIndex = revRegIndex === null ? 0 : parseInt(revRegIndex) + + const revocationState = anoncreds.createOrUpdateRevocationState({ + revocationRegistryDefinition, + revocationStatusList, + revocationRegistryIndex, + tailsPath + }) + + const presentation = anoncreds.createW3cPresentation({ + presentationRequest, + credentials: [ + { + credential: credentialReceived, + revocationState, + timestamp: timeCreateRevStatusList + } + ], + credentialDefinitions: { 'mock:uri': credentialDefinition }, + credentialsProve: [ + { + entryIndex: 0, + isPredicate: false, + referent: 'attr1_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: false, + referent: 'attr2_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: true, + referent: 'predicate1_referent', + reveal: true + } + ], + linkSecret, + schemas: { 'mock:uri': schemaObj } + }) + + expect(presentation.handle).toStrictEqual(expect.any(Number)) + + const verify = anoncreds.verifyW3cPresentation({ + presentation, + presentationRequest, + schemas: [schemaObj], + schemaIds: ['mock:uri'], + credentialDefinitions: [credentialDefinition], + credentialDefinitionIds: ['mock:uri'], + revocationRegistryDefinitions: [revocationRegistryDefinition], + revocationRegistryDefinitionIds: ['mock:uri'], + revocationStatusLists: [revocationStatusList] + }) + + expect(verify).toBeTruthy() + }) + test('create and verify presentation (no revocation use case)', () => { const schemaObj = anoncreds.createSchema({ name: 'schema-1', @@ -663,4 +929,122 @@ describe('bindings', () => { expect(verify).toBeTruthy() }) + + test('create and verify w3c presentation (no revocation use case)', () => { + const schemaObj = anoncreds.createSchema({ + name: 'schema-1', + issuerId: 'mock:uri', + version: '1', + attributeNames: ['name', 'age', 'sex', 'height'] + }) + + const { credentialDefinition, keyCorrectnessProof, credentialDefinitionPrivate } = + anoncreds.createCredentialDefinition({ + schemaId: 'mock:uri', + issuerId: 'mock:uri', + schema: schemaObj, + signatureType: 'CL', + supportRevocation: false, + tag: 'TAG' + }) + + const credentialOffer = anoncreds.createCredentialOffer({ + schemaId: 'mock:uri', + credentialDefinitionId: 'mock:uri', + keyCorrectnessProof + }) + + const linkSecret = anoncreds.createLinkSecret() + const linkSecretId = 'link secret id' + + const { credentialRequestMetadata, credentialRequest } = anoncreds.createCredentialRequest({ + entropy: ENTROPY, + credentialDefinition, + linkSecret, + linkSecretId, + credentialOffer + }) + + const credential = anoncreds.createW3cCredential({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + attributeRawValues: { name: 'Alex', height: '175', age: '28', sex: 'male' } + }) + + const credReceived = anoncreds.processW3cCredential({ + credential, + credentialDefinition, + credentialRequestMetadata, + linkSecret + }) + + const nonce = anoncreds.generateNonce() + + const presentationRequest = anoncreds.presentationRequestFromJson({ + json: JSON.stringify({ + nonce, + name: 'pres_req_1', + version: '0.1', + requested_attributes: { + attr1_referent: { + name: 'name', + issuer: 'mock:uri' + }, + attr2_referent: { + names: ['name', 'height'] + } + }, + requested_predicates: { + predicate1_referent: { name: 'age', p_type: '>=', p_value: 18 } + } + }) + }) + + const presentation = anoncreds.createW3cPresentation({ + presentationRequest, + credentials: [ + { + credential: credReceived + } + ], + credentialDefinitions: { 'mock:uri': credentialDefinition }, + credentialsProve: [ + { + entryIndex: 0, + isPredicate: false, + referent: 'attr1_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: false, + referent: 'attr2_referent', + reveal: true + }, + { + entryIndex: 0, + isPredicate: true, + referent: 'predicate1_referent', + reveal: true + } + ], + linkSecret, + schemas: { 'mock:uri': schemaObj } + }) + + expect(presentation.handle).toStrictEqual(expect.any(Number)) + + const verify = anoncreds.verifyW3cPresentation({ + presentation, + presentationRequest, + schemas: [schemaObj], + schemaIds: ['mock:uri'], + credentialDefinitions: [credentialDefinition], + credentialDefinitionIds: ['mock:uri'] + }) + + expect(verify).toBeTruthy() + }) }) diff --git a/wrappers/javascript/packages/anoncreds-react-native/cpp/HostObject.cpp b/wrappers/javascript/packages/anoncreds-react-native/cpp/HostObject.cpp index e6980c22..5e533b38 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/cpp/HostObject.cpp +++ b/wrappers/javascript/packages/anoncreds-react-native/cpp/HostObject.cpp @@ -87,6 +87,28 @@ fMap.insert(std::make_tuple("createRevocationStatusList", &anoncreds::credentialDefinitionPrivateFromJson)); fMap.insert(std::make_tuple("keyCorrectnessProofFromJson", &anoncreds::keyCorrectnessProofFromJson)); + fMap.insert( + std::make_tuple("createW3cCredential", &anoncreds::createW3cCredential)); + fMap.insert( + std::make_tuple("createW3cPresentation", &anoncreds::createW3cPresentation)); + fMap.insert(std::make_tuple("w3cCredentialGetIntegrityProofDetails", + &anoncreds::w3cCredentialGetIntegrityProofDetails)); + fMap.insert(std::make_tuple("w3cCredentialProofGetAttribute", + &anoncreds::w3cCredentialProofGetAttribute)); + fMap.insert( + std::make_tuple("processW3cCredential", &anoncreds::processW3cCredential)); + fMap.insert( + std::make_tuple("verifyW3cPresentation", &anoncreds::verifyW3cPresentation)); + fMap.insert(std::make_tuple("w3cPresentationFromJson", + &anoncreds::w3cPresentationFromJson)); + fMap.insert( + std::make_tuple("w3cCredentialFromJson", &anoncreds::w3cCredentialFromJson)); + fMap.insert( + std::make_tuple("credentialToW3c", + &anoncreds::credentialToW3c)); + fMap.insert( + std::make_tuple("credentialFromW3c", + &anoncreds::credentialFromW3c)); return fMap; } diff --git a/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.cpp b/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.cpp index fb5ac140..59b2e142 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.cpp +++ b/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.cpp @@ -682,4 +682,217 @@ jsi::Value revocationRegistryDefinitionGetAttribute(jsi::Runtime &rt, return createReturnValue(rt, code, &out); }; +jsi::Value w3cPresentationFromJson(jsi::Runtime &rt, jsi::Object options) { + auto json = jsiToValue(rt, options, "json"); + + ObjectHandle out; + ByteBuffer b = stringToByteBuffer(json); + + ErrorCode code = anoncreds_w3c_presentation_from_json(b, &out); + auto returnValue = createReturnValue(rt, code, &out); + + // Free memory + delete[] b.data; + + return returnValue; +}; + +jsi::Value w3cCredentialFromJson(jsi::Runtime &rt, jsi::Object options) { + auto json = jsiToValue(rt, options, "json"); + + ObjectHandle out; + ByteBuffer b = stringToByteBuffer(json); + + ErrorCode code = anoncreds_w3c_credential_from_json(b, &out); + auto returnValue = createReturnValue(rt, code, &out); + + // Free memory + delete[] b.data; + + return returnValue; +}; + +jsi::Value createW3cPresentation(jsi::Runtime &rt, jsi::Object options) { + auto presentationRequest = + jsiToValue(rt, options, "presentationRequest"); + auto credentials = + jsiToValue(rt, options, "credentials"); + auto credentialsProve = + jsiToValue(rt, options, "credentialsProve"); + auto linkSecret = jsiToValue(rt, options, "linkSecret"); + auto schemas = jsiToValue(rt, options, "schemas"); + auto schemaIds = jsiToValue(rt, options, "schemaIds"); + auto credentialDefinitions = + jsiToValue(rt, options, "credentialDefinitions"); + auto credentialDefinitionIds = + jsiToValue(rt, options, "credentialDefinitionIds"); + auto w3cVersion = + jsiToValue(rt, options, "w3cVersion", true); + + ObjectHandle out; + + ErrorCode code = anoncreds_create_w3c_presentation( + presentationRequest, credentials, credentialsProve, linkSecret.c_str(), + schemas, schemaIds, credentialDefinitions, credentialDefinitionIds, + w3cVersion.length() ? w3cVersion.c_str() : nullptr, &out); + + auto returnValue = createReturnValue(rt, code, &out); + + // Free memory + delete[] credentials.data; + for (int i = 0; i < credentialsProve.count; i++) { + delete[] credentialsProve.data[i].referent; + } + for (int i = 0; i < schemaIds.count; i++) { + delete[] schemaIds.data[i]; + } + delete[] schemas.data; + for (int i = 0; i < credentialDefinitionIds.count; i++) { + delete[] credentialDefinitionIds.data[i]; + } + delete[] credentialDefinitions.data; + + return returnValue; +}; + +jsi::Value verifyW3cPresentation(jsi::Runtime &rt, jsi::Object options) { + auto presentation = jsiToValue(rt, options, "presentation"); + auto presentationRequest = + jsiToValue(rt, options, "presentationRequest"); + auto schemas = jsiToValue(rt, options, "schemas"); + auto schemaIds = jsiToValue(rt, options, "schemaIds"); + auto credentialDefinitions = + jsiToValue(rt, options, "credentialDefinitions"); + auto credentialDefinitionIds = + jsiToValue(rt, options, "credentialDefinitionIds"); + auto revocationRegistryDefinitions = jsiToValue( + rt, options, "revocationRegistryDefinitions", true); + auto revocationRegistryDefinitionIds = jsiToValue( + rt, options, "revocationRegistryDefinitionIds", true); + auto revocationStatusLists = jsiToValue( + rt, options, "revocationStatusLists", true); + auto nonRevokedIntervalOverrides = + jsiToValue( + rt, options, "nonRevokedIntervalOverrides", true); + + int8_t out; + + ErrorCode code = anoncreds_verify_w3c_presentation( + presentation, presentationRequest, schemas, schemaIds, + credentialDefinitions, credentialDefinitionIds, + revocationRegistryDefinitions, revocationRegistryDefinitionIds, + revocationStatusLists, nonRevokedIntervalOverrides, &out); + + auto returnValue = createReturnValue(rt, code, &out); + + // Free memory + for (int i = 0; i < schemaIds.count; i++) { + delete[] schemaIds.data[i]; + } + delete[] schemas.data; + for (int i = 0; i < credentialDefinitionIds.count; i++) { + delete[] credentialDefinitionIds.data[i]; + } + delete[] credentialDefinitions.data; + + return returnValue; +}; + +jsi::Value createW3cCredential(jsi::Runtime &rt, jsi::Object options) { + auto credentialDefinition = + jsiToValue(rt, options, "credentialDefinition"); + auto credentialDefinitionPrivate = + jsiToValue(rt, options, "credentialDefinitionPrivate"); + auto credentialOffer = + jsiToValue(rt, options, "credentialOffer"); + auto credentialRequest = + jsiToValue(rt, options, "credentialRequest"); + auto attributeNames = jsiToValue(rt, options, "attributeNames"); + auto attributeRawValues = + jsiToValue(rt, options, "attributeRawValues"); + auto revocation = + jsiToValue(rt, options, "revocationConfiguration", true); + auto w3cVersion = + jsiToValue(rt, options, "w3cVersion", true); + + ObjectHandle out; + + ErrorCode code = anoncreds_create_w3c_credential( + credentialDefinition, credentialDefinitionPrivate, credentialOffer, + credentialRequest, attributeNames, attributeRawValues, + revocation.reg_def ? &revocation : 0, + w3cVersion.length() ? w3cVersion.c_str() : nullptr, &out); + + return createReturnValue(rt, code, &out); +}; + +jsi::Value w3cCredentialGetIntegrityProofDetails(jsi::Runtime &rt, jsi::Object options) { + auto handle = jsiToValue(rt, options, "objectHandle"); + + ObjectHandle out; + + ErrorCode code = + anoncreds_w3c_credential_get_integrity_proof_details(handle, &out); + + return createReturnValue(rt, code, &out); +}; + +jsi::Value w3cCredentialProofGetAttribute(jsi::Runtime &rt, jsi::Object options) { + auto handle = jsiToValue(rt, options, "objectHandle"); + auto name = jsiToValue(rt, options, "name"); + + const char *out; + + ErrorCode code = + anoncreds_w3c_credential_proof_get_attribute(handle, name.c_str(), &out); + + return createReturnValue(rt, code, &out); +}; + +jsi::Value processW3cCredential(jsi::Runtime &rt, jsi::Object options) { + auto credential = jsiToValue(rt, options, "credential"); + auto credentialRequestMetadata = + jsiToValue(rt, options, "credentialRequestMetadata"); + auto linkSecret = jsiToValue(rt, options, "linkSecret"); + auto credentialDefinition = + jsiToValue(rt, options, "credentialDefinition"); + auto revocationRegistryDefinition = jsiToValue( + rt, options, "revocationRegistryDefinition", true); + + ObjectHandle out; + + ErrorCode code = anoncreds_process_w3c_credential( + credential, credentialRequestMetadata, linkSecret.c_str(), credentialDefinition, + revocationRegistryDefinition, &out); + + return createReturnValue(rt, code, &out); +}; + +jsi::Value credentialToW3c(jsi::Runtime &rt, jsi::Object options) { + auto credential = jsiToValue(rt, options, "objectHandle"); + auto credentialDefinition = + jsiToValue(rt, options, "credentialDefinition"); + auto w3cVersion = + jsiToValue(rt, options, "w3cVersion", true); + + ObjectHandle out; + + ErrorCode code = anoncreds_credential_to_w3c( + credential, credentialDefinition, + w3cVersion.length() ? w3cVersion.c_str() : nullptr, &out); + + return createReturnValue(rt, code, &out); +}; + +jsi::Value credentialFromW3c(jsi::Runtime &rt, jsi::Object options) { + auto credential = jsiToValue(rt, options, "objectHandle"); + + ObjectHandle out; + + ErrorCode code = anoncreds_credential_from_w3c( + credential, &out); + + return createReturnValue(rt, code, &out); +}; + } // namespace anoncreds diff --git a/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.h b/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.h index 4f03b4df..be53686c 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.h +++ b/wrappers/javascript/packages/anoncreds-react-native/cpp/anoncreds.h @@ -61,10 +61,14 @@ jsi::Value credentialDefinitionFromJson(jsi::Runtime &rt, jsi::Object options); jsi::Value credentialDefinitionPrivateFromJson(jsi::Runtime &rt, jsi::Object options); jsi::Value keyCorrectnessProofFromJson(jsi::Runtime &rt, jsi::Object options); +jsi::Value w3cCredentialFromJson(jsi::Runtime &rt, jsi::Object options); +jsi::Value w3cPresentationFromJson(jsi::Runtime &rt, jsi::Object options); // Proofs jsi::Value createPresentation(jsi::Runtime &rt, jsi::Object options); jsi::Value verifyPresentation(jsi::Runtime &rt, jsi::Object options); +jsi::Value createW3cPresentation(jsi::Runtime &rt, jsi::Object options); +jsi::Value verifyW3cPresentation(jsi::Runtime &rt, jsi::Object options); // Credentials jsi::Value createCredential(jsi::Runtime &rt, jsi::Object options); @@ -74,6 +78,13 @@ jsi::Value credentialGetAttribute(jsi::Runtime &rt, jsi::Object options); jsi::Value encodeCredentialAttributes(jsi::Runtime &rt, jsi::Object options); jsi::Value processCredential(jsi::Runtime &rt, jsi::Object options); +jsi::Value createW3cCredential(jsi::Runtime &rt, jsi::Object options); +jsi::Value w3cCredentialGetIntegrityProofDetails(jsi::Runtime &rt, jsi::Object options); +jsi::Value w3cCredentialProofGetAttribute(jsi::Runtime &rt, jsi::Object options); +jsi::Value processW3cCredential(jsi::Runtime &rt, jsi::Object options); +jsi::Value credentialToW3c(jsi::Runtime &rt, jsi::Object options); +jsi::Value credentialFromW3c(jsi::Runtime &rt, jsi::Object options); + // Revocation jsi::Value createOrUpdateRevocationState(jsi::Runtime &rt, jsi::Object options); jsi::Value createRevocationStatusList(jsi::Runtime &rt, jsi::Object options); diff --git a/wrappers/javascript/packages/anoncreds-react-native/cpp/include/libanoncreds.h b/wrappers/javascript/packages/anoncreds-react-native/cpp/include/libanoncreds.h index 26129996..b6310bdc 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/cpp/include/libanoncreds.h +++ b/wrappers/javascript/packages/anoncreds-react-native/cpp/include/libanoncreds.h @@ -440,6 +440,65 @@ ErrorCode anoncreds_verify_presentation(ObjectHandle presentation, struct FfiList_FfiNonrevokedIntervalOverride nonrevoked_interval_override, int8_t *result_p); +ErrorCode anoncreds_create_w3c_credential(ObjectHandle cred_def, + ObjectHandle cred_def_private, + ObjectHandle cred_offer, + ObjectHandle cred_request, + FfiStrList attr_names, + FfiStrList attr_raw_values, + const struct FfiCredRevInfo *revocation, + FfiStr w3c_version, + ObjectHandle *cred_p); + +ErrorCode anoncreds_process_w3c_credential(ObjectHandle cred, + ObjectHandle cred_req_metadata, + FfiStr link_secret, + ObjectHandle cred_def, + ObjectHandle rev_reg_def, + ObjectHandle *cred_p); + +ErrorCode anoncreds_credential_to_w3c(ObjectHandle cred, + ObjectHandle cred_def, + FfiStr w3c_version, + ObjectHandle *cred_p); + +ErrorCode anoncreds_credential_from_w3c(ObjectHandle cred, + ObjectHandle *cred_p); + +ErrorCode anoncreds_create_w3c_presentation(ObjectHandle pres_req, + struct FfiList_FfiCredentialEntry credentials, + struct FfiList_FfiCredentialProve credentials_prove, + FfiStr link_secret, + struct FfiList_ObjectHandle schemas, + FfiStrList schema_ids, + struct FfiList_ObjectHandle cred_defs, + FfiStrList cred_def_ids, + FfiStr w3c_version, + ObjectHandle *presentation_p); + +ErrorCode anoncreds_verify_w3c_presentation(ObjectHandle presentation, + ObjectHandle pres_req, + struct FfiList_ObjectHandle schemas, + FfiStrList schema_ids, + struct FfiList_ObjectHandle cred_defs, + FfiStrList cred_def_ids, + struct FfiList_ObjectHandle rev_reg_defs, + FfiStrList rev_reg_def_ids, + struct FfiList_ObjectHandle rev_status_list, + struct FfiList_FfiNonrevokedIntervalOverride nonrevoked_interval_override, + int8_t *result_p); + +ErrorCode anoncreds_w3c_credential_get_integrity_proof_details(ObjectHandle handle, + ObjectHandle *result_p); + +ErrorCode anoncreds_w3c_credential_proof_get_attribute(ObjectHandle handle, + FfiStr name, + const char **result_p); + +ErrorCode anoncreds_w3c_credential_from_json(struct ByteBuffer json, ObjectHandle *result_p); + +ErrorCode anoncreds_w3c_presentation_from_json(struct ByteBuffer json, ObjectHandle *result_p); + char *anoncreds_version(void); #ifdef __cplusplus diff --git a/wrappers/javascript/packages/anoncreds-react-native/src/NativeBindings.ts b/wrappers/javascript/packages/anoncreds-react-native/src/NativeBindings.ts index 12df01d3..2a531475 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/src/NativeBindings.ts +++ b/wrappers/javascript/packages/anoncreds-react-native/src/NativeBindings.ts @@ -185,4 +185,69 @@ export type NativeBindings = { credentialDefinitionPrivateFromJson(options: { json: string }): ReturnObject keyCorrectnessProofFromJson(options: { json: string }): ReturnObject + + createW3cCredential(options: { + credentialDefinition: number + credentialDefinitionPrivate: number + credentialOffer: number + credentialRequest: number + attributeNames: string[] + attributeRawValues: string[] + revocationConfiguration?: { + registryIndex: number + revocationRegistryDefinition: number + revocationRegistryDefinitionPrivate: number + revocationStatusList?: number + } + w3cVersion?: string + }): ReturnObject + + processW3cCredential(options: { + credential: number + credentialRequestMetadata: number + linkSecret: string + credentialDefinition: number + revocationRegistryDefinition?: number + }): ReturnObject + + w3cCredentialGetIntegrityProofDetails(options: { objectHandle: number }): ReturnObject + + w3cCredentialProofGetAttribute(options: { objectHandle: number; name: string }): ReturnObject + + credentialToW3c(options: { + objectHandle: number + credentialDefinition: number + w3cVersion?: string + }): ReturnObject + + credentialFromW3c(options: { objectHandle: number }): ReturnObject + + createW3cPresentation(options: { + presentationRequest: number + credentials: { credential: number; timestamp?: number; revocationState?: number }[] + credentialsProve: NativeCredentialProve[] + linkSecret: string + schemaIds: string[] + schemas: number[] + credentialDefinitionIds: string[] + credentialDefinitions: number[] + w3cVersion?: string + }): ReturnObject + + verifyW3cPresentation(options: { + presentation: number + presentationRequest: number + schemas: number[] + schemaIds: string[] + credentialDefinitions: number[] + credentialDefinitionIds: string[] + revocationRegistryDefinitions?: number[] + revocationRegistryDefinitionIds?: string[] + revocationStatusLists?: number[] + nonRevokedIntervalOverrides?: NativeNonRevokedIntervalOverride[] + }): ReturnObject + + w3cCredentialFromJson(options: { json: string }): ReturnObject + + w3cPresentationFromJson(options: { json: string }): ReturnObject } diff --git a/wrappers/javascript/packages/anoncreds-react-native/src/ReactNativeAnoncreds.ts b/wrappers/javascript/packages/anoncreds-react-native/src/ReactNativeAnoncreds.ts index 9f855405..254dfead 100644 --- a/wrappers/javascript/packages/anoncreds-react-native/src/ReactNativeAnoncreds.ts +++ b/wrappers/javascript/packages/anoncreds-react-native/src/ReactNativeAnoncreds.ts @@ -392,4 +392,132 @@ export class ReactNativeAnoncreds implements Anoncreds { const handle = this.handleError(this.anoncreds.keyCorrectnessProofFromJson(serializeArguments(options))) return new ObjectHandle(handle) } + + public createW3cCredential(options: { + credentialDefinition: ObjectHandle + credentialDefinitionPrivate: ObjectHandle + credentialOffer: ObjectHandle + credentialRequest: ObjectHandle + attributeRawValues: Record + revocationConfiguration?: NativeCredentialRevocationConfig + w3cVersion?: string + }): ObjectHandle { + const attributeNames = Object.keys(options.attributeRawValues) + const attributeRawValues = Object.values(options.attributeRawValues) + + const credential = this.handleError( + this.anoncreds.createW3cCredential({ + ...serializeArguments(options), + attributeRawValues, + attributeNames, + revocationConfiguration: options.revocationConfiguration + ? { + registryIndex: options.revocationConfiguration.registryIndex, + revocationRegistryDefinition: options.revocationConfiguration.revocationRegistryDefinition.handle, + revocationRegistryDefinitionPrivate: + options.revocationConfiguration.revocationRegistryDefinitionPrivate.handle, + revocationStatusList: options.revocationConfiguration.revocationStatusList.handle + } + : undefined, + w3cVersion: options.w3cVersion + }) + ) + + return new ObjectHandle(credential) + } + + public processW3cCredential(options: { + credential: ObjectHandle + credentialRequestMetadata: ObjectHandle + linkSecret: string + credentialDefinition: ObjectHandle + revocationRegistryDefinition?: ObjectHandle + }): ObjectHandle { + const handle = this.handleError(this.anoncreds.processW3cCredential(serializeArguments(options))) + return new ObjectHandle(handle) + } + + public createW3cPresentation(options: { + presentationRequest: ObjectHandle + credentials: NativeCredentialEntry[] + credentialsProve: NativeCredentialProve[] + linkSecret: string + schemas: Record + credentialDefinitions: Record + w3cVersion?: string + }): ObjectHandle { + const schemaKeys = Object.keys(options.schemas) + const schemaValues = Object.values(options.schemas).map((o) => o.handle) + const credentialDefinitionKeys = Object.keys(options.credentialDefinitions) + const credentialDefinitionValues = Object.values(options.credentialDefinitions).map((o) => o.handle) + + const credentialEntries = options.credentials.map((value) => ({ + credential: value.credential.handle, + timestamp: value.timestamp ?? -1, + revocationState: value.revocationState?.handle ?? 0 + })) + + const handle = this.handleError( + this.anoncreds.createW3cPresentation({ + presentationRequest: options.presentationRequest.handle, + linkSecret: options.linkSecret, + credentialsProve: options.credentialsProve, + credentials: credentialEntries, + schemas: schemaValues, + schemaIds: schemaKeys, + credentialDefinitions: credentialDefinitionValues, + credentialDefinitionIds: credentialDefinitionKeys, + w3cVersion: options.w3cVersion + }) + ) + return new ObjectHandle(handle) + } + + public verifyW3cPresentation(options: { + presentation: ObjectHandle + presentationRequest: ObjectHandle + schemas: ObjectHandle[] + schemaIds: string[] + credentialDefinitions: ObjectHandle[] + credentialDefinitionIds: string[] + revocationRegistryDefinitions?: ObjectHandle[] + revocationRegistryDefinitionIds?: string[] + revocationStatusLists?: ObjectHandle[] + nonRevokedIntervalOverrides?: NativeNonRevokedIntervalOverride[] + }): boolean { + return Boolean(this.handleError(this.anoncreds.verifyW3cPresentation(serializeArguments(options)))) + } + + public w3cCredentialGetIntegrityProofDetails(options: { objectHandle: ObjectHandle }): ObjectHandle { + const handle = this.handleError(this.anoncreds.w3cCredentialGetIntegrityProofDetails(serializeArguments(options))) + return new ObjectHandle(handle) + } + + public w3cCredentialProofGetAttribute(options: { objectHandle: ObjectHandle; name: string }): string { + return this.handleError(this.anoncreds.w3cCredentialProofGetAttribute(serializeArguments(options))) + } + + public w3cPresentationFromJson(options: { json: string }): ObjectHandle { + const handle = this.handleError(this.anoncreds.w3cPresentationFromJson(serializeArguments(options))) + return new ObjectHandle(handle) + } + + public w3cCredentialFromJson(options: { json: string }): ObjectHandle { + const handle = this.handleError(this.anoncreds.w3cCredentialFromJson(serializeArguments(options))) + return new ObjectHandle(handle) + } + + public credentialToW3c(options: { + objectHandle: ObjectHandle + credentialDefinition: ObjectHandle + w3cVersion?: string + }): ObjectHandle { + const handle = this.handleError(this.anoncreds.credentialToW3c(serializeArguments(options))) + return new ObjectHandle(handle) + } + + public credentialFromW3c(options: { objectHandle: ObjectHandle }): ObjectHandle { + const handle = this.handleError(this.anoncreds.credentialFromW3c(serializeArguments(options))) + return new ObjectHandle(handle) + } } diff --git a/wrappers/javascript/packages/anoncreds-shared/src/Anoncreds.ts b/wrappers/javascript/packages/anoncreds-shared/src/Anoncreds.ts index 911b74b4..e011fe47 100644 --- a/wrappers/javascript/packages/anoncreds-shared/src/Anoncreds.ts +++ b/wrappers/javascript/packages/anoncreds-shared/src/Anoncreds.ts @@ -204,4 +204,61 @@ export type Anoncreds = { getTypeName(options: { objectHandle: ObjectHandle }): string objectFree(options: { objectHandle: ObjectHandle }): void + + createW3cCredential(options: { + credentialDefinition: ObjectHandle + credentialDefinitionPrivate: ObjectHandle + credentialOffer: ObjectHandle + credentialRequest: ObjectHandle + attributeRawValues: Record + revocationConfiguration?: NativeCredentialRevocationConfig + w3cVersion?: string + }): ObjectHandle + + processW3cCredential(options: { + credential: ObjectHandle + credentialRequestMetadata: ObjectHandle + linkSecret: string + credentialDefinition: ObjectHandle + revocationRegistryDefinition?: ObjectHandle + }): ObjectHandle + + createW3cPresentation(options: { + presentationRequest: ObjectHandle + credentials: NativeCredentialEntry[] + credentialsProve: NativeCredentialProve[] + linkSecret: string + schemas: Record + credentialDefinitions: Record + w3cVersion?: string + }): ObjectHandle + + verifyW3cPresentation(options: { + presentation: ObjectHandle + presentationRequest: ObjectHandle + schemas: ObjectHandle[] + schemaIds: string[] + credentialDefinitions: ObjectHandle[] + credentialDefinitionIds: string[] + revocationRegistryDefinitions?: ObjectHandle[] + revocationRegistryDefinitionIds?: string[] + revocationStatusLists?: ObjectHandle[] + nonRevokedIntervalOverrides?: NativeNonRevokedIntervalOverride[] + }): boolean + + w3cPresentationFromJson(options: { json: string }): ObjectHandle + + w3cCredentialFromJson(options: { json: string }): ObjectHandle + + credentialToW3c(options: { + objectHandle: ObjectHandle + credentialDefinition: ObjectHandle + w3cVersion?: string + }): ObjectHandle + + credentialFromW3c(options: { objectHandle: ObjectHandle }): ObjectHandle + + w3cCredentialGetIntegrityProofDetails(options: { objectHandle: ObjectHandle }): ObjectHandle + + w3cCredentialProofGetAttribute(options: { objectHandle: ObjectHandle; name: string }): string } diff --git a/wrappers/javascript/packages/anoncreds-shared/src/api/Credential.ts b/wrappers/javascript/packages/anoncreds-shared/src/api/Credential.ts index e8857875..3982ded8 100644 --- a/wrappers/javascript/packages/anoncreds-shared/src/api/Credential.ts +++ b/wrappers/javascript/packages/anoncreds-shared/src/api/Credential.ts @@ -12,6 +12,7 @@ import { CredentialOffer } from './CredentialOffer' import { CredentialRequest } from './CredentialRequest' import { CredentialRequestMetadata } from './CredentialRequestMetadata' import { RevocationRegistryDefinition } from './RevocationRegistryDefinition' +import { W3cCredential } from './W3cCredential' import { pushToArray } from './utils' export type CreateCredentialOptions = { @@ -33,6 +34,15 @@ export type ProcessCredentialOptions = { revocationRegistryDefinition?: RevocationRegistryDefinition | JsonObject } +export type CredentialToW3cOptions = { + credentialDefinition: CredentialDefinition | JsonObject + w3cVersion?: string +} + +export type CredentialFromW3cOptions = { + credential: W3cCredential +} + export class Credential extends AnoncredsObject { public static create(options: CreateCredentialOptions) { let credential @@ -123,7 +133,6 @@ export class Credential extends AnoncredsObject { } return this } - public get schemaId() { return anoncreds.credentialGetAttribute({ objectHandle: this.handle, name: 'schema_id' }) } @@ -140,4 +149,33 @@ export class Credential extends AnoncredsObject { const index = anoncreds.credentialGetAttribute({ objectHandle: this.handle, name: 'rev_reg_index' }) return index ? Number(index) : undefined } + + public toW3c(options: CredentialToW3cOptions): W3cCredential { + let credential + // Objects created within this method must be freed up + const objectHandles: ObjectHandle[] = [] + try { + const credentialDefinition = + options.credentialDefinition instanceof CredentialDefinition + ? options.credentialDefinition.handle + : pushToArray(CredentialDefinition.fromJson(options.credentialDefinition).handle, objectHandles) + + credential = new W3cCredential( + anoncreds.credentialToW3c({ + objectHandle: this.handle, + credentialDefinition, + w3cVersion: options.w3cVersion + }).handle + ) + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return credential + } + + public static fromW3c(options: CredentialFromW3cOptions) { + return new Credential(anoncreds.credentialFromW3c({ objectHandle: options.credential.handle }).handle) + } } diff --git a/wrappers/javascript/packages/anoncreds-shared/src/api/W3cCredential.ts b/wrappers/javascript/packages/anoncreds-shared/src/api/W3cCredential.ts new file mode 100644 index 00000000..9f3c69cf --- /dev/null +++ b/wrappers/javascript/packages/anoncreds-shared/src/api/W3cCredential.ts @@ -0,0 +1,202 @@ +import type { CredentialRevocationConfig } from './CredentialRevocationConfig' +import type { RevocationStatusList } from './RevocationStatusList' +import type { ObjectHandle } from '../ObjectHandle' +import type { JsonObject } from '../types' + +import { AnoncredsObject } from '../AnoncredsObject' +import { anoncreds } from '../register' + +import { Credential } from './Credential' +import { CredentialDefinition } from './CredentialDefinition' +import { CredentialDefinitionPrivate } from './CredentialDefinitionPrivate' +import { CredentialOffer } from './CredentialOffer' +import { CredentialRequest } from './CredentialRequest' +import { CredentialRequestMetadata } from './CredentialRequestMetadata' +import { RevocationRegistryDefinition } from './RevocationRegistryDefinition' +import { pushToArray } from './utils' + +export type CreateW3cCredentialOptions = { + credentialDefinition: CredentialDefinition | JsonObject + credentialDefinitionPrivate: CredentialDefinitionPrivate | JsonObject + credentialOffer: CredentialOffer | JsonObject + credentialRequest: CredentialRequest | JsonObject + attributeRawValues: Record + revocationRegistryId?: string + revocationConfiguration?: CredentialRevocationConfig + revocationStatusList?: RevocationStatusList | JsonObject + w3cVersion?: string +} + +export type ProcessW3cCredentialOptions = { + credentialRequestMetadata: CredentialRequestMetadata | JsonObject + linkSecret: string + credentialDefinition: CredentialDefinition | JsonObject + revocationRegistryDefinition?: RevocationRegistryDefinition | JsonObject +} + +export type W3cCredentialFromLegacyOptions = { + credential: Credential + credentialDefinition: CredentialDefinition | JsonObject + w3cVersion?: string +} + +export class W3cCredential extends AnoncredsObject { + private proofDetails?: ObjectHandle + + public static create(options: CreateW3cCredentialOptions) { + let credential + // Objects created within this method must be freed up + const objectHandles: ObjectHandle[] = [] + try { + const credentialDefinition = + options.credentialDefinition instanceof CredentialDefinition + ? options.credentialDefinition.handle + : pushToArray(CredentialDefinition.fromJson(options.credentialDefinition).handle, objectHandles) + + const credentialDefinitionPrivate = + options.credentialDefinitionPrivate instanceof CredentialDefinitionPrivate + ? options.credentialDefinitionPrivate.handle + : pushToArray(CredentialDefinitionPrivate.fromJson(options.credentialDefinitionPrivate).handle, objectHandles) + + const credentialOffer = + options.credentialOffer instanceof CredentialOffer + ? options.credentialOffer.handle + : pushToArray(CredentialOffer.fromJson(options.credentialOffer).handle, objectHandles) + + const credentialRequest = + options.credentialRequest instanceof CredentialRequest + ? options.credentialRequest.handle + : pushToArray(CredentialRequest.fromJson(options.credentialRequest).handle, objectHandles) + + credential = anoncreds.createW3cCredential({ + credentialDefinition, + credentialDefinitionPrivate, + credentialOffer, + credentialRequest, + attributeRawValues: options.attributeRawValues, + revocationConfiguration: options.revocationConfiguration?.native, + w3cVersion: options.w3cVersion + }) + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return new W3cCredential(credential.handle) + } + + public static fromJson(json: JsonObject) { + return new W3cCredential(anoncreds.w3cCredentialFromJson({ json: JSON.stringify(json) }).handle) + } + + public process(options: ProcessW3cCredentialOptions) { + let credential + // Objects created within this method must be freed up + const objectHandles: ObjectHandle[] = [] + try { + const credentialDefinition = + options.credentialDefinition instanceof CredentialDefinition + ? options.credentialDefinition.handle + : pushToArray(CredentialDefinition.fromJson(options.credentialDefinition).handle, objectHandles) + + const credentialRequestMetadata = + options.credentialRequestMetadata instanceof CredentialRequestMetadata + ? options.credentialRequestMetadata.handle + : pushToArray(CredentialRequestMetadata.fromJson(options.credentialRequestMetadata).handle, objectHandles) + + const revocationRegistryDefinition = + options.revocationRegistryDefinition instanceof RevocationRegistryDefinition + ? options.revocationRegistryDefinition.handle + : options.revocationRegistryDefinition !== undefined + ? pushToArray( + RevocationRegistryDefinition.fromJson(options.revocationRegistryDefinition).handle, + objectHandles + ) + : undefined + + credential = anoncreds.processW3cCredential({ + credential: this.handle, + credentialDefinition, + credentialRequestMetadata, + linkSecret: options.linkSecret, + revocationRegistryDefinition + }) + + // We can discard previous handle and store the new one + this.handle.clear() + this.handle = credential + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return this + } + + private getProofDetails(): ObjectHandle { + if (!this.proofDetails) { + this.proofDetails = anoncreds.w3cCredentialGetIntegrityProofDetails({ objectHandle: this.handle }) + } + return this.proofDetails + } + + public get schemaId() { + const proofDetails = this.getProofDetails() + return anoncreds.w3cCredentialProofGetAttribute({ objectHandle: proofDetails, name: 'schema_id' }) + } + + public get credentialDefinitionId() { + const proofDetails = this.getProofDetails() + return anoncreds.w3cCredentialProofGetAttribute({ objectHandle: proofDetails, name: 'cred_def_id' }) + } + + public get revocationRegistryId() { + const proofDetails = this.getProofDetails() + return anoncreds.w3cCredentialProofGetAttribute({ objectHandle: proofDetails, name: 'rev_reg_id' }) + } + + public get revocationRegistryIndex() { + const proofDetails = this.getProofDetails() + const index = anoncreds.w3cCredentialProofGetAttribute({ objectHandle: proofDetails, name: 'rev_reg_index' }) + return index ? Number(index) : undefined + } + + public get timestamp() { + const proofDetails = this.getProofDetails() + const index = anoncreds.w3cCredentialProofGetAttribute({ objectHandle: proofDetails, name: 'timestamp' }) + return index ? Number(index) : undefined + } + + public toLegacy(): Credential { + return new Credential( + anoncreds.credentialFromW3c({ + objectHandle: this.handle + }).handle + ) + } + + public static fromLegacy(options: W3cCredentialFromLegacyOptions): W3cCredential { + let credential + // Objects created within this method must be freed up + const objectHandles: ObjectHandle[] = [] + try { + const credentialDefinition = + options.credentialDefinition instanceof CredentialDefinition + ? options.credentialDefinition.handle + : pushToArray(CredentialDefinition.fromJson(options.credentialDefinition).handle, objectHandles) + + credential = new W3cCredential( + anoncreds.credentialToW3c({ + objectHandle: options.credential.handle, + credentialDefinition, + w3cVersion: options.w3cVersion + }).handle + ) + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return credential + } +} diff --git a/wrappers/javascript/packages/anoncreds-shared/src/api/W3cPresentation.ts b/wrappers/javascript/packages/anoncreds-shared/src/api/W3cPresentation.ts new file mode 100644 index 00000000..f233bc8f --- /dev/null +++ b/wrappers/javascript/packages/anoncreds-shared/src/api/W3cPresentation.ts @@ -0,0 +1,160 @@ +import type { CredentialProve, NonRevokedIntervalOverride } from './Presentation' +import type { ObjectHandle } from '../ObjectHandle' +import type { JsonObject } from '../types' + +import { AnoncredsObject } from '../AnoncredsObject' +import { anoncreds } from '../register' + +import { CredentialDefinition } from './CredentialDefinition' +import { CredentialRevocationState } from './CredentialRevocationState' +import { PresentationRequest } from './PresentationRequest' +import { RevocationRegistryDefinition } from './RevocationRegistryDefinition' +import { RevocationStatusList } from './RevocationStatusList' +import { Schema } from './Schema' +import { W3cCredential } from './W3cCredential' +import { pushToArray } from './utils' + +// TODO: Simplify Presentation API (see PresentCredentials object in python wrapper)) + +export type W3cCredentialEntry = { + credential: W3cCredential | JsonObject + timestamp?: number + revocationState?: CredentialRevocationState | JsonObject +} + +export type CreateW3cPresentationOptions = { + presentationRequest: PresentationRequest | JsonObject + credentials: W3cCredentialEntry[] + credentialsProve: CredentialProve[] + linkSecret: string + schemas: Record + credentialDefinitions: Record +} + +export type VerifyW3cPresentationOptions = { + presentationRequest: PresentationRequest | JsonObject + schemas: Record + credentialDefinitions: Record + revocationRegistryDefinitions?: Record + revocationStatusLists?: (RevocationStatusList | JsonObject)[] + nonRevokedIntervalOverrides?: NonRevokedIntervalOverride[] +} + +export class W3cPresentation extends AnoncredsObject { + public static create(options: CreateW3cPresentationOptions) { + let presentationHandle + // Objects created within this method must be freed up + const objectHandles: ObjectHandle[] = [] + try { + const presentationRequest = + options.presentationRequest instanceof PresentationRequest + ? options.presentationRequest.handle + : pushToArray(PresentationRequest.fromJson(options.presentationRequest).handle, objectHandles) + + presentationHandle = anoncreds.createW3cPresentation({ + presentationRequest, + credentials: options.credentials.map((item) => ({ + credential: + item.credential instanceof W3cCredential + ? item.credential.handle + : pushToArray(W3cCredential.fromJson(item.credential).handle, objectHandles), + revocationState: + item.revocationState instanceof CredentialRevocationState + ? item.revocationState.handle + : item.revocationState !== undefined + ? pushToArray(CredentialRevocationState.fromJson(item.revocationState).handle, objectHandles) + : undefined, + + timestamp: item.timestamp + })), + credentialsProve: options.credentialsProve, + linkSecret: options.linkSecret, + schemas: Object.entries(options.schemas).reduce>((prev, [id, object]) => { + const objectHandle = + object instanceof Schema ? object.handle : pushToArray(Schema.fromJson(object).handle, objectHandles) + + prev[id] = objectHandle + return prev + }, {}), + credentialDefinitions: Object.entries(options.credentialDefinitions).reduce>( + (prev, [id, object]) => { + const objectHandle = + object instanceof CredentialDefinition + ? object.handle + : pushToArray(CredentialDefinition.fromJson(object).handle, objectHandles) + + prev[id] = objectHandle + return prev + }, + {} + ) + }).handle + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return new W3cPresentation(presentationHandle) + } + + public static fromJson(json: JsonObject) { + return new W3cPresentation(anoncreds.w3cPresentationFromJson({ json: JSON.stringify(json) }).handle) + } + + public verify(options: VerifyW3cPresentationOptions) { + const schemas = Object.values(options.schemas) + const schemaIds = Object.keys(options.schemas) + + const credentialDefinitions = Object.values(options.credentialDefinitions) + const credentialDefinitionIds = Object.keys(options.credentialDefinitions) + + const revocationRegistryDefinitions = options.revocationRegistryDefinitions + ? Object.values(options.revocationRegistryDefinitions) + : undefined + + const revocationRegistryDefinitionIds = options.revocationRegistryDefinitions + ? Object.keys(options.revocationRegistryDefinitions) + : undefined + + let verified + const objectHandles: ObjectHandle[] = [] + try { + const presentationRequest = + options.presentationRequest instanceof PresentationRequest + ? options.presentationRequest.handle + : pushToArray(PresentationRequest.fromJson(options.presentationRequest).handle, objectHandles) + + verified = anoncreds.verifyW3cPresentation({ + presentation: this.handle, + presentationRequest, + schemas: schemas.map((o) => + o instanceof Schema ? o.handle : pushToArray(Schema.fromJson(o).handle, objectHandles) + ), + schemaIds, + credentialDefinitions: credentialDefinitions.map((o) => + o instanceof CredentialDefinition + ? o.handle + : pushToArray(CredentialDefinition.fromJson(o).handle, objectHandles) + ), + credentialDefinitionIds, + revocationRegistryDefinitions: revocationRegistryDefinitions?.map((o) => + o instanceof RevocationRegistryDefinition + ? o.handle + : pushToArray(RevocationRegistryDefinition.fromJson(o).handle, objectHandles) + ), + revocationRegistryDefinitionIds, + revocationStatusLists: options.revocationStatusLists?.map((o) => + o instanceof RevocationStatusList + ? o.handle + : pushToArray(RevocationStatusList.fromJson(o).handle, objectHandles) + ), + nonRevokedIntervalOverrides: options.nonRevokedIntervalOverrides + }) + } finally { + objectHandles.forEach((handle) => { + handle.clear() + }) + } + return verified + } +} diff --git a/wrappers/javascript/packages/anoncreds-shared/src/api/index.ts b/wrappers/javascript/packages/anoncreds-shared/src/api/index.ts index 6f2750c8..e5e3239c 100644 --- a/wrappers/javascript/packages/anoncreds-shared/src/api/index.ts +++ b/wrappers/javascript/packages/anoncreds-shared/src/api/index.ts @@ -15,3 +15,5 @@ export * from './RevocationRegistry' export * from './RevocationRegistryDefinition' export * from './RevocationRegistryDefinitionPrivate' export * from './RevocationStatusList' +export * from './W3cCredential' +export * from './W3cPresentation' diff --git a/wrappers/python/anoncreds/__init__.py b/wrappers/python/anoncreds/__init__.py index c86a2b85..846a8843 100644 --- a/wrappers/python/anoncreds/__init__.py +++ b/wrappers/python/anoncreds/__init__.py @@ -21,6 +21,8 @@ RevocationStatusList, RevocationRegistryDefinition, RevocationRegistryDefinitionPrivate, + W3cCredential, + W3cPresentation, ) __all__ = ( @@ -48,4 +50,6 @@ "RevocationRegistryDefinition", "RevocationRegistryDefinitionPrivate", "Schema", + "W3cCredential", + "W3cPresentation" ) diff --git a/wrappers/python/anoncreds/bindings.py b/wrappers/python/anoncreds/bindings.py index 28122f8b..c4073379 100644 --- a/wrappers/python/anoncreds/bindings.py +++ b/wrappers/python/anoncreds/bindings.py @@ -964,3 +964,166 @@ def create_or_update_revocation_state( byref(updated_rev_state), ) return updated_rev_state + + +def create_w3c_credential( + cred_def: ObjectHandle, + cred_def_private: ObjectHandle, + cred_offer: ObjectHandle, + cred_request: ObjectHandle, + attr_raw_values: Mapping[str, str], + revocation_config: Optional[RevocationConfig], + w3c_version: Optional[str], +) -> ObjectHandle: + cred = ObjectHandle() + attr_keys = list(attr_raw_values.keys()) + names_list = FfiStrList.create(attr_keys) + raw_values_list = FfiStrList.create(str(attr_raw_values[k]) for k in attr_keys) + do_call( + "anoncreds_create_w3c_credential", + cred_def, + cred_def_private, + cred_offer, + cred_request, + names_list, + raw_values_list, + pointer(revocation_config) + if revocation_config + else POINTER(RevocationConfig)(), + encode_str(w3c_version), + byref(cred), + ) + return cred + + +def process_w3c_credential( + cred: ObjectHandle, + cred_req_metadata: ObjectHandle, + link_secret: str, + cred_def: ObjectHandle, + rev_reg_def: Optional[ObjectHandle], +) -> ObjectHandle: + result = ObjectHandle() + do_call( + "anoncreds_process_w3c_credential", + cred, + cred_req_metadata, + encode_str(link_secret), + cred_def, + rev_reg_def or ObjectHandle(), + byref(result), + ) + return result + + +def credential_to_w3c( + cred: ObjectHandle, + cred_def: ObjectHandle, + w3c_version: Optional[str], +) -> ObjectHandle: + result = ObjectHandle() + do_call( + "anoncreds_credential_to_w3c", + cred, + cred_def, + encode_str(w3c_version), + byref(result), + ) + return result + + +def credential_from_w3c( + cred: ObjectHandle, +) -> ObjectHandle: + result = ObjectHandle() + do_call( + "anoncreds_credential_from_w3c", + cred, + byref(result), + ) + return result + + +def create_w3c_presentation( + pres_req: ObjectHandle, + credentials: Sequence[CredentialEntry], + credentials_prove: Sequence[CredentialProve], + link_secret: str, + schemas: Sequence[ObjectHandle], + schema_ids: Sequence[str], + cred_defs: Sequence[ObjectHandle], + cred_def_ids: Sequence[str], + w3c_version: Optional[str], +) -> ObjectHandle: + entry_list = CredentialEntryList() + entry_list.count = len(credentials) + entry_list.data = (CredentialEntry * entry_list.count)(*credentials) + prove_list = CredentialProveList() + prove_list.count = len(credentials_prove) + prove_list.data = (CredentialProve * prove_list.count)(*credentials_prove) + present = ObjectHandle() + + do_call( + "anoncreds_create_w3c_presentation", + pres_req, + entry_list, + prove_list, + encode_str(link_secret), + FfiObjectHandleList.create(schemas), + FfiStrList.create(schema_ids), + FfiObjectHandleList.create(cred_defs), + FfiStrList.create(cred_def_ids), + encode_str(w3c_version), + byref(present), + ) + return present + + +def verify_w3c_presentation( + presentation: ObjectHandle, + pres_req: ObjectHandle, + schema_ids: Sequence[str], + schemas: Sequence[ObjectHandle], + cred_def_ids: Sequence[str], + cred_defs: Sequence[ObjectHandle], + rev_reg_def_ids: Optional[Sequence[str]], + rev_reg_defs: Optional[Sequence[ObjectHandle]], + rev_status_lists: Optional[Sequence[ObjectHandle]], + nonrevoked_interval_overrides: Optional[Sequence[NonrevokedIntervalOverride]], +) -> bool: + verify = c_int8() + + nonrevoked_interval_overrides_list = NonrevokedIntervalOverrideList() + if nonrevoked_interval_overrides: + nonrevoked_interval_overrides_list.count = len(nonrevoked_interval_overrides) + nonrevoked_interval_overrides_list.data = ( + NonrevokedIntervalOverride * nonrevoked_interval_overrides.count + )(*nonrevoked_interval_overrides) + + do_call( + "anoncreds_verify_w3c_presentation", + presentation, + pres_req, + FfiObjectHandleList.create(schemas), + FfiStrList.create(schema_ids), + FfiObjectHandleList.create(cred_defs), + FfiStrList.create(cred_def_ids), + FfiObjectHandleList.create(rev_reg_defs), + FfiStrList.create(rev_reg_def_ids), + FfiObjectHandleList.create(rev_status_lists), + nonrevoked_interval_overrides_list, + byref(verify), + ) + return bool(verify) + + +def w3c_credential_get_integrity_proof_details( + cred: ObjectHandle, +) -> ObjectHandle: + result = ObjectHandle() + do_call( + "anoncreds_w3c_credential_get_integrity_proof_details", + cred, + byref(result), + ) + return result diff --git a/wrappers/python/anoncreds/types.py b/wrappers/python/anoncreds/types.py index 46ab4f14..ab49bf93 100644 --- a/wrappers/python/anoncreds/types.py +++ b/wrappers/python/anoncreds/types.py @@ -1,3 +1,4 @@ +import json from typing import Mapping, Optional, Sequence, Tuple, Union from . import bindings @@ -342,6 +343,166 @@ def rev_reg_index(self) -> Optional[int]: ) return int(str(sval)) if sval is not None else None + def to_w3c( + self, + cred_def: Union[str, CredentialDefinition], + w3c_version: Optional[str] = None, + ) -> "W3cCredential": + if not isinstance(cred_def, bindings.AnoncredsObject): + cred_def = CredentialDefinition.load(cred_def) + return W3cCredential( + bindings.credential_to_w3c( + self.handle, + cred_def.handle, + w3c_version + ) + ) + + @classmethod + def from_w3c(cls, cred: "W3cCredential") -> "Credential": + return Credential( + bindings.credential_from_w3c( + cred.handle + ) + ) + + +class W3cCredential(bindings.AnoncredsObject): + GET_ATTR = "anoncreds_w3c_credential_proof_get_attribute" + _proof_details = None + + @classmethod + def create( + cls, + cred_def: Union[str, CredentialDefinition], + cred_def_private: Union[str, CredentialDefinitionPrivate], + cred_offer: Union[str, CredentialOffer], + cred_request: Union[str, CredentialRequest], + attr_raw_values: Mapping[str, str], + revocation_config: Optional["CredentialRevocationConfig"] = None, + w3c_version: Optional[str] = None, + ) -> "W3cCredential": + if not isinstance(cred_def, bindings.AnoncredsObject): + cred_def = CredentialDefinition.load(cred_def) + if not isinstance(cred_def_private, bindings.AnoncredsObject): + cred_def_private = CredentialDefinitionPrivate.load(cred_def_private) + if not isinstance(cred_offer, bindings.AnoncredsObject): + cred_offer = CredentialOffer.load(cred_offer) + if not isinstance(cred_request, bindings.AnoncredsObject): + cred_request = CredentialRequest.load(cred_request) + cred = bindings.create_w3c_credential( + cred_def.handle, + cred_def_private.handle, + cred_offer.handle, + cred_request.handle, + attr_raw_values, + revocation_config._native if revocation_config else None, + w3c_version, + ) + return W3cCredential(cred) + + def process( + self, + cred_req_metadata: Union[str, CredentialRequestMetadata], + link_secret: str, + cred_def: Union[str, CredentialDefinition], + rev_reg_def: Optional[Union[str, "RevocationRegistryDefinition"]] = None, + ) -> "W3cCredential": + if not isinstance(cred_req_metadata, bindings.AnoncredsObject): + cred_req_metadata = CredentialRequestMetadata.load(cred_req_metadata) + if not isinstance(cred_def, bindings.AnoncredsObject): + cred_def = CredentialDefinition.load(cred_def) + if rev_reg_def and not isinstance(rev_reg_def, bindings.AnoncredsObject): + rev_reg_def = RevocationRegistryDefinition.load(rev_reg_def) + return W3cCredential( + bindings.process_w3c_credential( + self.handle, + cred_req_metadata.handle, + link_secret, + cred_def.handle, + rev_reg_def.handle if rev_reg_def else None, + ) + ) + + @classmethod + def load(cls, value: Union[dict, str, bytes, memoryview]) -> "W3cCredential": + return W3cCredential( + bindings._object_from_json("anoncreds_w3c_credential_from_json", value) + ) + + def to_legacy( + self + ) -> "Credential": + return Credential.from_w3c(self) + + @classmethod + def from_legacy( + cls, + cred: "Credential", + cred_def: Union[str, CredentialDefinition], + w3c_version: Optional[str] = None + ) -> "W3cCredential": + return cred.to_w3c(cred_def, w3c_version) + + def _get_proof_details(self) -> bindings.ObjectHandle: + if self._proof_details is None: + self._proof_details = bindings.w3c_credential_get_integrity_proof_details(self.handle) + return self._proof_details + + + @property + def schema_id(self) -> str: + proof_details = self._get_proof_details() + return str( + bindings._object_get_attribute( + self.GET_ATTR, + proof_details, + "schema_id", + ) + ) + + @property + def cred_def_id(self) -> str: + proof_details = self._get_proof_details() + return str( + bindings._object_get_attribute( + self.GET_ATTR, + proof_details, + "cred_def_id", + ) + ) + + @property + def rev_reg_id(self) -> str: + proof_details = self._get_proof_details() + return str( + bindings._object_get_attribute( + self.GET_ATTR, + proof_details, + "rev_reg_id", + ) + ) + + @property + def rev_reg_index(self) -> Optional[int]: + proof_details = self._get_proof_details() + sval = bindings._object_get_attribute( + self.GET_ATTR, + proof_details, + "rev_reg_index", + ) + return int(str(sval)) if sval is not None else None + + @property + def timestamp(self) -> Optional[int]: + proof_details = self._get_proof_details() + sval = bindings._object_get_attribute( + self.GET_ATTR, + proof_details, + "timestamp", + ) + return int(str(sval)) if sval is not None else None + class PresentationRequest(bindings.AnoncredsObject): @classmethod @@ -364,7 +525,7 @@ def add_self_attested(self, attest: Mapping[str, str]): def _get_entry( self, - cred: Credential, + cred: Union[Credential, W3cCredential], timestamp: Optional[int] = None, rev_state: Union[None, str, "CredentialRevocationState"] = None, ): @@ -380,7 +541,7 @@ def _get_entry( def add_attributes( self, - cred: Credential, + cred: Union[Credential, W3cCredential], *referents: Sequence[str], reveal: bool = True, timestamp: Optional[int] = None, @@ -394,7 +555,7 @@ def add_attributes( def add_predicates( self, - cred: Credential, + cred: Union[Credential, W3cCredential], *referents: Sequence[str], timestamp: Optional[int] = None, rev_state: Union[None, str, "CredentialRevocationState"] = None, @@ -552,6 +713,152 @@ def verify( ) +class W3cPresentation(bindings.AnoncredsObject): + @classmethod + def create( + cls, + pres_req: Union[str, PresentationRequest], + present_creds: PresentCredentials, + link_secret: str, + schemas: Mapping[str, Union[str, Schema]], + cred_defs: Mapping[str, Union[str, CredentialDefinition]], + w3c_version: Optional[str] = None, + ) -> "W3cPresentation": + if not isinstance(pres_req, bindings.AnoncredsObject): + pres_req = PresentationRequest.load(pres_req) + schema_ids = list(schemas.keys()) + cred_def_ids = list(cred_defs.keys()) + schema_handles = [ + ( + Schema.load(s) if not isinstance(s, bindings.AnoncredsObject) else s + ).handle + for s in schemas.values() + ] + cred_def_handles = [ + ( + CredentialDefinition.load(c) + if not isinstance(c, bindings.AnoncredsObject) + else c + ).handle + for c in cred_defs.values() + ] + creds = [] + creds_prove = [] + for cred, cred_ts in present_creds.entries.items(): + for timestamp, (attrs, preds, rev_state) in cred_ts.items(): + entry_idx = len(creds) + creds.append( + bindings.CredentialEntry.create( + cred, timestamp, rev_state and rev_state + ) + ) + for reft, reveal in attrs: + creds_prove.append( + bindings.CredentialProve.attribute(entry_idx, reft, reveal) + ) + for reft in preds: + creds_prove.append( + bindings.CredentialProve.predicate(entry_idx, reft) + ) + return W3cPresentation( + bindings.create_w3c_presentation( + pres_req.handle, + creds, + creds_prove, + link_secret, + schema_handles, + schema_ids, + cred_def_handles, + cred_def_ids, + w3c_version, + ) + ) + + @classmethod + def load(cls, value: Union[dict, str, bytes, memoryview]) -> "W3cPresentation": + return W3cPresentation( + bindings._object_from_json("anoncreds_w3c_presentation_from_json", value) + ) + + def verify( + self, + pres_req: Union[str, PresentationRequest], + schemas: Mapping[str, Union[str, Schema]], + cred_defs: Mapping[str, Union[str, CredentialDefinition]], + rev_reg_defs: Optional[ + Mapping[str, Union[str, "RevocationRegistryDefinition"]] + ] = None, + rev_status_lists: Optional[Sequence[Union[str, "RevocationStatusList"]]] = None, + nonrevoked_interval_overrides: Optional[ + Sequence["NonrevokedIntervalOverride"] + ] = None, + ) -> bool: + if not isinstance(pres_req, bindings.AnoncredsObject): + pres_req = PresentationRequest.load(pres_req) + + schema_ids = list(schemas.keys()) + schema_handles = [ + ( + Schema.load(s) if not isinstance(s, bindings.AnoncredsObject) else s + ).handle + for s in schemas.values() + ] + + cred_def_ids = list(cred_defs.keys()) + cred_def_handles = [ + ( + CredentialDefinition.load(c) + if not isinstance(c, bindings.AnoncredsObject) + else c + ).handle + for c in cred_defs.values() + ] + + if rev_reg_defs: + rev_reg_def_ids = list(rev_reg_defs.keys()) + rev_reg_def_handles = [ + ( + RevocationRegistryDefinition.load(r) + if not isinstance(r, bindings.AnoncredsObject) + else r + ).handle + for r in rev_reg_defs.values() + ] + else: + rev_reg_def_ids = None + rev_reg_def_handles = None + + if rev_status_lists: + rev_status_list_handles = [ + ( + RevocationStatusList.load(r) + if not isinstance(r, bindings.AnoncredsObject) + else r + ).handle + for r in rev_status_lists + ] + else: + rev_status_list_handles = None + + nonrevoked_interval_overrides_native = [] + if nonrevoked_interval_overrides: + for o in nonrevoked_interval_overrides: + nonrevoked_interval_overrides_native.append(o._native) + + return bindings.verify_w3c_presentation( + self.handle, + pres_req.handle, + schema_ids, + schema_handles, + cred_def_ids, + cred_def_handles, + rev_reg_def_ids, + rev_reg_def_handles, + rev_status_list_handles, + nonrevoked_interval_overrides_native, + ) + + class RevocationRegistryDefinitionPrivate(bindings.AnoncredsObject): @classmethod def load( diff --git a/wrappers/python/demo/w3c_test.py b/wrappers/python/demo/w3c_test.py new file mode 100644 index 00000000..916c8e10 --- /dev/null +++ b/wrappers/python/demo/w3c_test.py @@ -0,0 +1,276 @@ +from anoncreds import ( + generate_nonce, + create_link_secret, + Credential, + W3cCredential, + CredentialDefinition, + CredentialOffer, + CredentialRequest, + CredentialRevocationConfig, + CredentialRevocationState, + PresentationRequest, + Presentation, + W3cPresentation, + PresentCredentials, + RevocationRegistryDefinition, + RevocationStatusList, + Schema +) + +issuer_id = "mock:uri" +schema_id = "mock:uri" +cred_def_id = "mock:uri" +rev_reg_id = "mock:uri:revregid" +entropy = "entropy" +rev_idx = 1 + +schema = Schema.create( + "schema name", "schema version", issuer_id, ["name", "age", "sex", "height"] +) + +cred_def_pub, cred_def_priv, cred_def_correctness = CredentialDefinition.create( + schema_id, schema, issuer_id, "tag", "CL", support_revocation=True +) + +(rev_reg_def_pub, rev_reg_def_private) = RevocationRegistryDefinition.create( + cred_def_id, cred_def_pub, issuer_id, "some_tag", "CL_ACCUM", 10 +) + +time_create_rev_status_list = 12 +revocation_status_list = RevocationStatusList.create( + cred_def_pub, + rev_reg_id, + rev_reg_def_pub, + rev_reg_def_private, + issuer_id, + True, + time_create_rev_status_list, +) + +link_secret = create_link_secret() +link_secret_id = "default" + +cred_offer = CredentialOffer.create(schema_id, cred_def_id, cred_def_correctness) + +cred_request, cred_request_metadata = CredentialRequest.create( + entropy, None, cred_def_pub, link_secret, link_secret_id, cred_offer +) + +issue_cred = W3cCredential.create( + cred_def_pub, + cred_def_priv, + cred_offer, + cred_request, + {"sex": "male", "name": "Alex", "height": "175", "age": "28"}, + CredentialRevocationConfig( + rev_reg_def_pub, + rev_reg_def_private, + revocation_status_list, + rev_idx, + ), + None, +) + +recv_cred = issue_cred.process( + cred_request_metadata, link_secret, cred_def_pub, rev_reg_def_pub +) + +assert schema_id == recv_cred.schema_id +assert cred_def_id == recv_cred.cred_def_id +assert rev_idx == recv_cred.rev_reg_index +assert None == recv_cred.timestamp + +print("W3c Credential") +print(recv_cred.to_json()) + +legacy_cred = recv_cred.to_legacy() +print("Legacy Credential `to_legacy`") +print(legacy_cred.to_json()) + +legacy_cred = Credential.from_w3c(recv_cred) +print("Legacy Credential `from_w3c`") +print(legacy_cred.to_json()) + +w3c_cred = legacy_cred.to_w3c(cred_def_pub) +print("W3c converted Credential `to_w3c`") +print(w3c_cred.to_json()) + +w3c_cred_restored = W3cCredential.from_legacy(legacy_cred, cred_def_pub) +print("W3C restored Credential `from_legacy`") +print(w3c_cred_restored.to_json()) + +time_after_creating_cred = time_create_rev_status_list + 1 +issued_rev_status_list = revocation_status_list.update( + cred_def_pub, + rev_reg_def_pub, + rev_reg_def_private, + [rev_idx], + None, + time_after_creating_cred, +) + +nonce = generate_nonce() +pres_req = PresentationRequest.load( + { + "nonce": nonce, + "name": "pres_req_1", + "version": "0.1", + "requested_attributes": { + "attr1_referent": {"name": "name", "issuer_id": issuer_id}, + "attr2_referent": {"names": ["name", "height"]}, + }, + "requested_predicates": { + "predicate1_referent": {"name": "age", "p_type": ">=", "p_value": 18} + }, + "non_revoked": {"from": 10, "to": 200}, + } +) + +rev_state = CredentialRevocationState.create( + rev_reg_def_pub, + revocation_status_list, + rev_idx, + rev_reg_def_pub.tails_location, +) + +schemas = {schema_id: schema} +cred_defs = {cred_def_id: cred_def_pub} +rev_reg_defs = {rev_reg_id: rev_reg_def_pub} +rev_status_lists = [issued_rev_status_list] + +# Create Presentation using W3C credential +present = PresentCredentials() + +present.add_attributes( + recv_cred, + "attr1_referent", + reveal=True, + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +present.add_attributes( + recv_cred, + "attr2_referent", + reveal=True, + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +present.add_predicates( + recv_cred, + "predicate1_referent", + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +presentation = W3cPresentation.create( + pres_req, + present, + link_secret, + schemas, + cred_defs, +) + +verified = presentation.verify( + pres_req, schemas, cred_defs, rev_reg_defs, rev_status_lists +) +assert verified + +# Create Presentation using Legacy credential +present = PresentCredentials() + +present.add_attributes( + legacy_cred, + "attr1_referent", + reveal=True, + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +present.add_attributes( + legacy_cred, + "attr2_referent", + reveal=True, + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +present.add_predicates( + legacy_cred, + "predicate1_referent", + timestamp=time_after_creating_cred, + rev_state=rev_state, +) + +presentation = Presentation.create( + pres_req, + present, + {}, + link_secret, + schemas, + cred_defs, +) + +verified = presentation.verify( + pres_req, schemas, cred_defs, rev_reg_defs, rev_status_lists +) +assert verified + +# Issuer revokes credential + +time_revoke_cred = time_after_creating_cred + 1 +revoked_status_list = issued_rev_status_list.update( + cred_def_pub, + rev_reg_def_pub, + rev_reg_def_private, + None, + [rev_idx], + time_revoke_cred, +) + +rev_status_lists.append(revoked_status_list) + +rev_state.update( + rev_reg_def_pub, + revocation_status_list, + rev_idx, + rev_reg_def_pub.tails_location, + revoked_status_list, +) + +present = PresentCredentials() +present.add_attributes( + recv_cred, + "attr1_referent", + reveal=True, + timestamp=time_revoke_cred, + rev_state=rev_state, +) + +present.add_attributes( + recv_cred, + "attr2_referent", + reveal=True, + timestamp=time_revoke_cred, + rev_state=rev_state, +) + +present.add_predicates( + recv_cred, "predicate1_referent", timestamp=time_revoke_cred, rev_state=rev_state +) + +presentation = W3cPresentation.create( + pres_req, present, link_secret, schemas, cred_defs +) + +verified = presentation.verify( + pres_req, + schemas, + cred_defs, + rev_reg_defs, + rev_status_lists, +) +assert not verified + +print("ok")