diff --git a/did_peer/src/error.rs b/did_peer/src/error.rs index fe14a85a47..3ce03fca43 100644 --- a/did_peer/src/error.rs +++ b/did_peer/src/error.rs @@ -3,7 +3,7 @@ use std::convert::Infallible; use did_doc::schema::verification_method::VerificationMethodType; use thiserror::Error; -use crate::peer_did::Numalgo; +use crate::peer_did::numalgos::NumalgoKind; #[derive(Debug, Error)] pub enum DidPeerError { @@ -20,7 +20,7 @@ pub enum DidPeerError { #[error("Sovrin DID document builder error: {0}")] DidDocumentSovBuilderError(#[from] did_doc_sov::error::DidDocumentSovError), #[error("Unsupported numalgo: {0}")] - UnsupportedNumalgo(Numalgo), + UnsupportedNumalgo(NumalgoKind), #[error("Invalid numalgo character: {0}")] InvalidNumalgoCharacter(char), #[error("Unsupported purpose character: {0}")] diff --git a/did_peer/src/key/verification_method.rs b/did_peer/src/key/verification_method.rs index cab3ea49b5..572c795f49 100644 --- a/did_peer/src/key/verification_method.rs +++ b/did_peer/src/key/verification_method.rs @@ -10,7 +10,7 @@ use super::{Key, KeyType}; pub fn get_verification_methods_by_key( key: &Key, did: &Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Result, DidPeerError> { let id = to_did_url_reference(key)?; let vm_type = match key.key_type() { @@ -59,7 +59,7 @@ fn build_verification_methods_from_type_and_key( key: &Key, id: DidUrl, did: Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Vec { vec![add_public_key_to_builder( VerificationMethod::builder(id, did, vm_type), @@ -72,7 +72,7 @@ fn build_verification_methods_from_bls_multikey( g1_key: &Key, g2_key: &Key, did: Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Vec { let id1 = to_did_url_reference(g1_key).unwrap(); let id2 = to_did_url_reference(g2_key).unwrap(); @@ -97,7 +97,7 @@ fn build_verification_methods_from_bls_multikey( fn add_public_key_to_builder( builder: IncompleteVerificationMethodBuilder, key: &Key, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> VerificationMethod { match public_key_encoding { PublicKeyEncoding::Base58 => builder.add_public_key_base58(key.base58()).build(), @@ -157,7 +157,7 @@ mod tests { // Multibase encoded keys are multicodec-prefixed by their encoding type ... fn test_get_verification_methods_by_key_multibase(key: &Key) { - let vms = get_verification_methods_by_key(key, &did(), &PublicKeyEncoding::Multibase).unwrap(); + let vms = get_verification_methods_by_key(key, &did(), PublicKeyEncoding::Multibase).unwrap(); assert_eq!(vms.len(), 1); assert_eq!( vms[0].public_key().key_decoded().unwrap(), @@ -168,7 +168,7 @@ mod tests { // ... and base58 encoded keys are not fn test_get_verification_methods_by_key_base58(key: &Key) { - let vms = get_verification_methods_by_key(key, &did(), &PublicKeyEncoding::Base58).unwrap(); + let vms = get_verification_methods_by_key(key, &did(), PublicKeyEncoding::Base58).unwrap(); assert_eq!(vms.len(), 1); assert_ne!( vms[0].public_key().key_decoded().unwrap(), diff --git a/did_peer/src/numalgos/numalgo2/generate/mod.rs b/did_peer/src/numalgos/numalgo2/generate/mod.rs index ab7e34b576..2cca2e8c6b 100644 --- a/did_peer/src/numalgos/numalgo2/generate/mod.rs +++ b/did_peer/src/numalgos/numalgo2/generate/mod.rs @@ -3,15 +3,18 @@ mod helpers; use did_doc::schema::did_doc::DidDocument; use did_doc_sov::extra_fields::ExtraFieldsSov; -use crate::{error::DidPeerError, peer_did::PeerDid}; +use crate::{ + error::DidPeerError, + peer_did::{numalgos::numalgo2::Numalgo2, peer_did::PeerDid}, +}; use self::helpers::{append_encoded_key_segments, append_encoded_service_segment}; -pub fn generate_numalgo2(did_document: DidDocument) -> Result { +pub fn generate_numalgo2(did_document: DidDocument) -> Result, DidPeerError> { let mut did = String::from("did:peer:2"); did = append_encoded_key_segments(did, &did_document)?; did = append_encoded_service_segment(did, &did_document)?; - PeerDid::parse(did) + PeerDid::::parse(did) } diff --git a/did_peer/src/numalgos/numalgo2/resolve/helpers.rs b/did_peer/src/numalgos/numalgo2/resolve/helpers.rs index e98cb2e7d7..138b30b3d7 100644 --- a/did_peer/src/numalgos/numalgo2/resolve/helpers.rs +++ b/did_peer/src/numalgos/numalgo2/resolve/helpers.rs @@ -13,7 +13,7 @@ use crate::{ pub fn process_elements( mut did_doc_builder: DidDocumentBuilder, did: &Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Result, DidPeerError> { let mut service_index: usize = 0; @@ -30,7 +30,7 @@ fn process_element( mut did_doc_builder: DidDocumentBuilder, service_index: &mut usize, did: &Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Result, DidPeerError> { let purpose: ElementPurpose = element .chars() @@ -80,7 +80,7 @@ fn process_key_element( element: &str, mut did_doc_builder: DidDocumentBuilder, did: &Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, purpose: ElementPurpose, ) -> Result, DidPeerError> { let key = Key::from_fingerprint(&element)?; @@ -164,7 +164,7 @@ mod tests { let built_ddo = process_elements( DidDocumentBuilder::::new(did.clone()), &did, - &PublicKeyEncoding::Base58, + PublicKeyEncoding::Base58, ) .unwrap() .build(); @@ -182,7 +182,7 @@ mod tests { let processed_did_doc_builder = process_elements( DidDocumentBuilder::::new(did.clone()), &did, - &PublicKeyEncoding::Multibase, + PublicKeyEncoding::Multibase, ) .unwrap(); let built_ddo = processed_did_doc_builder.build(); @@ -204,7 +204,7 @@ mod tests { match process_elements( DidDocumentBuilder::::new(did.clone()), &did, - &PublicKeyEncoding::Multibase, + PublicKeyEncoding::Multibase, ) { Ok(_) => panic!("Expected Err, got Ok"), Err(e) => { @@ -288,7 +288,7 @@ mod tests { purposeless_key_element, ddo_builder, &did, - &public_key_encoding, + public_key_encoding, ElementPurpose::Verification, ) .unwrap() @@ -307,7 +307,7 @@ mod tests { "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", DidDocumentBuilder::::new(did.clone()), &did, - &PublicKeyEncoding::Multibase, + PublicKeyEncoding::Multibase, ElementPurpose::Service ) .is_err()); diff --git a/did_peer/src/numalgos/numalgo2/resolve/mod.rs b/did_peer/src/numalgos/numalgo2/resolve/mod.rs index a2c0d16dbe..200368617d 100644 --- a/did_peer/src/numalgos/numalgo2/resolve/mod.rs +++ b/did_peer/src/numalgos/numalgo2/resolve/mod.rs @@ -10,7 +10,7 @@ use self::helpers::process_elements; pub fn resolve_numalgo2( did: &Did, - public_key_encoding: &PublicKeyEncoding, + public_key_encoding: PublicKeyEncoding, ) -> Result, DidPeerError> { let mut did_doc_builder: DidDocumentBuilder = DidDocument::builder(did.to_owned()); diff --git a/did_peer/src/numalgos/numalgo3/generate.rs b/did_peer/src/numalgos/numalgo3/generate.rs index 03c4fd330d..374d0857a3 100644 --- a/did_peer/src/numalgos/numalgo3/generate.rs +++ b/did_peer/src/numalgos/numalgo3/generate.rs @@ -1,12 +1,15 @@ use did_parser::Did; use sha256::digest; -use crate::{error::DidPeerError, peer_did::PeerDid}; +use crate::{ + error::DidPeerError, + peer_did::{numalgos::numalgo3::Numalgo3, peer_did::PeerDid}, +}; -pub fn generate_numalgo3(did: &Did) -> Result { +pub fn generate_numalgo3(did: &Did) -> Result, DidPeerError> { let numalgoless_id = did.id().chars().skip(2).collect::(); let numalgoless_id_hashed = digest(numalgoless_id); - PeerDid::parse(format!("did:peer:3.{}", numalgoless_id_hashed)) + PeerDid::::parse(format!("did:peer:3.{}", numalgoless_id_hashed)) } #[cfg(test)] @@ -14,15 +17,17 @@ mod tests { use super::*; #[test] - fn test_shorten() { + fn test_generate_numalgo3() { let peer_did_2 = Did::parse("did:peer:2\ .Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ .Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V\ .Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg\ .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0".to_string()).unwrap(); assert_eq!( - PeerDid::parse("did:peer:3.0e857e93798921e83cfc2ef8bee9cafc25f15f4c9c7bee5ed9a9c62b56a62cca".to_string()) - .unwrap(), + PeerDid::::parse( + "did:peer:3.0e857e93798921e83cfc2ef8bee9cafc25f15f4c9c7bee5ed9a9c62b56a62cca".to_string() + ) + .unwrap(), generate_numalgo3(&peer_did_2).unwrap() ); } diff --git a/did_peer/src/peer_did/generate.rs b/did_peer/src/peer_did/generate.rs new file mode 100644 index 0000000000..a76e8b6f99 --- /dev/null +++ b/did_peer/src/peer_did/generate.rs @@ -0,0 +1,16 @@ +use crate::{error::DidPeerError, numalgos::numalgo2, numalgos::numalgo3}; +use did_doc::schema::did_doc::DidDocument; +use did_doc_sov::extra_fields::ExtraFieldsSov; + +use super::{ + numalgos::{numalgo2::Numalgo2, numalgo3::Numalgo3}, + peer_did::PeerDid, +}; + +pub fn generate_numalgo2(did_document: DidDocument) -> Result, DidPeerError> { + numalgo2::generate_numalgo2(did_document) +} + +pub fn generate_numalgo3(did_document: DidDocument) -> Result, DidPeerError> { + numalgo3::generate_numalgo3(generate_numalgo2(did_document)?.did()) +} diff --git a/did_peer/src/peer_did/mod.rs b/did_peer/src/peer_did/mod.rs index 15debd2917..0f8a6bd1c6 100644 --- a/did_peer/src/peer_did/mod.rs +++ b/did_peer/src/peer_did/mod.rs @@ -1,7 +1,7 @@ -mod numalgo; -mod peer_did; -mod regex; -mod transform; +pub mod generate; +pub mod numalgos; +pub mod peer_did; -pub use numalgo::Numalgo; -pub use peer_did::PeerDid; +mod parse; +mod regex; +mod validate; diff --git a/did_peer/src/peer_did/numalgo.rs b/did_peer/src/peer_did/numalgo.rs deleted file mode 100644 index f02fe5c55a..0000000000 --- a/did_peer/src/peer_did/numalgo.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::fmt::Display; - -use crate::error::DidPeerError; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Numalgo { - InceptionKeyWithoutDoc, - GenesisDoc, - MultipleInceptionKeys, - DidShortening, -} - -impl Display for Numalgo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Numalgo::InceptionKeyWithoutDoc => write!(f, "0"), - Numalgo::GenesisDoc => write!(f, "1"), - Numalgo::MultipleInceptionKeys => write!(f, "2"), - Numalgo::DidShortening => write!(f, "3"), - } - } -} - -impl TryFrom for Numalgo { - type Error = DidPeerError; - - fn try_from(value: char) -> Result { - match value { - '0' => Ok(Numalgo::InceptionKeyWithoutDoc), - '1' => Ok(Numalgo::GenesisDoc), - '2' => Ok(Numalgo::MultipleInceptionKeys), - '3' => Ok(Numalgo::DidShortening), - c @ _ => Err(DidPeerError::InvalidNumalgoCharacter(c)), - } - } -} diff --git a/did_peer/src/peer_did/numalgos/mod.rs b/did_peer/src/peer_did/numalgos/mod.rs new file mode 100644 index 0000000000..269a3fa8b8 --- /dev/null +++ b/did_peer/src/peer_did/numalgos/mod.rs @@ -0,0 +1,50 @@ +pub mod numalgo0; +pub mod numalgo1; +pub mod numalgo2; +pub mod numalgo3; + +pub(super) mod traits; + +use std::fmt::Display; + +use numalgo0::Numalgo0; +use numalgo1::Numalgo1; +use numalgo2::Numalgo2; +use numalgo3::Numalgo3; + +use crate::error::DidPeerError; + +use self::traits::Numalgo; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum NumalgoKind { + InceptionKeyWithoutDoc(Numalgo0), + GenesisDoc(Numalgo1), + MultipleInceptionKeys(Numalgo2), + DidShortening(Numalgo3), +} + +impl Display for NumalgoKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NumalgoKind::InceptionKeyWithoutDoc(_) => Numalgo0::NUMALGO_CHAR.fmt(f), + NumalgoKind::GenesisDoc(_) => Numalgo1::NUMALGO_CHAR.fmt(f), + NumalgoKind::MultipleInceptionKeys(_) => Numalgo2::NUMALGO_CHAR.fmt(f), + NumalgoKind::DidShortening(_) => Numalgo3::NUMALGO_CHAR.fmt(f), + } + } +} + +impl TryFrom for NumalgoKind { + type Error = DidPeerError; + + fn try_from(value: char) -> Result { + match value { + Numalgo0::NUMALGO_CHAR => Ok(NumalgoKind::InceptionKeyWithoutDoc(Numalgo0)), + Numalgo1::NUMALGO_CHAR => Ok(NumalgoKind::GenesisDoc(Numalgo1)), + Numalgo2::NUMALGO_CHAR => Ok(NumalgoKind::MultipleInceptionKeys(Numalgo2)), + Numalgo3::NUMALGO_CHAR => Ok(NumalgoKind::DidShortening(Numalgo3)), + c @ _ => Err(DidPeerError::InvalidNumalgoCharacter(c)), + } + } +} diff --git a/did_peer/src/peer_did/numalgos/numalgo0.rs b/did_peer/src/peer_did/numalgos/numalgo0.rs new file mode 100644 index 0000000000..469949e76d --- /dev/null +++ b/did_peer/src/peer_did/numalgos/numalgo0.rs @@ -0,0 +1,8 @@ +use super::traits::Numalgo; + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +pub struct Numalgo0; + +impl Numalgo for Numalgo0 { + const NUMALGO_CHAR: char = '0'; +} diff --git a/did_peer/src/peer_did/numalgos/numalgo1.rs b/did_peer/src/peer_did/numalgos/numalgo1.rs new file mode 100644 index 0000000000..0405dafea9 --- /dev/null +++ b/did_peer/src/peer_did/numalgos/numalgo1.rs @@ -0,0 +1,8 @@ +use super::traits::Numalgo; + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +pub struct Numalgo1; + +impl Numalgo for Numalgo1 { + const NUMALGO_CHAR: char = '1'; +} diff --git a/did_peer/src/peer_did/numalgos/numalgo2.rs b/did_peer/src/peer_did/numalgos/numalgo2.rs new file mode 100644 index 0000000000..64f9c6259a --- /dev/null +++ b/did_peer/src/peer_did/numalgos/numalgo2.rs @@ -0,0 +1,38 @@ +use did_doc::schema::did_doc::DidDocument; +use did_doc_sov::extra_fields::ExtraFieldsSov; +use did_parser::Did; + +use crate::{ + error::DidPeerError, + numalgos::{numalgo2::resolve_numalgo2, numalgo3::generate_numalgo3}, + peer_did::peer_did::PeerDid, + peer_did_resolver::options::PublicKeyEncoding, +}; + +use super::{ + numalgo3::Numalgo3, + traits::{Numalgo, ResolvableNumalgo, ToNumalgo3}, +}; + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +pub struct Numalgo2; + +impl Numalgo for Numalgo2 { + const NUMALGO_CHAR: char = '2'; +} + +impl ResolvableNumalgo for Numalgo2 { + fn resolve( + &self, + did: &Did, + public_key_encoding: PublicKeyEncoding, + ) -> Result, DidPeerError> { + resolve_numalgo2(did, public_key_encoding) + } +} + +impl ToNumalgo3 for Numalgo2 { + fn to_numalgo3(did: &Did) -> Result, DidPeerError> { + generate_numalgo3(did) + } +} diff --git a/did_peer/src/peer_did/numalgos/numalgo3.rs b/did_peer/src/peer_did/numalgos/numalgo3.rs new file mode 100644 index 0000000000..a0798862b4 --- /dev/null +++ b/did_peer/src/peer_did/numalgos/numalgo3.rs @@ -0,0 +1,18 @@ +use did_parser::Did; + +use crate::{error::DidPeerError, peer_did::peer_did::PeerDid}; + +use super::traits::{Numalgo, ToNumalgo3}; + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +pub struct Numalgo3; + +impl Numalgo for Numalgo3 { + const NUMALGO_CHAR: char = '3'; +} + +impl ToNumalgo3 for Numalgo3 { + fn to_numalgo3(did: &Did) -> Result, DidPeerError> { + Ok(PeerDid::from_parts(did.to_owned(), Self::default())) + } +} diff --git a/did_peer/src/peer_did/numalgos/traits.rs b/did_peer/src/peer_did/numalgos/traits.rs new file mode 100644 index 0000000000..80d22e0db7 --- /dev/null +++ b/did_peer/src/peer_did/numalgos/traits.rs @@ -0,0 +1,50 @@ +use did_doc::schema::did_doc::DidDocument; +use did_doc_sov::extra_fields::ExtraFieldsSov; +use did_parser::Did; + +use crate::{ + error::DidPeerError, + peer_did::{peer_did::PeerDid, validate::validate}, + peer_did_resolver::options::PublicKeyEncoding, +}; + +use super::numalgo3::Numalgo3; + +pub trait Numalgo: Sized + Default { + const NUMALGO_CHAR: char; + + fn parse(did: T) -> Result, DidPeerError> + where + Did: TryFrom, + >::Error: Into, + { + let did: Did = did.try_into().map_err(Into::into)?; + + let numalgo_char = did.id().chars().nth(0).ok_or_else(|| { + DidPeerError::DidValidationError(format!( + "Invalid did: unable to read numalgo character in did {}", + did.did() + )) + })?; + + if numalgo_char != Self::NUMALGO_CHAR { + return Err(DidPeerError::InvalidNumalgoCharacter(numalgo_char)); + } + + validate(&did)?; + + Ok(PeerDid::from_parts(did, Self::default())) + } +} + +pub trait ResolvableNumalgo: Numalgo { + fn resolve( + &self, + did: &Did, + public_key_encoding: PublicKeyEncoding, + ) -> Result, DidPeerError>; +} + +pub trait ToNumalgo3: Numalgo { + fn to_numalgo3(did: &Did) -> Result, DidPeerError>; +} diff --git a/did_peer/src/peer_did/parse.rs b/did_peer/src/peer_did/parse.rs new file mode 100644 index 0000000000..0d5395f3b6 --- /dev/null +++ b/did_peer/src/peer_did/parse.rs @@ -0,0 +1,13 @@ +use did_parser::Did; + +use crate::error::DidPeerError; + +use super::numalgos::NumalgoKind; + +pub fn parse_numalgo(did: &Did) -> Result { + did.id() + .chars() + .nth(0) + .ok_or_else(|| DidPeerError::DidValidationError(format!("Invalid did: {}", did.did())))? + .try_into() +} diff --git a/did_peer/src/peer_did/peer_did.rs b/did_peer/src/peer_did/peer_did.rs deleted file mode 100644 index 33e7525168..0000000000 --- a/did_peer/src/peer_did/peer_did.rs +++ /dev/null @@ -1,164 +0,0 @@ -use std::fmt::Display; - -use crate::{error::DidPeerError, numalgos::numalgo2::generate_numalgo2, numalgos::numalgo3::generate_numalgo3}; -use did_doc::schema::did_doc::DidDocument; -use did_doc_sov::extra_fields::ExtraFieldsSov; -use did_parser::Did; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use super::{numalgo::Numalgo, regex::PEER_DID_REGEX, transform::Transform}; - -#[derive(Clone, Debug, PartialEq)] -pub struct PeerDid { - did: Did, - numalgo: Numalgo, - transform: Option, -} - -impl PeerDid { - pub fn parse(did: T) -> Result - where - Did: TryFrom, - >::Error: Into, - { - let did: Did = did.try_into().map_err(Into::into)?; - let numalgo = Self::parse_numalgo(&did)?; - let transform = match numalgo { - Numalgo::InceptionKeyWithoutDoc | Numalgo::GenesisDoc => Some(Self::parse_transform(&did)?), - _ => None, - }; - Self::validate(&did)?; - Ok(Self { - did, - numalgo, - transform, - }) - } - pub fn did(&self) -> &Did { - &self.did - } - - pub fn numalgo(&self) -> &Numalgo { - &self.numalgo - } - - pub fn transform(&self) -> Option<&Transform> { - self.transform.as_ref() - } - - pub fn generate_numalgo2(did_document: DidDocument) -> Result { - generate_numalgo2(did_document) - } - - pub fn generate_numalgo3(did_document: DidDocument) -> Result { - generate_numalgo3(Self::generate_numalgo2(did_document)?.did()) - } - - pub fn to_numalgo3(&self) -> Result { - match self.numalgo() { - Numalgo::MultipleInceptionKeys => generate_numalgo3(self.did()), - n @ _ => Err(DidPeerError::UnsupportedNumalgo(n.clone())), - } - } - - fn validate(did: &Did) -> Result<(), DidPeerError> { - if !PEER_DID_REGEX.is_match(did.did()) { - Err(DidPeerError::DidValidationError(format!("Invalid did: {}", did.did()))) - } else { - Ok(()) - } - } - - fn parse_numalgo(did: &Did) -> Result { - did.id() - .chars() - .nth(0) - .ok_or_else(|| DidPeerError::DidValidationError(format!("Invalid did: {}", did.did())))? - .try_into() - } - - fn parse_transform(did: &Did) -> Result { - did.id() - .chars() - .nth(1) - .ok_or_else(|| DidPeerError::DidValidationError(format!("Invalid did: {}", did.did())))? - .try_into() - } -} - -impl Serialize for PeerDid { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(self.did.did()) - } -} - -impl<'de> Deserialize<'de> for PeerDid { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let did = String::deserialize(deserializer)?; - Self::parse(did).map_err(serde::de::Error::custom) - } -} - -impl Display for PeerDid { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.did) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod parse { - use super::*; - - macro_rules! generate_negative_parse_test { - ($test_name:ident, $input:expr, $error_pattern:pat) => { - #[test] - fn $test_name() { - let result = PeerDid::parse($input.to_string()); - assert!(matches!(result, Err($error_pattern))); - } - }; - } - - generate_negative_parse_test!( - test_peer_did_parse_unsupported_transform_code, - "did:peer:2\ - .Ea6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ - .Va6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V\ - .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0", - DidPeerError::DidValidationError(_) - ); - - generate_negative_parse_test!( - test_peer_did_parse_malformed_base58_encoding_signing, - "did:peer:2\ - .Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ - .Vz6MkqRYqQiSgvZQdnBytw86Qbs0ZWUkGv22od935YF4s8M7V\ - .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0", - DidPeerError::DidValidationError(_) - ); - - generate_negative_parse_test!( - test_peer_did_parse_malformed_base58_encoding_encryption, - "did:peer:2\ - .Ez6LSbysY2xFMRpG0hb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ - .Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V\ - .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0=", - DidPeerError::DidParserError(_) - ); - - #[test] - fn test_peer_did_numalgo2() { - let did = "did:peer:3.8a33de52d9e9e9cfd5c5fd8a7e5da5d3c73208bfc5e5fd5a4eb4af3f3b3f3a3a"; - assert!(PeerDid::parse(did.to_string()).is_ok()); - } - } -} diff --git a/did_peer/src/peer_did/peer_did/generic.rs b/did_peer/src/peer_did/peer_did/generic.rs new file mode 100644 index 0000000000..f4cd0c8715 --- /dev/null +++ b/did_peer/src/peer_did/peer_did/generic.rs @@ -0,0 +1,144 @@ +use crate::{ + error::DidPeerError, + peer_did::{ + numalgos::{numalgo2::Numalgo2, numalgo3::Numalgo3, NumalgoKind}, + parse::parse_numalgo, + validate::validate, + }, +}; +use did_parser::Did; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use super::PeerDid; + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericPeerDid { + Numalgo2(PeerDid), + Numalgo3(PeerDid), +} + +impl GenericPeerDid { + pub fn parse(did: T) -> Result + where + Did: TryFrom, + >::Error: Into, + { + let did: Did = did.try_into().map_err(Into::into)?; + let numalgo = parse_numalgo(&did)?; + validate(&did)?; + let parsed = match numalgo { + NumalgoKind::MultipleInceptionKeys(numalgo) => GenericPeerDid::Numalgo2(PeerDid { did, numalgo }), + _ => GenericPeerDid::Numalgo3(PeerDid { did, numalgo: Numalgo3 }), + }; + Ok(parsed) + } + + pub fn numalgo(&self) -> NumalgoKind { + match self { + GenericPeerDid::Numalgo2(peer_did) => NumalgoKind::MultipleInceptionKeys(peer_did.numalgo), + GenericPeerDid::Numalgo3(peer_did) => NumalgoKind::DidShortening(peer_did.numalgo), + } + } +} + +impl Serialize for GenericPeerDid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match &self { + GenericPeerDid::Numalgo2(peer_did) => serializer.serialize_str(peer_did.did().did()), + GenericPeerDid::Numalgo3(peer_did) => serializer.serialize_str(peer_did.did().did()), + } + } +} + +impl<'de> Deserialize<'de> for GenericPeerDid { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let did = String::deserialize(deserializer)?; + Self::parse(did).map_err(serde::de::Error::custom) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const VALID_PEER_DID_NUMALGO2: &str = "did:peer:2\ + .Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH\ + .VzXwpBnMdCm1cLmKuzgESn29nqnonp1ioqrQMRHNsmjMyppzx8xB2pv7cw8q1PdDacSrdWE3dtB9f7Nxk886mdzNFoPtY\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0"; + + const INVALID_PEER_DID_NUMALGO2: &str = "did:peer:2\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX1"; + + const VALID_PEER_DID_NUMALGO3: &str = "did:peer:3.d8da5079c166b183cf815ee27747f34e116977103d8b23c96dcba9a9d9429688"; + + const INVALID_PEER_DID_NUMALGO3: &str = + "did:peer:3.d8da5079c166b183cfz15ee27747f34e116977103d8b23c96dcba9a9d9429689"; + + fn generic_peer_did_numalgo2() -> GenericPeerDid { + GenericPeerDid::Numalgo2(PeerDid { + did: VALID_PEER_DID_NUMALGO2.parse().unwrap(), + numalgo: Numalgo2, + }) + } + + fn generic_peer_did_numalgo3() -> GenericPeerDid { + GenericPeerDid::Numalgo3(PeerDid { + did: VALID_PEER_DID_NUMALGO3.parse().unwrap(), + numalgo: Numalgo3, + }) + } + + mod serialize { + use super::*; + + #[test] + fn numalgo2() { + let serialized = serde_json::to_string(&generic_peer_did_numalgo2()).unwrap(); + assert_eq!(serialized, format!("\"{}\"", VALID_PEER_DID_NUMALGO2)); + } + + #[test] + fn numalgo3() { + let serialized = serde_json::to_string(&generic_peer_did_numalgo3()).unwrap(); + assert_eq!(serialized, format!("\"{}\"", VALID_PEER_DID_NUMALGO3)); + } + } + + mod deserialize { + use super::*; + + #[test] + fn numalgo2() { + let deserialized: GenericPeerDid = + serde_json::from_str(&format!("\"{}\"", VALID_PEER_DID_NUMALGO2)).unwrap(); + assert_eq!(deserialized, generic_peer_did_numalgo2()); + } + + #[test] + fn numalgo2_invalid() { + let deserialized: Result = + serde_json::from_str(&format!("\"{}\"", INVALID_PEER_DID_NUMALGO2)); + assert!(deserialized.is_err()); + } + + #[test] + fn numalgo3() { + let deserialized: GenericPeerDid = + serde_json::from_str(&format!("\"{}\"", VALID_PEER_DID_NUMALGO3)).unwrap(); + assert_eq!(deserialized, generic_peer_did_numalgo3()); + } + + #[test] + fn numalgo3_invalid() { + let deserialized: Result = + serde_json::from_str(&format!("\"{}\"", INVALID_PEER_DID_NUMALGO3)); + assert!(deserialized.is_err()); + } + } +} diff --git a/did_peer/src/peer_did/peer_did/mod.rs b/did_peer/src/peer_did/peer_did/mod.rs new file mode 100644 index 0000000000..b6f6467a5f --- /dev/null +++ b/did_peer/src/peer_did/peer_did/mod.rs @@ -0,0 +1,223 @@ +pub mod generic; + +use core::fmt; +use std::{fmt::Display, marker::PhantomData}; + +use crate::error::DidPeerError; +use did_parser::Did; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +use super::numalgos::{ + numalgo3::Numalgo3, + traits::{Numalgo, ToNumalgo3}, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct PeerDid { + did: Did, + numalgo: N, +} + +impl PeerDid { + pub fn parse(did: T) -> Result, DidPeerError> + where + Did: TryFrom, + >::Error: Into, + { + N::parse(did) + } + + pub fn did(&self) -> &Did { + &self.did + } + + pub(crate) fn from_parts(did: Did, numalgo: N) -> PeerDid { + Self { did, numalgo } + } +} + +impl PeerDid { + pub fn to_numalgo3(&self) -> Result, DidPeerError> { + N::to_numalgo3(self.did()) + } +} + +impl Serialize for PeerDid { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.did.did()) + } +} + +impl<'de, N: Numalgo> Deserialize<'de> for PeerDid { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PeerDidVisitor(PhantomData); + + impl<'de, N: Numalgo> Visitor<'de> for PeerDidVisitor { + type Value = PeerDid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string representing a DID") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match N::parse(value.to_string()) { + Ok(peer_did) => Ok(peer_did), + Err(err) => Err(E::custom(format!("Failed to parse numalgo: {err}"))), + } + } + } + + deserializer.deserialize_str(PeerDidVisitor(PhantomData)) + } +} + +impl Display for PeerDid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.did) + } +} + +#[cfg(test)] +mod tests { + use crate::peer_did::numalgos::numalgo2::Numalgo2; + + use super::*; + + const VALID_PEER_DID_NUMALGO2: &str = "did:peer:2\ + .Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH\ + .VzXwpBnMdCm1cLmKuzgESn29nqnonp1ioqrQMRHNsmjMyppzx8xB2pv7cw8q1PdDacSrdWE3dtB9f7Nxk886mdzNFoPtY\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0"; + + const VALID_PEER_DID_NUMALGO3: &str = "did:peer:3.d8da5079c166b183cf815ee27747f34e116977103d8b23c96dcba9a9d9429688"; + + fn peer_did_numalgo2() -> PeerDid { + PeerDid { + did: VALID_PEER_DID_NUMALGO2.parse().unwrap(), + numalgo: Numalgo2, + } + } + + fn peer_did_numalgo3() -> PeerDid { + PeerDid { + did: VALID_PEER_DID_NUMALGO3.parse().unwrap(), + numalgo: Numalgo3, + } + } + + mod parse { + use super::*; + + macro_rules! generate_negative_parse_test { + ($test_name:ident, $input:expr, $error_pattern:pat) => { + #[test] + fn $test_name() { + let result = PeerDid::::parse($input.to_string()); + assert!(matches!(result, Err($error_pattern))); + } + }; + } + + generate_negative_parse_test!( + unsupported_transform_code, + "did:peer:2\ + .Ea6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ + .Va6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0", + DidPeerError::DidValidationError(_) + ); + + generate_negative_parse_test!( + malformed_base58_encoding_signing, + "did:peer:2\ + .Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ + .Vz6MkqRYqQiSgvZQdnBytw86Qbs0ZWUkGv22od935YF4s8M7V\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0", + DidPeerError::DidValidationError(_) + ); + + generate_negative_parse_test!( + malformed_base58_encoding_encryption, + "did:peer:2\ + .Ez6LSbysY2xFMRpG0hb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc\ + .Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V\ + .SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXX0=", + DidPeerError::DidParserError(_) + ); + + #[test] + fn numalgo3() { + let expected = PeerDid { + did: VALID_PEER_DID_NUMALGO3.parse().unwrap(), + numalgo: Numalgo3, + }; + assert_eq!( + expected, + PeerDid::::parse(VALID_PEER_DID_NUMALGO3.to_string()).unwrap() + ); + } + } + + mod to_numalgo3 { + use super::*; + + #[test] + fn numalgo2() { + assert_eq!(peer_did_numalgo3(), peer_did_numalgo2().to_numalgo3().unwrap()); + } + + #[test] + fn numalgo3() { + assert_eq!(peer_did_numalgo3(), peer_did_numalgo3().to_numalgo3().unwrap()); + } + } + + mod serialize { + use super::*; + + #[test] + fn numalgo2() { + assert_eq!( + serde_json::to_string(&peer_did_numalgo2()).unwrap(), + format!("\"{}\"", VALID_PEER_DID_NUMALGO2) + ); + } + + #[test] + fn numalgo3() { + assert_eq!( + serde_json::to_string(&peer_did_numalgo3()).unwrap(), + format!("\"{VALID_PEER_DID_NUMALGO3}\"") + ); + } + } + + mod deserialize { + use super::*; + + #[test] + fn numalgo2() { + let deserialized: PeerDid = + serde_json::from_str(&format!("\"{}\"", VALID_PEER_DID_NUMALGO2)).unwrap(); + assert_eq!(peer_did_numalgo2(), deserialized); + } + + #[test] + fn numalgo3() { + let deserialized: PeerDid = + serde_json::from_str(&format!("\"{}\"", VALID_PEER_DID_NUMALGO3)).unwrap(); + assert_eq!(peer_did_numalgo3(), deserialized); + } + } +} diff --git a/did_peer/src/peer_did/transform.rs b/did_peer/src/peer_did/transform.rs deleted file mode 100644 index 1a4cede324..0000000000 --- a/did_peer/src/peer_did/transform.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::error::DidPeerError; - -#[derive(Clone, Debug, PartialEq)] -pub enum Transform { - BASE58BTC, -} - -impl TryFrom for Transform { - type Error = DidPeerError; - - fn try_from(value: char) -> Result { - match value { - 'z' => Ok(Transform::BASE58BTC), - c @ _ => Err(DidPeerError::DidValidationError(format!( - "Unsupported transform character: {}", - c - ))), - } - } -} diff --git a/did_peer/src/peer_did/validate.rs b/did_peer/src/peer_did/validate.rs new file mode 100644 index 0000000000..2e5465a376 --- /dev/null +++ b/did_peer/src/peer_did/validate.rs @@ -0,0 +1,13 @@ +use did_parser::Did; + +use crate::error::DidPeerError; + +use super::regex::PEER_DID_REGEX; + +pub fn validate(did: &Did) -> Result<(), DidPeerError> { + if !PEER_DID_REGEX.is_match(did.did()) { + Err(DidPeerError::DidValidationError(format!("Invalid did: {}", did.did()))) + } else { + Ok(()) + } +} diff --git a/did_peer/src/peer_did_resolver/options.rs b/did_peer/src/peer_did_resolver/options.rs index c6901c6658..6794551c40 100644 --- a/did_peer/src/peer_did_resolver/options.rs +++ b/did_peer/src/peer_did_resolver/options.rs @@ -1,3 +1,4 @@ +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PublicKeyEncoding { Multibase, Base58, @@ -25,7 +26,7 @@ impl ExtraFieldsOptions { self } - pub fn public_key_encoding(&self) -> &PublicKeyEncoding { - &self.public_key_encoding + pub fn public_key_encoding(&self) -> PublicKeyEncoding { + self.public_key_encoding } } diff --git a/did_peer/src/peer_did_resolver/resolver.rs b/did_peer/src/peer_did_resolver/resolver.rs index 4e43bdc7be..2854bd8e9a 100644 --- a/did_peer/src/peer_did_resolver/resolver.rs +++ b/did_peer/src/peer_did_resolver/resolver.rs @@ -9,11 +9,7 @@ use did_resolver::{ }, }; -use crate::{ - error::DidPeerError, - numalgos::numalgo2::resolve_numalgo2, - peer_did::{Numalgo, PeerDid}, -}; +use crate::{error::DidPeerError, numalgos::numalgo2::resolve_numalgo2, peer_did::peer_did::generic::GenericPeerDid}; use super::options::ExtraFieldsOptions; @@ -35,9 +31,9 @@ impl DidResolvable for PeerDidResolver { did: &Did, options: &DidResolutionOptions, ) -> Result, GenericError> { - let peer_did = PeerDid::parse(did.to_owned())?; - match peer_did.numalgo() { - Numalgo::MultipleInceptionKeys => { + let peer_did = GenericPeerDid::parse(did.to_owned())?; + match peer_did { + GenericPeerDid::Numalgo2(peer_did) => { let did_doc = resolve_numalgo2(&peer_did.did(), options.extra().public_key_encoding())?; let resolution_metadata = DidResolutionMetadata::builder() .content_type("application/did+json".to_string()) @@ -45,7 +41,7 @@ impl DidResolvable for PeerDidResolver { let builder = DidResolutionOutput::builder(did_doc).did_resolution_metadata(resolution_metadata); Ok(builder.build()) } - n @ _ => Err(Box::new(DidPeerError::UnsupportedNumalgo(n.clone()))), + n @ _ => Err(Box::new(DidPeerError::UnsupportedNumalgo(n.numalgo()))), } } } diff --git a/did_peer/tests/generate.rs b/did_peer/tests/generate.rs index c857bd7bad..0d598b3043 100644 --- a/did_peer/tests/generate.rs +++ b/did_peer/tests/generate.rs @@ -2,7 +2,12 @@ mod fixtures; use did_doc::schema::did_doc::DidDocument; use did_doc_sov::extra_fields::ExtraFieldsSov; -use did_peer::peer_did::PeerDid; +use did_peer::peer_did::{ + generate::{generate_numalgo2, generate_numalgo3}, + numalgos::numalgo2::Numalgo2, + numalgos::numalgo3::Numalgo3, + peer_did::PeerDid, +}; use crate::fixtures::{ basic::{DID_DOC_BASIC, PEER_DID_NUMALGO_2_BASIC, PEER_DID_NUMALGO_3_BASIC}, @@ -21,8 +26,8 @@ macro_rules! generate_test_numalgo2 { fn $test_name() { let did_document = serde_json::from_str::>($did_doc).unwrap(); assert_eq!( - PeerDid::parse($peer_did.to_string()).unwrap(), - PeerDid::generate_numalgo2(did_document).unwrap() + PeerDid::::parse($peer_did.to_string()).unwrap(), + generate_numalgo2(did_document).unwrap() ); } }; @@ -34,8 +39,8 @@ macro_rules! generate_test_numalgo3 { fn $test_name() { let did_document = serde_json::from_str::>($did_doc).unwrap(); assert_eq!( - PeerDid::parse($peer_did.to_string()).unwrap(), - PeerDid::generate_numalgo3(did_document).unwrap() + PeerDid::::parse($peer_did.to_string()).unwrap(), + generate_numalgo3(did_document).unwrap() ); } };