Skip to content

Commit

Permalink
refactor: only issuer id for legacy->w3c (#320)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed Feb 6, 2024
1 parent 3c027a9 commit c12eb3c
Show file tree
Hide file tree
Showing 23 changed files with 109 additions and 150 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock.android

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "anoncreds"
version = "0.2.0-dev.10"
version = "0.2.0-dev.11"
authors = [
"Hyperledger AnonCreds Contributors <anoncreds@lists.hyperledger.org>",
]
Expand Down
73 changes: 37 additions & 36 deletions docs/design/w3c/w3c-representation.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
## Design for W3C representation of AnonCreds credentials and presentation

This design document describes how W3C formatted Verifiable Credentials and Presentations are supported in `anoncreds-rs` library.
This design document describes how W3C formatted Verifiable Credentials and Presentations are supported in `anoncreds-rs` library.

### Goals and ideas

* Use legacy styled credentials to generate W3C AnonCreds credentials and presentations
* Credentials conversion:
* Convert legacy styled AnonCreds credentials into W3C form
* Convert W3C styled AnonCreds credentials into legacy form
* Presentation conversion (Optional):
* Convert legacy styled AnonCreds presentation into W3C form
* Convert W3C styled AnonCreds presentation into legacy form
* Issue AnonCreds credentials in W3C form
* Create W3C presentations using W3C AnonCreds credentials
* Verify W3C presentations containing AnonCreds proof
* Extend W3C credentials:
* Ability to set Data Integrity proof signatures for generated W3C credential objects:
* W3C credentials may contain multiple signatures
* AnonCreds-Rs only generates/handle AnonCreds signatures
* Ability to add additional credential metadata
- Use legacy styled credentials to generate W3C AnonCreds credentials and presentations
- Credentials conversion:
- Convert legacy styled AnonCreds credentials into W3C form
- Convert W3C styled AnonCreds credentials into legacy form
- Presentation conversion (Optional):
- Convert legacy styled AnonCreds presentation into W3C form
- Convert W3C styled AnonCreds presentation into legacy form
- Issue AnonCreds credentials in W3C form
- Create W3C presentations using W3C AnonCreds credentials
- Verify W3C presentations containing AnonCreds proof
- Extend W3C credentials:
- Ability to set Data Integrity proof signatures for generated W3C credential objects:
- W3C credentials may contain multiple signatures
- AnonCreds-Rs only generates/handle AnonCreds signatures
- Ability to add additional credential metadata

#### Out of scope

* Credentials: Verify validity of non-AnonCreds Data Integrity proof signatures
* Presentations: Create presentations using non-AnonCreds Data Integrity proof signature
* Presentations: Verify validity of presentations including non-AnonCreds Data Integrity proof signatures
* Presentations: Support different formats (for example DIF) of Presentation Request
- Credentials: Verify validity of non-AnonCreds Data Integrity proof signatures
- Presentations: Create presentations using non-AnonCreds Data Integrity proof signature
- Presentations: Verify validity of presentations including non-AnonCreds Data Integrity proof signatures
- Presentations: Support different formats (for example DIF) of Presentation Request

### Public API

Expand All @@ -40,15 +40,16 @@ is require application uses conversion methods to get required format.
These methods allow to solve both cases:

Methods purpose - have to forms of credentials (probably even duplicate in wallet) to cover both cases: legacy and W3C
* `anoncreds_credential_to_w3c` - create W3C Presentation using a credential previously issued in legacy form
* `anoncreds_credential_from_w3c` - create a legacy styled presentation (for legacy Verifier) using a credential issued in W3C form

- `anoncreds_credential_to_w3c` - create W3C Presentation using a credential previously issued in legacy form
- `anoncreds_credential_from_w3c` - create a legacy styled presentation (for legacy Verifier) using a credential issued in W3C form

```rust
/// Convert credential in legacy form into W3C AnonCreds credential form
///
/// # Params
/// cred: object handle pointing to credential in legacy form to convert
/// cred_def: object handle pointing to the credential definition
/// issuer_id: issuer_id of the credential. Can be extracted from the cred_def and will be used as the `issuer` field in the w3c credential
/// w3c_version: version of w3c verifiable credential specification (1.1 or 2.0) to use
/// cred_p: reference that will contain converted credential (in W3C form) instance pointer
///
Expand All @@ -57,7 +58,7 @@ Methods purpose - have to forms of credentials (probably even duplicate in walle
#[no_mangle]
pub extern "C" fn anoncreds_credential_to_w3c(
cred: ObjectHandle,
cred_def: ObjectHandle,
issuer_id: FfiStr,
w3c_version: FfiStr,
cred_p: *mut ObjectHandle,
) -> ErrorCode {}
Expand Down Expand Up @@ -90,18 +91,18 @@ So the credentials and presentations themselves are generated using new flow met
The reasons for adding duplication methods:

- avoid breaking changes in the existing API
- for example if we want Credential Offer pointing to the form of a credential to be issued
- for example if we want Credential Offer pointing to the form of a credential to be issued
- clear separation between flows
- if a flow targeting issuing of W3C Credential the specific set of function to be used
- if a flow targeting issuing of W3C Credential the specific set of function to be used
- avoid the situation when function result value may be in different forms
- example:
- issuer creates offer in legacy form but with resulting credential format indication (legacy or w3c )
- as the flow execution result, create credential function returns credential either in w3c or legacy form
depending on offer
- if application analyze credential somehow it cause difficulties
- example:
- issuer creates offer in legacy form but with resulting credential format indication (legacy or w3c )
- as the flow execution result, create credential function returns credential either in w3c or legacy form
depending on offer
- if application analyze credential somehow it cause difficulties
- easier deprecation of legacy styled credentials and APIs
- presentation conversion methods are not needed anymore in this case
- only credential conversion method to do migration for previously issued credentials
- only credential conversion method to do migration for previously issued credentials

```rust
/// Create Credential in W3C form according to the specification.
Expand Down Expand Up @@ -172,7 +173,7 @@ pub extern "C" fn anoncreds_w3c_credential_get_integrity_proof_details(
) -> ErrorCode {}

/// Create W3C Presentation according to the specification.
///
///
/// # Params
/// pres_req: object handle pointing to presentation request
/// credentials: credentials (in W3C form) to use for presentation preparation
Expand Down Expand Up @@ -248,7 +249,7 @@ legacy_credential = Holder.anoncreds_process_credential(legacy_credential,...)
/// Convert legacy styled credential to W3C credential form
w3c_credential = Holder.anoncreds_credential_to_w3c(legacy_credential)
/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests?
/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests?
Wallet.store_legacy_credential(legacy_credential)
Wallet.store_w3c_credential(w3c_credential)
Expand All @@ -270,7 +271,7 @@ w3c_credential = Holder.anoncreds_w3c_process_credential(w3c_credential,...)
/// Convert W3C credential to legacy form
legacy_credential = Holder.anoncreds_credential_from_w3c(w3c_credential)
/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests?
/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests?
Wallet.store_legacy_credential(legacy_credential)
Wallet.store_w3c_credential(w3c_credential)
Expand All @@ -289,7 +290,7 @@ w3c_credential_request = Holder.anoncreds_w3c_create_credential_request(w3c_cred
w3c_credential = Issuer.anoncreds_w3c_create_credential(w3c_credential_request,...)
w3c_credential = Holder.anoncreds_w3c_process_credential(w3c_credential,...)
/// Do wallets need to store both credential forms to handle legacy and DIF presentations requests?
/// 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 W3C crdential form
Expand Down
11 changes: 7 additions & 4 deletions src/ffi/w3c/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub extern "C" fn anoncreds_process_w3c_credential(
///
/// # Params
/// cred: object handle pointing to credential in legacy form to convert
/// cred_def: object handle pointing to the credential definition
/// issuer_id: issuer_id of the credential. Can be extracted from the cred_def and will be used as the `issuer` field in the w3c credential
/// w3c_version: version of w3c verifiable credential specification (1.1 or 2.0) to use
/// cred_p: reference that will contain converted credential (in W3C form) instance pointer
///
Expand All @@ -134,7 +134,7 @@ pub extern "C" fn anoncreds_process_w3c_credential(
#[no_mangle]
pub extern "C" fn anoncreds_credential_to_w3c(
cred: ObjectHandle,
cred_def: ObjectHandle,
issuer_id: FfiStr,
w3c_version: FfiStr,
cred_p: *mut ObjectHandle,
) -> ErrorCode {
Expand All @@ -148,8 +148,11 @@ pub extern "C" fn anoncreds_credential_to_w3c(
None => None,
};

let w3c_credential =
credential_to_w3c(credential, cred_def.load()?.cast_ref()?, w3c_version)?;
let issuer_id = issuer_id
.as_opt_str()
.ok_or_else(|| err_msg!("Missing issuer id"))?
.try_into()?;
let w3c_credential = credential_to_w3c(credential, &issuer_id, w3c_version)?;
let w3c_cred = ObjectHandle::create(w3c_credential)?;

unsafe { *cred_p = w3c_cred };
Expand Down
32 changes: 19 additions & 13 deletions src/services/w3c/credential_conversion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::data_types::cred_def::CredentialDefinition;
use crate::data_types::issuer_id::IssuerId;
use crate::data_types::w3c::credential::W3CCredential;
use crate::data_types::w3c::credential_attributes::CredentialSubject;
use crate::data_types::w3c::proof::{CredentialSignatureProofValue, DataIntegrityProof};
Expand Down Expand Up @@ -81,25 +81,25 @@ use crate::Error;
/// None,
/// ).expect("Unable to process the credential");
///
/// let _w3c_credential = w3c::credential_conversion::credential_to_w3c(&credential, &cred_def, None)
/// let _w3c_credential = w3c::credential_conversion::credential_to_w3c(&credential, &cred_def.issuer_id, None)
/// .expect("Unable to convert credential to w3c form");
///
/// ```
pub fn credential_to_w3c(
credential: &Credential,
cred_def: &CredentialDefinition,
issuer_id: &IssuerId,
version: Option<VerifiableCredentialSpecVersion>,
) -> Result<W3CCredential, Error> {
trace!(
"credential_to_w3c >>> credential: {:?}, cred_def: {:?}",
"credential_to_w3c >>> credential: {:?}, issuer_id: {:?}",
credential,
cred_def
issuer_id
);

credential.validate()?;

let credential = credential.try_clone()?;
let issuer = cred_def.issuer_id.clone();
let issuer = issuer_id.clone();
let attributes = CredentialSubject::from(&credential.values);
let signature = CredentialSignatureProofValue {
schema_id: credential.schema_id,
Expand Down Expand Up @@ -228,7 +228,7 @@ pub fn credential_from_w3c(w3c_credential: &W3CCredential) -> Result<Credential,
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::data_types::cred_def::CredentialDefinitionId;
use crate::data_types::cred_def::{CredentialDefinition, CredentialDefinitionId};
use crate::data_types::issuer_id::IssuerId;
use crate::data_types::schema::{Schema, SchemaId};
use crate::data_types::w3c::constants::ANONCREDS_CREDENTIAL_TYPES;
Expand Down Expand Up @@ -323,9 +323,12 @@ pub(crate) mod tests {
#[test]
fn test_convert_credential_to_and_from_w3c() {
let original_legacy_credential = legacy_credential();
let w3c_credential =
credential_to_w3c(&original_legacy_credential, &credential_definition(), None)
.expect("unable to convert credential to w3c form");
let w3c_credential = credential_to_w3c(
&original_legacy_credential,
&credential_definition().issuer_id,
None,
)
.expect("unable to convert credential to w3c form");
let legacy_credential = credential_from_w3c(&w3c_credential)
.expect("unable to convert credential to legacy form");
assert_eq!(json!(original_legacy_credential), json!(legacy_credential),)
Expand All @@ -339,9 +342,12 @@ pub(crate) mod tests {
#[case] expected_context: Contexts,
) {
let legacy_credential = legacy_credential();
let w3c_credential =
credential_to_w3c(&legacy_credential, &credential_definition(), Some(version))
.expect("unable to convert credential to w3c form");
let w3c_credential = credential_to_w3c(
&legacy_credential,
&credential_definition().issuer_id,
Some(version),
)
.expect("unable to convert credential to w3c form");

assert_eq!(w3c_credential.context, expected_context.clone());
assert_eq!(w3c_credential.type_, ANONCREDS_CREDENTIAL_TYPES.clone());
Expand Down
2 changes: 1 addition & 1 deletion tests/utils/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ impl<'a> ProverWallet<'a> {
match credential {
Credentials::Legacy(legacy_cred) => {
// Convert legacy credential into W3C form
let w3c_cred = credential_to_w3c(&legacy_cred, cred_def, None)
let w3c_cred = credential_to_w3c(&legacy_cred, &cred_def.issuer_id, None)
.expect("Error converting legacy credential into W3C form");

// Store w3c credential in wallet
Expand Down
2 changes: 1 addition & 1 deletion wrappers/javascript/lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.2.0-dev.10",
"version": "0.2.0-dev.11",
"npmClient": "pnpm",
"command": {
"version": {
Expand Down
4 changes: 2 additions & 2 deletions wrappers/javascript/packages/anoncreds-nodejs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hyperledger/anoncreds-nodejs",
"version": "0.2.0-dev.10",
"version": "0.2.0-dev.11",
"license": "Apache-2.0",
"description": "Nodejs wrapper for Anoncreds",
"main": "build/index",
Expand Down Expand Up @@ -43,7 +43,7 @@
"binary": {
"module_name": "anoncreds",
"module_path": "native",
"remote_path": "v0.2.0-dev.10",
"remote_path": "v0.2.0-dev.11",
"host": "https://github.com/hyperledger/anoncreds-rs/releases/download/",
"package_name": "library-{platform}-{arch}.tar.gz"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -656,16 +656,12 @@ export class NodeJSAnoncreds implements Anoncreds {
return Boolean(handleReturnPointer<number>(ret))
}

public credentialToW3c(options: {
objectHandle: ObjectHandle
credentialDefinition: ObjectHandle
w3cVersion?: string
}): ObjectHandle {
const { objectHandle, credentialDefinition, w3cVersion } = serializeArguments(options)
public credentialToW3c(options: { objectHandle: ObjectHandle; issuerId: string; w3cVersion?: string }): ObjectHandle {
const { objectHandle, issuerId, w3cVersion } = serializeArguments(options)

const ret = allocatePointer()

this.nativeAnoncreds.anoncreds_credential_to_w3c(objectHandle, credentialDefinition, w3cVersion, ret)
this.nativeAnoncreds.anoncreds_credential_to_w3c(objectHandle, issuerId, w3cVersion, ret)
this.handleError()

return new ObjectHandle(handleReturnPointer<number>(ret))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,7 @@ export const nativeBindings = {
FFI_INT8_PTR
]
],
anoncreds_credential_to_w3c: [
FFI_ERRORCODE,
[FFI_OBJECT_HANDLE, FFI_OBJECT_HANDLE, FFI_STRING, FFI_OBJECT_HANDLE_PTR]
],
anoncreds_credential_to_w3c: [FFI_ERRORCODE, [FFI_OBJECT_HANDLE, FFI_STRING, 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]],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,11 +647,11 @@ describe('API W3C', () => {
expect('mock:uri').toEqual(legacyCredentialFrom.schemaId)
expect('mock:uri').toEqual(legacyCredentialFrom.credentialDefinitionId)

const w3cCredential = W3cCredential.fromLegacy({ credential: legacyCredential, credentialDefinition })
const w3cCredential = W3cCredential.fromLegacy({ credential: legacyCredential, issuerId: 'mock:uri' })
expect('mock:uri').toEqual(w3cCredential.schemaId)
expect('mock:uri').toEqual(w3cCredential.credentialDefinitionId)

const convertedW3cCredential = legacyCredential.toW3c({ credentialDefinition })
const convertedW3cCredential = legacyCredential.toW3c({ issuerId: 'mock:uri' })
expect('mock:uri').toEqual(convertedW3cCredential.schemaId)
expect('mock:uri').toEqual(convertedW3cCredential.credentialDefinitionId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -870,15 +870,15 @@ jsi::Value processW3cCredential(jsi::Runtime &rt, jsi::Object options) {

jsi::Value credentialToW3c(jsi::Runtime &rt, jsi::Object options) {
auto credential = jsiToValue<ObjectHandle>(rt, options, "objectHandle");
auto credentialDefinition =
jsiToValue<ObjectHandle>(rt, options, "credentialDefinition");
auto issuerId =
jsiToValue<std::string>(rt, options, "issuerId");
auto w3cVersion =
jsiToValue<std::string>(rt, options, "w3cVersion", true);

ObjectHandle out;

ErrorCode code = anoncreds_credential_to_w3c(
credential, credentialDefinition,
credential, issuerId.c_str(),
w3cVersion.length() ? w3cVersion.c_str() : nullptr, &out);

return createReturnValue(rt, code, &out);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hyperledger/anoncreds-react-native",
"version": "0.2.0-dev.10",
"version": "0.2.0-dev.11",
"license": "Apache-2.0",
"description": "React Native wrapper for Anoncreds",
"main": "build/index",
Expand Down Expand Up @@ -51,7 +51,7 @@
"binary": {
"module_name": "anoncreds",
"module_path": "native",
"remote_path": "v0.2.0-dev.10",
"remote_path": "v0.2.0-dev.11",
"host": "https://github.com/hyperledger/anoncreds-rs/releases/download/",
"package_name": "library-ios-android.tar.gz"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,7 @@ export type NativeBindings = {

w3cCredentialProofGetAttribute(options: { objectHandle: number; name: string }): ReturnObject<string>

credentialToW3c(options: {
objectHandle: number
credentialDefinition: number
w3cVersion?: string
}): ReturnObject<Handle>
credentialToW3c(options: { objectHandle: number; issuerId: string; w3cVersion?: string }): ReturnObject<Handle>

credentialFromW3c(options: { objectHandle: number }): ReturnObject<Handle>

Expand Down
Loading

0 comments on commit c12eb3c

Please sign in to comment.