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

Support numbers values in AnonCreds W3C VC #305

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
61 changes: 43 additions & 18 deletions src/data_types/w3c/credential_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl Drop for CredentialAttributes {
impl Zeroize for CredentialAttributes {
fn zeroize(&mut self) {
for attr in self.0.values_mut() {
if let CredentialAttributeValue::Attribute(attr) = attr {
if let CredentialAttributeValue::String(attr) = attr {
attr.zeroize()
}
}
Expand All @@ -43,10 +43,17 @@ impl From<&CredentialValues> for CredentialAttributes {
.0
.iter()
.map(|(attribute, values)| {
(
attribute.to_owned(),
CredentialAttributeValue::Attribute(values.raw.to_owned()),
)
if let Ok(number) = values.raw.parse::<i32>() {
(
attribute.to_string(),
CredentialAttributeValue::Number(number),
)
} else {
(
attribute.to_string(),
CredentialAttributeValue::String(values.raw.to_string()),
)
}
})
.collect(),
)
Expand All @@ -60,30 +67,35 @@ impl CredentialAttributes {

pub(crate) fn add_predicate(&mut self, attribute: String) -> crate::Result<()> {
match self.0.get(&attribute) {
Some(value) => match value {
CredentialAttributeValue::Attribute(_) => {
return Err(err_msg!("Predicate cannot be added for revealed attribute"));
Some(value) => {
match value {
CredentialAttributeValue::String(_) | CredentialAttributeValue::Number(_) => {
Err(err_msg!("Predicate cannot be added for revealed attribute"))
}
CredentialAttributeValue::Bool(_) => {
// predicate already exists
Ok(())
}
}
CredentialAttributeValue::Predicate(_) => {
// predicate already exists
return Ok(());
}
},
}
None => {
self.0
.insert(attribute, CredentialAttributeValue::Predicate(true));
.insert(attribute, CredentialAttributeValue::Bool(true));
Ok(())
}
}
Ok(())
}

pub(crate) fn encode(&self) -> crate::Result<CredentialValues> {
let mut cred_values = MakeCredentialValues::default();
for (attribute, raw_value) in self.0.iter() {
match raw_value {
CredentialAttributeValue::Attribute(raw_value) => {
CredentialAttributeValue::String(raw_value) => {
cred_values.add_raw(attribute, raw_value)?
}
CredentialAttributeValue::Number(raw_value) => {
cred_values.add_raw(attribute, raw_value.to_string())?
}
value => {
return Err(err_msg!(
"Encoding is not supported for credential value {:?}",
Expand All @@ -99,6 +111,19 @@ impl CredentialAttributes {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum CredentialAttributeValue {
Attribute(String),
Predicate(bool),
// attribute representation
String(String),
Number(i32),
// predicates representation
Bool(bool),
}

impl ToString for CredentialAttributeValue {
fn to_string(&self) -> String {
match self {
CredentialAttributeValue::String(string) => string.to_owned(),
CredentialAttributeValue::Number(number) => number.to_string(),
CredentialAttributeValue::Bool(bool) => bool.to_string(),
}
}
}
24 changes: 12 additions & 12 deletions src/services/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ pub(crate) fn verify_requested_restrictions(
})?;
let filter = gather_filter_info(identifier, schemas, cred_defs)?;

let attr_value_map: HashMap<String, Option<&str>> = if let Some(name) =
let attr_value_map: HashMap<String, Option<String>> = if let Some(name) =
info.name.as_ref()
{
let mut map = HashMap::new();
Expand All @@ -441,17 +441,17 @@ pub(crate) fn verify_requested_restrictions(
requested_proof
.revealed_attrs
.get(referent)
.map(|attr| attr.raw.as_str()),
.map(|attr| attr.raw.to_string()),
);
map
} else if let Some(names) = info.names.as_ref() {
let mut map = HashMap::new();
let mut map: HashMap<String, Option<String>> = HashMap::new();
let attrs = requested_proof
.revealed_attr_groups
.get(referent)
.ok_or_else(|| err_msg!("Proof does not have referent from proof request"))?;
for name in names {
let val = attrs.values.get(name).map(|attr| attr.raw.as_str());
let val = attrs.values.get(name).map(|attr| attr.raw.clone());
map.insert(name.clone(), val);
}
map
Expand Down Expand Up @@ -484,7 +484,7 @@ pub(crate) fn verify_requested_restrictions(
let filter = gather_filter_info(identifier, schemas, cred_defs)?;

// start with the predicate requested attribute, which is un-revealed
let mut attr_value_map = HashMap::new();
let mut attr_value_map: HashMap<String, Option<String>> = HashMap::new();
attr_value_map.insert(info.name.to_string(), None);

// include any revealed attributes for the same credential (based on sub_proof_index)
Expand All @@ -499,7 +499,7 @@ pub(crate) fn verify_requested_restrictions(
if pred_sub_proof_index == attr_sub_proof_index {
let attr_name = requested_attrs.get(attr_referent).unwrap().name.clone();
if let Some(name) = attr_name {
attr_value_map.insert(name, Some(attr_info.raw.as_str()));
attr_value_map.insert(name, Some(attr_info.raw.clone()));
}
}
}
Expand All @@ -511,7 +511,7 @@ pub(crate) fn verify_requested_restrictions(
let attr_sub_proof_index = attr_info.sub_proof_index;
if pred_sub_proof_index == attr_sub_proof_index {
for name in attr_info.values.keys() {
let raw_val = attr_info.values.get(name).unwrap().raw.as_str();
let raw_val = attr_info.values.get(name).unwrap().raw.clone();
attr_value_map.insert(name.to_string(), Some(raw_val));
}
}
Expand Down Expand Up @@ -568,7 +568,7 @@ pub(crate) fn gather_filter_info(
}

pub(crate) fn process_operator(
attr_value_map: &HashMap<String, Option<&str>>,
attr_value_map: &HashMap<String, Option<String>>,
restriction_op: &Query,
filter: &Filter,
) -> Result<()> {
Expand Down Expand Up @@ -637,7 +637,7 @@ pub(crate) fn process_operator(
}

fn process_filter(
attr_value_map: &HashMap<String, Option<&str>>,
attr_value_map: &HashMap<String, Option<String>>,
tag: &str,
tag_value: &str,
filter: &Filter,
Expand Down Expand Up @@ -695,7 +695,7 @@ fn precess_filed(filed: &str, filter_value: impl Into<String>, tag_value: &str)
}
}

fn is_attr_internal_tag(key: &str, attr_value_map: &HashMap<String, Option<&str>>) -> bool {
fn is_attr_internal_tag(key: &str, attr_value_map: &HashMap<String, Option<String>>) -> bool {
INTERNAL_TAG_MATCHER.captures(key).map_or(false, |caps| {
caps.get(1).map_or(false, |s| {
attr_value_map.contains_key(&s.as_str().to_string())
Expand All @@ -706,7 +706,7 @@ fn is_attr_internal_tag(key: &str, attr_value_map: &HashMap<String, Option<&str>
fn check_internal_tag_revealed_value(
key: &str,
tag_value: &str,
attr_value_map: &HashMap<String, Option<&str>>,
attr_value_map: &HashMap<String, Option<String>>,
) -> Result<()> {
let attr_name = INTERNAL_TAG_MATCHER
.captures(key)
Expand Down Expand Up @@ -1019,7 +1019,7 @@ mod tests {
revealed_value: Option<&str>,
) -> Result<()> {
let mut attr_value_map = HashMap::new();
attr_value_map.insert(attr.to_string(), revealed_value);
attr_value_map.insert(attr.to_string(), revealed_value.map(String::from));
process_operator(&attr_value_map, restriction_op, filter)
}

Expand Down
6 changes: 4 additions & 2 deletions src/services/w3c/credential_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ pub(crate) mod tests {
pub fn w3c_credential() -> W3CCredential {
W3CCredential::new(
issuer_id(),
CredentialAttributes::from(&cred_values()),
CredentialAttributes::try_from(&cred_values()).unwrap(),
DataIntegrityProof::new_credential_proof(&credential_signature_proof()).unwrap(),
None,
)
Expand Down Expand Up @@ -345,9 +345,11 @@ pub(crate) mod tests {

assert_eq!(w3c_credential.context, expected_context.clone());
assert_eq!(w3c_credential.type_, ANONCREDS_CREDENTIAL_TYPES.clone());

let expected_attributes = CredentialAttributes::from(&legacy_credential.values);
assert_eq!(
w3c_credential.credential_subject.attributes,
CredentialAttributes::from(&legacy_credential.values)
expected_attributes
);

let proof = w3c_credential
Expand Down
24 changes: 15 additions & 9 deletions src/services/w3c/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,31 @@ impl W3CCredential {
.ok_or_else(|| err_msg!("Credential attribute {} not found", requested_attribute))
}

pub(crate) fn get_attribute(&self, requested_attribute: &str) -> Result<(String, String)> {
pub(crate) fn get_attribute(
&self,
requested_attribute: &str,
) -> Result<(String, CredentialAttributeValue)> {
let (attribute, value) = self.get_case_insensitive_attribute(requested_attribute)?;
match value {
CredentialAttributeValue::Attribute(value) => Ok((attribute, value)),
CredentialAttributeValue::Predicate(_) => Err(err_msg!(
CredentialAttributeValue::String(_) => Ok((attribute, value)),
CredentialAttributeValue::Number(_) => Ok((attribute, value)),
CredentialAttributeValue::Bool(_) => Err(err_msg!(
"Credential attribute {} not found",
requested_attribute
)),
}
}

pub(crate) fn get_predicate(&self, requested_predicate: &str) -> Result<(String, bool)> {
pub(crate) fn get_predicate(
&self,
requested_predicate: &str,
) -> Result<(String, CredentialAttributeValue)> {
let (attribute, value) = self.get_case_insensitive_attribute(requested_predicate)?;
match value {
CredentialAttributeValue::Predicate(value) => Ok((attribute, value)),
CredentialAttributeValue::Attribute(_) => Err(err_msg!(
"Credential predicate {} not found",
requested_predicate
)),
CredentialAttributeValue::Bool(_) => Ok((attribute, value)),
CredentialAttributeValue::String(_) | CredentialAttributeValue::Number(_) => Err(
err_msg!("Credential predicate {} not found", requested_predicate),
),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/w3c/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ impl MakeCredentialAttributes {
pub fn add(&mut self, name: impl Into<String>, raw: impl Into<String>) {
self.0
.0
.insert(name.into(), CredentialAttributeValue::Attribute(raw.into()));
.insert(name.into(), CredentialAttributeValue::String(raw.into()));
}
}

Expand Down
17 changes: 10 additions & 7 deletions src/services/w3c/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ fn check_credential_restrictions(
timestamp: None,
};
let filter = gather_filter_info(&identifier, schemas, cred_defs)?;
let mut attr_value_map: HashMap<String, Option<&str>> = HashMap::new();
let mut attr_value_map: HashMap<String, Option<String>> = HashMap::new();
for (attribute, value) in credential.credential_subject.attributes.0.iter() {
if let CredentialAttributeValue::Attribute(value) = value {
attr_value_map.insert(attribute.to_owned(), Some(value));
if let CredentialAttributeValue::String(value) = value {
attr_value_map.insert(attribute.to_owned(), Some(value.to_string()));
}
if let CredentialAttributeValue::Number(value) = value {
attr_value_map.insert(attribute.to_owned(), Some(value.to_string()));
}
}
process_operator(&attr_value_map, restrictions, &filter).map_err(err_map!(
Expand Down Expand Up @@ -194,7 +197,7 @@ fn check_requested_attribute<'a>(
.get(index)
.ok_or_else(|| err_msg!("Unable to get credential proof for index {}", index))?;

let encoded = encode_credential_attribute(&value)?;
let encoded = encode_credential_attribute(&value.to_string())?;
if verify_revealed_attribute_value(&attribute, &proof.sub_proof, &encoded).is_err() {
continue;
}
Expand Down Expand Up @@ -394,13 +397,13 @@ pub(crate) mod tests {
CredentialAttributes(HashMap::from([
(
"name".to_string(),
CredentialAttributeValue::Attribute("Alice".to_string()),
CredentialAttributeValue::String("Alice".to_string()),
),
(
"height".to_string(),
CredentialAttributeValue::Attribute("178".to_string()),
CredentialAttributeValue::String("178".to_string()),
),
("age".to_string(), CredentialAttributeValue::Predicate(true)),
("age".to_string(), CredentialAttributeValue::Bool(true)),
]))
}

Expand Down
Loading