Skip to content

Commit

Permalink
Add AnyDidMethod::generate.
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg committed May 31, 2024
1 parent b9fe96f commit c3a2b0d
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 76 deletions.
4 changes: 3 additions & 1 deletion crates/dids/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ aleo = ["did-pkh/aleo"]
solana = ["did-pkh/solana"]

[dependencies]
ssi-jwk.workspace = true
ssi-dids-core.workspace = true
did-ethr.workspace = true
did-ion.workspace = true
did-jwk.workspace = true
did-method-key.workspace = true
did-pkh.workspace = true
did-tz.workspace = true
did-web.workspace = true
did-web.workspace = true
thiserror.workspace = true
4 changes: 4 additions & 0 deletions crates/dids/core/src/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ impl DIDBuf {
pub fn into_uri(self) -> UriBuf {
unsafe { UriBuf::new_unchecked(self.0) }
}

pub fn into_string(self) -> String {
unsafe { String::from_utf8_unchecked(self.0) }
}
}

impl TryFrom<String> for DIDBuf {
Expand Down
91 changes: 52 additions & 39 deletions crates/dids/methods/key/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,97 +36,110 @@ pub enum Unsupported {
P384,
}

#[derive(Debug, thiserror::Error)]
pub enum GenerateError {
#[error("missing curve")]
MissingCurve,

#[error("unsupported curve `{0}`")]
UnsupportedCurve(String),

#[error("unsupported key type")]
UnsupportedKeyType,

#[error("invalid input key")]
InvalidInputKey,
}

pub struct DIDKey;

impl DIDKey {
pub fn generate(jwk: &JWK) -> Option<DIDBuf> {
pub fn generate(jwk: &JWK) -> Result<DIDBuf, GenerateError> {
use ssi_jwk::Params;
let id = match jwk.params {
Params::OKP(ref params) => {
match &params.curve[..] {
"Ed25519" => Some(multibase::encode(
multibase::Base::Base58Btc,
[DID_KEY_ED25519_PREFIX.to_vec(), params.public_key.0.clone()].concat(),
)),
"Bls12381G2" => Some(multibase::encode(
multibase::Base::Base58Btc,
[
DID_KEY_BLS12381_G2_PREFIX.to_vec(),
params.public_key.0.clone(),
]
.concat(),
)),
//_ => return Some(Err(DIDKeyError::UnsupportedCurve(params.curve.clone()))),
_ => return None,
}
}
Params::OKP(ref params) => match &params.curve[..] {
"Ed25519" => multibase::encode(
multibase::Base::Base58Btc,
[DID_KEY_ED25519_PREFIX.to_vec(), params.public_key.0.clone()].concat(),
),
"Bls12381G2" => multibase::encode(
multibase::Base::Base58Btc,
[
DID_KEY_BLS12381_G2_PREFIX.to_vec(),
params.public_key.0.clone(),
]
.concat(),
),
_ => return Err(GenerateError::UnsupportedCurve(params.curve.clone())),
},
Params::EC(ref params) => {
let curve = match params.curve {
Some(ref curve) => curve,
None => return None,
None => return Err(GenerateError::MissingCurve),
};
match &curve[..] {

match curve.as_str() {
#[cfg(feature = "secp256k1")]
"secp256k1" => {
use k256::elliptic_curve::sec1::ToEncodedPoint;
let pk = match k256::PublicKey::try_from(params) {
Ok(pk) => pk,
Err(_err) => return None,
Err(_err) => return Err(GenerateError::InvalidInputKey),
};

Some(multibase::encode(
multibase::encode(
multibase::Base::Base58Btc,
[
DID_KEY_SECP256K1_PREFIX.to_vec(),
pk.to_encoded_point(true).as_bytes().to_vec(),
]
.concat(),
))
)
}
#[cfg(feature = "secp256r1")]
"P-256" => {
use p256::elliptic_curve::sec1::ToEncodedPoint;
let pk = match p256::PublicKey::try_from(params) {
Ok(pk) => pk,
Err(_err) => return None,
Err(_err) => return Err(GenerateError::InvalidInputKey),
};

Some(multibase::encode(
multibase::encode(
multibase::Base::Base58Btc,
[
DID_KEY_P256_PREFIX.to_vec(),
pk.to_encoded_point(true).as_bytes().to_vec(),
]
.concat(),
))
)
}
#[cfg(feature = "secp384r1")]
"P-384" => {
let pk_bytes = match ssi_jwk::serialize_p384(params) {
Ok(pk) => pk,
Err(_err) => return None,
Err(_err) => return Err(GenerateError::InvalidInputKey),
};

Some(multibase::encode(
multibase::encode(
multibase::Base::Base58Btc,
[DID_KEY_P384_PREFIX.to_vec(), pk_bytes].concat(),
))
)
}
//_ => return Some(Err(DIDKeyError::UnsupportedCurve(params.curve.clone()))),
_ => return None,
_ => return Err(GenerateError::UnsupportedCurve(curve.to_owned())),
}
}
Params::RSA(ref params) => {
let der = simple_asn1::der_encode(&params.to_public()).ok()?;
Some(multibase::encode(
let der = simple_asn1::der_encode(&params.to_public())
.map_err(|_| GenerateError::InvalidInputKey)?;
multibase::encode(
multibase::Base::Base58Btc,
[DID_KEY_RSA_PREFIX.to_vec(), der.to_vec()].concat(),
))
)
}
_ => return None, // _ => return Some(Err(DIDKeyError::UnsupportedKeyType)),
_ => return Err(GenerateError::UnsupportedKeyType),
};

id.map(|id| DIDBuf::from_string(format!("did:key:{id}")).unwrap())
Ok(DIDBuf::from_string(format!("did:key:{id}")).unwrap())
}
}

Expand Down Expand Up @@ -314,7 +327,7 @@ fn build_public_key(id: &str, data: &[u8]) -> Result<(PublicKey, VerificationMet
Err(_) => Err(Error::InvalidMethodSpecificId(id.to_owned())),
}
#[cfg(not(feature = "secp256k1"))]
Err(Error::Internal(Box::new(Unsupported::Secp256k1)))
Err(Error::internal(Unsupported::Secp256k1))
} else if data[0] == DID_KEY_P256_PREFIX[0] && data[1] == DID_KEY_P256_PREFIX[1] {
#[cfg(feature = "secp256r1")]
{
Expand All @@ -329,7 +342,7 @@ fn build_public_key(id: &str, data: &[u8]) -> Result<(PublicKey, VerificationMet
))
}
#[cfg(not(feature = "secp256r1"))]
return Err(Error::Internal(Box::new(Unsupported::P256)));
return Err(Error::internal(Unsupported::P256));
} else if data[0] == DID_KEY_P384_PREFIX[0] && data[1] == DID_KEY_P384_PREFIX[1] {
#[cfg(feature = "secp384r1")]
match ssi_jwk::p384_parse(&data[2..]) {
Expand Down
1 change: 1 addition & 0 deletions crates/dids/methods/pkh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async-trait.workspace = true
bs58 = { workspace = true, features = ["check"] }
bech32 = "0.8"
chrono = { workspace = true, features = ["serde"] }
thiserror.workspace = true

[target.'cfg(target_arch = "wasm32")'.dependencies]
chrono = { workspace = true, features = ["serde", "wasmbind"] }
Expand Down
92 changes: 56 additions & 36 deletions crates/dids/methods/pkh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,31 +525,31 @@ impl DIDMethodResolver for DIDPKH {
}
}

fn generate_sol(jwk: &JWK) -> Option<String> {
fn generate_sol(jwk: &JWK) -> Result<String, GenerateError> {
match jwk.params {
Params::OKP(ref params) if params.curve == "Ed25519" => {
Some(bs58::encode(&params.public_key.0).into_string())
Ok(bs58::encode(&params.public_key.0).into_string())
}
_ => None,
_ => Err(GenerateError::UnsupportedKeyType),
}
}

#[cfg(feature = "ripemd-160")]
fn generate_btc(key: &JWK) -> Result<String, String> {
let addr = ssi_jwk::ripemd160::hash_public_key(key, 0x00).map_err(|e| e.to_string())?;
fn generate_btc(key: &JWK) -> Result<String, GenerateError> {
let addr = ssi_jwk::ripemd160::hash_public_key(key, 0x00).map_err(GenerateError::other)?;
#[cfg(test)]
if !addr.starts_with('1') {
return Err("Expected Bitcoin address".to_string());
return Err(GenerateError::other("Expected Bitcoin address"));
}
Ok(addr)
}

#[cfg(feature = "ripemd-160")]
fn generate_doge(key: &JWK) -> Result<String, String> {
let addr = ssi_jwk::ripemd160::hash_public_key(key, 0x1e).map_err(|e| e.to_string())?;
fn generate_doge(key: &JWK) -> Result<String, GenerateError> {
let addr = ssi_jwk::ripemd160::hash_public_key(key, 0x1e).map_err(GenerateError::other)?;
#[cfg(test)]
if !addr.starts_with('D') {
return Err("Expected Dogecoin address".to_string());
return Err(GenerateError::other("Expected Dogecoin address"));
}
Ok(addr)
}
Expand All @@ -558,8 +558,8 @@ fn generate_doge(key: &JWK) -> Result<String, String> {
fn generate_caip10_tezos(
key: &JWK,
ref_opt: Option<String>,
) -> Result<BlockchainAccountId, String> {
let hash = ssi_jwk::blakesig::hash_public_key(key).map_err(|e| e.to_string())?;
) -> Result<BlockchainAccountId, GenerateError> {
let hash = ssi_jwk::blakesig::hash_public_key(key).map_err(GenerateError::other)?;
let reference = ref_opt.unwrap_or_else(|| REFERENCE_TEZOS_MAINNET.to_string());
Ok(BlockchainAccountId {
account_address: hash,
Expand All @@ -574,8 +574,8 @@ fn generate_caip10_tezos(
fn generate_caip10_eip155(
key: &JWK,
ref_opt: Option<String>,
) -> Result<BlockchainAccountId, String> {
let hash = ssi_jwk::eip155::hash_public_key_eip55(key).map_err(|e| e.to_string())?;
) -> Result<BlockchainAccountId, GenerateError> {
let hash = ssi_jwk::eip155::hash_public_key_eip55(key).map_err(GenerateError::other)?;
let reference = ref_opt.unwrap_or_else(|| REFERENCE_EIP155_ETHEREUM_MAINNET.to_string());
Ok(BlockchainAccountId {
account_address: hash,
Expand All @@ -590,24 +590,24 @@ fn generate_caip10_eip155(
fn generate_caip10_bip122(
key: &JWK,
ref_opt: Option<String>,
) -> Result<BlockchainAccountId, String> {
) -> Result<BlockchainAccountId, GenerateError> {
let reference = ref_opt.unwrap_or_else(|| REFERENCE_BIP122_BITCOIN_MAINNET.to_string());
let addr;
match &reference[..] {
REFERENCE_BIP122_BITCOIN_MAINNET => {
addr = ssi_jwk::ripemd160::hash_public_key(key, 0x00).map_err(|e| e.to_string())?;
addr = ssi_jwk::ripemd160::hash_public_key(key, 0x00).map_err(GenerateError::other)?;
if !addr.starts_with('1') {
return Err("Expected Bitcoin address".to_string());
return Err(GenerateError::other("Expected Bitcoin address"));
}
}
REFERENCE_BIP122_DOGECOIN_MAINNET => {
addr = ssi_jwk::ripemd160::hash_public_key(key, 0x1e).map_err(|e| e.to_string())?;
addr = ssi_jwk::ripemd160::hash_public_key(key, 0x1e).map_err(GenerateError::other)?;
if !addr.starts_with('D') {
return Err("Expected Dogecoin address".to_string());
return Err(GenerateError::other("Expected Dogecoin address"));
}
}
_ => {
return Err("Expected Bitcoin address type".to_string());
return Err(GenerateError::other("Expected Bitcoin address type"));
}
}

Expand Down Expand Up @@ -666,7 +666,7 @@ fn generate_caip10_aleo(key: &JWK, ref_opt: Option<String>) -> Result<Blockchain
}

#[allow(unused, unreachable_code)]
fn generate_caip10_did(key: &JWK, name: &str) -> Result<DIDBuf, String> {
fn generate_caip10_did(key: &JWK, name: &str) -> Result<DIDBuf, GenerateError> {
// Require name to be a either CAIP-2 namespace or a
// full CAIP-2 string - namespace and reference (e.g. internal
// chain id or genesis hash).
Expand All @@ -676,7 +676,7 @@ fn generate_caip10_did(key: &JWK, name: &str) -> Result<DIDBuf, String> {
let (namespace, reference_opt) = match name.splitn(2, ':').collect::<Vec<&str>>().as_slice() {
[namespace] => (namespace.to_string(), None),
[namespace, reference] => (namespace.to_string(), Some(reference.to_string())),
_ => return Err("Unable to parse chain id or namespace".to_string()),
_ => return Err(GenerateError::InvalidChainId),
};
let account_id: BlockchainAccountId = match &namespace[..] {
#[cfg(feature = "tezos")]
Expand All @@ -689,35 +689,55 @@ fn generate_caip10_did(key: &JWK, name: &str) -> Result<DIDBuf, String> {
"solana" => generate_caip10_solana(key, reference_opt)?,
#[cfg(feature = "aleo")]
"aleo" => generate_caip10_aleo(key, reference_opt)?,
_ => return Err("Namespace not supported".to_string()),
_ => return Err(GenerateError::UnsupportedNamespace),
};

Ok(DIDBuf::from_string(format!("did:pkh:{}", account_id)).unwrap())
}

#[derive(Debug, thiserror::Error)]
pub enum GenerateError {
#[error("Unable to parse chain id or namespace")]
InvalidChainId,

#[error("Namespace not supported")]
UnsupportedNamespace,

#[error("Unsupported key type")]
UnsupportedKeyType,

#[error("{0}")]
Other(String),
}

impl GenerateError {
pub fn other(e: impl ToString) -> Self {
Self::Other(e.to_string())
}
}

impl DIDPKH {
pub fn generate(key: &JWK, pkh_name: &str) -> Option<DIDBuf> {
let addr = match match pkh_name {
pub fn generate(key: &JWK, pkh_name: &str) -> Result<DIDBuf, GenerateError> {
let addr = match pkh_name {
// Aliases for did:pkh pre-CAIP-10. Deprecate?
#[cfg(feature = "tezos")]
"tz" => ssi_jwk::blakesig::hash_public_key(key).ok(),
"tz" => ssi_jwk::blakesig::hash_public_key(key).map_err(GenerateError::other)?,
#[cfg(feature = "eip")]
"eth" => ssi_jwk::eip155::hash_public_key(key).ok(),
"eth" => ssi_jwk::eip155::hash_public_key(key).map_err(GenerateError::other)?,
#[cfg(feature = "eip")]
"celo" => ssi_jwk::eip155::hash_public_key(key).ok(),
"celo" => ssi_jwk::eip155::hash_public_key(key).map_err(GenerateError::other)?,
#[cfg(feature = "eip")]
"poly" => ssi_jwk::eip155::hash_public_key(key).ok(),
"sol" => generate_sol(key),
"poly" => ssi_jwk::eip155::hash_public_key(key).map_err(GenerateError::other)?,
"sol" => generate_sol(key)?,
#[cfg(feature = "ripemd-160")]
"btc" => generate_btc(key).ok(),
"btc" => generate_btc(key)?,
#[cfg(feature = "ripemd-160")]
"doge" => generate_doge(key).ok(),
"doge" => generate_doge(key)?,
// CAIP-10/CAIP-2 chain id
name => return generate_caip10_did(key, name).ok(),
} {
Some(addr) => addr,
None => return None,
name => return generate_caip10_did(key, name),
};
Some(DIDBuf::from_string(format!("did:pkh:{}:{}", pkh_name, addr)).unwrap())

Ok(DIDBuf::from_string(format!("did:pkh:{}:{}", pkh_name, addr)).unwrap())
}
}

Expand Down
Loading

0 comments on commit c3a2b0d

Please sign in to comment.