Skip to content

Commit

Permalink
ecdsa-sd-2023 cryptosuite. (#582)
Browse files Browse the repository at this point in the history
* Fix sd dataset canonicalization
  • Loading branch information
timothee-haudebourg committed Jul 19, 2024
1 parent 01d1368 commit 88110a9
Show file tree
Hide file tree
Showing 78 changed files with 2,992 additions and 586 deletions.
9 changes: 7 additions & 2 deletions crates/claims/crates/data-integrity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository = "https://github.com/spruceid/ssi/"
documentation = "https://docs.rs/ssi-claims/"

[features]
# default = ["w3c", "ed25519", "rsa", "secp256k1", "secp256r1", "tezos", "ethereum", "eip712", "aleo", "solana"]
default = ["w3c", "ed25519", "rsa", "secp256k1", "secp256r1"]

## Signature suites specified by the W3C.
##
Expand Down Expand Up @@ -118,4 +118,9 @@ serde.workspace = true
serde_json.workspace = true
json-syntax.workspace = true
thiserror.workspace = true
chrono.workspace = true
chrono.workspace = true

[dev-dependencies]
ssi-multicodec.workspace = true
serde = { workspace = true, features = ["derive"] }
async-std = { workspace = true, features = ["attributes"] }
6 changes: 5 additions & 1 deletion crates/claims/crates/data-integrity/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ description = "Verifiable Credential Data Integrity 1.0 core implementation for
repository = "https://github.com/spruceid/ssi/"
documentation = "https://docs.rs/ssi-data-integrity/"

[features]
secp256r1 = ["ssi-verification-methods/secp256r1"]
secp384r1 = ["ssi-verification-methods/secp384r1"]

[dependencies]
ssi-verification-methods-core.workspace = true
ssi-verification-methods.workspace = true
rdf-types.workspace = true
xsd-types = { workspace = true, features = ["serde"] }
linked-data = { workspace = true, features = ["derive"] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ where
context: &C,
data: &T,
proof_configuration: ProofConfigurationRef<'_, S>,
_verification_method: &S::VerificationMethod,
_transformation_options: TransformationOptions<S>,
) -> Result<Self::Output, TransformationError> {
let mut ld = LdEnvironment::default();
Expand Down
2 changes: 1 addition & 1 deletion crates/claims/crates/data-integrity/core/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use ssi_verification_methods_core::{ProofPurpose, ReferenceOrOwned};
use ssi_verification_methods::{ProofPurpose, ReferenceOrOwned};

use crate::{suite::ConfigurationError, CryptographicSuite, ProofConfiguration};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use iref::Iri;
use serde::Serialize;
use ssi_verification_methods_core::{ProofPurpose, ReferenceOrOwned};
use ssi_verification_methods::{ProofPurpose, ReferenceOrOwned};
use static_iref::iri;
use std::collections::BTreeMap;

Expand Down Expand Up @@ -140,6 +140,26 @@ impl<S: CryptographicSuite> ProofConfiguration<S> {
Self::from_method_and_options(type_, verification_method, Default::default())
}

pub fn into_suite_and_options(
self,
) -> (S, ProofOptions<S::VerificationMethod, S::ProofOptions>) {
(
self.type_,
ProofOptions {
context: self.context,
created: self.created,
verification_method: Some(self.verification_method),
proof_purpose: self.proof_purpose,
expires: self.expires,
domains: self.domains,
challenge: self.challenge,
nonce: self.nonce,
options: self.options,
extra_properties: self.extra_properties,
},
)
}

pub fn into_options(self) -> ProofOptions<S::VerificationMethod, S::ProofOptions> {
ProofOptions {
context: self.context,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use educe::Educe;
use serde::Serialize;
use ssi_verification_methods_core::{ProofPurpose, ReferenceOrOwnedRef};
use ssi_verification_methods::{ProofPurpose, ReferenceOrOwnedRef};
use std::collections::BTreeMap;

use crate::{
Expand Down
155 changes: 155 additions & 0 deletions crates/claims/crates/data-integrity/core/src/proof/de/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use crate::{
suite::bounds::{OptionsOf, VerificationMethodOf},
CryptosuiteString, DeserializeCryptographicSuite, ProofConfiguration, Type,
};
use serde::{
de::{DeserializeSeed, MapAccess},
Deserialize,
};
use ssi_core::de::WithType;
use std::{collections::BTreeMap, marker::PhantomData};

use super::{Field, RefOrValue, ReplayMap, TypeField};

impl<'de, T: DeserializeCryptographicSuite<'de>> ProofConfiguration<T> {
fn deserialize_with_type<S>(type_: Type, mut deserializer: S) -> Result<Self, S::Error>
where
S: serde::de::MapAccess<'de>,
{
let suite: T = type_
.try_into()
.map_err(|_| serde::de::Error::custom("unexpected cryptosuite"))?;

let mut context = None;
let mut created = None;
let mut verification_method = None;
let mut proof_purpose = None;
let mut expires = None;
let mut domains = None;
let mut challenge = None;
let mut nonce = None;

let mut other = Vec::new();

while let Some(key) = deserializer.next_key::<Field>()? {
match key {
Field::Context => context = Some(deserializer.next_value()?),
Field::Created => created = Some(deserializer.next_value()?),
Field::VerificationMethod => {
verification_method = Some({
deserializer
.next_value_seed(
WithType::<T, RefOrValue<VerificationMethodOf<T>>>::new(&suite),
)?
})
}
Field::ProofPurpose => proof_purpose = Some(deserializer.next_value()?),
Field::Expires => expires = Some(deserializer.next_value()?),
Field::Domains => domains = Some(deserializer.next_value()?),
Field::Challenge => challenge = Some(deserializer.next_value()?),
Field::Nonce => nonce = Some(deserializer.next_value()?),
Field::Other(key) => other.push(Some((key, deserializer.next_value()?))),
}
}

let options = WithType::<T, OptionsOf<T>>::new(&suite)
.deserialize(serde::__private::de::FlatMapDeserializer(
&mut other,
PhantomData,
))?
.0;

Ok(Self {
context,
type_: suite,
created,
verification_method: verification_method
.map(|v| v.map(VerificationMethodOf::unwrap).into())
.ok_or_else(|| serde::de::Error::custom("missing `verificationMethod` property"))?,
proof_purpose: proof_purpose
.ok_or_else(|| serde::de::Error::custom("missing `proofPurpose` property"))?,
expires,
domains: domains.unwrap_or_default(),
challenge,
nonce,
options,
extra_properties: BTreeMap::deserialize(serde::__private::de::FlatMapDeserializer(
&mut other,
PhantomData,
))?,
})
}
}

impl<'de, T: DeserializeCryptographicSuite<'de>> serde::Deserialize<'de> for ProofConfiguration<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(ProofConfigurationVisitor(PhantomData))
}
}

struct ProofConfigurationVisitor<T>(PhantomData<T>);

impl<'de, T: DeserializeCryptographicSuite<'de>> serde::de::Visitor<'de>
for ProofConfigurationVisitor<T>
{
type Value = ProofConfiguration<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "Data-Integrity proof")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut keep = Vec::new();
let mut cryptosuite = None;
let mut data_integrity_proof = false;

while let Some(key) = map.next_key::<TypeField>()? {
match key {
TypeField::Type => {
let name = map.next_value::<String>()?;

if name == "DataIntegrityProof" {
match cryptosuite.take() {
Some(c) => {
return ProofConfiguration::<T>::deserialize_with_type(
Type::DataIntegrityProof(c),
ReplayMap::new(keep, map),
);
}
None => {
data_integrity_proof = true;
}
}
} else {
return ProofConfiguration::<T>::deserialize_with_type(
Type::Other(name),
ReplayMap::new(keep, map),
);
}
}
TypeField::Cryptosuite => {
let name = map.next_value::<CryptosuiteString>()?;
if data_integrity_proof {
return ProofConfiguration::<T>::deserialize_with_type(
Type::DataIntegrityProof(name),
ReplayMap::new(keep, map),
);
} else {
cryptosuite = Some(name)
}
}
TypeField::Other(key) => {
keep.push((key, map.next_value()?));
}
}
}

Err(serde::de::Error::custom("missing type"))
}
}
2 changes: 2 additions & 0 deletions crates/claims/crates/data-integrity/core/src/proof/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub use ref_or_value::*;
mod replay_map;
pub use replay_map::*;

mod configuration;

impl<'de, T: DeserializeCryptographicSuite<'de>> Proof<T> {
fn deserialize_with_type<S>(type_: Type, mut deserializer: S) -> Result<Self, S::Error>
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::marker::PhantomData;
use iref::IriBuf;
use serde::de::MapAccess;
use ssi_core::de::DeserializeTyped;
use ssi_verification_methods_core::ReferenceOrOwned;
use ssi_verification_methods::ReferenceOrOwned;

pub enum RefOrValue<T> {
Ref(IriBuf),
Expand Down
25 changes: 24 additions & 1 deletion crates/claims/crates/data-integrity/core/src/proof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use educe::Educe;
use serde::{Deserialize, Serialize};
use ssi_claims_core::{AttachProof, ProofValidationError, ProofValidity, ResourceProvider};
use ssi_core::{one_or_many::OneOrManyRef, OneOrMany};
use ssi_verification_methods_core::{ProofPurpose, ReferenceOrOwned};
use ssi_verification_methods::{ProofPurpose, ReferenceOrOwned};
use std::collections::BTreeMap;
use std::{
borrow::{Borrow, BorrowMut},
Expand Down Expand Up @@ -178,6 +178,29 @@ impl<T: CryptographicSuite> Proof<T> {
extra_properties: &self.extra_properties,
}
}

pub fn map_type<U: CryptographicSuite>(
self,
type_: impl FnOnce(T) -> U,
verification_method: impl FnOnce(T::VerificationMethod) -> U::VerificationMethod,
options: impl FnOnce(T::ProofOptions) -> U::ProofOptions,
signature: impl FnOnce(T::Signature) -> U::Signature,
) -> Proof<U> {
Proof {
context: self.context,
type_: type_(self.type_),
created: self.created,
verification_method: self.verification_method.map(verification_method),
proof_purpose: self.proof_purpose,
expires: self.expires,
domains: self.domains,
challenge: self.challenge,
nonce: self.nonce,
options: options(self.options),
signature: signature(self.signature),
extra_properties: self.extra_properties,
}
}
}

impl<S: CloneCryptographicSuite> Clone for Proof<S> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::BTreeMap;

use ssi_verification_methods_core::{ProofPurpose, ReferenceOrOwnedRef};
use ssi_verification_methods::{ProofPurpose, ReferenceOrOwnedRef};

use crate::{CryptographicSuite, ProofConfigurationRef};

Expand Down
19 changes: 19 additions & 0 deletions crates/claims/crates/data-integrity/core/src/proof/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ impl Type {
}
}

impl TryFrom<CompactType> for Type {
type Error = MissingCryptosuite;

fn try_from(value: CompactType) -> Result<Self, MissingCryptosuite> {
Self::new(value.name, value.cryptosuite)
}
}

impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand All @@ -68,6 +76,17 @@ impl<'a> PartialEq<TypeRef<'a>> for Type {
}
}

impl<'de> Deserialize<'de> for Type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
CompactType::deserialize(deserializer)?
.try_into()
.map_err(serde::de::Error::custom)
}
}

/// Proof type reference.
pub enum TypeRef<'a> {
DataIntegrityProof(&'a CryptosuiteStr),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ssi_claims_core::{ProofValidationError, SignatureError};
use ssi_crypto::algorithm::{SignatureAlgorithmInstance, SignatureAlgorithmType};
use ssi_jwk::{Algorithm, JWK};
use ssi_jws::{CompactJWSString, JWSSignature, JWS};
use ssi_verification_methods_core::{MessageSigner, VerifyBytes, VerifyBytesWithRecoveryJwk};
use ssi_verification_methods::{MessageSigner, VerifyBytes, VerifyBytesWithRecoveryJwk};

use crate::{
suite::standard::{
Expand Down
Loading

0 comments on commit 88110a9

Please sign in to comment.