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

AnonCreds Credentials using the W3C Standard - implementation #271

Merged
merged 34 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d4471a7
feat: Add CredentialW3C credential format
Abdulbois Nov 1, 2023
6127276
feat: Work on credential conversion methods
Artemkaaas Nov 3, 2023
cf062a3
feat: W3C credential and presentation format support
Artemkaaas Nov 9, 2023
9be394d
feat: Refactoring
Artemkaaas Nov 9, 2023
5fa8a90
feat: Applied code formatter
Artemkaaas Nov 9, 2023
7def052
feat: Code refactoring and fixes
Artemkaaas Nov 9, 2023
4e3fb30
feat: Reuse implemented service methods
Artemkaaas Nov 10, 2023
5d42a36
feat: Changed reference to anoncreds-cs dependency
Artemkaaas Nov 10, 2023
e3a0f9b
Exclude design documents
Artemkaaas Nov 10, 2023
cbc46fc
Code cleanup and refactoring
Artemkaaas Nov 10, 2023
7f531aa
fix: Fixed sample code in function documentation
Artemkaaas Nov 10, 2023
3f6519b
Use W3C credentials/presentation in multiple credentials test
Artemkaaas Nov 10, 2023
ea7188b
Refactored mapping structure
Artemkaaas Nov 10, 2023
572001a
Added function to retrieve credential attribute
Artemkaaas Nov 13, 2023
65eb5b6
Different predicate representation + pass cred def into conversion me…
Artemkaaas Nov 14, 2023
731734f
Support case incentive case + refactored presentation creation/verifi…
Artemkaaas Nov 15, 2023
5676fd8
Fixed issue with taking revocation state
Artemkaaas Nov 15, 2023
30099e0
Fixed formatting
Artemkaaas Nov 15, 2023
8e594ec
Corrected presentation type
Artemkaaas Nov 15, 2023
46d3dcc
Corrected non-anoncreds proof parsing
Artemkaaas Nov 16, 2023
498b28e
Code clean up
Artemkaaas Nov 16, 2023
e4bc7ad
Use enum for credential value
Artemkaaas Nov 20, 2023
142af17
Use credential status property for exposing revocation registry id
Artemkaaas Nov 21, 2023
cdebb35
Moved w3c methods to separate folders
Artemkaaas Nov 21, 2023
37444c4
Move w3c modules under the feature
Artemkaaas Nov 22, 2023
354d92b
Changed method name
Artemkaaas Nov 22, 2023
fa67696
Use serde alias for type and id fields
Artemkaaas Nov 22, 2023
ac39a95
Refactored demo tests to work with both legacy and w3c formats
Artemkaaas Nov 29, 2023
dd376e8
Code refactoring
Artemkaaas Dec 1, 2023
95b01e2
Code refactoring
Artemkaaas Dec 1, 2023
b825666
Added check of encoded attributes
Artemkaaas Dec 4, 2023
8267bc9
Changed name of create and verify presentation functions to be consis…
Artemkaaas Dec 6, 2023
77df074
Process review comments
Artemkaaas Dec 7, 2023
59a8a8c
Merge remote-tracking branch 'origin/main' into anoncreds-wc3
Artemkaaas Dec 7, 2023
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
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ path = "src/lib.rs"
crate-type = ["staticlib", "rlib", "cdylib"]

[features]
default = ["ffi", "logger", "zeroize"]
default = ["ffi", "logger", "zeroize", "w3c"]
ffi = ["dep:ffi-support"]
zeroize = ["dep:zeroize"]
logger = ["dep:env_logger"]
vendored = ["anoncreds-clsignatures/openssl_vendored"]
w3c = ["base64", "chrono"]

[dependencies]
anoncreds-clsignatures = "0.2.4"
Expand All @@ -40,6 +41,11 @@ serde_json = { version = "1.0.94", features = ["raw_value"] }
sha2 = "0.10.6"
thiserror = "1.0.39"
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
base64 = { version = "0.21.5", optional = true }
chrono = { version = "0.4.31", optional = true, features = ["serde"] }

[dev-dependencies]
rstest = "0.18.2"

[profile.release]
lto = true
Expand Down
117 changes: 115 additions & 2 deletions src/data_types/credential.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::collections::HashMap;

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

use crate::cl::{CredentialSignature, RevocationRegistry, SignatureCorrectnessProof, Witness};
use crate::error::{ConversionError, ValidationError};
use crate::types::MakeCredentialValues;
use crate::utils::validation::Validatable;
use crate::Error;

use super::rev_reg_def::RevocationRegistryDefinitionId;
use super::{cred_def::CredentialDefinitionId, schema::SchemaId};
Expand Down Expand Up @@ -73,14 +77,71 @@ impl Validatable for Credential {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct CredentialInfo {
pub referent: String,
pub attrs: ShortCredentialValues,
pub attrs: RawCredentialValues,
pub schema_id: SchemaId,
pub cred_def_id: CredentialDefinitionId,
pub rev_reg_id: Option<RevocationRegistryDefinitionId>,
pub cred_rev_id: Option<String>,
}

pub type ShortCredentialValues = HashMap<String, String>;
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct RawCredentialValues(pub HashMap<String, String>);

#[cfg(feature = "zeroize")]
impl Drop for RawCredentialValues {
fn drop(&mut self) {
self.zeroize();
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for RawCredentialValues {
fn zeroize(&mut self) {
for attr in self.0.values_mut() {
attr.zeroize();
}
}
}

impl Validatable for RawCredentialValues {
fn validate(&self) -> Result<(), ValidationError> {
if self.0.is_empty() {
return Err("RawCredentialValues validation failed: empty list has been passed".into());
andrewwhitehead marked this conversation as resolved.
Show resolved Hide resolved
}

Ok(())
}
}

impl From<&CredentialValues> for RawCredentialValues {
fn from(values: &CredentialValues) -> Self {
RawCredentialValues(
values
.0
.iter()
.map(|(attribute, values)| (attribute.to_owned(), values.raw.to_owned()))
.collect(),
)
}
}

impl RawCredentialValues {
pub fn encode(&self, encoding: &CredentialValuesEncoding) -> Result<CredentialValues, Error> {
match encoding {
CredentialValuesEncoding::Auto => {
let mut cred_values = MakeCredentialValues::default();
for (attribute, raw_value) in self.0.iter() {
cred_values.add_raw(attribute, raw_value)?;
}
Ok(cred_values.into())
}
encoding => Err(err_msg!(
"Credential values encoding {:?} is not supported",
encoding
)),
}
}
}

#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct CredentialValues(pub HashMap<String, AttributeValues>);
Expand Down Expand Up @@ -117,3 +178,55 @@ pub struct AttributeValues {
pub raw: String,
pub encoded: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CredentialValuesEncoding {
Auto,
Other(String),
Artemkaaas marked this conversation as resolved.
Show resolved Hide resolved
}

impl ToString for CredentialValuesEncoding {
Artemkaaas marked this conversation as resolved.
Show resolved Hide resolved
fn to_string(&self) -> String {
match self {
CredentialValuesEncoding::Auto => "auto".to_string(),
CredentialValuesEncoding::Other(other) => other.to_string(),
}
}
}

impl From<&str> for CredentialValuesEncoding {
fn from(value: &str) -> Self {
match value {
"auto" => CredentialValuesEncoding::Auto,
other => CredentialValuesEncoding::Other(other.to_string()),
}
}
}

impl Serialize for CredentialValuesEncoding {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Value::String(self.to_string()).serialize(serializer)
}
}

impl<'de> Deserialize<'de> for CredentialValuesEncoding {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Value::deserialize(deserializer)
.map_err(de::Error::custom)?
.as_str()
.map(CredentialValuesEncoding::from)
.ok_or_else(|| de::Error::custom("Cannot parse credential value encoding"))
}
}

impl Default for CredentialValuesEncoding {
fn default() -> Self {
CredentialValuesEncoding::Auto
}
}
4 changes: 4 additions & 0 deletions src/data_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ pub mod macros;

/// Identifier wrapper for the issuer
pub mod issuer_id;

#[cfg(feature = "w3c")]
/// W3C Credential standard definitions
pub mod w3c;
36 changes: 36 additions & 0 deletions src/data_types/w3c/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use once_cell::sync::Lazy;
use std::collections::HashSet;

use crate::data_types::w3c::credential::{Contexts, Types};
use crate::data_types::w3c::uri::URI;

// Contexts
pub const W3C_CONTEXT: &str = "https://www.w3.org/2018/credentials/v1";
pub const W3C_ANONCREDS_CONTEXT: &str = "https://raw.githubusercontent.com/hyperledger/anoncreds-spec/main/data/anoncreds-w3c-context.json";

// Types
pub const W3C_CREDENTIAL_TYPE: &str = "VerifiableCredential";
pub const W3C_PRESENTATION_TYPE: &str = "VerifiablePresentation";
pub const W3C_ANONCREDS_CREDENTIAL_TYPE: &str = "AnonCredsCredential";
pub const W3C_ANONCREDS_PRESENTATION_TYPE: &str = "AnonCredsPresentation";

pub(crate) static ANONCREDS_CONTEXTS: Lazy<Contexts> = Lazy::new(|| {
Contexts(HashSet::from([
URI::from(W3C_CONTEXT),
URI::from(W3C_ANONCREDS_CONTEXT),
]))
});

pub(crate) static ANONCREDS_CREDENTIAL_TYPES: Lazy<Types> = Lazy::new(|| {
Types(HashSet::from([
String::from(W3C_CREDENTIAL_TYPE),
String::from(W3C_ANONCREDS_CREDENTIAL_TYPE),
]))
});

pub(crate) static ANONCREDS_PRESENTATION_TYPES: Lazy<Types> = Lazy::new(|| {
Types(HashSet::from([
String::from(W3C_PRESENTATION_TYPE),
String::from(W3C_ANONCREDS_PRESENTATION_TYPE),
]))
});
Loading