Add DidKey type

This commit is contained in:
silverpill 2022-11-09 01:51:44 +00:00
parent dae6e9437b
commit a5c9da78ef
10 changed files with 90 additions and 12 deletions

View file

@ -47,7 +47,10 @@ pub fn parse_identity_proof(
}; };
let did = attachment.name.parse::<Did>() let did = attachment.name.parse::<Did>()
.map_err(|_| ValidationError("invalid did"))?; .map_err(|_| ValidationError("invalid did"))?;
let Did::Pkh(ref did_pkh) = did; let did_pkh = match did {
Did::Pkh(ref did_pkh) => did_pkh,
_ => return Err(ValidationError("invalid proof issuer")),
};
let signature = attachment.signature_value.as_ref() let signature = attachment.signature_value.as_ref()
.ok_or(ValidationError("missing signature"))?; .ok_or(ValidationError("missing signature"))?;
verify_eip191_identity_proof( verify_eip191_identity_proof(

View file

@ -8,12 +8,14 @@ use serde::{
de::Error as DeserializerError, de::Error as DeserializerError,
}; };
use super::did_key::DidKey;
use super::did_pkh::DidPkh; use super::did_pkh::DidPkh;
const DID_RE: &str = r"did:(?P<method>\w+):.+"; const DID_RE: &str = r"did:(?P<method>\w+):.+";
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Did { pub enum Did {
Key(DidKey),
Pkh(DidPkh), Pkh(DidPkh),
} }
@ -28,6 +30,10 @@ impl FromStr for Did {
let did_re = Regex::new(DID_RE).unwrap(); let did_re = Regex::new(DID_RE).unwrap();
let caps = did_re.captures(value).ok_or(DidParseError)?; let caps = did_re.captures(value).ok_or(DidParseError)?;
let did = match &caps["method"] { let did = match &caps["method"] {
"key" => {
let did_key = DidKey::from_str(value)?;
Self::Key(did_key)
},
"pkh" => { "pkh" => {
let did_pkh = DidPkh::from_str(value)?; let did_pkh = DidPkh::from_str(value)?;
Self::Pkh(did_pkh) Self::Pkh(did_pkh)
@ -41,6 +47,7 @@ impl FromStr for Did {
impl fmt::Display for Did { impl fmt::Display for Did {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let did_str = match self { let did_str = match self {
Self::Key(did_key) => did_key.to_string(),
Self::Pkh(did_pkh) => did_pkh.to_string(), Self::Pkh(did_pkh) => did_pkh.to_string(),
}; };
write!(formatter, "{}", did_str) write!(formatter, "{}", did_str)

46
src/identity/did_key.rs Normal file
View file

@ -0,0 +1,46 @@
/// https://w3c-ccg.github.io/did-method-key/
use std::fmt;
use std::str::FromStr;
use regex::Regex;
use super::did::DidParseError;
const DID_KEY_RE: &str = r"did:key:(?P<key>z[a-km-zA-HJ-NP-Z1-9]+)";
#[derive(Clone, Debug, PartialEq)]
pub struct DidKey {
pub key: String,
}
impl FromStr for DidKey {
type Err = DidParseError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let did_key_re = Regex::new(DID_KEY_RE).unwrap();
let caps = did_key_re.captures(value).ok_or(DidParseError)?;
let did_key = Self {
key: caps["key"].to_string(),
};
Ok(did_key)
}
}
impl fmt::Display for DidKey {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let did_str = format!("did:key:{}", self.key);
write!(formatter, "{}", did_str)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_did_key_string_conversion() {
let did_str = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
let did_key: DidKey = did_str.parse().unwrap();
assert_eq!(did_key.to_string(), did_str);
}
}

View file

@ -49,14 +49,14 @@ impl FromStr for DidPkh {
fn from_str(value: &str) -> Result<Self, Self::Err> { fn from_str(value: &str) -> Result<Self, Self::Err> {
let did_pkh_re = Regex::new(DID_PKH_RE).unwrap(); let did_pkh_re = Regex::new(DID_PKH_RE).unwrap();
let caps = did_pkh_re.captures(value).ok_or(DidParseError)?; let caps = did_pkh_re.captures(value).ok_or(DidParseError)?;
let did = Self { let did_pkh = Self {
chain_id: ChainId { chain_id: ChainId {
namespace: caps["network"].to_string(), namespace: caps["network"].to_string(),
reference: caps["chain"].to_string(), reference: caps["chain"].to_string(),
}, },
address: caps["address"].to_string(), address: caps["address"].to_string(),
}; };
Ok(did) Ok(did_pkh)
} }
} }

View file

@ -1,2 +1,3 @@
pub mod did; pub mod did;
pub mod did_key;
pub mod did_pkh; pub mod did_pkh;

View file

@ -79,7 +79,10 @@ impl Account {
let mut identity_proofs = vec![]; let mut identity_proofs = vec![];
for proof in profile.identity_proofs.clone().into_inner() { for proof in profile.identity_proofs.clone().into_inner() {
let Did::Pkh(did_pkh) = proof.issuer; let did_pkh = match proof.issuer {
Did::Pkh(did_pkh) => did_pkh,
_ => continue,
};
// Skip proof if it doesn't map to field name // Skip proof if it doesn't map to field name
if let Some(currency) = did_pkh.currency() { if let Some(currency) = did_pkh.currency() {
let field_name = currency.field_name(); let field_name = currency.field_name();

View file

@ -261,10 +261,14 @@ async fn send_signed_update(
).map_err(|_| HttpError::InternalError)?; ).map_err(|_| HttpError::InternalError)?;
let canonical_json = canonicalize_object(&activity) let canonical_json = canonicalize_object(&activity)
.map_err(|_| HttpError::InternalError)?; .map_err(|_| HttpError::InternalError)?;
let Did::Pkh(signer) = signer; let proof = match signer {
Did::Key(_) => return Err(ValidationError("unsupported DID type").into()),
Did::Pkh(signer) => {
verify_jcs_eip191_signature(&signer, &canonical_json, &data.signature) verify_jcs_eip191_signature(&signer, &canonical_json, &data.signature)
.map_err(|_| ValidationError("invalid signature"))?; .map_err(|_| ValidationError("invalid signature"))?;
let proof = IntegrityProof::jcs_eip191(&signer, &data.signature); IntegrityProof::jcs_eip191(&signer, &data.signature)
},
};
let mut activity_value = serde_json::to_value(activity) let mut activity_value = serde_json::to_value(activity)
.map_err(|_| HttpError::InternalError)?; .map_err(|_| HttpError::InternalError)?;
add_integrity_proof(&mut activity_value, proof) add_integrity_proof(&mut activity_value, proof)
@ -291,8 +295,12 @@ async fn get_identity_claim(
let db_client = &**get_database_client(&db_pool).await?; let db_client = &**get_database_client(&db_pool).await?;
let current_user = get_current_user(db_client, auth.token()).await?; let current_user = get_current_user(db_client, auth.token()).await?;
let actor_id = current_user.profile.actor_id(&config.instance_url()); let actor_id = current_user.profile.actor_id(&config.instance_url());
let Did::Pkh(did) = query_params.did.parse::<Did>() let did = query_params.did.parse::<Did>()
.map_err(|_| ValidationError("invalid DID"))?; .map_err(|_| ValidationError("invalid DID"))?;
let did = match did {
Did::Key(_) => return Err(ValidationError("unsupported DID type").into()),
Did::Pkh(did_pkh) => did_pkh,
};
let claim = create_identity_claim(&actor_id, &did) let claim = create_identity_claim(&actor_id, &did)
.map_err(|_| HttpError::InternalError)?; .map_err(|_| HttpError::InternalError)?;
let response = IdentityClaim { claim }; let response = IdentityClaim { claim };
@ -311,7 +319,10 @@ async fn create_identity_proof(
let actor_id = current_user.profile.actor_id(&config.instance_url()); let actor_id = current_user.profile.actor_id(&config.instance_url());
let did = proof_data.did.parse::<Did>() let did = proof_data.did.parse::<Did>()
.map_err(|_| ValidationError("invalid DID"))?; .map_err(|_| ValidationError("invalid DID"))?;
let Did::Pkh(ref did_pkh) = did; let did_pkh = match did {
Did::Key(_) => return Err(ValidationError("unsupported DID type").into()),
Did::Pkh(ref did_pkh) => did_pkh,
};
if did_pkh.chain_id != ChainId::ethereum_mainnet() { if did_pkh.chain_id != ChainId::ethereum_mainnet() {
// DID must point to Ethereum Mainnet because it is a valid // DID must point to Ethereum Mainnet because it is a valid
// identifier on any Ethereum chain // identifier on any Ethereum chain

View file

@ -434,6 +434,7 @@ pub async fn search_profiles_by_did(
did_pkh.currency() did_pkh.currency()
.map(|currency| (currency, did_pkh.address.clone())) .map(|currency| (currency, did_pkh.address.clone()))
}, },
_ => None,
}; };
let unverified = if let Some((currency, address)) = maybe_currency_address { let unverified = if let Some((currency, address)) = maybe_currency_address {
// If currency is Ethereum, // If currency is Ethereum,

View file

@ -430,7 +430,10 @@ mod tests {
fn test_identity_proof_serialization() { fn test_identity_proof_serialization() {
let json_data = r#"{"issuer":"did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a","proof_type":"ethereum-eip191-00","value":"dbfe"}"#; let json_data = r#"{"issuer":"did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a","proof_type":"ethereum-eip191-00","value":"dbfe"}"#;
let proof: IdentityProof = serde_json::from_str(json_data).unwrap(); let proof: IdentityProof = serde_json::from_str(json_data).unwrap();
let Did::Pkh(ref did_pkh) = proof.issuer; let did_pkh = match proof.issuer {
Did::Pkh(ref did_pkh) => did_pkh,
_ => panic!("unexpected did method"),
};
assert_eq!(did_pkh.address, "0xb9c5714089478a327f09197987f16f9e5d936e8a"); assert_eq!(did_pkh.address, "0xb9c5714089478a327f09197987f16f9e5d936e8a");
let serialized = serde_json::to_string(&proof).unwrap(); let serialized = serde_json::to_string(&proof).unwrap();
assert_eq!(serialized, json_data); assert_eq!(serialized, json_data);

View file

@ -49,7 +49,10 @@ impl User {
/// Returns wallet address if it is verified /// Returns wallet address if it is verified
pub fn public_wallet_address(&self, currency: &Currency) -> Option<String> { pub fn public_wallet_address(&self, currency: &Currency) -> Option<String> {
for proof in self.profile.identity_proofs.clone().into_inner() { for proof in self.profile.identity_proofs.clone().into_inner() {
let Did::Pkh(did_pkh) = proof.issuer; let did_pkh = match proof.issuer {
Did::Pkh(did_pkh) => did_pkh,
_ => continue,
};
// Return the first matching address, because only // Return the first matching address, because only
// one proof per currency is allowed. // one proof per currency is allowed.
if let Some(ref address_currency) = did_pkh.currency() { if let Some(ref address_currency) = did_pkh.currency() {