Skip to content

Commit

Permalink
Merge pull request #276 from DSRCorporation/anoncreds-w3c-encoding
Browse files Browse the repository at this point in the history
AnonCreds Credentials using the W3C Standard - proof value encoding
  • Loading branch information
swcurran committed Jan 12, 2024
2 parents 4937151 + 63e7085 commit 6d843c9
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 29 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ffi = ["dep:ffi-support"]
zeroize = ["dep:zeroize"]
logger = ["dep:env_logger"]
vendored = ["anoncreds-clsignatures/openssl_vendored"]
w3c = ["base64", "chrono"]
w3c = ["base64", "chrono", "rmp-serde"]

[dependencies]
anoncreds-clsignatures = "0.3.0"
Expand All @@ -43,6 +43,7 @@ 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"] }
rmp-serde = { version = "1.1.2", optional = true }

[dev-dependencies]
rstest = "0.18.2"
Expand Down
30 changes: 28 additions & 2 deletions src/data_types/nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use std::hash::{Hash, Hasher};

use crate::cl::{new_nonce, Nonce as CryptoNonce};
use crate::error::ConversionError;
use serde::de::{Error, SeqAccess};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;

pub struct Nonce {
strval: String,
Expand Down Expand Up @@ -52,6 +54,13 @@ impl Nonce {
Ok(Self { strval, native })
}

pub fn from_bytes(bytes: &[u8]) -> Result<Self, ConversionError> {
let native = CryptoNonce::from_bytes(bytes).map_err(|err| {
ConversionError::from_msg(format!("Error converting nonce from bytes: {err}"))
})?;
Self::from_native(native)
}

pub fn try_clone(&self) -> Result<Self, ConversionError> {
Self::from_dec(self.strval.clone())
}
Expand Down Expand Up @@ -157,7 +166,7 @@ impl<'a> Deserialize<'a> for Nonce {
type Value = Nonce;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("integer or string nonce")
formatter.write_str("integer or string nonce or bytes")
}

fn visit_i64<E>(self, value: i64) -> Result<Nonce, E>
Expand Down Expand Up @@ -187,9 +196,26 @@ impl<'a> Deserialize<'a> for Nonce {
{
Nonce::from_dec(value).map_err(E::custom)
}

fn visit_seq<E>(self, mut seq: E) -> Result<Self::Value, E::Error>
where
E: SeqAccess<'a>,
{
let mut vec = Vec::new();

while let Ok(Some(Value::Number(elem))) = seq.next_element() {
vec.push(
elem.as_u64()
.ok_or_else(|| E::Error::custom("invalid nonce"))?
as u8,
);
}

Nonce::from_bytes(&vec).map_err(E::Error::custom)
}
}

deserializer.deserialize_str(BigNumberVisitor)
deserializer.deserialize_any(BigNumberVisitor)
}
}

Expand Down
36 changes: 18 additions & 18 deletions src/data_types/w3c/proof.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use crate::data_types::cred_def::CredentialDefinitionId;
use crate::data_types::rev_reg_def::RevocationRegistryDefinitionId;
use crate::data_types::schema::SchemaId;
use crate::utils::base64;
use crate::utils::{base64, msg_pack};
use crate::Result;
use anoncreds_clsignatures::{
AggregatedProof, CredentialSignature, RevocationRegistry, SignatureCorrectnessProof, SubProof,
Witness,
};
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::json;
use std::fmt::Debug;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -55,20 +54,20 @@ impl DataIntegrityProof {
verification_method: String,
value: &V,
challenge: Option<String>,
) -> Self {
DataIntegrityProof {
) -> Result<Self> {
Ok(DataIntegrityProof {
type_: DataIntegrityProofType::DataIntegrityProof,
cryptosuite,
proof_purpose,
verification_method,
proof_value: value.encode(),
proof_value: value.encode()?,
challenge,
}
})
}

pub(crate) fn new_credential_proof(
value: &CredentialSignatureProofValue,
) -> DataIntegrityProof {
) -> Result<DataIntegrityProof> {
DataIntegrityProof::new(
CryptoSuite::AnonCredsVc2023,
ProofPurpose::AssertionMethod,
Expand All @@ -80,7 +79,7 @@ impl DataIntegrityProof {

pub(crate) fn new_credential_presentation_proof(
value: &CredentialPresentationProofValue,
) -> DataIntegrityProof {
) -> Result<DataIntegrityProof> {
DataIntegrityProof::new(
CryptoSuite::AnonCredsPresVc2023,
ProofPurpose::AssertionMethod,
Expand All @@ -94,7 +93,7 @@ impl DataIntegrityProof {
value: &PresentationProofValue,
challenge: String,
verification_method: String,
) -> DataIntegrityProof {
) -> Result<DataIntegrityProof> {
DataIntegrityProof::new(
CryptoSuite::AnonCredsPresVp2023,
ProofPurpose::Authentication,
Expand Down Expand Up @@ -233,13 +232,13 @@ pub struct CredentialProofDetails {
const BASE_HEADER: char = 'u';

pub trait EncodedObject {
fn encode(&self) -> String
fn encode(&self) -> Result<String>
where
Self: Serialize,
{
let json = json!(self).to_string();
let serialized = base64::encode(json);
format!("{}{}", BASE_HEADER, serialized)
let msg_pack_encoded = msg_pack::encode(self)?;
let base64_encoded = base64::encode(msg_pack_encoded);
Ok(format!("{}{}", BASE_HEADER, base64_encoded))
}

fn decode(string: &str) -> Result<Self>
Expand All @@ -253,7 +252,7 @@ pub trait EncodedObject {
value => return Err(err_msg!("Unexpected multibase base header {:?}", value)),
}
let decoded = base64::decode(&string[1..])?;
let obj: Self = serde_json::from_slice(&decoded)?;
let obj: Self = msg_pack::decode(&decoded)?;
Ok(obj)
}
}
Expand Down Expand Up @@ -359,20 +358,20 @@ pub(crate) mod tests {
type_: "Test".to_string(),
value: 1,
};
let encoded = obj.encode();
assert_eq!("ueyJ0eXBlXyI6IlRlc3QiLCJ2YWx1ZSI6MX0", encoded);
let encoded = obj.encode().unwrap();
assert_eq!("ugqV0eXBlX6RUZXN0pXZhbHVlAQ", encoded);
let decoded = TestObject::decode(&encoded).unwrap();
assert_eq!(obj, decoded)
}

fn credential_proof() -> DataIntegrityProof {
let credential_proof = credential_signature_proof();
DataIntegrityProof::new_credential_proof(&credential_proof)
DataIntegrityProof::new_credential_proof(&credential_proof).unwrap()
}

fn credential_pres_proof() -> DataIntegrityProof {
let credential_pres_proof = credential_pres_proof_value();
DataIntegrityProof::new_credential_presentation_proof(&credential_pres_proof)
DataIntegrityProof::new_credential_presentation_proof(&credential_pres_proof).unwrap()
}

fn presentation_proof() -> DataIntegrityProof {
Expand All @@ -382,6 +381,7 @@ pub(crate) mod tests {
"1".to_string(),
cred_def_id().to_string(),
)
.unwrap()
}

#[rstest]
Expand Down
4 changes: 2 additions & 2 deletions src/services/w3c/credential_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn credential_to_w3c(
rev_reg: credential.rev_reg,
witness: credential.witness,
};
let proof = DataIntegrityProof::new_credential_proof(&signature);
let proof = DataIntegrityProof::new_credential_proof(&signature)?;
let w3c_credential = W3CCredential::new(issuer, attributes, proof, version.as_ref());

trace!("credential_to_w3c <<< w3c_credential {:?}", w3c_credential);
Expand Down Expand Up @@ -315,7 +315,7 @@ pub(crate) mod tests {
W3CCredential::new(
issuer_id(),
CredentialAttributes::from(&cred_values()),
DataIntegrityProof::new_credential_proof(&credential_signature_proof()),
DataIntegrityProof::new_credential_proof(&credential_signature_proof()).unwrap(),
None,
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/w3c/issuer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn create_credential(
witness,
};

let proof = DataIntegrityProof::new_credential_proof(&signature);
let proof = DataIntegrityProof::new_credential_proof(&signature)?;
let credential = W3CCredential::new(
cred_def.issuer_id.to_owned(),
raw_credential_values,
Expand Down
6 changes: 3 additions & 3 deletions src/services/w3c/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub fn process_credential(
credential_signature.witness.as_ref(),
)?;

*proof = DataIntegrityProof::new_credential_proof(&credential_signature);
*proof = DataIntegrityProof::new_credential_proof(&credential_signature)?;

trace!("process_w3c_credential <<< ");

Expand Down Expand Up @@ -193,7 +193,7 @@ pub fn create_presentation(
timestamp: present.timestamp,
sub_proof,
};
let proof = DataIntegrityProof::new_credential_presentation_proof(&proof);
let proof = DataIntegrityProof::new_credential_presentation_proof(&proof)?;
let credential = W3CCredential::derive(credential_attributes, proof, present.cred);
verifiable_credentials.push(credential);
// Temporary hack - use `cred_def_id` verification_method for presentation
Expand All @@ -207,7 +207,7 @@ pub fn create_presentation(
&presentation_proof,
presentation_request.nonce.to_string(),
pres_verification_method,
);
)?;
let presentation = W3CPresentation::new(verifiable_credentials, proof, version.as_ref());

trace!(
Expand Down
6 changes: 4 additions & 2 deletions src/services/w3c/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ pub(crate) mod tests {

fn _credential() -> W3CCredential {
let proof =
DataIntegrityProof::new_credential_presentation_proof(&credential_pres_proof_value());
DataIntegrityProof::new_credential_presentation_proof(&credential_pres_proof_value())
.unwrap();
W3CCredential::new(issuer_id(), credential_attributes(), proof, None)
}

Expand All @@ -430,7 +431,8 @@ pub(crate) mod tests {
&presentation_proof_value(),
"1".to_string(),
cred_def_id().to_string(),
);
)
.unwrap();
W3CPresentation::new(vec![_credential()], proof, None)
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ pub mod hash;

pub mod query;

pub mod msg_pack;

#[macro_use]
pub mod macros;
13 changes: 13 additions & 0 deletions src/utils/msg_pack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use serde::de::DeserializeOwned;
use serde::Serialize;

use crate::Result;

pub fn encode<T: Serialize>(val: T) -> Result<Vec<u8>> {
rmp_serde::to_vec_named(&val)
.map_err(|_| err_msg!("unable to encode message using message pack"))
}

pub fn decode<T: DeserializeOwned>(val: &[u8]) -> Result<T> {
rmp_serde::from_slice(val).map_err(|_| err_msg!("unable to decode message using message pack"))
}

0 comments on commit 6d843c9

Please sign in to comment.