Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: only issuer id for legacy->w3c #320

Merged
merged 5 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
9 changes: 5 additions & 4 deletions src/ffi/w3c/credential.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::data_types::issuer_id::IssuerId;
use crate::data_types::w3c::VerifiableCredentialSpecVersion;
use ffi_support::{rust_string_to_c, FfiStr};
use std::ffi::c_char;
Expand Down Expand Up @@ -125,7 +126,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 +135,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 +149,8 @@ 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 = IssuerId::new(issuer_id).expect("Invalid issuer ID");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a rust expert. Is this validated and handled correctly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If new returns an error it will panic. You can use ? instead to not panic but pass along the error back over ffi.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Will update

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
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 @@ -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
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
Original file line number Diff line number Diff line change
Expand Up @@ -507,11 +507,7 @@ export class ReactNativeAnoncreds implements Anoncreds {
return new ObjectHandle(handle)
}

public credentialToW3c(options: {
objectHandle: ObjectHandle
credentialDefinition: ObjectHandle
w3cVersion?: string
}): ObjectHandle {
public credentialToW3c(options: { objectHandle: ObjectHandle; issuerId: string; w3cVersion?: string }): ObjectHandle {
const handle = this.handleError(this.anoncreds.credentialToW3c(serializeArguments(options)))
return new ObjectHandle(handle)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,7 @@ export type Anoncreds = {

w3cCredentialFromJson(options: { json: string }): ObjectHandle

credentialToW3c(options: {
objectHandle: ObjectHandle
credentialDefinition: ObjectHandle
w3cVersion?: string
}): ObjectHandle
credentialToW3c(options: { objectHandle: ObjectHandle; issuerId: string; w3cVersion?: string }): ObjectHandle

credentialFromW3c(options: { objectHandle: ObjectHandle }): ObjectHandle

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export type ProcessCredentialOptions = {
}

export type CredentialToW3cOptions = {
credentialDefinition: CredentialDefinition | JsonObject
issuerId: string
w3cVersion?: string
}

Expand Down Expand Up @@ -151,28 +151,13 @@ export class Credential extends AnoncredsObject {
}

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
return new W3cCredential(
anoncreds.credentialToW3c({
objectHandle: this.handle,
issuerId: options.issuerId,
w3cVersion: options.w3cVersion
}).handle
)
}

public static fromW3c(options: CredentialFromW3cOptions) {
Expand Down
Loading
Loading