diff --git a/crates/bbs/src/lib.rs b/crates/bbs/src/lib.rs index 5412af411..2ce0b6c70 100644 --- a/crates/bbs/src/lib.rs +++ b/crates/bbs/src/lib.rs @@ -77,7 +77,7 @@ impl MultiSigningMethod for Multikey { ) -> Result, MessageSignatureError> { use zkryptium::schemes::generics::{BlindSignature, Signature}; - let DecodedMultikey::Bls12_381(pk) = self.decode()? else { + let DecodedMultikey::Bls12_381(pk) = self.public_key.decode()? else { return Err(MessageSignatureError::InvalidPublicKey); }; diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/derive.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/derive.rs index bd7c5e1c5..8845cd749 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/derive.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/derive.rs @@ -226,7 +226,7 @@ where .map(|quad| format!("{quad} .\n").into_bytes()) .collect(); - let DecodedMultikey::Bls12_381(pk) = verification_method.decode()? else { + let DecodedMultikey::Bls12_381(pk) = verification_method.public_key.decode()? else { return Err(DeriveError::InvalidPublicKey); }; diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/hashing.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/hashing.rs index 33dff3474..6ceeae295 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/hashing.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/hashing.rs @@ -5,6 +5,7 @@ use ssi_data_integrity_core::{ ProofConfigurationRef, }; use ssi_rdf::{urdna2015::NormalizingSubstitution, IntoNQuads}; +use ssi_verification_methods::Multikey; use crate::Bbs2023; @@ -21,6 +22,7 @@ impl HashingAlgorithm for Bbs2023Hashing { fn hash( input: TransformedData, _proof_configuration: ProofConfigurationRef, + _verification_method: &Multikey, ) -> Result { match input { Transformed::Base(t) => { diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/signature/base.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/signature/base.rs index eae672f66..0bccda0e6 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/signature/base.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/signature/base.rs @@ -22,7 +22,7 @@ where T::MessageSigner: MultiMessageSigner, { // See: - let DecodedMultikey::Bls12_381(public_key) = verification_method.decode()? else { + let DecodedMultikey::Bls12_381(public_key) = verification_method.public_key.decode()? else { return Err(SignatureError::InvalidPublicKey); }; let feature_option = hash_data.transformed_document.options.feature_option; @@ -44,7 +44,7 @@ where let message_signer = signer .for_method(Cow::Borrowed(verification_method)) - .await + .await? .ok_or(SignatureError::MissingSigner)?; let (algorithm, description) = match feature_option { diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/tests/mod.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/tests/mod.rs index cf621a69b..d3c4b34e1 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/tests/mod.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/tests/mod.rs @@ -10,7 +10,7 @@ use lazy_static::lazy_static; use rdf_types::{BlankIdBuf, VocabularyMut}; use serde::{Deserialize, Serialize}; use ssi_bbs::{BBSplusPublicKey, BBSplusSecretKey}; -use ssi_claims_core::{ClaimsValidity, Validate}; +use ssi_claims_core::{ClaimsValidity, ValidateClaims}; use ssi_di_sd_primitives::JsonPointerBuf; use ssi_json_ld::{JsonLdError, JsonLdNodeObject, JsonLdObject, JsonLdTypes}; use ssi_rdf::{Interpretation, LdEnvironment, LinkedDataResource, LinkedDataSubject}; @@ -43,8 +43,8 @@ impl JsonLdNodeObject for JsonCredential { } } -impl Validate for JsonCredential { - fn validate(&self, _env: &E, _proof: &P) -> ClaimsValidity { +impl ValidateClaims for JsonCredential { + fn validate_claims(&self, _env: &E, _proof: &P) -> ClaimsValidity { Ok(()) } } diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/transformation/mod.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/transformation/mod.rs index c76450094..c1f4f8cf6 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/transformation/mod.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/transformation/mod.rs @@ -9,7 +9,7 @@ use ssi_data_integrity_core::{ ProofConfigurationRef, }; use ssi_di_sd_primitives::canonicalize::create_hmac_id_label_map_function; -use ssi_json_ld::{ContextLoaderEnvironment, Expandable, ExpandedDocument, JsonLdNodeObject}; +use ssi_json_ld::{Expandable, ExpandedDocument, JsonLdLoaderProvider, JsonLdNodeObject}; use ssi_rdf::{urdna2015::NormalizingSubstitution, LexicalInterpretation}; use std::collections::HashMap; @@ -24,7 +24,7 @@ impl TransformationAlgorithm for Bbs2023Transformation { impl TypedTransformationAlgorithm for Bbs2023Transformation where - C: ContextLoaderEnvironment, + C: JsonLdLoaderProvider, T: Serialize + JsonLdNodeObject + Expandable, T::Expanded: Into, { diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/verification.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/verification.rs index 7acd43d33..477f7b1c9 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/verification.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/bbs_2023/verification.rs @@ -25,7 +25,7 @@ impl VerificationAlgorithm for Bbs2023SignatureAlgorithm { // Verify Derived Proof algorithm. // See: - let DecodedMultikey::Bls12_381(public_key) = method.decode()? else { + let DecodedMultikey::Bls12_381(public_key) = method.public_key.decode()? else { return Err(ProofValidationError::InvalidKey); }; @@ -120,7 +120,7 @@ fn create_verify_data3<'a>( #[cfg(test)] mod tests { - use ssi_claims_core::VerifiableClaims; + use ssi_claims_core::VerificationParameters; use ssi_data_integrity_core::DataIntegrity; use ssi_verification_methods::Multikey; use static_iref::uri; @@ -144,6 +144,7 @@ mod tests { let mut methods = HashMap::new(); methods.insert(VERIFICATION_METHOD_IRI.to_owned(), verification_method); - assert!(document.verify(&methods).await.unwrap().is_ok()) + let params = VerificationParameters::from_resolver(methods); + assert!(document.verify(params).await.unwrap().is_ok()) } } diff --git a/crates/claims/crates/data-integrity/suites/src/suites/w3c/ecdsa_rdfc_2019.rs b/crates/claims/crates/data-integrity/suites/src/suites/w3c/ecdsa_rdfc_2019.rs index 2d71463eb..9de444c6d 100644 --- a/crates/claims/crates/data-integrity/suites/src/suites/w3c/ecdsa_rdfc_2019.rs +++ b/crates/claims/crates/data-integrity/suites/src/suites/w3c/ecdsa_rdfc_2019.rs @@ -2,8 +2,6 @@ //! //! See: use core::fmt; - -use k256::sha2::{Sha256, Sha384}; use ssi_data_integrity_core::{ canonicalization::{ CanonicalClaimsAndConfiguration, CanonicalizeClaimsAndConfiguration, @@ -14,9 +12,9 @@ use ssi_data_integrity_core::{ standard::{HashingAlgorithm, HashingError}, NoConfiguration, }, - ProofConfigurationRef, StandardCryptographicSuite, TypeRef, + CryptosuiteStr, ProofConfigurationRef, StandardCryptographicSuite, TypeRef, }; -use ssi_verification_methods::Multikey; +use ssi_verification_methods::{multikey::DecodedMultikey, Multikey}; use static_iref::iri; /// The `ecdsa-rdfc-2019` cryptosuite. @@ -45,7 +43,7 @@ impl StandardCryptographicSuite for EcdsaRdfc2019 { type ProofOptions = (); fn type_(&self) -> TypeRef { - TypeRef::DataIntegrityProof("ecdsa-rdfc-2019") + TypeRef::DataIntegrityProof(CryptosuiteStr::new("ecdsa-rdfc-2019").unwrap()) } } @@ -59,19 +57,29 @@ impl HashingAlgorithm for EcdsaRdfc2019HashingAlgorithm { proof_configuration: ProofConfigurationRef, verification_method: &Multikey, ) -> Result { - match verification_method.public_key.codec() { - ssi_multicodec::P256_PUB => HashCanonicalClaimsAndConfiguration::::hash( - input, - proof_configuration, - verification_method, - ) - .map(EcdsaRdfc2019Hash::Sha256), - ssi_multicodec::P384_PUB => HashCanonicalClaimsAndConfiguration::::hash( - input, - proof_configuration, - verification_method, - ) - .map(EcdsaRdfc2019Hash::Sha384), + match verification_method + .public_key + .decode() + .map_err(|_| HashingError::InvalidKey)? + { + #[cfg(feature = "secp256r1")] + DecodedMultikey::P256(_) => { + HashCanonicalClaimsAndConfiguration::::hash( + input, + proof_configuration, + verification_method, + ) + .map(EcdsaRdfc2019Hash::Sha256) + } + #[cfg(feature = "secp384r1")] + DecodedMultikey::P384(_) => { + HashCanonicalClaimsAndConfiguration::::hash( + input, + proof_configuration, + verification_method, + ) + .map(EcdsaRdfc2019Hash::Sha384) + } _ => Err(HashingError::InvalidKey), } } @@ -117,9 +125,15 @@ impl AlgorithmSelection for ES256OrES384 { verification_method: &Multikey, _options: &O, ) -> Result { - match verification_method.public_key.codec() { - ssi_multicodec::P256_PUB => Ok(Self::ES256), - ssi_multicodec::P384_PUB => Ok(Self::ES384), + match verification_method + .public_key + .decode() + .map_err(|_| AlgorithmSelectionError::InvalidKey)? + { + #[cfg(feature = "secp256r1")] + DecodedMultikey::P256(_) => Ok(Self::ES256), + #[cfg(feature = "secp384r1")] + DecodedMultikey::P384(_) => Ok(Self::ES384), _ => Err(AlgorithmSelectionError::InvalidKey), } } diff --git a/crates/claims/crates/data-integrity/suites/tests/suite.rs b/crates/claims/crates/data-integrity/suites/tests/suite.rs index a05682022..bb21be74e 100644 --- a/crates/claims/crates/data-integrity/suites/tests/suite.rs +++ b/crates/claims/crates/data-integrity/suites/tests/suite.rs @@ -85,10 +85,7 @@ impl VerificationMethodResolver for MultikeyRing { Ok(Cow::Owned(ssi_verification_methods::Multikey { id: id.to_owned(), controller: UriBuf::new(controller.to_owned().into_bytes()).unwrap(), - public_key: ssi_verification_methods::multikey::PublicKey::decode( - public_key, - ) - .unwrap(), + public_key: public_key.into(), })) } None => Err(VerificationMethodResolutionError::UnknownKey), diff --git a/crates/multicodec/src/lib.rs b/crates/multicodec/src/lib.rs index 0c36d4122..981645bae 100644 --- a/crates/multicodec/src/lib.rs +++ b/crates/multicodec/src/lib.rs @@ -5,7 +5,7 @@ pub use codec::*; include!(concat!(env!("OUT_DIR"), "/table.rs")); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Clone, thiserror::Error)] pub enum Error { #[error(transparent)] Varint(#[from] unsigned_varint::decode::Error), diff --git a/crates/verification-methods/src/methods/w3c/multikey.rs b/crates/verification-methods/src/methods/w3c/multikey.rs index 3a9685517..88e2a1e95 100644 --- a/crates/verification-methods/src/methods/w3c/multikey.rs +++ b/crates/verification-methods/src/methods/w3c/multikey.rs @@ -1,5 +1,11 @@ use iref::{Iri, IriBuf, UriBuf}; use multibase::Base; +use rdf_types::{ + dataset::PatternMatchingDataset, + interpretation::{ReverseIriInterpretation, ReverseLiteralInterpretation}, + vocabulary::{IriVocabularyMut, LiteralVocabulary}, + Interpretation, Vocabulary, +}; use serde::{Deserialize, Serialize}; use ssi_claims_core::{InvalidProof, ProofValidationError, ProofValidity, SignatureError}; use ssi_jwk::JWK; @@ -10,7 +16,7 @@ use ssi_verification_methods_core::{ VerifyBytes, }; use static_iref::iri; -use std::{borrow::Cow, hash::Hash}; +use std::{borrow::Cow, hash::Hash, str::FromStr, sync::OnceLock}; use crate::{ ExpectedType, GenericVerificationMethod, InvalidVerificationMethod, TypedVerificationMethod, @@ -53,10 +59,10 @@ pub struct Multikey { /// [MULTIBASE]: #[serde(rename = "publicKeyMultibase")] #[ld("sec:publicKeyMultibase")] - pub public_key: MultibaseBuf, + pub public_key: PublicKey, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Clone, thiserror::Error)] pub enum InvalidPublicKey { #[error(transparent)] Multibase(#[from] multibase::Error), @@ -88,12 +94,7 @@ impl Multikey { pub const IRI: &'static Iri = iri!("https://w3id.org/security#Multikey"); pub fn public_key_jwk(&self) -> Option { - self.decode().ok()?.to_jwk() - } - - pub fn decode(&self) -> Result { - let pk_multi_encoded = MultiEncodedBuf::new(self.public_key.decode()?.1)?; - pk_multi_encoded.decode().map_err(Into::into) + self.public_key.decode().ok()?.to_jwk() } #[cfg(feature = "ed25519")] @@ -113,7 +114,7 @@ impl Multikey { Self { id, controller, - public_key: MultibaseBuf::encode(Base::Base58Btc, MultiEncodedBuf::encode(public_key)), + public_key: PublicKey::new(public_key), } } } @@ -172,29 +173,6 @@ impl> VerifyBytes for Multikey { } } -#[cfg(feature = "ed25519")] -impl VerifyBytes for Multikey { - fn verify_bytes( - &self, - _algorithm: ssi_jwk::algorithm::EdDSA, - signing_bytes: &[u8], - signature: &[u8], - ) -> Result { - #[allow(unreachable_patterns)] - match self.decode()? { - DecodedMultikey::Ed25519(public_key) => { - use ed25519_dalek::Verifier; - let signature = ed25519_dalek::Signature::try_from(signature) - .map_err(|_| ProofValidationError::InvalidSignature)?; - Ok(public_key - .verify(signing_bytes, &signature) - .map_err(|_| InvalidProof::Signature)) - } - _ => Err(ProofValidationError::InvalidKey), - } - } -} - impl SigningMethod for Multikey { fn sign_bytes( &self, @@ -268,6 +246,143 @@ impl TryFrom for Multikey { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +pub struct PublicKey { + pub encoded: MultibaseBuf, + + #[serde(skip)] + decoded: OnceLock>, +} + +impl PublicKey { + pub fn new(public_key: &impl MultiCodec) -> Self { + Self::from_multibase(MultibaseBuf::encode( + Base::Base58Btc, + MultiEncodedBuf::encode(public_key), + )) + } + + pub fn from_multibase(encoded: MultibaseBuf) -> Self { + Self { + encoded, + decoded: OnceLock::new(), + } + } + + pub fn decode(&self) -> Result<&DecodedMultikey, InvalidPublicKey> { + self.decoded + .get_or_init(|| { + let pk_multi_encoded = MultiEncodedBuf::new(self.encoded.decode()?.1)?; + pk_multi_encoded.decode().map_err(Into::into) + }) + .as_ref() + .map_err(Clone::clone) + } +} + +impl From for PublicKey { + fn from(value: MultibaseBuf) -> Self { + Self::from_multibase(value) + } +} + +impl PartialEq for PublicKey { + fn eq(&self, other: &Self) -> bool { + self.encoded == other.encoded + } +} + +impl Eq for PublicKey {} + +impl PartialOrd for PublicKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PublicKey { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.encoded.cmp(&other.encoded) + } +} + +impl Hash for PublicKey { + fn hash(&self, state: &mut H) { + self.encoded.hash(state) + } +} + +impl FromStr for PublicKey { + type Err = std::convert::Infallible; + + fn from_str(s: &str) -> Result { + MultibaseBuf::from_str(s).map(Self::from_multibase) + } +} + +impl linked_data::LinkedDataResource for PublicKey +where + V: IriVocabularyMut, +{ + fn interpretation( + &self, + vocabulary: &mut V, + interpretation: &mut I, + ) -> linked_data::ResourceInterpretation { + self.encoded.interpretation(vocabulary, interpretation) + } +} + +impl linked_data::LinkedDataPredicateObjects for PublicKey +where + V: IriVocabularyMut, +{ + fn visit_objects(&self, visitor: S) -> Result + where + S: linked_data::PredicateObjectsVisitor, + { + self.encoded.visit_objects(visitor) + } +} + +impl linked_data::LinkedDataSubject for PublicKey { + fn visit_subject(&self, visitor: S) -> Result + where + S: linked_data::SubjectVisitor, + { + self.encoded.visit_subject(visitor) + } +} + +impl linked_data::LinkedDataDeserializeSubject for PublicKey +where + V: LiteralVocabulary, + I: ReverseIriInterpretation + ReverseLiteralInterpretation, +{ + fn deserialize_subject_in( + vocabulary: &V, + interpretation: &I, + dataset: &D, + graph: Option<&I::Resource>, + resource: &::Resource, + context: linked_data::Context, + ) -> Result + where + D: PatternMatchingDataset, + { + Ok(Self::from_multibase(MultibaseBuf::deserialize_subject_in( + vocabulary, + interpretation, + dataset, + graph, + resource, + context, + )?)) + } +} + +#[derive(Debug, Clone)] #[non_exhaustive] pub enum DecodedMultikey { #[cfg(feature = "ed25519")]