From 361e5ca93c7ef17364f8611293fadb31cf4e801c Mon Sep 17 00:00:00 2001 From: "Charles E. Lehner" Date: Mon, 25 Oct 2021 09:18:07 -0400 Subject: [PATCH] Add did:pkh for Aleo --- did-pkh/Cargo.toml | 1 + did-pkh/src/lib.rs | 84 +++++++++++++++++++++++++++++++++++ did-pkh/tests/did-aleo.jsonld | 21 +++++++++ 3 files changed, 106 insertions(+) create mode 100644 did-pkh/tests/did-aleo.jsonld diff --git a/did-pkh/Cargo.toml b/did-pkh/Cargo.toml index 45297e572..29e823531 100644 --- a/did-pkh/Cargo.toml +++ b/did-pkh/Cargo.toml @@ -18,6 +18,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-trait = "0.1" bs58 = { version = "0.4", features = ["check"] } +bech32 = "0.8" [dev-dependencies] tokio = { version = "1.0", features = ["macros", "rt"] } diff --git a/did-pkh/src/lib.rs b/did-pkh/src/lib.rs index ade6e3574..0aebd7727 100644 --- a/did-pkh/src/lib.rs +++ b/did-pkh/src/lib.rs @@ -365,6 +365,61 @@ async fn resolve_bip122(did: &str, account_address: String, reference: &str) -> resolution_result(doc) } +async fn resolve_aleo(did: &str, account_address: String, reference: &str) -> ResolutionResult { + use bech32::FromBase32; + let (hrp, data, _variant) = match bech32::decode(&account_address) { + Err(_e) => return resolution_error(ERROR_INVALID_DID), + Ok(data) => data, + }; + if data.is_empty() { + return resolution_error(ERROR_INVALID_DID); + } + if hrp != "aleo" { + return resolution_error(ERROR_INVALID_DID); + } + let data = match Vec::::from_base32(&data) { + Err(_e) => return resolution_error(ERROR_INVALID_DID), + Ok(data) => data, + }; + // Address data is decoded for validation only. + // The verification method object just uses the account address in blockchainAccountId. + if data.len() != 32 { + return resolution_error(ERROR_INVALID_DID); + } + let chain_id = ChainId { + namespace: "aleo".to_string(), + reference: reference.to_string(), + }; + let blockchain_account_id = BlockchainAccountId { + account_address, + chain_id, + }; + let vm_url = DIDURL { + did: did.to_string(), + fragment: Some("blockchainAccountId".to_string()), + ..Default::default() + }; + let vm = VerificationMethod::Map(VerificationMethodMap { + id: vm_url.to_string(), + type_: "BlockchainVerificationMethod2021".to_string(), + controller: did.to_string(), + blockchain_account_id: Some(blockchain_account_id.to_string()), + ..Default::default() + }); + let doc = Document { + context: Contexts::Many(vec![ + Context::URI(DEFAULT_CONTEXT.to_string()), + Context::URI("https://w3id.org/security/suites/blockchain-2021/v1".to_string()), + ]), + id: did.to_string(), + verification_method: Some(vec![vm]), + authentication: Some(vec![VerificationMethod::DIDURL(vm_url.clone())]), + assertion_method: Some(vec![VerificationMethod::DIDURL(vm_url)]), + ..Default::default() + }; + resolution_result(doc) +} + async fn resolve_caip10(did: &str, account_id: String) -> ResolutionResult { let account_id = match BlockchainAccountId::from_str(&account_id) { Ok(account_id) => account_id, @@ -377,6 +432,7 @@ async fn resolve_caip10(did: &str, account_id: String) -> ResolutionResult { "eip155" => resolve_eip155(did, account_id.account_address, &reference, false).await, "bip122" => resolve_bip122(did, account_id.account_address, &reference).await, "solana" => resolve_solana(did, account_id.account_address, &reference).await, + "aleo" => resolve_aleo(did, account_id.account_address, &reference).await, _ => resolution_error(ERROR_INVALID_DID), } } @@ -524,6 +580,28 @@ fn generate_caip10_solana( }) } +fn generate_caip10_aleo(key: &JWK, ref_opt: Option) -> Result { + let reference = ref_opt.unwrap_or_else(|| "1".to_string()); + let chain_id = ChainId { + namespace: "aleo".to_string(), + reference, + }; + use bech32::ToBase32; + let pk_bs58 = match key.params { + Params::OKP(ref params) if params.curve == "AleoTestnet1Key" => bech32::encode( + "aleo", + ¶ms.public_key.0.to_base32(), + bech32::Variant::Bech32m, + ) + .unwrap(), + _ => return Err("Invalid public key type for Aleo".to_string()), + }; + Ok(BlockchainAccountId { + account_address: pk_bs58, + chain_id, + }) +} + fn generate_caip10_did(key: &JWK, name: &str) -> Result { // Require name to be a either CAIP-2 namespace or a // full CAIP-2 string - namespace and reference (e.g. internal @@ -541,6 +619,7 @@ fn generate_caip10_did(key: &JWK, name: &str) -> Result { "eip155" => generate_caip10_eip155(key, reference_opt)?, "bip122" => generate_caip10_bip122(key, reference_opt)?, "solana" => generate_caip10_solana(key, reference_opt)?, + "aleo" => generate_caip10_aleo(key, reference_opt)?, _ => return Err("Namespace not supported".to_string()), }; Ok(format!("did:pkh:{}", account_id)) @@ -729,6 +808,11 @@ mod tests { include_str!("../tests/did-doge.jsonld"), ) .await; + test_resolve( + "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348", + include_str!("../tests/did-aleo.jsonld"), + ) + .await; // non-CAIP-10 (deprecated) test_resolve( diff --git a/did-pkh/tests/did-aleo.jsonld b/did-pkh/tests/did-aleo.jsonld new file mode 100644 index 000000000..66689dc81 --- /dev/null +++ b/did-pkh/tests/did-aleo.jsonld @@ -0,0 +1,21 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/blockchain-2021/v1" + ], + "id": "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348", + "verificationMethod": [ + { + "id": "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348#blockchainAccountId", + "type": "BlockchainVerificationMethod2021", + "controller": "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348", + "blockchainAccountId": "aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348" + } + ], + "authentication": [ + "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348#blockchainAccountId" + ], + "assertionMethod": [ + "did:pkh:aleo:1:aleo1y90yg3yzs4g7q25f9nn8khuu00m8ysynxmcw8aca2d0phdx8dgpq4vw348#blockchainAccountId" + ] +}