From cec4dc3e5177e8d77a4c34fc366e70920a31b104 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 13 Jul 2024 22:50:05 +0900 Subject: [PATCH 01/58] Add encryption key to DID Document --- Cargo.toml | 8 +- src/common/cipher/jws.rs | 190 +++-- src/common/cipher/mod.rs | 1 - src/common/cipher/signer.rs | 84 -- src/common/mod.rs | 1 - src/common/runtime/base64_url.rs | 117 --- src/common/runtime/bip32.rs | 77 -- src/common/runtime/jcs.rs | 38 - src/common/runtime/mod.rs | 5 - src/common/runtime/multihash.rs | 143 +--- src/common/runtime/random.rs | 37 - src/common/runtime/secp256k1.rs | 172 ---- src/common/utils.rs | 15 - src/did/did_repository.rs | 144 +++- src/did/sidetree/payload.rs | 218 ++--- src/didcomm/encrypted.rs | 323 +++----- src/didcomm/types.rs | 38 +- src/keyring/extension/trng.rs | 65 +- src/keyring/jwk.rs | 136 ++++ src/keyring/keypair.rs | 181 +++-- src/keyring/mod.rs | 1 + src/keyring/secp256k1.rs | 757 +++++++++--------- .../credential_signer.rs | 58 +- src/verifiable_credentials/did_vc.rs | 127 +-- 24 files changed, 1173 insertions(+), 1763 deletions(-) delete mode 100644 src/common/cipher/signer.rs delete mode 100644 src/common/runtime/base64_url.rs delete mode 100644 src/common/runtime/bip32.rs delete mode 100644 src/common/runtime/jcs.rs delete mode 100644 src/common/runtime/random.rs delete mode 100644 src/common/runtime/secp256k1.rs delete mode 100644 src/common/utils.rs create mode 100644 src/keyring/jwk.rs diff --git a/Cargo.toml b/Cargo.toml index d59b31c..1b01f1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2018" keywords = ["did", "embedded", "iot", "root-of-trust"] name = "nodex-didcomm" readme = "README.md" -version = "0.1.0" +version = "0.1.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -16,19 +16,16 @@ chrono = { version = "0.4" } cuid = { version = "1.3.2" } data-encoding = { version = "2.5.0" } didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.0", default-features = false, features = ["raw-crypto"] } -getrandom = { version = "0.2" } -hdwallet = { version = "0.4.1" } hex = { version = "0.4.3" } hmac = { version = "0.12.1" } http = { version = "1.1.0" } -ibig = { version = "0.3.6" } k256 = { version = "0.13.3", features = [ "ecdh", "ecdsa", "serde", "sha256", ] } -libloading = { version = "0.8.3" } +elliptic-curve = { version = "0.13.8", features = ["sec1"] } log = { version = "0.4.21" } serde = { version = "1.0.204", features = ["derive"] } serde_jcs = { version = "0.1.0" } @@ -36,6 +33,7 @@ serde_json = { version = "1.0.116" } sha2 = { version = "0.10.8" } thiserror = "1.0.59" x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } +rand_core = "0.6.4" [dev-dependencies] actix-rt = { version = "2.9.0" } diff --git a/src/common/cipher/jws.rs b/src/common/cipher/jws.rs index 310fb9f..cba5384 100644 --- a/src/common/cipher/jws.rs +++ b/src/common/cipher/jws.rs @@ -1,13 +1,13 @@ +use data_encoding::BASE64URL_NOPAD; +use hmac::digest::generic_array::GenericArray; +use k256::ecdsa::{ + signature::{Signer, Verifier}, + Signature, SigningKey, VerifyingKey, +}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::Value; use thiserror::Error; -use super::signer::{Signer, SignerError}; -use crate::{ - common::runtime::{self, base64_url::PaddingType}, - keyring::secp256k1::Secp256k1, -}; - #[derive(Debug, Serialize, Deserialize)] struct JWSHeader { alg: String, @@ -15,24 +15,22 @@ struct JWSHeader { crit: Vec, } -pub struct Jws {} - #[derive(Debug, Error)] pub enum JwsEncodeError { - #[error(transparent)] - SignerError(#[from] SignerError), + #[error("PublicKeyConvertError : {0:?}")] + SignatureError(#[from] k256::ecdsa::Error), + #[error("CanonicalizeError : {0:?}")] + CanonicalizeError(#[from] serde_json::Error), } #[derive(Debug, Error)] pub enum JwsDecodeError { - #[error(transparent)] - SignerError(#[from] SignerError), - #[error("InvalidJws : {0}")] - InvalidJws(String), - #[error(transparent)] - Base64UrlError(#[from] runtime::base64_url::Base64UrlError), + #[error("DecodeError: {0}")] + DecodeError(#[from] data_encoding::DecodeError), #[error(transparent)] JsonParseError(#[from] serde_json::Error), + #[error("invalid signature length: {0}")] + InvalidSignatureLength(usize), #[error("InvalidAlgorithm: {0}")] InvalidAlgorithm(String), #[error("b64 option is not supported")] @@ -41,86 +39,88 @@ pub enum JwsDecodeError { B64NotSupportedButContained, #[error("EmptyPayload")] EmptyPayload, + #[error("InvalidJws : {0}")] + InvalidJws(String), + #[error("CryptError: {0}")] + CryptError(#[from] k256::ecdsa::Error), + #[error("FromUtf8Error: {0}")] + FromUtf8Error(#[from] std::string::FromUtf8Error), } -impl Jws { - pub fn encode(object: &Value, context: &Secp256k1) -> Result { - // NOTE: header - let header = - JWSHeader { alg: "ES256K".to_string(), b64: false, crit: vec!["b64".to_string()] }; - let header = runtime::base64_url::Base64Url::encode( - json!(&header).to_string().as_bytes(), - &PaddingType::NoPadding, - ); - - // NOTE: payload - let payload = runtime::base64_url::Base64Url::encode( - object.to_string().as_bytes(), - &PaddingType::NoPadding, - ); - - // NOTE: message - let message = [header.clone(), payload].join("."); - - // NOTE: signature - let signature = Signer::sign(&message, context)?; - let signature = runtime::base64_url::Base64Url::encode(&signature, &PaddingType::NoPadding); - - Ok([header, "".to_string(), signature].join(".")) +pub fn sign(object: &Value, secret_key: &k256::SecretKey) -> Result { + // NOTE: header + let header = JWSHeader { alg: "ES256K".to_string(), b64: false, crit: vec!["b64".to_string()] }; + let header = serde_jcs::to_string(&header)?; + let header = BASE64URL_NOPAD.encode(header.as_bytes()); + // NOTE: payload + let payload = BASE64URL_NOPAD.encode(object.to_string().as_bytes()); + // NOTE: message + let message = [header.clone(), payload].join("."); + let message: &[u8] = message.as_bytes(); + + // NOTE: signature + let signing_key: SigningKey = secret_key.into(); + let signature: Signature = signing_key.try_sign(message)?; + let signature = BASE64URL_NOPAD.encode(&signature.to_vec()); + + Ok([header, "".to_string(), signature].join(".")) +} + +pub fn verify( + object: &Value, + jws: &str, + public_key: &k256::PublicKey, +) -> Result<(), JwsDecodeError> { + let split: Vec = jws.split('.').map(|v| v.to_string()).collect(); + + if split.len() != 3 { + return Err(JwsDecodeError::InvalidJws(jws.to_string())); } - pub fn verify(object: &Value, jws: &str, context: &Secp256k1) -> Result { - let split: Vec = jws.split('.').map(|v| v.to_string()).collect(); - - if split.len() != 3 { - return Err(JwsDecodeError::InvalidJws(jws.to_string())); - } - - let _header = split[0].clone(); - let __payload = split[1].clone(); - let _signature = split[2].clone(); - - // NOTE: header - let decoded = - runtime::base64_url::Base64Url::decode_as_string(&_header, &PaddingType::NoPadding)?; - let header = serde_json::from_str::(&decoded)?; - - if header.alg != *"ES256K" { - return Err(JwsDecodeError::InvalidAlgorithm(header.alg)); - } - if header.b64 { - return Err(JwsDecodeError::B64NotSupported); - } - if header.crit.iter().all(|v| v != "b64") { - return Err(JwsDecodeError::B64NotSupportedButContained); - }; - - // NOTE: payload - if __payload != *"".to_string() { - return Err(JwsDecodeError::EmptyPayload); - } - let _payload = runtime::base64_url::Base64Url::encode( - object.to_string().as_bytes(), - &PaddingType::NoPadding, - ); - - // NOTE: message - let message = [_header, _payload].join("."); - - // NOTE: signature - let signature = - runtime::base64_url::Base64Url::decode_as_bytes(&_signature, &PaddingType::NoPadding)?; - - // NOTE: verify - Ok(Signer::verify(&message, &signature, context)?) + let _header = split[0].clone(); + let __payload = split[1].clone(); + let _signature = split[2].clone(); + + // NOTE: header + let decoded = BASE64URL_NOPAD.decode(_header.as_bytes())?; + let decoded = String::from_utf8(decoded)?; + let header = serde_json::from_str::(&decoded)?; + + if header.alg != *"ES256K" { + return Err(JwsDecodeError::InvalidAlgorithm(header.alg)); + } + if header.b64 { + return Err(JwsDecodeError::B64NotSupported); + } + if header.crit.iter().all(|v| v != "b64") { + return Err(JwsDecodeError::B64NotSupportedButContained); + }; + + // NOTE: payload + if __payload != *"".to_string() { + return Err(JwsDecodeError::EmptyPayload); + } + let _payload = BASE64URL_NOPAD.encode(object.to_string().as_bytes()); + + // NOTE: message + let message = [_header, _payload].join("."); + + // NOTE: signature + let signature = BASE64URL_NOPAD.decode(_signature.as_bytes())?; + if signature.len() != 64 { + return Err(JwsDecodeError::InvalidSignatureLength(signature.len())); } + let r = GenericArray::from_slice(&signature[0..32]); + let s = GenericArray::from_slice(&signature[32..]); + let wrapped_signature = Signature::from_scalars(*r, *s)?; + + let verify_key = VerifyingKey::from(public_key); + Ok(verify_key.verify(message.as_bytes(), &wrapped_signature)?) } #[cfg(test)] pub mod tests { - use super::*; - use crate::keyring::{self}; const SECRET_KEY: [u8; 32] = [ 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, 0x90, 0xac, 0xdf, 0x76, @@ -146,25 +146,17 @@ pub mod tests { #[test] pub fn test_encode() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), SECRET_KEY.to_vec()).unwrap(); - + let sk = k256::SecretKey::from_slice(&SECRET_KEY).unwrap(); let json: Value = serde_json::from_str(&message()).unwrap(); - - let result = Jws::encode(&json, &context).unwrap(); + let result = sign(&json, &sk).unwrap(); assert_eq!(result, signature()) } #[test] pub fn test_verify() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), SECRET_KEY.to_vec()).unwrap(); - + let pk = k256::PublicKey::from_sec1_bytes(&PUBLIC_KEY).unwrap(); let json: Value = serde_json::from_str(&message()).unwrap(); - - let result = Jws::verify(&json, &signature(), &context).unwrap(); - - assert!(result) + let _ = verify(&json, &signature(), &pk).unwrap(); } } diff --git a/src/common/cipher/mod.rs b/src/common/cipher/mod.rs index 36add51..d2df536 100644 --- a/src/common/cipher/mod.rs +++ b/src/common/cipher/mod.rs @@ -1,2 +1 @@ pub(crate) mod jws; -pub(crate) mod signer; diff --git a/src/common/cipher/signer.rs b/src/common/cipher/signer.rs deleted file mode 100644 index bc10769..0000000 --- a/src/common/cipher/signer.rs +++ /dev/null @@ -1,84 +0,0 @@ -use thiserror::Error; - -use crate::{common::runtime, keyring::secp256k1::Secp256k1}; - -pub struct Signer {} - -#[derive(Debug, Error)] -pub enum SignerError { - #[error(transparent)] - Secp256k1Error(#[from] runtime::secp256k1::Secp256k1Error), -} - -impl Signer { - pub fn sign(message: &str, context: &Secp256k1) -> Result, SignerError> { - Ok(runtime::secp256k1::Secp256k1::ecdsa_sign( - message.as_bytes(), - &context.get_secret_key(), - )?) - } - - pub fn verify( - message: &str, - signature: &[u8], - context: &Secp256k1, - ) -> Result { - Ok(runtime::secp256k1::Secp256k1::ecdsa_verify( - signature, - message.as_bytes(), - &context.get_public_key(), - )?) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - use crate::keyring::{self}; - - const SECRET_KEY: [u8; 32] = [ - 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, 0x90, 0xac, 0xdf, 0x76, - 0x15, 0x64, 0x0a, 0xa6, 0xed, 0x31, 0xb8, 0xf1, 0x82, 0x43, 0xf0, 0x4a, 0x57, 0x2c, 0x52, - 0x8e, 0xdb, - ]; - - const PUBLIC_KEY: [u8; 33] = [ - 0x02, 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, 0xcc, 0xea, 0x96, 0xa2, - 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, 0x6f, 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, - 0x60, 0x5c, 0x44, - ]; - - fn message() -> String { - String::from(r#"{"k":"0123456789abcdef"}"#) - } - - fn digest() -> Vec { - vec![ - 0xf7, 0x85, 0xd1, 0x25, 0xdd, 0x45, 0x64, 0x1b, 0xad, 0x3c, 0x54, 0x67, 0x4b, 0xf6, - 0xc1, 0xdf, 0xef, 0xf9, 0xe0, 0x05, 0xc8, 0xe0, 0xcf, 0x23, 0x5e, 0x29, 0x79, 0x28, - 0xb2, 0xa4, 0x54, 0x6e, 0x37, 0x4f, 0xcf, 0x9f, 0x09, 0xb9, 0x2d, 0x9c, 0x71, 0x6f, - 0xf5, 0x58, 0xd4, 0x30, 0x2b, 0xa6, 0x5c, 0x5c, 0xf5, 0xfb, 0x8a, 0xac, 0xdd, 0x26, - 0xb0, 0xc2, 0x10, 0xfa, 0xe1, 0x4c, 0xd9, 0x10, - ] - } - - #[test] - pub fn test_sign() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), SECRET_KEY.to_vec()).unwrap(); - - let result = Signer::sign(&message(), &context).unwrap(); - - assert_eq!(result, digest()) - } - - #[test] - pub fn test_verify() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), SECRET_KEY.to_vec()).unwrap(); - - let result = Signer::verify(&message(), &digest(), &context).unwrap(); - assert!(result) - } -} diff --git a/src/common/mod.rs b/src/common/mod.rs index fc34ee3..cf2a8f9 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,2 @@ pub mod cipher; pub mod runtime; -pub mod utils; diff --git a/src/common/runtime/base64_url.rs b/src/common/runtime/base64_url.rs deleted file mode 100644 index fde74ec..0000000 --- a/src/common/runtime/base64_url.rs +++ /dev/null @@ -1,117 +0,0 @@ -use data_encoding::{BASE64URL, BASE64URL_NOPAD}; -use thiserror::Error; - -pub struct Base64Url {} - -pub enum PaddingType { - #[allow(dead_code)] - Padding, - NoPadding, -} - -#[derive(Error, Debug)] -pub enum Base64UrlError { - #[error("decode failed")] - DecodeFailed(#[from] data_encoding::DecodeError), - #[error("convert from utf8 failed")] - ConvertFromUtf8Error(#[from] std::string::FromUtf8Error), -} - -impl Base64Url { - pub fn encode(content: &[u8], padding: &PaddingType) -> String { - match padding { - PaddingType::Padding => BASE64URL.encode(content), - PaddingType::NoPadding => BASE64URL_NOPAD.encode(content), - } - } - - pub fn decode_as_bytes( - message: &str, - padding: &PaddingType, - ) -> Result, Base64UrlError> { - (match padding { - PaddingType::Padding => BASE64URL.decode(message.as_bytes()), - PaddingType::NoPadding => BASE64URL_NOPAD.decode(message.as_bytes()), - }) - .map_err(|e| e.into()) - } - - pub fn decode_as_string( - message: &str, - padding: &PaddingType, - ) -> Result { - let bytes = (match padding { - PaddingType::Padding => BASE64URL.decode(message.as_bytes()), - PaddingType::NoPadding => BASE64URL_NOPAD.decode(message.as_bytes()), - })?; - - String::from_utf8(bytes).map_err(|e| e.into()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn message() -> String { - String::from("0123456789abcdef") - } - - #[test] - fn test_base64url_encode() { - let result = Base64Url::encode(message().as_bytes(), &PaddingType::Padding); - - assert_eq!(result, String::from("MDEyMzQ1Njc4OWFiY2RlZg==")); - } - - #[test] - fn test_base64url_encode_nopad() { - let result = Base64Url::encode(message().as_bytes(), &PaddingType::NoPadding); - - assert_eq!(result, String::from("MDEyMzQ1Njc4OWFiY2RlZg")); - } - - #[test] - fn test_base64url_decode_byte() { - let encoded = Base64Url::encode(message().as_bytes(), &PaddingType::Padding); - let result = Base64Url::decode_as_bytes(&encoded, &PaddingType::Padding).unwrap(); - - assert_eq!( - result, - vec![ - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, - 0x65, 0x66, - ] - ); - } - - #[test] - fn test_base64url_decode_byte_nopad() { - let encoded = Base64Url::encode(message().as_bytes(), &PaddingType::NoPadding); - let result = Base64Url::decode_as_bytes(&encoded, &PaddingType::NoPadding).unwrap(); - - assert_eq!( - result, - vec![ - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, - 0x65, 0x66, - ] - ); - } - - #[test] - fn test_base64url_decode_string() { - let encoded = Base64Url::encode(message().as_bytes(), &PaddingType::Padding); - let result = Base64Url::decode_as_string(&encoded, &PaddingType::Padding).unwrap(); - - assert_eq!(result, message()); - } - - #[test] - fn test_base64url_decode_string_nopad() { - let encoded = Base64Url::encode(message().as_bytes(), &PaddingType::NoPadding); - let result = Base64Url::decode_as_string(&encoded, &PaddingType::NoPadding).unwrap(); - - assert_eq!(result, message()); - } -} diff --git a/src/common/runtime/bip32.rs b/src/common/runtime/bip32.rs deleted file mode 100644 index 62c76e0..0000000 --- a/src/common/runtime/bip32.rs +++ /dev/null @@ -1,77 +0,0 @@ -use hdwallet::{ChainPath, DefaultKeyChain, ExtendedPrivKey, ExtendedPubKey, KeyChain}; -use thiserror::Error; - -use crate::keyring::secp256k1::{COMPRESSED_PUBLIC_KEY_SIZE, PRIVATE_KEY_SIZE}; - -#[derive(Debug)] -pub struct BIP32Container { - pub public_key: [u8; COMPRESSED_PUBLIC_KEY_SIZE], - pub private_key: [u8; PRIVATE_KEY_SIZE], -} - -pub struct BIP32 {} - -#[derive(Error, Debug)] -pub enum BIP32Error { - #[error("error in hdwallet")] - Hdwallet(hdwallet::error::Error), -} - -impl BIP32 { - pub fn get_node(seed: &[u8], derivation_path: &str) -> Result { - let master = ExtendedPrivKey::with_seed(seed).map_err(BIP32Error::Hdwallet)?; - - let chain = DefaultKeyChain::new(master); - let path = ChainPath::new(derivation_path); - - let (private_key, _) = chain.derive_private_key(path).map_err(BIP32Error::Hdwallet)?; - - let public_key = ExtendedPubKey::from_private_key(&private_key); - - Ok(BIP32Container { - private_key: private_key.private_key.secret_bytes(), - public_key: public_key.public_key.serialize(), - }) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - - fn seed() -> Vec { - vec![ - 0x2d, 0xc5, 0x00, 0xab, 0xea, 0x17, 0xfe, 0x36, 0x19, 0x46, 0xd6, 0x11, 0x3a, 0xf6, - 0xbc, 0x26, 0xf4, 0x8e, 0xed, 0x90, 0x4d, 0x95, 0x27, 0xb5, 0x69, 0x18, 0xbf, 0xb5, - 0xce, 0x24, 0x42, 0x51, - ] - } - - fn derivation_path() -> String { - // NOTE: Ethereum - String::from("m/44'/60'/0'/0/0") - } - - #[test] - fn test_get_node() { - let result = BIP32::get_node(&seed(), &derivation_path()).unwrap(); - - assert_eq!( - result.private_key, - [ - 0xc8, 0x05, 0x4c, 0xe7, 0x85, 0x6e, 0x13, 0x9a, 0xab, 0xec, 0x36, 0x5f, 0x6b, 0xe4, - 0xf1, 0x1b, 0x51, 0x50, 0xb2, 0xd7, 0x6c, 0x6b, 0xb4, 0xf0, 0x05, 0xfd, 0x0b, 0x1e, - 0x0b, 0x5c, 0x92, 0x87, - ] - ); - assert_eq!( - result.public_key, - [ - 0x02, 0x67, 0xfe, 0x57, 0xf7, 0x1e, 0xdb, 0x76, 0x60, 0x96, 0x99, 0x35, 0x0f, 0x5b, - 0x29, 0x75, 0xba, 0x6e, 0xf9, 0x00, 0x6e, 0x65, 0x27, 0xf2, 0xe4, 0xfb, 0xad, 0x41, - 0xd3, 0x74, 0xf0, 0x4f, 0x3a, - ] - ); - } -} diff --git a/src/common/runtime/jcs.rs b/src/common/runtime/jcs.rs deleted file mode 100644 index d5462c8..0000000 --- a/src/common/runtime/jcs.rs +++ /dev/null @@ -1,38 +0,0 @@ -use serde_jcs; -use serde_json::{self, Value}; -use thiserror::Error; - -pub struct Jcs {} - -#[derive(Debug, Error)] -pub enum JcsError { - #[error("Decode failed")] - DecodeFailed(serde_json::Error), - #[error("Serialize failed")] - SerializeFailed(serde_json::Error), -} - -impl Jcs { - pub fn canonicalize(input: &str) -> Result { - let json = serde_json::from_str::(input).map_err(JcsError::DecodeFailed)?; - - serde_jcs::to_string(&json).map_err(JcsError::SerializeFailed) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - - fn json() -> String { - String::from(r#"{"c":2,"a":1,"b":[]}"#) - } - - #[test] - fn test_canonicalize() { - let result = Jcs::canonicalize(&json()).unwrap(); - - assert_eq!(result, r#"{"a":1,"b":[],"c":2}"#); - } -} diff --git a/src/common/runtime/mod.rs b/src/common/runtime/mod.rs index 43b1d7a..02257ba 100644 --- a/src/common/runtime/mod.rs +++ b/src/common/runtime/mod.rs @@ -1,6 +1 @@ -pub mod base64_url; -pub mod bip32; -pub mod jcs; pub mod multihash; -pub mod random; -pub mod secp256k1; diff --git a/src/common/runtime/multihash.rs b/src/common/runtime/multihash.rs index 2c696fe..99c0d27 100644 --- a/src/common/runtime/multihash.rs +++ b/src/common/runtime/multihash.rs @@ -1,89 +1,28 @@ -use sha2::{Digest, Sha256}; -use thiserror::Error; +use std::convert::TryInto; -use super::jcs::JcsError; -use crate::common::runtime::base64_url::{Base64Url, PaddingType}; +use data_encoding::BASE64URL_NOPAD; +use sha2::{Digest, Sha256}; const MULTIHASH_SHA256_CODE: u8 = 0x12; // 0x12 = 18 -const MULTIHASH_SHA256_SIZE: u8 = 0x20; // 0x20 = 32 - -pub struct Multihash {} -#[derive(Eq, PartialEq, Debug)] -pub struct DecodedContainer { - hash: Vec, - algorithm: u64, +// [NOTE]: SHA2-256 ONLY +pub fn hash(message: &[u8]) -> Vec { + let mut prefix = Vec::from([MULTIHASH_SHA256_CODE]); + let mut hashed = Sha256::digest(message).to_vec(); + prefix.push(hashed.len().try_into().unwrap()); + prefix.append(&mut hashed); + prefix } -#[derive(Debug, Error)] -pub enum MultihashError { - #[error(transparent)] - FromUtf8Error(#[from] std::string::FromUtf8Error), - #[error(transparent)] - JsonCanonicalizationError(#[from] JcsError), - #[error("InvalidLength: {0}")] - InvalidLength(usize), - #[error("expected length is {0}, but actual length is {1}")] - SizeValidationFailed(usize, usize), +pub fn double_hash_encode(message: &[u8]) -> String { + let mes = hash(message); + let mes = hash(&mes); + BASE64URL_NOPAD.encode(&mes) } -impl Multihash { - pub fn hash_as_non_multihash_buffer(message: &[u8]) -> Vec { - let mut hasher = Sha256::new(); - - hasher.update(message); - - hasher.finalize().to_vec() - } - - // [NOTE]: SHA2-256 ONLY - pub fn hash(message: &[u8]) -> Vec { - let mut prefix: Vec = Vec::from([MULTIHASH_SHA256_CODE, MULTIHASH_SHA256_SIZE]); - - let mut hashed: Vec = Multihash::hash_as_non_multihash_buffer(message); - let mut joined: Vec = Vec::from([]); - - joined.append(&mut prefix); - joined.append(&mut hashed); - - joined - } - - pub fn hash_then_encode(message: &[u8]) -> String { - let hashed = Multihash::hash(message); - - Base64Url::encode(&hashed, &PaddingType::NoPadding) - } - - pub fn canonicalize_then_double_hash_then_encode( - message: &[u8], - ) -> Result { - let plain = String::from_utf8(message.to_vec())?; - - let canonicalized = super::jcs::Jcs::canonicalize(&plain)?; - - let hashed = Multihash::hash_as_non_multihash_buffer(canonicalized.as_bytes()); - - Ok(Multihash::hash_then_encode(&hashed)) - } - - #[allow(dead_code)] - pub fn decode(encoded: &[u8]) -> Result { - // check for: [ code, size, digest... ] - if encoded.len() < 2 { - return Err(MultihashError::InvalidLength(encoded.len())); - } - - let code = encoded[0]; - let length = encoded[1]; - let digest = encoded[2..].to_vec(); - - if digest.len() != usize::from(length) { - return Err(MultihashError::SizeValidationFailed(usize::from(length), digest.len())); - } - - Ok(DecodedContainer { hash: digest, algorithm: u64::from(code) }) - } +pub fn hash_encode(message: &[u8]) -> String { + let mes = hash(message); + BASE64URL_NOPAD.encode(&mes) } #[cfg(test)] @@ -97,8 +36,7 @@ mod tests { #[test] fn test_hash() { - let result = Multihash::hash(message().as_bytes()); - + let result = hash(message().as_bytes()); assert_eq!( result, vec![ @@ -109,54 +47,15 @@ mod tests { ); } - #[test] - fn test_hash_as_non_multihash_buffer() { - let result = Multihash::hash_as_non_multihash_buffer(message().as_bytes()); - - assert_eq!( - result, - vec![ - 0x5f, 0x46, 0x25, 0xd4, 0xf6, 0x1e, 0xdb, 0x52, 0x78, 0x07, 0x45, 0x5f, 0x48, 0xf8, - 0xbe, 0x27, 0x8e, 0x71, 0xe8, 0x4a, 0xa9, 0x4d, 0x23, 0x11, 0x1f, 0xfa, 0xb3, 0xb6, - 0x30, 0x93, 0xa7, 0x13, - ] - ); - } - #[test] fn test_canonicalize_then_double_hash_then_encode() { - let result = - match Multihash::canonicalize_then_double_hash_then_encode(message().as_bytes()) { - Ok(v) => v, - Err(_) => panic!(), - }; - - assert_eq!(result, String::from("EiAEX1W46vVid7IjJyFY5ibjmyrgepTjW0rYrw-wo4xLCw")); + let result = double_hash_encode(message().as_bytes()); + assert_eq!(result, String::from("EiC_GPLc6eWMwiyIuGr1oEWiSqrTufglVlDGco8oaQL_nQ")); } #[test] fn test_hash_then_encode() { - let result = Multihash::hash_then_encode(message().as_bytes()); - + let result = hash_encode(message().as_bytes()); assert_eq!(result, String::from("EiBfRiXU9h7bUngHRV9I-L4njnHoSqlNIxEf-rO2MJOnEw")); } - - #[test] - fn test_decode() { - let encoded = Multihash::hash(message().as_bytes()); - let result = Multihash::decode(&encoded); - - assert!(result.is_ok()); - assert_eq!( - result.unwrap(), - DecodedContainer { - hash: vec![ - 0x5f, 0x46, 0x25, 0xd4, 0xf6, 0x1e, 0xdb, 0x52, 0x78, 0x07, 0x45, 0x5f, 0x48, - 0xf8, 0xbe, 0x27, 0x8e, 0x71, 0xe8, 0x4a, 0xa9, 0x4d, 0x23, 0x11, 0x1f, 0xfa, - 0xb3, 0xb6, 0x30, 0x93, 0xa7, 0x13, - ], - algorithm: 18, - } - ); - } } diff --git a/src/common/runtime/random.rs b/src/common/runtime/random.rs deleted file mode 100644 index 2cb5739..0000000 --- a/src/common/runtime/random.rs +++ /dev/null @@ -1,37 +0,0 @@ -use thiserror::Error; - -pub struct Random {} - -#[derive(Debug, Error)] -pub enum RandomError { - #[error(transparent)] - GetRandomError(#[from] getrandom::Error), -} - -impl Random { - pub fn bytes(size: &usize) -> Result, RandomError> { - let mut bytes = vec![0u8; *size]; - - getrandom::getrandom(&mut bytes)?; - Ok(bytes) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_should_success_random_bytes_32() { - let result = Random::bytes(&32).unwrap(); - - assert_eq!(result.len(), 32); - } - - #[test] - fn it_should_success_random_bytes_128() { - let result = Random::bytes(&128).unwrap(); - - assert_eq!(result.len(), 128); - } -} diff --git a/src/common/runtime/secp256k1.rs b/src/common/runtime/secp256k1.rs deleted file mode 100644 index c5c381b..0000000 --- a/src/common/runtime/secp256k1.rs +++ /dev/null @@ -1,172 +0,0 @@ -use hmac::digest::generic_array::GenericArray; -use k256::{ - ecdsa::{ - signature::{Signer, Verifier}, - Signature, SigningKey, VerifyingKey, - }, - elliptic_curve::{ecdh::diffie_hellman, sec1::ToEncodedPoint}, - PublicKey, SecretKey, -}; -use thiserror::Error; - -pub struct Secp256k1 {} - -#[derive(Debug, Error)] -pub enum Secp256k1Error { - #[error("SecretKeyConvertError")] - KeyConvertError(#[from] k256::elliptic_curve::Error), - #[error("PublicKeyConvertError : {0:?}")] - SignatureError(#[from] k256::ecdsa::Error), - #[error("invalid signature length: {0}")] - InvalidSignatureLength(usize), -} - -impl Secp256k1 { - pub fn ecdh(private_key: &[u8], public_key: &[u8]) -> Result<[u8; 32], Secp256k1Error> { - let sk = SecretKey::from_slice(private_key)?; - let pk = PublicKey::from_sec1_bytes(public_key)?; - - let shared = diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine()); - let converted: [u8; 32] = (*shared.raw_secret_bytes()).into(); - - Ok(converted) - } - - #[allow(dead_code)] - pub fn generate_public_key(private_key: &[u8]) -> Result, Secp256k1Error> { - let signing_key = SigningKey::from_slice(private_key)?; - Ok(signing_key.verifying_key().to_sec1_bytes().to_vec()) - } - - pub fn convert_public_key( - public_key: &[u8], - compress: bool, - ) -> Result, Secp256k1Error> { - let public_key = PublicKey::from_sec1_bytes(public_key)?; - Ok(public_key.to_encoded_point(compress).as_bytes().to_vec()) - } - - pub fn ecdsa_sign(message: &[u8], private_key: &[u8]) -> Result, Secp256k1Error> { - let signing_key = SigningKey::from_slice(private_key)?; - - let signature: Signature = signing_key.try_sign(message)?; - - Ok(signature.to_vec()) - } - - pub fn ecdsa_verify( - signature: &[u8], - message: &[u8], - public_key: &[u8], - ) -> Result { - let verify_key = VerifyingKey::from_sec1_bytes(public_key)?; - - if signature.len() != 64 { - return Err(Secp256k1Error::InvalidSignatureLength(signature.len())); - } - - let r = GenericArray::from_slice(&signature[0..32]); - let s = GenericArray::from_slice(&signature[32..]); - - let wrapped_signature = Signature::from_scalars(*r, *s)?; - - match verify_key.verify(message, &wrapped_signature) { - Ok(()) => Ok(true), - Err(e) => { - log::error!("signature error occurred. {:?}", e); - Ok(false) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn message() -> String { - String::from("0123456789abcdef") - } - - const PRIVATE_KEY: [u8; 32] = [ - 0x03, 0xb4, 0xad, 0xcd, 0x59, 0x36, 0x3f, 0x4e, 0xb9, 0xd0, 0x9f, 0x2a, 0x34, 0xcd, 0x3d, - 0x26, 0xa8, 0x12, 0x33, 0x0c, 0x2f, 0x88, 0x7c, 0xe5, 0xf8, 0x53, 0x89, 0x48, 0xff, 0xac, - 0x74, 0xc0, - ]; - - const PUBLIC_KEY: [u8; 33] = [ - 0x02, 0x51, 0x84, 0x22, 0x3f, 0xe8, 0x5d, 0x53, 0x20, 0x3c, 0xf9, 0xd3, 0x7f, 0x68, 0x22, - 0xe6, 0x33, 0xe8, 0xd7, 0x9f, 0x48, 0xb1, 0x32, 0xdf, 0x0b, 0x8c, 0x8a, 0x64, 0x11, 0x41, - 0xf3, 0x19, 0xb6, - ]; - - const UNCOMPRESSED_PUBLIC_KEY: [u8; 65] = [ - 0x04, 0x51, 0x84, 0x22, 0x3f, 0xe8, 0x5d, 0x53, 0x20, 0x3c, 0xf9, 0xd3, 0x7f, 0x68, 0x22, - 0xe6, 0x33, 0xe8, 0xd7, 0x9f, 0x48, 0xb1, 0x32, 0xdf, 0x0b, 0x8c, 0x8a, 0x64, 0x11, 0x41, - 0xf3, 0x19, 0xb6, 0xa3, 0x70, 0x7c, 0xa5, 0x18, 0x61, 0xe1, 0xe2, 0xde, 0xa4, 0x3c, 0x23, - 0x84, 0xf1, 0x79, 0xed, 0x44, 0xe9, 0x8c, 0x4a, 0xd0, 0x38, 0x89, 0x21, 0xd9, 0x6a, 0x1e, - 0x05, 0x93, 0x15, 0xe7, 0x54, - ]; - - #[test] - fn test() { - let shared_1 = Secp256k1::ecdh(&PRIVATE_KEY, &PUBLIC_KEY).unwrap(); - - let shared_2 = Secp256k1::ecdh(&PRIVATE_KEY, &UNCOMPRESSED_PUBLIC_KEY).unwrap(); - - assert_eq!(shared_1.len(), 32); - assert_eq!(shared_1, shared_2); - } - - #[test] - fn test_generate_public_key() { - let result = Secp256k1::generate_public_key(&PRIVATE_KEY).unwrap(); - - assert_eq!(result, &PUBLIC_KEY); - } - - #[test] - fn test_convert_public_key() { - let result_1 = Secp256k1::convert_public_key(&PUBLIC_KEY, true).unwrap(); - - assert_eq!(result_1, &PUBLIC_KEY); - - let result_2 = Secp256k1::convert_public_key(&PUBLIC_KEY, false).unwrap(); - - assert_eq!(result_2, &UNCOMPRESSED_PUBLIC_KEY); - } - - #[test] - fn test_ecdsa_sign() { - let message = message().as_bytes().to_vec(); - - let result = Secp256k1::ecdsa_sign(&message, &PRIVATE_KEY).unwrap(); - - assert_eq!( - result, - vec![ - 0x5c, 0xc6, 0x48, 0x2a, 0x15, 0xd8, 0x0d, 0xc0, 0xbe, 0x6d, 0xb8, 0x31, 0xad, 0x9c, - 0x4c, 0xac, 0xd9, 0x3a, 0xb3, 0x6c, 0x08, 0x8a, 0x8b, 0x3c, 0x49, 0xc5, 0xbc, 0x79, - 0x9b, 0xf1, 0xa2, 0x69, 0x3a, 0xc2, 0xa7, 0xdc, 0xd9, 0xa5, 0x16, 0x52, 0x66, 0xa2, - 0x6d, 0xe1, 0x23, 0x41, 0x98, 0xa2, 0x4d, 0xba, 0x08, 0x74, 0x87, 0x5d, 0xba, 0xc4, - 0x00, 0x70, 0x9c, 0x99, 0x3d, 0xd0, 0xf0, 0x2c - ] - ); - } - - #[test] - fn test_ecdsa_verify() { - let message = message().as_bytes().to_vec(); - - let signature = Secp256k1::ecdsa_sign(&message, &PRIVATE_KEY).unwrap(); - - let result_1 = Secp256k1::ecdsa_verify(&signature, &message, &PUBLIC_KEY).unwrap(); - - assert!(result_1); - - let result_2 = - Secp256k1::ecdsa_verify(&signature, &message, &UNCOMPRESSED_PUBLIC_KEY).unwrap(); - - assert!(result_2); - } -} diff --git a/src/common/utils.rs b/src/common/utils.rs deleted file mode 100644 index bd71b53..0000000 --- a/src/common/utils.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub mod json { - use serde_json::Value; - - pub fn merge(a: &mut Value, b: Value) { - match (a, b) { - (a @ &mut Value::Object(_), Value::Object(b)) => { - let a = a.as_object_mut().unwrap(); - for (k, v) in b { - merge(a.entry(k).or_insert(Value::Null), v); - } - } - (a, b) => *a = b, - } - } -} diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 6d54e1e..510b0c0 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -1,24 +1,29 @@ -use anyhow::Context; +use std::convert::TryInto; + use http::StatusCode; use super::sidetree::{ client::{HttpError, SidetreeHttpClient}, - payload::{CommitmentKeys, DIDCreateRequest, OperationPayloadBuilder}, + payload::{did_create_payload, DIDReplacePayload, ToPublicKey}, +}; +use crate::{ + did::sidetree::payload::DIDResolutionResponse, + keyring::{ + jwk::Jwk, + keypair::{KeyPair, KeyPairing}, + }, }; -use crate::{did::sidetree::payload::DIDResolutionResponse, keyring::keypair::KeyPairing}; #[derive(Debug, thiserror::Error)] pub enum CreateIdentifierError { - #[error("Failed to convert public key: {0}")] - PublicKeyConvertFailed(crate::keyring::secp256k1::Secp256k1Error), - #[error("Failed to convert to JWK: {0}")] - JwkConvertFailed(#[from] crate::keyring::secp256k1::Secp256k1Error), + #[error("Failed to convert to JWK")] + JwkError, #[error("Failed to build operation payload: {0}")] - PayloadBuildFailed(#[from] crate::did::sidetree::payload::OperationPayloadBuilderError), + PayloadBuildFailed(#[from] crate::did::sidetree::payload::DIDCreatePayloadError), + #[error("Failed to parse body: {0}")] + BodyParseError(#[from] serde_json::Error), #[error("Failed to send request to sidetree: {0}")] SidetreeRequestFailed(anyhow::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), } impl From for CreateIdentifierError { @@ -31,6 +36,8 @@ impl From for CreateIdentifierError { pub enum FindIdentifierError { #[error("Failed to send request to sidetree: {0}")] SidetreeRequestFailed(anyhow::Error), + #[error("Failed to parse body: {0}")] + BodyParseError(#[from] serde_json::Error), #[error(transparent)] Other(#[from] anyhow::Error), } @@ -41,6 +48,20 @@ impl From for FindIdentifierError { } } +#[derive(Debug, thiserror::Error)] +pub enum GetPublicKeyError { + #[error("Failed to find indentifier: {0}")] + FindIdentifierError(#[from] FindIdentifierError), + #[error("Failed to get did document: {0}")] + DidDocNotFound(String), + #[error("Failed to get public key")] + PublicKeyNotFound(String), + #[error("Failed to convert from JWK: {0}")] + JwkToK256Error(#[from] crate::keyring::jwk::JwkToK256Error), + #[error("Failed to convert from JWK: {0}")] + JwkToX25519Error(#[from] crate::keyring::jwk::JwkToX25519Error), +} + #[async_trait::async_trait] pub trait DidRepository { async fn create_identifier( @@ -51,6 +72,37 @@ pub trait DidRepository { &self, did: &str, ) -> Result, FindIdentifierError>; + async fn get_sign_key(&self, did: &str) -> Result { + let did_document = self.find_identifier(did).await?; + let public_keys = did_document + .ok_or(GetPublicKeyError::DidDocNotFound(did.to_string()))? + .did_document + .public_key + .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; + let public_key = public_keys + .iter() + .find(|pk| pk.id == "signingKey") + .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; + let public_key: k256::PublicKey = public_key.public_key_jwk.clone().try_into()?; + Ok(public_key) + } + async fn get_encrypt_key( + &self, + did: &str, + ) -> Result { + let did_document = self.find_identifier(did).await?; + let public_keys = did_document + .ok_or(GetPublicKeyError::DidDocNotFound(did.to_string()))? + .did_document + .public_key + .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; + let public_key = public_keys + .iter() + .find(|pk| pk.id == "encryptionKey") + .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; + let public_key: x25519_dalek::PublicKey = public_key.public_key_jwk.clone().try_into()?; + Ok(public_key) + } } pub struct DidRepositoryImpl { @@ -78,22 +130,42 @@ impl DidRepository for DidRepositoryImpl &self, keyring: KeyPairing, ) -> Result { - let public = keyring + // https://w3c.github.io/did-spec-registries/#assertionmethod + let sign = keyring .sign - .to_public_key("signingKey", &["auth", "general"]) - .map_err(CreateIdentifierError::PublicKeyConvertFailed)?; - - let update = keyring.update.to_jwk(false)?; - let recovery = keyring.recovery.to_jwk(false)?; - let payload = OperationPayloadBuilder::did_create_payload(&DIDCreateRequest { - public_keys: vec![public], - commitment_keys: CommitmentKeys { recovery, update }, - service_endpoints: vec![], - })?; + .get_public_key() + .to_public_key( + "EcdsaSecp256k1VerificationKey2019".to_string(), + "signingKey".to_string(), + vec!["assertionMethod".to_string()], + ) + .map_err(|_| CreateIdentifierError::JwkError)?; + let enc = keyring + .encrypt + .get_public_key() + .to_public_key( + "X25519KeyAgreementKey2019".to_string(), + "encryptionKey".to_string(), + vec!["keyAgreement".to_string()], + ) + .map_err(|_| CreateIdentifierError::JwkError)?; + let update: Jwk = keyring + .update + .get_public_key() + .try_into() + .map_err(|_| CreateIdentifierError::JwkError)?; + let recovery: Jwk = keyring + .recovery + .get_public_key() + .try_into() + .map_err(|_| CreateIdentifierError::JwkError)?; + let document = + DIDReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; + let payload = did_create_payload(document, &update, &recovery)?; let response = self.client.post_create_identifier(&payload).await?; if response.status_code.is_success() { - let response = serde_json::from_str(&response.body).context("failed to parse body")?; + let response = serde_json::from_str(&response.body)?; Ok(response) } else { Err(CreateIdentifierError::SidetreeRequestFailed(anyhow::anyhow!( @@ -110,9 +182,7 @@ impl DidRepository for DidRepositoryImpl let response = self.client.get_find_identifier(did).await?; match response.status_code { - StatusCode::OK => { - Ok(Some(serde_json::from_str(&response.body).context("failed to parse body")?)) - } + StatusCode::OK => Ok(Some(serde_json::from_str(&response.body)?)), StatusCode::NOT_FOUND => Ok(None), _ => Err(FindIdentifierError::SidetreeRequestFailed(anyhow::anyhow!( "Failed to find identifier. response: {:?}", @@ -162,11 +232,25 @@ pub mod mocks { if let Some(keyrings) = self.map.get(did) { let public_keys = keyrings .iter() - .map(|keyring| DidPublicKey { - id: did.to_string() + "#signingKey", - controller: String::new(), - r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), - public_key_jwk: keyring.sign.to_jwk(false).unwrap(), + .flat_map(|keyring| { + vec![ + DidPublicKey { + id: "signingKey".to_string(), + controller: String::new(), + r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), + public_key_jwk: keyring.sign.get_public_key().try_into().unwrap(), + }, + DidPublicKey { + id: "encryptionKey".to_string(), + controller: String::new(), + r#type: "X25519KeyAgreementKey2019".to_string(), + public_key_jwk: keyring + .encrypt + .get_public_key() + .try_into() + .unwrap(), + }, + ] }) .collect(); diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index eb411ae..15f8022 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -1,16 +1,11 @@ +use core::convert::TryInto; + +use data_encoding::BASE64URL_NOPAD; use serde::{Deserialize, Serialize}; -use serde_json::json; +use serde_jcs; use thiserror::Error; -use crate::{ - common::runtime::{ - base64_url::{Base64Url, PaddingType}, - multihash::Multihash, - }, - keyring::secp256k1::KeyPairSecp256K1, -}; - -pub struct OperationPayloadBuilder {} +use crate::{common::runtime::multihash, keyring::jwk::Jwk}; #[derive(Debug, Serialize, Deserialize)] pub struct ServiceEndpoint { @@ -39,7 +34,7 @@ pub struct DidPublicKey { pub r#type: String, #[serde(rename = "publicKeyJwk")] - pub public_key_jwk: KeyPairSecp256K1, + pub public_key_jwk: Jwk, } #[derive(Debug, Serialize, Deserialize)] @@ -79,59 +74,44 @@ pub struct PublicKeyPayload { pub r#type: String, #[serde(rename = "jwk")] - pub jwk: KeyPairSecp256K1, + pub jwk: Jwk, #[serde(rename = "purpose")] pub purpose: Vec, } -// ACTION: add-public-keys -#[allow(dead_code)] -struct DIDAddPublicKeysPayload { - id: String, - r#type: String, - jwk: KeyPairSecp256K1, - purpose: Vec, -} - -#[allow(dead_code)] -struct DIDAddPublicKeysAction { - action: String, //'add-public-keys', - public_keys: Vec, -} - -// ACTION: remove-public-keys -#[allow(dead_code)] -struct DIDRemovePublicKeysAction { - action: String, // 'remove-public-keys', - ids: Vec, -} - -// ACTION: add-services -#[allow(dead_code)] -struct DIDAddServicesPayload {} - -#[allow(dead_code)] -struct DIDAddServicesAction { - action: String, // 'add-services', - services: Vec, +pub trait ToPublicKey> { + fn to_public_key( + self, + key_type: String, + key_id: String, + purpose: Vec, + ) -> Result; } -// ACTION: remove-services -#[allow(dead_code)] -struct DIDRemoveServicesAction { - action: String, // 'remove-services', - ids: Vec, +impl ToPublicKey for T +where + T: TryInto, +{ + fn to_public_key( + self, + key_type: String, + key_id: String, + purpose: Vec, + ) -> Result { + let jwk: Jwk = self.try_into()?; + Ok(PublicKeyPayload { id: key_id.into(), r#type: key_type.into(), jwk, purpose }) + } } // ACTION: replace #[derive(Debug, Serialize, Deserialize)] -struct DIDReplacePayload { +pub struct DIDReplacePayload { #[serde(rename = "public_keys")] - public_keys: Vec, + pub public_keys: Vec, #[serde(rename = "service_endpoints")] - service_endpoints: Vec, + pub service_endpoints: Vec, } #[derive(Debug, Serialize, Deserialize)] @@ -191,10 +171,10 @@ pub struct DIDResolutionResponse { #[derive(Clone, Serialize, Deserialize)] pub struct CommitmentKeys { #[serde(rename = "recovery")] - pub recovery: KeyPairSecp256K1, + pub recovery: Jwk, #[serde(rename = "update")] - pub update: KeyPairSecp256K1, + pub update: Jwk, } #[derive(Clone, Serialize, Deserialize)] @@ -228,102 +208,68 @@ pub struct DIDCreateResponse { pub method_metadata: MethodMetadata, } -#[allow(dead_code)] -struct DIDUpdateRequest { - // NOT IMPLEMENTED -} - -#[allow(dead_code)] -struct DIDUpdateResponse { - // NOT IMPLEMENTED -} - -#[allow(dead_code)] -struct DIDRecoverRequest { - // NOT IMPLEMENTED -} - -#[allow(dead_code)] -struct DIDRecoverResponse { - // NOT IMPLEMENTED -} - -#[allow(dead_code)] -struct DIDDeactivateRequest { - // NOT IMPLEMENTED -} - -#[allow(dead_code)] -struct DIDDeactivateResponse { - // NOT IMPLEMENTED -} - #[derive(Debug, Error)] -pub enum OperationPayloadBuilderError { +pub enum DIDCreatePayloadError { #[error(transparent)] - MultihashError(#[from] crate::common::runtime::multihash::MultihashError), + SerdeJsonError(#[from] serde_json::Error), } -impl OperationPayloadBuilder { - pub fn did_create_payload( - params: &DIDCreateRequest, - ) -> Result { - let update = json!(¶ms.commitment_keys.update); - let update_commitment = - Multihash::canonicalize_then_double_hash_then_encode(update.to_string().as_bytes())?; - - let recovery = json!(¶ms.commitment_keys.recovery); - let recovery_commitment = - Multihash::canonicalize_then_double_hash_then_encode(recovery.to_string().as_bytes())?; - - let document: DIDReplacePayload = DIDReplacePayload { - public_keys: params.public_keys.clone(), - service_endpoints: params.service_endpoints.clone(), - }; - let patch: DIDReplaceAction = DIDReplaceAction { action: "replace".to_string(), document }; - - let delta = - json!(DIDReplaceDeltaObject { patches: vec![patch], update_commitment }).to_string(); - - let delta_bytes = delta.as_bytes(); - let delta_hash = Base64Url::encode(&Multihash::hash(delta_bytes), &PaddingType::NoPadding); - - let suffix = json!(DIDReplaceSuffixObject { delta_hash, recovery_commitment }).to_string(); - - let suffix_bytes = suffix.as_bytes(); - - let encoded_delta = Base64Url::encode(delta_bytes, &PaddingType::NoPadding); - let encoded_suffix = Base64Url::encode(suffix_bytes, &PaddingType::NoPadding); - - let payload: DIDCreatePayload = DIDCreatePayload { - r#type: "create".to_string(), - delta: encoded_delta, - suffix_data: encoded_suffix, - }; +#[inline] +fn canon(value: &T) -> Result, serde_json::Error> +where + T: ?Sized + Serialize, +{ + Ok(serde_jcs::to_string(value)?.into_bytes()) +} - Ok(json!(payload).to_string()) - } +pub fn did_create_payload( + replace_payload: DIDReplacePayload, + update_key: &Jwk, + recovery_key: &Jwk, +) -> Result { + let update = canon(update_key)?; + let update_commitment = multihash::double_hash_encode(&update); + let recovery = canon(recovery_key)?; + let recovery_commitment = multihash::double_hash_encode(&recovery); + let patch = DIDReplaceAction { action: "replace".to_string(), document: replace_payload }; + let delta = DIDReplaceDeltaObject { patches: vec![patch], update_commitment }; + let delta = canon(&delta)?; + let delta_hash = multihash::hash_encode(&delta); + + let suffix = DIDReplaceSuffixObject { delta_hash, recovery_commitment }; + let suffix = canon(&suffix)?; + let encoded_delta = BASE64URL_NOPAD.encode(&delta); + let encoded_suffix = BASE64URL_NOPAD.encode(&suffix); + + let payload = DIDCreatePayload { + r#type: "create".to_string(), + delta: encoded_delta, + suffix_data: encoded_suffix, + }; + + Ok(serde_jcs::to_string(&payload)?) } #[cfg(test)] pub mod tests { + use rand_core::OsRng; + use super::*; - use crate::{keyring, keyring::extension::trng::OSRandomNumberGenerator}; + use crate::{keyring, keyring::keypair::KeyPair}; #[test] pub fn test_did_create_payload() { - let trng: OSRandomNumberGenerator = OSRandomNumberGenerator::default(); - let keyring = keyring::keypair::KeyPairing::create_keyring(&trng).unwrap(); - - let public = keyring.sign.to_public_key("key_id", &[""]).unwrap(); - let update = keyring.recovery.to_jwk(false).unwrap(); - let recovery = keyring.update.to_jwk(false).unwrap(); - - let _result = OperationPayloadBuilder::did_create_payload(&DIDCreateRequest { - public_keys: vec![public], - commitment_keys: CommitmentKeys { recovery, update }, - service_endpoints: vec![], - }) - .unwrap(); + let keyring = keyring::keypair::KeyPairing::create_keyring(OsRng); + let public = keyring + .sign + .get_public_key() + .to_public_key("".to_string(), "key_id".to_string(), vec!["".to_string()]) + .unwrap(); + let update: Jwk = keyring.recovery.get_public_key().try_into().unwrap(); + let recovery: Jwk = keyring.update.get_public_key().try_into().unwrap(); + + let document = DIDReplacePayload { public_keys: vec![public], service_endpoints: vec![] }; + + let _result = did_create_payload(document, &update, &recovery).unwrap(); } } diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 5089df7..7ae2187 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -1,80 +1,62 @@ -use anyhow::Context; -use arrayref::array_ref; +use std::marker::Sync; + use chrono::{DateTime, Utc}; use cuid; -use didcomm_rs::{ - crypto::{CryptoAlgorithm, SignatureAlgorithm}, - AttachmentBuilder, AttachmentDataBuilder, Message, -}; +use didcomm_rs::{crypto::CryptoAlgorithm, AttachmentBuilder, AttachmentDataBuilder, Message}; use serde_json::Value; use thiserror::Error; -use x25519_dalek::{PublicKey, StaticSecret}; use crate::{ - common::runtime, - did::did_repository::{CreateIdentifierError, DidRepository, FindIdentifierError}, - didcomm::types::DIDCommMessage, - keyring::{self, keypair::KeyPairing}, + did::did_repository::{ + CreateIdentifierError, DidRepository, FindIdentifierError, GetPublicKeyError, + }, + didcomm::types::{DIDCommMessage, FindSenderError}, + keyring::keypair::{KeyPair, KeyPairing}, verifiable_credentials::{ - did_vc::{DIDVCService, DIDVCServiceGenerateError}, + did_vc::{DIDVCService, DIDVCServiceGenerateError, DIDVCServiceVerifyError}, types::{VerifiableCredentials, VerifiedContainer}, }, }; -pub struct DIDCommEncryptedService { +#[derive(Clone)] +pub struct DIDCommEncryptedService { vc_service: DIDVCService, attachment_link: String, } -impl Clone for DIDCommEncryptedService -where - R: DidRepository + Clone, - DIDVCService: Clone, -{ - fn clone(&self) -> Self { - Self { vc_service: self.vc_service.clone(), attachment_link: self.attachment_link.clone() } - } -} - #[derive(Debug, Error)] pub enum DIDCommEncryptedServiceGenerateError { - #[error("Secp256k1 error")] - KeyringSecp256k1Error(#[from] keyring::secp256k1::Secp256k1Error), - #[error("Secp256k1 error")] - RuntimeSecp256k1Error(#[from] runtime::secp256k1::Secp256k1Error), - #[error("did not found : {0}")] - DIDNotFound(String), #[error("did public key not found. did: {0}")] - DidPublicKeyNotFound(String), + DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("something went wrong with vc service")] VCServiceError(#[from] DIDVCServiceGenerateError), - #[error("failed to find identifier")] - SidetreeFindRequestFailed(#[from] FindIdentifierError), #[error("failed to create identifier")] SidetreeCreateRequestFailed(#[from] CreateIdentifierError), - #[error("failed to encrypt message")] - EncryptFailed(anyhow::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), + #[error("failed to encrypt message with error: {0}")] + EncryptFailed(#[from] didcomm_rs::Error), + #[error("failed serialize/deserialize : {0}")] + JsonError(#[from] serde_json::Error), } #[derive(Debug, Error)] pub enum DIDCommEncryptedServiceVerifyError { - #[error("Secp256k1 error")] - KeyringSecp256k1Error(#[from] keyring::secp256k1::Secp256k1Error), - #[error("Secp256k1 error")] - RuntimeSecp256k1Error(#[from] runtime::secp256k1::Secp256k1Error), - #[error("did not found : {0}")] - DIDNotFound(String), + #[error("something went wrong with vc service")] + VCServiceError(#[from] DIDVCServiceVerifyError), #[error("failed to find identifier")] SidetreeFindRequestFailed(#[from] FindIdentifierError), - #[error("did public key not found : did = {0}")] - DidPublicKeyNotFound(String), - #[error(transparent)] - Other(#[from] anyhow::Error), + #[error("did public key not found. did: {0}")] + DidPublicKeyNotFound(#[from] GetPublicKeyError), + #[error("failed to decrypt message : {0}")] + DecryptFailed(#[from] didcomm_rs::Error), + #[error("failed to get body : {0:?}")] + MetadataBodyNotFound(Option), + #[error("failed serialize/deserialize : {0}")] + JsonError(#[from] serde_json::Error), + #[error("failed to find sender did : {0}")] + FindSenderError(#[from] FindSenderError), } -impl DIDCommEncryptedService { +impl DIDCommEncryptedService { pub fn new(did_repository: R, attachment_link: Option) -> DIDCommEncryptedService { fn default_attachment_link() -> String { std::env::var("NODEX_DID_ATTACHMENT_LINK") @@ -96,42 +78,11 @@ impl DIDCommEncryptedService { metadata: Option<&Value>, issuance_date: DateTime, ) -> Result { - // NOTE: recipient to - let did_document = - self.vc_service.did_repository.find_identifier(to_did).await?.ok_or_else(|| { - DIDCommEncryptedServiceGenerateError::DIDNotFound(to_did.to_string()) - })?; - - let public_keys = did_document.did_document.public_key.ok_or_else(|| { - DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound(to_did.to_string()) - })?; - - // FIXME: workaround - if public_keys.len() != 1 { - return Err(anyhow::anyhow!("public_keys length must be 1").into()); - } - - let public_key = public_keys[0].clone(); - - let other_key = keyring::secp256k1::Secp256k1::from_jwk(&public_key.public_key_jwk)?; - - // NOTE: ecdh - let shared_key = runtime::secp256k1::Secp256k1::ecdh( - &from_keyring.sign.get_secret_key(), - &other_key.get_public_key(), - )?; - - let sk = StaticSecret::from(array_ref!(shared_key, 0, 32).to_owned()); - let pk = PublicKey::from(&sk); - // NOTE: message let body = self.vc_service.generate(from_did, from_keyring, message, issuance_date)?; - let body = serde_json::to_string(&body).context("failed to serialize")?; + let body = serde_json::to_string(&body)?; - let mut message = - Message::new().from(from_did).to(&[to_did]).body(&body).map_err(|e| { - anyhow::anyhow!("Failed to initialize message with error = {:?}", e) - })?; + let mut message = Message::new().from(from_did).to(&[to_did]).body(&body)?; // NOTE: Has attachment if let Some(value) = metadata { @@ -147,22 +98,17 @@ impl DIDCommEncryptedService { ) } - let seal_signed_message = message - .as_jwe(&CryptoAlgorithm::XC20P, Some(pk.as_bytes().to_vec())) - .seal_signed( - sk.to_bytes().as_ref(), - Some(vec![Some(pk.as_bytes().to_vec())]), - SignatureAlgorithm::Es256k, - &from_keyring.sign.get_secret_key(), - ) - .map_err(|e| { - DIDCommEncryptedServiceGenerateError::EncryptFailed(anyhow::Error::msg( - e.to_string(), - )) - })?; - - Ok(serde_json::from_str::(&seal_signed_message) - .context("failed to convert to json")?) + // NOTE: recipient to + let public_key = + self.vc_service.did_repository.get_encrypt_key(to_did).await?.as_bytes().to_vec(); + let public_key = Some(public_key); + + let seal_message = message.as_jwe(&CryptoAlgorithm::XC20P, public_key.clone()).seal( + &from_keyring.encrypt.get_secret_key().as_bytes().to_vec(), + Some(vec![public_key]), + )?; + + Ok(serde_json::from_str::(&seal_message)?) } pub async fn verify( @@ -171,60 +117,36 @@ impl DIDCommEncryptedService { message: &DIDCommMessage, ) -> Result { let other_did = message.find_sender()?; - - let did_document = self - .vc_service - .did_repository - .find_identifier(&other_did) - .await? - .ok_or(DIDCommEncryptedServiceVerifyError::DIDNotFound(other_did.to_string()))?; - - let public_keys = did_document.did_document.public_key.ok_or( - DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound(other_did.to_string()), - )?; - - // FIXME: workaround - if public_keys.len() != 1 { - return Err(anyhow::anyhow!("public_keys length must be 1").into()); - } - - let public_key = public_keys[0].clone(); - - let other_key = keyring::secp256k1::Secp256k1::from_jwk(&public_key.public_key_jwk)?; - - // NOTE: ecdh - let shared_key = runtime::secp256k1::Secp256k1::ecdh( - &my_keyring.sign.get_secret_key(), - &other_key.get_public_key(), - )?; - - let sk = StaticSecret::from(array_ref!(shared_key, 0, 32).to_owned()); - let pk = PublicKey::from(&sk); + let public_key = + self.vc_service.did_repository.get_encrypt_key(&other_did).await?.as_bytes().to_vec(); + let public_key = Some(public_key); let message = Message::receive( - &serde_json::to_string(&message).context("failed to serialize didcomm message")?, - Some(sk.to_bytes().as_ref()), - Some(pk.as_bytes().to_vec()), - Some(&other_key.get_public_key()), - ) - .map_err(|e| anyhow::anyhow!("failed to decrypt message : {:?}", e))?; + &serde_json::to_string(&message)?, + Some(&my_keyring.encrypt.get_secret_key().as_bytes().to_vec()), + public_key, + None, + )?; let metadata = message.attachment_iter().find(|item| match item.format.clone() { Some(value) => value == "metadata", None => false, }); - let body = - message.get_body().map_err(|e| anyhow::anyhow!("failed to get body : {:?}", e))?; - let body = - serde_json::from_str::(&body).context("failed to parse body")?; + let body = message + .get_body() + .map_err(|e| DIDCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; + let body = serde_json::from_str::(&body)?; + let body = self.vc_service.verify(body).await?; match metadata { Some(metadata) => { - let metadata = - metadata.data.json.as_ref().ok_or(anyhow::anyhow!("metadata not found"))?; - let metadata = serde_json::from_str::(metadata) - .context("failed to parse metadata to json")?; + let metadata = metadata + .data + .json + .as_ref() + .ok_or(DIDCommEncryptedServiceVerifyError::MetadataBodyNotFound(None))?; + let metadata = serde_json::from_str::(metadata)?; Ok(VerifiedContainer { message: body, metadata: Some(metadata) }) } None => Ok(VerifiedContainer { message: body, metadata: None }), @@ -236,13 +158,13 @@ impl DIDCommEncryptedService { mod tests { use std::{collections::BTreeMap, iter::FromIterator as _}; + use rand_core::OsRng; use serde_json::json; use super::*; use crate::{ - did::did_repository::mocks::MockDidRepository, - didcomm::test_utils::create_random_did, - keyring::{extension::trng::OSRandomNumberGenerator, keypair::KeyPairing}, + did::did_repository::mocks::MockDidRepository, didcomm::test_utils::create_random_did, + keyring::keypair::KeyPairing, }; #[actix_rt::test] @@ -250,9 +172,8 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); - let to_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let to_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); let repo = MockDidRepository::from_single(BTreeMap::from_iter([ (from_did.clone(), from_keyring.clone()), @@ -277,7 +198,6 @@ mod tests { } mod generate_failed { - use self::keyring::secp256k1::Secp256k1; use super::*; use crate::did::did_repository::mocks::NoPublicKeyDidRepository; @@ -286,8 +206,7 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); let repo = MockDidRepository::from_single(BTreeMap::from_iter([( from_did.clone(), @@ -304,7 +223,10 @@ mod tests { .await .unwrap_err(); - if let DIDCommEncryptedServiceGenerateError::DIDNotFound(did) = res { + if let DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound( + GetPublicKeyError::DidDocNotFound(did), + ) = res + { assert_eq!(did, to_did); } else { panic!("unexpected result: {:?}", res); @@ -316,8 +238,7 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); let repo = NoPublicKeyDidRepository; @@ -331,47 +252,15 @@ mod tests { .await .unwrap_err(); - if let DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound(did) = res { + if let DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound( + GetPublicKeyError::PublicKeyNotFound(did), + ) = res + { assert_eq!(did, to_did); } else { panic!("unexpected result: {:?}", res); } } - - #[actix_rt::test] - async fn test_runtime_secp256k1_error() { - let from_did = create_random_did(); - let to_did = create_random_did(); - - let trng = OSRandomNumberGenerator::default(); - let to_keyring = KeyPairing::create_keyring(&trng).unwrap(); - let mut from_keyring = KeyPairing::create_keyring(&trng).unwrap(); - from_keyring.sign = Secp256k1::new( - from_keyring.sign.get_public_key(), - vec![0; from_keyring.sign.get_secret_key().len()], - ) - .unwrap(); - - let repo = MockDidRepository::from_single(BTreeMap::from_iter([ - (from_did.clone(), from_keyring.clone()), - (to_did.clone(), to_keyring.clone()), - ])); - - let service = DIDCommEncryptedService::new(repo, None); - - let message = json!({"test": "0123456789abcdef"}); - let issuance_date = Utc::now(); - - let res = service - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap_err(); - - if let DIDCommEncryptedServiceGenerateError::RuntimeSecp256k1Error(_) = res { - } else { - panic!("unexpected result: {:?}", res); - } - } } mod verify_failed { @@ -405,9 +294,8 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); - let to_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let to_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -432,21 +320,63 @@ mod tests { let res = service.verify(&from_keyring, &res).await.unwrap_err(); - if let DIDCommEncryptedServiceVerifyError::DIDNotFound(did) = res { + if let DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound( + GetPublicKeyError::DidDocNotFound(did), + ) = res + { assert_eq!(did, from_did); } else { panic!("unexpected result: {:?}", res); } } + #[actix_rt::test] + async fn test_cannot_steal_message() { + let from_did = create_random_did(); + let to_did = create_random_did(); + let other_did = create_random_did(); + + let to_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let other_keyring = KeyPairing::create_keyring(&mut OsRng); + + let message = json!({"test": "0123456789abcdef"}); + let issuance_date = Utc::now(); + + let res = create_didcomm( + &from_did, + &to_did, + &from_keyring, + &to_keyring, + &message, + None, + issuance_date, + ) + .await; + + let repo = MockDidRepository::from_single(BTreeMap::from_iter([ + (from_did.clone(), from_keyring.clone()), + (to_did.clone(), to_keyring.clone()), + (other_did.clone(), other_keyring.clone()), + ])); + + let service = DIDCommEncryptedService::new(repo, None); + + let res = service.verify(&other_keyring, &res).await.unwrap_err(); + + if let DIDCommEncryptedServiceVerifyError::DecryptFailed(_) = res { + } else { + panic!("unexpected result: {:?}", res); + } + } + #[actix_rt::test] async fn test_did_public_key_not_found() { let from_did = create_random_did(); let to_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); - let to_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let to_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(&mut OsRng); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -468,7 +398,10 @@ mod tests { let res = service.verify(&from_keyring, &res).await.unwrap_err(); - if let DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound(did) = res { + if let DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound( + GetPublicKeyError::PublicKeyNotFound(did), + ) = res + { assert_eq!(did, from_did); } else { panic!("unexpected result: {:?}", res); diff --git a/src/didcomm/types.rs b/src/didcomm/types.rs index dc6223b..1732c31 100644 --- a/src/didcomm/types.rs +++ b/src/didcomm/types.rs @@ -1,7 +1,6 @@ -use anyhow::Context as _; +use data_encoding::BASE64URL_NOPAD; use serde::{Deserialize, Serialize}; - -use crate::common::runtime::base64_url::{self, PaddingType}; +use thiserror::Error; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct DIDCommMessage { @@ -35,26 +34,35 @@ pub struct Epk { pub x: String, } -impl DIDCommMessage { - pub fn find_receivers(&self) -> anyhow::Result> { - let to_dids = self.recipients.iter().map(|v| v.header.kid.clone()).collect(); +#[derive(Debug, Error)] +pub enum FindSenderError { + #[error("failed serialize/deserialize: {0}")] + JsonError(#[from] serde_json::Error), + #[error("failed to base64 decode protected: {0}")] + FromUtf8Error(#[from] std::string::FromUtf8Error), + #[error("failed to base64 decode protected: {0}")] + DecodeError(#[from] data_encoding::DecodeError), + #[error("skid error")] + SkidError, +} - Ok(to_dids) +impl DIDCommMessage { + pub fn find_receivers(&self) -> Vec { + self.recipients.iter().map(|v| v.header.kid.clone()).collect() } - pub fn find_sender(&self) -> anyhow::Result { + pub fn find_sender(&self) -> Result { let protected = &self.protected; - let decoded = base64_url::Base64Url::decode_as_string(protected, &PaddingType::NoPadding) - .context("failed to base64 decode protected")?; - let decoded = serde_json::from_str::(&decoded) - .context("failed to decode to json")?; + let decoded = BASE64URL_NOPAD.decode(protected.as_bytes())?; + let decoded = String::from_utf8(decoded)?; + let decoded = serde_json::from_str::(&decoded)?; let from_did = decoded .get("skid") - .context("skid not found")? + .ok_or(FindSenderError::SkidError)? .as_str() - .context("failed to serialize skid")? + .ok_or(FindSenderError::SkidError)? .to_string(); Ok(from_did) @@ -88,7 +96,7 @@ mod tests { #[test] fn extract_to_did() { let message: DIDCommMessage = serde_json::from_str(MESSAGE).unwrap(); - let result = message.find_receivers().unwrap(); + let result = message.find_receivers(); let expected_did = vec![TO_DID.to_string()]; assert_eq!(result, expected_did); } diff --git a/src/keyring/extension/trng.rs b/src/keyring/extension/trng.rs index adb7669..a8c8812 100644 --- a/src/keyring/extension/trng.rs +++ b/src/keyring/extension/trng.rs @@ -1,63 +1,2 @@ -use std::{ffi::CStr, num::NonZeroU32}; - -use thiserror::Error; - -use super::Extension; -use crate::common::runtime::random::{Random, RandomError}; - -#[derive(Error, Debug)] -pub enum TrngError { - #[error("Buffer length over")] - BufferLengthOver, - #[error("Library loading error")] - LibraryLoadingError(#[from] libloading::Error), - #[error("External function failed")] - ExternalFunctionFailed(NonZeroU32), - #[error("Random generation failed")] - RandomGenerationFailed(#[from] RandomError), -} - -pub trait Trng { - const MAX_BUFFER_LENGTH: usize = 1024; - fn generate(&self, size: &usize) -> Result, TrngError>; -} - -#[derive(Default)] -pub struct OSRandomNumberGenerator {} - -impl Trng for OSRandomNumberGenerator { - fn generate(&self, size: &usize) -> Result, TrngError> { - Random::bytes(size).map_err(TrngError::RandomGenerationFailed) - } -} - -pub struct ExternalTrng { - extension: Extension, -} - -impl Trng for ExternalTrng { - fn generate(&self, size: &usize) -> Result, TrngError> { - if Self::MAX_BUFFER_LENGTH < *size { - return Err(TrngError::BufferLengthOver); - } - - unsafe { - let buffer = [0u8; Self::MAX_BUFFER_LENGTH + 1]; - let buffer_ptr: *const i8 = buffer.as_ptr().cast(); - - let lib = libloading::Library::new(&self.extension.filename)?; - - let func: libloading::Symbol< - unsafe extern "C" fn(buf: *const i8, bufsize: usize, size: usize) -> u32, - > = lib.get(self.extension.symbol.as_bytes())?; - - let result = func(buffer_ptr, buffer.len(), *size); - - if let Some(exit_status) = NonZeroU32::new(result) { - return Err(TrngError::ExternalFunctionFailed(exit_status)); - } - - Ok(CStr::from_ptr(buffer_ptr as *const core::ffi::c_char).to_bytes().to_vec()) - } - } -} +// TODO: Impl TRNG based CryptoRng trait +// https://rust-random.github.io/rand/src/rand/rngs/std.rs.html#94 diff --git a/src/keyring/jwk.rs b/src/keyring/jwk.rs new file mode 100644 index 0000000..a2b36b2 --- /dev/null +++ b/src/keyring/jwk.rs @@ -0,0 +1,136 @@ +use std::convert::{From, Into, TryFrom, TryInto}; + +use data_encoding::BASE64URL_NOPAD; +use elliptic_curve::sec1::ToEncodedPoint; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Jwk { + #[serde(rename = "kty")] + kty: String, + + #[serde(rename = "crv")] + crv: String, + + #[serde(rename = "x")] + x: String, + + #[serde(rename = "y", skip_serializing_if = "Option::is_none")] + y: Option, +} + +#[derive(Error, Debug)] +pub enum JwkToK256Error { + #[error("missing y")] + MissingY, + #[error("decode error")] + DecodeError, + #[error("different crv")] + DifferentCrv, + #[error("crypt error")] + CryptError, +} + +#[derive(Error, Debug)] +pub enum JwkToX25519Error { + #[error("decode error")] + DecodeError, + #[error("different crv")] + DifferentCrv, + #[error("crypt error")] + CryptError, +} + +fn decode_base64url( + s: &str, +) -> Result, JwkToK256Error> { + let mut result = elliptic_curve::FieldBytes::::default(); + BASE64URL_NOPAD + .decode_mut(s.as_bytes(), &mut result) + .map_err(|_| JwkToK256Error::DecodeError)?; + Ok(result) +} + +impl TryFrom for k256::PublicKey { + type Error = JwkToK256Error; + fn try_from(value: Jwk) -> Result { + if value.crv != "secp256k1" { + return Err(JwkToK256Error::DifferentCrv); + } + if let Some(y) = value.y { + let x = decode_base64url(&value.x)?; + let y = decode_base64url(&y)?; + let pk = k256::EncodedPoint::from_affine_coordinates(&x, &y, false); + let pk = k256::PublicKey::from_sec1_bytes(pk.as_bytes()); + pk.map_err(|_| JwkToK256Error::CryptError) + } else { + Err(JwkToK256Error::MissingY) + } + } +} + +impl TryFrom for Jwk { + type Error = (); + fn try_from(value: k256::PublicKey) -> Result { + let value = value.to_encoded_point(false); + let kty = "EC".to_string(); + let crv = "secp256k1".to_string(); + match value.coordinates() { + elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { + let x = BASE64URL_NOPAD.encode(&x); + let y = Some(BASE64URL_NOPAD.encode(&y)); + Ok(Jwk { kty, crv, x, y }) + } + _ => Err(()), + } + } +} + +impl TryFrom for x25519_dalek::PublicKey { + type Error = JwkToX25519Error; + fn try_from(value: Jwk) -> Result { + if value.crv != "X25519" { + return Err(JwkToX25519Error::DifferentCrv); + } + let pk = BASE64URL_NOPAD + .decode(&value.x.as_bytes()) + .map_err(|_| JwkToX25519Error::DecodeError)?; + let pk: [u8; 32] = pk.try_into().map_err(|_| JwkToX25519Error::DecodeError)?; + Ok(pk.into()) + } +} + +impl From for Jwk { + fn from(value: x25519_dalek::PublicKey) -> Self { + let x = BASE64URL_NOPAD.encode(value.as_bytes()); + let kty = "OKP".to_string(); + let crv = "X25519".to_string(); + Jwk { kty, crv, x, y: None } + } +} + +#[cfg(test)] +pub mod tests { + use rand_core::OsRng; + + use super::*; + + #[test] + pub fn x25519_enc_dec() { + let sk = x25519_dalek::StaticSecret::random_from_rng(OsRng); + let pk = x25519_dalek::PublicKey::from(&sk); + let jwk: Jwk = pk.into(); + let _pk: x25519_dalek::PublicKey = jwk.try_into().unwrap(); + assert_eq!(pk, _pk); + } + + #[test] + pub fn k256_enc_dec() { + let sk = k256::SecretKey::random(&mut OsRng); + let pk = sk.public_key(); + let jwk: Jwk = pk.try_into().unwrap(); + let _pk: k256::PublicKey = jwk.try_into().unwrap(); + assert_eq!(pk, _pk); + } +} diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 5e51dd0..57f7524 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -1,66 +1,135 @@ -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; +use elliptic_curve::sec1::ToEncodedPoint; +use hex::FromHexError; +use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use super::{ - extension::trng::{self, Trng}, - secp256k1::{Secp256k1, Secp256k1Error, Secp256k1HexKeyPair}, -}; -use crate::common::runtime; +#[derive(Clone, Serialize, Deserialize)] +pub struct HexKeyPair { + // MEMO: Matching schema in NodeX config. + public_key: String, + secret_key: String, +} -#[derive(Debug, Clone)] -pub struct KeyPairing { - pub sign: Secp256k1, - pub update: Secp256k1, - pub recovery: Secp256k1, - pub encrypt: Secp256k1, +#[derive(Error, Debug)] +pub enum KeyPairingError { + #[error("from hex error")] + FromHexError(#[from] FromHexError), + #[error("crypt error")] + CryptError, } -#[derive(Debug, Clone, Serialize, Deserialize)] +pub trait KeyPair: Sized { + fn get_secret_key(&self) -> S; + fn get_public_key(&self) -> P; + fn to_hex_key_pair(&self) -> HexKeyPair; + fn from_hex_key_pair(kp: &HexKeyPair) -> Result; +} -pub struct KeyPairingHex { - pub sign: Secp256k1HexKeyPair, - pub update: Secp256k1HexKeyPair, - // MEMO: Matching schema in NodeX config. - pub recover: Secp256k1HexKeyPair, - pub encrypt: Secp256k1HexKeyPair, +#[derive(Clone)] +pub struct K256KeyPair { + secret_key: k256::SecretKey, + public_key: k256::PublicKey, } -#[derive(Error, Debug)] -pub enum KeyPairingError { - #[error("secp256k1 error")] - KeyInitializationFailed(#[from] Secp256k1Error), - #[error("Trng error")] - TrngGenerationFailed(#[from] trng::TrngError), - #[error("BIP32 error")] - BIP32Error(#[from] runtime::bip32::BIP32Error), +impl K256KeyPair { + pub fn new(secret_key: k256::SecretKey) -> Self { + let public_key = secret_key.public_key(); + K256KeyPair { public_key, secret_key } + } } -impl KeyPairing { - const SIGN_DERIVATION_PATH: &'static str = "m/44'/0'/0'/0/10"; - const UPDATE_DERIVATION_PATH: &'static str = "m/44'/0'/0'/0/20"; - const RECOVERY_DERIVATION_PATH: &'static str = "m/44'/0'/0'/0/30"; - const ENCRYPT_DERIVATION_PATH: &'static str = "m/44'/0'/0'/0/40"; +impl KeyPair for K256KeyPair { + fn get_secret_key(&self) -> k256::SecretKey { + self.secret_key.clone() + } + fn get_public_key(&self) -> k256::PublicKey { + self.public_key.clone() + } + fn to_hex_key_pair(&self) -> HexKeyPair { + let sk = self.secret_key.to_bytes(); + let secret_key = hex::encode(&sk); + let pk = self.public_key.to_encoded_point(false); + let public_key = hex::encode(pk.as_bytes()); + HexKeyPair { secret_key, public_key } + } + fn from_hex_key_pair(kp: &HexKeyPair) -> Result { + let secret_key = hex::decode(&kp.secret_key)?; + let secret_key = + k256::SecretKey::from_slice(&secret_key).map_err(|_| KeyPairingError::CryptError)?; + let public_key = hex::decode(&kp.public_key)?; + let public_key = k256::PublicKey::from_sec1_bytes(&public_key) + .map_err(|_| KeyPairingError::CryptError)?; + Ok(K256KeyPair { public_key, secret_key }) + } +} - pub fn create_keyring(trng: &T) -> Result { - let seed = trng.generate(&(256 / 8))?; +#[derive(Clone)] +pub struct X25519KeyPair { + secret_key: x25519_dalek::StaticSecret, + public_key: x25519_dalek::PublicKey, +} - let sign = Self::generate_secp256k1(&seed, Self::SIGN_DERIVATION_PATH)?; - let update = Self::generate_secp256k1(&seed, Self::UPDATE_DERIVATION_PATH)?; - let recovery = Self::generate_secp256k1(&seed, Self::RECOVERY_DERIVATION_PATH)?; - let encrypt = Self::generate_secp256k1(&seed, Self::ENCRYPT_DERIVATION_PATH)?; +impl X25519KeyPair { + pub fn new(secret_key: x25519_dalek::StaticSecret) -> Self { + let public_key = x25519_dalek::PublicKey::from(&secret_key); + X25519KeyPair { public_key, secret_key } + } +} - Ok(KeyPairing { sign, update, recovery, encrypt }) +impl KeyPair for X25519KeyPair { + fn get_secret_key(&self) -> x25519_dalek::StaticSecret { + self.secret_key.clone() } + fn get_public_key(&self) -> x25519_dalek::PublicKey { + self.public_key.clone() + } + fn to_hex_key_pair(&self) -> HexKeyPair { + let sk = self.secret_key.as_bytes(); + let secret_key = hex::encode(sk); + let pk = self.public_key.as_bytes(); + let public_key = hex::encode(pk); + HexKeyPair { secret_key, public_key } + } + fn from_hex_key_pair(kp: &HexKeyPair) -> Result { + let secret_key = hex::decode(&kp.secret_key)?; + let secret_key: [u8; 32] = + secret_key.try_into().map_err(|_| KeyPairingError::CryptError)?; + let secret_key = x25519_dalek::StaticSecret::from(secret_key); + let public_key = hex::decode(&kp.public_key)?; + let public_key: [u8; 32] = + public_key.try_into().map_err(|_| KeyPairingError::CryptError)?; + let public_key = x25519_dalek::PublicKey::from(public_key); + Ok(X25519KeyPair { public_key, secret_key }) + } +} - fn generate_secp256k1( - seed: &[u8], - derivation_path: &str, - ) -> Result { - let node = runtime::bip32::BIP32::get_node(seed, derivation_path)?; +#[derive(Clone)] +pub struct KeyPairing { + pub sign: K256KeyPair, + pub update: K256KeyPair, + pub recovery: K256KeyPair, + pub encrypt: X25519KeyPair, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct KeyPairingHex { + pub sign: HexKeyPair, + pub update: HexKeyPair, + // MEMO: Matching schema in NodeX config. + pub recover: HexKeyPair, + pub encrypt: HexKeyPair, +} - Ok(Secp256k1::from_bip32(node)?) +impl KeyPairing { + pub fn create_keyring(mut csprng: T) -> Self { + let sign = K256KeyPair::new(k256::SecretKey::random(&mut csprng)); + let update = K256KeyPair::new(k256::SecretKey::random(&mut csprng)); + let recovery = K256KeyPair::new(k256::SecretKey::random(&mut csprng)); + let encrypt = X25519KeyPair::new(x25519_dalek::StaticSecret::random_from_rng(&mut csprng)); + KeyPairing { sign, update, recovery, encrypt } } } @@ -79,10 +148,10 @@ impl TryFrom<&KeyPairingHex> for KeyPairing { type Error = KeyPairingError; fn try_from(hex: &KeyPairingHex) -> Result { - let sign = Secp256k1::from_hex_key_pair(&hex.sign)?; - let update = Secp256k1::from_hex_key_pair(&hex.update)?; - let recovery = Secp256k1::from_hex_key_pair(&hex.recover)?; - let encrypt = Secp256k1::from_hex_key_pair(&hex.encrypt)?; + let sign = K256KeyPair::from_hex_key_pair(&hex.sign)?; + let update = K256KeyPair::from_hex_key_pair(&hex.update)?; + let recovery = K256KeyPair::from_hex_key_pair(&hex.recover)?; + let encrypt = X25519KeyPair::from_hex_key_pair(&hex.encrypt)?; Ok(KeyPairing { sign, update, recovery, encrypt }) } @@ -90,17 +159,17 @@ impl TryFrom<&KeyPairingHex> for KeyPairing { #[cfg(test)] pub mod tests { + use rand_core::OsRng; + use super::*; - use crate::keyring::extension::trng::OSRandomNumberGenerator; #[test] pub fn test_create_keyring() { - let trng = OSRandomNumberGenerator::default(); - let keyring = KeyPairing::create_keyring(&trng).unwrap(); + let keyring = KeyPairing::create_keyring(OsRng); - assert_eq!(keyring.sign.get_secret_key().len(), 32); - assert_eq!(keyring.update.get_secret_key().len(), 32); - assert_eq!(keyring.recovery.get_secret_key().len(), 32); - assert_eq!(keyring.encrypt.get_secret_key().len(), 32); + assert_eq!(keyring.sign.get_secret_key().to_bytes().len(), 32); + assert_eq!(keyring.update.get_secret_key().to_bytes().len(), 32); + assert_eq!(keyring.recovery.get_secret_key().to_bytes().len(), 32); + assert_eq!(keyring.encrypt.get_secret_key().as_bytes().len(), 32); } } diff --git a/src/keyring/mod.rs b/src/keyring/mod.rs index 8f11f70..542af99 100644 --- a/src/keyring/mod.rs +++ b/src/keyring/mod.rs @@ -1,3 +1,4 @@ pub mod extension; +pub mod jwk; pub mod keypair; pub mod secp256k1; diff --git a/src/keyring/secp256k1.rs b/src/keyring/secp256k1.rs index 1ebb9a6..28624d5 100644 --- a/src/keyring/secp256k1.rs +++ b/src/keyring/secp256k1.rs @@ -1,367 +1,390 @@ -use std::{cmp::Ordering, convert::TryFrom}; - -use hex; -use ibig::{ibig, IBig}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::{ - common::runtime::{self, base64_url::PaddingType, bip32::BIP32Container}, - did::sidetree::payload::PublicKeyPayload, -}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyPairSecp256K1 { - #[serde(rename = "kty")] - pub kty: String, - - #[serde(rename = "crv")] - pub crv: String, - - #[serde(rename = "x")] - pub x: String, - - #[serde(rename = "y")] - pub y: String, - - #[serde(rename = "d", skip_serializing_if = "Option::is_none")] - pub d: Option, - - #[serde(rename = "kid", skip_serializing_if = "Option::is_none")] - pub kid: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Secp256k1HexKeyPair { - // MEMO: Matching schema in NodeX config. - pub public_key: String, - pub secret_key: String, -} - -pub(crate) const PRIVATE_KEY_SIZE: usize = 32; // Buffer(PrivateKey (32 = 256 bit)) -pub(crate) const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33; // Buffer(0x04 + PublicKey (32 = 256 bit)) -pub(crate) const UNCOMPRESSED_PUBLIC_KEY_SIZE: usize = 65; // Buffer(0x04 + PublicKey (64 = 512 bit)) - -#[derive(Clone, Debug, PartialEq)] -pub struct Secp256k1 { - public: [u8; UNCOMPRESSED_PUBLIC_KEY_SIZE], - private: [u8; PRIVATE_KEY_SIZE], -} - -impl TryFrom<&Secp256k1HexKeyPair> for Secp256k1 { - type Error = Secp256k1Error; - - fn try_from(value: &Secp256k1HexKeyPair) -> Result { - Self::from_hex_key_pair(value) - } -} - -impl From<&Secp256k1> for Secp256k1HexKeyPair { - fn from(value: &Secp256k1) -> Self { - value.to_hex_key_pair() - } -} - -#[derive(Error, Debug)] -pub enum Secp256k1Error { - #[error("Invalid public key size")] - InvalidSecretKeySize, - #[error("Secp256k1 runtime error : {0:?}")] - Secp256k1RuntimeError(#[from] runtime::secp256k1::Secp256k1Error), - #[error("Invalid public key size")] - InvalidPublicKeySize, - #[error("Base64 decode error")] - Base64UrlError(#[from] runtime::base64_url::Base64UrlError), - #[error("Invalid first bytes")] - InvalidFirstBytes, - #[error("Number parsing error")] - NumberParsingError(#[from] ibig::error::ParseError), - #[error("Validation Failed")] - ValidationFailed, - #[error("Hex Decode failed")] - HexDecodeFailed(#[from] hex::FromHexError), - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -impl Secp256k1 { - pub fn from_bip32(container: BIP32Container) -> Result { - let public = container.public_key; - let private = container.private_key; - - let public = Secp256k1::transform_uncompressed_public_key(&public)?; - - let public = <[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) - .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - - Ok(Secp256k1 { public, private }) - } - - pub fn new(public: Vec, private: Vec) -> Result { - if private.len() != PRIVATE_KEY_SIZE { - return Err(Secp256k1Error::InvalidSecretKeySize); - } - - if public.len() == COMPRESSED_PUBLIC_KEY_SIZE { - let public = Secp256k1::transform_uncompressed_public_key(&public)?; - - let public = <[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) - .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - let private = <[u8; PRIVATE_KEY_SIZE]>::try_from(private) - .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - - if public[0] != 0x04 { - Err(Secp256k1Error::InvalidFirstBytes) - } else { - Ok(Secp256k1 { public, private }) - } - } else if public.len() == UNCOMPRESSED_PUBLIC_KEY_SIZE { - let public = <[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) - .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - let private = <[u8; PRIVATE_KEY_SIZE]>::try_from(private) - .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - - if public[0] != 0x04 { - Err(Secp256k1Error::InvalidFirstBytes) - } else { - Ok(Secp256k1 { public, private }) - } - } else { - Err(Secp256k1Error::InvalidPublicKeySize) - } - } - - pub fn get_public_key(&self) -> Vec { - self.public.to_vec() - } - - pub fn get_secret_key(&self) -> Vec { - self.private.to_vec() - } - - pub fn to_hex_key_pair(&self) -> Secp256k1HexKeyPair { - Secp256k1HexKeyPair { - public_key: hex::encode(self.get_public_key()), - secret_key: hex::encode(self.get_secret_key()), - } - } - - pub fn from_hex_key_pair(hex_key_pair: &Secp256k1HexKeyPair) -> Result { - let public = hex::decode(&hex_key_pair.public_key)?; - let private = hex::decode(&hex_key_pair.secret_key)?; - - Self::new(public, private) - } - - pub fn from_jwk(jwk: &KeyPairSecp256K1) -> Result { - let d = jwk.d.clone().unwrap_or_else(|| { - const EMPTY_PRIVATE_KEY: [u8; PRIVATE_KEY_SIZE] = [0; PRIVATE_KEY_SIZE]; - runtime::base64_url::Base64Url::encode(&EMPTY_PRIVATE_KEY, &PaddingType::NoPadding) - }); - - let x = runtime::base64_url::Base64Url::decode_as_bytes(&jwk.x, &PaddingType::NoPadding)?; - let y = runtime::base64_url::Base64Url::decode_as_bytes(&jwk.y, &PaddingType::NoPadding)?; - - let public = [&[0x04], &x[..], &y[..]].concat(); - let private = runtime::base64_url::Base64Url::decode_as_bytes(&d, &PaddingType::NoPadding)?; - - Self::new(public, private) - } - - pub fn to_jwk(&self, included_private_key: bool) -> Result { - let validated = self.validate_point()?; - - if !validated { - return Err(Secp256k1Error::ValidationFailed); - } - - let d = if included_private_key { - Some(runtime::base64_url::Base64Url::encode( - &self.get_secret_key(), - &PaddingType::NoPadding, - )) - } else { - None - }; - - Ok(KeyPairSecp256K1 { - kty: "EC".to_string(), - crv: "secp256k1".to_string(), - x: runtime::base64_url::Base64Url::encode(self.get_point_x(), &PaddingType::NoPadding), - y: runtime::base64_url::Base64Url::encode(self.get_point_y(), &PaddingType::NoPadding), - d, - kid: None, - }) - } - - pub fn to_public_key( - &self, - key_id: &str, - purpose: &[&str], - ) -> Result { - let validated = self.validate_point()?; - - if !validated { - return Err(Secp256k1Error::ValidationFailed); - } - - let jwk = self.to_jwk(false)?; - - Ok(PublicKeyPayload { - id: key_id.to_string(), - r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), - jwk, - purpose: purpose.to_vec().iter().map(|value| value.to_string()).collect(), - }) - } - - fn get_point_x(&self) -> &[u8] { - &self.public[1..33] - } - - fn get_point_y(&self) -> &[u8] { - &self.public[33..] - } - - pub fn validate_point(&self) -> Result { - let x = self.get_point_x(); - let y = self.get_point_y(); - - let nx = IBig::from_str_radix(&hex::encode(x), 16)?; - let ny = IBig::from_str_radix(&hex::encode(y), 16)?; - let np = IBig::from_str_radix( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - 16, - )?; - - let verified: IBig = (&ny * &ny - &nx * &nx * &nx - 7) % &np; - - Ok(verified.cmp(&ibig!(0)) == Ordering::Equal) - } - - pub fn transform_uncompressed_public_key(compressed: &[u8]) -> Result, Secp256k1Error> { - runtime::secp256k1::Secp256k1::convert_public_key(compressed, false).map_err(|e| e.into()) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - - const PRIVATE_KEY: [u8; 32] = [ - 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, 0x90, 0xac, 0xdf, 0x76, - 0x15, 0x64, 0x0a, 0xa6, 0xed, 0x31, 0xb8, 0xf1, 0x82, 0x43, 0xf0, 0x4a, 0x57, 0x2c, 0x52, - 0x8e, 0xdb, - ]; - - const PUBLIC_KEY: [u8; 33] = [ - 0x02, 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, 0xcc, 0xea, 0x96, 0xa2, - 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, 0x6f, 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, - 0x60, 0x5c, 0x44, - ]; - - #[test] - pub fn test_to_hex_key_pair() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let result = node.to_hex_key_pair(); - - assert_eq!( - result.secret_key, - "c739805ab03da62ddbe03390acdf7615640aa6ed31b8f18243f04a572c528edb" - ); - assert_eq!( - result.public_key, - "0470964532f083f45fe8e8ccea96a22f6018d46a406f583ab226b19283aa605c44851b9274e6a2ce2ad42b4169e37df5f6cb38e81604b3ca2ebe11dd085862b490" - ); - } - - #[test] - pub fn test_get_point_x() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - assert_eq!( - node.get_point_x(), - &[ - 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, 0xcc, 0xea, 0x96, 0xa2, - 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, 0x6f, 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, - 0xaa, 0x60, 0x5c, 0x44, - ] - ) - } - - #[test] - pub fn test_get_point_y() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - assert_eq!( - node.get_point_y(), - &[ - 0x85, 0x1b, 0x92, 0x74, 0xe6, 0xa2, 0xce, 0x2a, 0xd4, 0x2b, 0x41, 0x69, 0xe3, 0x7d, - 0xf5, 0xf6, 0xcb, 0x38, 0xe8, 0x16, 0x04, 0xb3, 0xca, 0x2e, 0xbe, 0x11, 0xdd, 0x08, - 0x58, 0x62, 0xb4, 0x90, - ] - ) - } - - #[test] - pub fn test_validate_point() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let result = node.validate_point().unwrap(); - - assert!(result) - } - - #[test] - pub fn test_to_jwk_with_private_key() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let result = node.to_jwk(true).unwrap(); - - assert_eq!(result.kty, "EC"); - assert_eq!(result.crv, "secp256k1"); - assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); - assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); - assert_eq!(result.d, Some("xzmAWrA9pi3b4DOQrN92FWQKpu0xuPGCQ_BKVyxSjts".to_string())); - assert_eq!(result.kid, None); - } - - #[test] - pub fn test_to_jwk_without_private_key() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let result = node.to_jwk(false).unwrap(); - - assert_eq!(result.kty, "EC"); - assert_eq!(result.crv, "secp256k1"); - assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); - assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); - assert_eq!(result.d, None); - assert_eq!(result.kid, None); - } - - #[test] - pub fn test_from_jwk_with_private_key() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let jwk = node.to_jwk(false).unwrap(); - - let clone = Secp256k1::from_jwk(&jwk).unwrap(); - - assert_eq!(clone.get_public_key(), node.get_public_key()); - assert_eq!(clone.get_secret_key(), &[0; 32]); - } - - #[test] - pub fn test_from_jwk_without_private_key() { - let node = Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); - - let jwk = node.to_jwk(true).unwrap(); - - let clone = Secp256k1::from_jwk(&jwk).unwrap(); - - assert_eq!(node, clone); - } -} +// use std::{cmp::Ordering, convert::TryFrom}; + +// use hex; +// use ibig::{ibig, IBig}; +// use serde::{Deserialize, Serialize}; +// use thiserror::Error; + +// use crate::{ +// common::runtime::{self, base64_url::PaddingType, bip32::BIP32Container}, +// did::sidetree::payload::PublicKeyPayload, +// }; + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct KeyPairSecp256K1 { +// #[serde(rename = "kty")] +// pub kty: String, + +// #[serde(rename = "crv")] +// pub crv: String, + +// #[serde(rename = "x")] +// pub x: String, + +// #[serde(rename = "y")] +// pub y: String, + +// #[serde(rename = "d", skip_serializing_if = "Option::is_none")] +// pub d: Option, + +// #[serde(rename = "kid", skip_serializing_if = "Option::is_none")] +// pub kid: Option, +// } + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct Secp256k1HexKeyPair { +// // MEMO: Matching schema in NodeX config. +// pub public_key: String, +// pub secret_key: String, +// } + +// pub(crate) const PRIVATE_KEY_SIZE: usize = 32; // Buffer(PrivateKey (32 = 256 +// bit)) pub(crate) const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33; // Buffer(0x04 +// + PublicKey (32 = 256 bit)) pub(crate) const UNCOMPRESSED_PUBLIC_KEY_SIZE: +// usize = 65; // Buffer(0x04 + PublicKey (64 = 512 bit)) + +// #[derive(Clone, Debug, PartialEq)] +// pub struct Secp256k1 { +// public: [u8; UNCOMPRESSED_PUBLIC_KEY_SIZE], +// private: [u8; PRIVATE_KEY_SIZE], +// } + +// impl TryFrom<&Secp256k1HexKeyPair> for Secp256k1 { +// type Error = Secp256k1Error; + +// fn try_from(value: &Secp256k1HexKeyPair) -> Result { +// Self::from_hex_key_pair(value) +// } +// } + +// impl From<&Secp256k1> for Secp256k1HexKeyPair { +// fn from(value: &Secp256k1) -> Self { +// value.to_hex_key_pair() +// } +// } + +// #[derive(Error, Debug)] +// pub enum Secp256k1Error { +// #[error("Invalid public key size")] +// InvalidSecretKeySize, +// #[error("Secp256k1 runtime error : {0:?}")] +// Secp256k1RuntimeError(#[from] runtime::secp256k1::Secp256k1Error), +// #[error("Invalid public key size")] +// InvalidPublicKeySize, +// #[error("Base64 decode error")] +// Base64UrlError(#[from] runtime::base64_url::Base64UrlError), +// #[error("Invalid first bytes")] +// InvalidFirstBytes, +// #[error("Number parsing error")] +// NumberParsingError(#[from] ibig::error::ParseError), +// #[error("Validation Failed")] +// ValidationFailed, +// #[error("Hex Decode failed")] +// HexDecodeFailed(#[from] hex::FromHexError), +// #[error(transparent)] +// Other(#[from] anyhow::Error), +// } + +// impl Secp256k1 { +// pub fn from_bip32(container: BIP32Container) -> Result { let public = container.public_key; +// let private = container.private_key; + +// let public = Secp256k1::transform_uncompressed_public_key(&public)?; + +// let public = <[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) +// .map_err(|_| anyhow::anyhow!("Conversion failed"))?; + +// Ok(Secp256k1 { public, private }) +// } + +// pub fn new(public: Vec, private: Vec) -> Result { if private.len() != PRIVATE_KEY_SIZE { +// return Err(Secp256k1Error::InvalidSecretKeySize); +// } + +// if public.len() == COMPRESSED_PUBLIC_KEY_SIZE { +// let public = +// Secp256k1::transform_uncompressed_public_key(&public)?; + +// let public = <[u8; +// UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) .map_err(|_| +// anyhow::anyhow!("Conversion failed"))?; let private = <[u8; +// PRIVATE_KEY_SIZE]>::try_from(private) .map_err(|_| +// anyhow::anyhow!("Conversion failed"))?; + +// if public[0] != 0x04 { +// Err(Secp256k1Error::InvalidFirstBytes) +// } else { +// Ok(Secp256k1 { public, private }) +// } +// } else if public.len() == UNCOMPRESSED_PUBLIC_KEY_SIZE { +// let public = <[u8; +// UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) .map_err(|_| +// anyhow::anyhow!("Conversion failed"))?; let private = <[u8; +// PRIVATE_KEY_SIZE]>::try_from(private) .map_err(|_| +// anyhow::anyhow!("Conversion failed"))?; + +// if public[0] != 0x04 { +// Err(Secp256k1Error::InvalidFirstBytes) +// } else { +// Ok(Secp256k1 { public, private }) +// } +// } else { +// Err(Secp256k1Error::InvalidPublicKeySize) +// } +// } + +// pub fn get_public_key(&self) -> Vec { +// self.public.to_vec() +// } + +// pub fn get_secret_key(&self) -> Vec { +// self.private.to_vec() +// } + +// pub fn to_hex_key_pair(&self) -> Secp256k1HexKeyPair { +// Secp256k1HexKeyPair { +// public_key: hex::encode(self.get_public_key()), +// secret_key: hex::encode(self.get_secret_key()), +// } +// } + +// pub fn from_hex_key_pair(hex_key_pair: &Secp256k1HexKeyPair) -> +// Result { let public = +// hex::decode(&hex_key_pair.public_key)?; let private = +// hex::decode(&hex_key_pair.secret_key)?; + +// Self::new(public, private) +// } + +// pub fn from_jwk(jwk: &KeyPairSecp256K1) -> Result { +// let d = jwk.d.clone().unwrap_or_else(|| { +// const EMPTY_PRIVATE_KEY: [u8; PRIVATE_KEY_SIZE] = [0; +// PRIVATE_KEY_SIZE]; +// runtime::base64_url::Base64Url::encode(&EMPTY_PRIVATE_KEY, +// &PaddingType::NoPadding) }); + +// let x = runtime::base64_url::Base64Url::decode_as_bytes(&jwk.x, +// &PaddingType::NoPadding)?; let y = +// runtime::base64_url::Base64Url::decode_as_bytes(&jwk.y, +// &PaddingType::NoPadding)?; + +// let public = [&[0x04], &x[..], &y[..]].concat(); +// let private = runtime::base64_url::Base64Url::decode_as_bytes(&d, +// &PaddingType::NoPadding)?; + +// Self::new(public, private) +// } + +// pub fn to_jwk(&self, included_private_key: bool) -> +// Result { let validated = +// self.validate_point()?; + +// if !validated { +// return Err(Secp256k1Error::ValidationFailed); +// } + +// let d = if included_private_key { +// Some(runtime::base64_url::Base64Url::encode( +// &self.get_secret_key(), +// &PaddingType::NoPadding, +// )) +// } else { +// None +// }; + +// Ok(KeyPairSecp256K1 { +// kty: "EC".to_string(), +// crv: "secp256k1".to_string(), +// x: runtime::base64_url::Base64Url::encode(self.get_point_x(), +// &PaddingType::NoPadding), y: +// runtime::base64_url::Base64Url::encode(self.get_point_y(), +// &PaddingType::NoPadding), d, +// kid: None, +// }) +// } + +// pub fn to_public_key( +// &self, +// key_id: &str, +// purpose: &[&str], +// ) -> Result { +// let validated = self.validate_point()?; + +// if !validated { +// return Err(Secp256k1Error::ValidationFailed); +// } + +// let jwk = self.to_jwk(false)?; + +// Ok(PublicKeyPayload { +// id: key_id.to_string(), +// r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), +// jwk, +// purpose: purpose.to_vec().iter().map(|value| +// value.to_string()).collect(), }) +// } + +// fn get_point_x(&self) -> &[u8] { +// &self.public[1..33] +// } + +// fn get_point_y(&self) -> &[u8] { +// &self.public[33..] +// } + +// pub fn validate_point(&self) -> Result { +// let x = self.get_point_x(); +// let y = self.get_point_y(); + +// let nx = IBig::from_str_radix(&hex::encode(x), 16)?; +// let ny = IBig::from_str_radix(&hex::encode(y), 16)?; +// let np = IBig::from_str_radix( +// +// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", +// 16, +// )?; + +// let verified: IBig = (&ny * &ny - &nx * &nx * &nx - 7) % &np; + +// Ok(verified.cmp(&ibig!(0)) == Ordering::Equal) +// } + +// pub fn transform_uncompressed_public_key(compressed: &[u8]) -> +// Result, Secp256k1Error> { +// runtime::secp256k1::Secp256k1::convert_public_key(compressed, +// false).map_err(|e| e.into()) } +// } + +// #[cfg(test)] +// pub mod tests { + +// use super::*; + +// const PRIVATE_KEY: [u8; 32] = [ +// 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, +// 0x90, 0xac, 0xdf, 0x76, 0x15, 0x64, 0x0a, 0xa6, 0xed, 0x31, 0xb8, +// 0xf1, 0x82, 0x43, 0xf0, 0x4a, 0x57, 0x2c, 0x52, 0x8e, 0xdb, +// ]; + +// const PUBLIC_KEY: [u8; 33] = [ +// 0x02, 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, +// 0xcc, 0xea, 0x96, 0xa2, 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, 0x6f, +// 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, 0x60, 0x5c, 0x44, +// ]; + +// #[test] +// pub fn test_to_hex_key_pair() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let result = node.to_hex_key_pair(); + +// assert_eq!( +// result.secret_key, +// +// "c739805ab03da62ddbe03390acdf7615640aa6ed31b8f18243f04a572c528edb" ); +// assert_eq!( +// result.public_key, +// +// "0470964532f083f45fe8e8ccea96a22f6018d46a406f583ab226b19283aa605c44851b9274e6a2ce2ad42b4169e37df5f6cb38e81604b3ca2ebe11dd085862b490" +// ); +// } + +// #[test] +// pub fn test_get_point_x() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// assert_eq!( +// node.get_point_x(), +// &[ +// 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, +// 0xcc, 0xea, 0x96, 0xa2, 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, +// 0x6f, 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, 0x60, +// 0x5c, 0x44, ] +// ) +// } + +// #[test] +// pub fn test_get_point_y() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// assert_eq!( +// node.get_point_y(), +// &[ +// 0x85, 0x1b, 0x92, 0x74, 0xe6, 0xa2, 0xce, 0x2a, 0xd4, 0x2b, +// 0x41, 0x69, 0xe3, 0x7d, 0xf5, 0xf6, 0xcb, 0x38, 0xe8, 0x16, +// 0x04, 0xb3, 0xca, 0x2e, 0xbe, 0x11, 0xdd, 0x08, 0x58, 0x62, +// 0xb4, 0x90, ] +// ) +// } + +// #[test] +// pub fn test_validate_point() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let result = node.validate_point().unwrap(); + +// assert!(result) +// } + +// #[test] +// pub fn test_to_jwk_with_private_key() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let result = node.to_jwk(true).unwrap(); + +// assert_eq!(result.kty, "EC"); +// assert_eq!(result.crv, "secp256k1"); +// assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); +// assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); +// assert_eq!(result.d, +// Some("xzmAWrA9pi3b4DOQrN92FWQKpu0xuPGCQ_BKVyxSjts".to_string())); +// assert_eq!(result.kid, None); +// } + +// #[test] +// pub fn test_to_jwk_without_private_key() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let result = node.to_jwk(false).unwrap(); + +// assert_eq!(result.kty, "EC"); +// assert_eq!(result.crv, "secp256k1"); +// assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); +// assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); +// assert_eq!(result.d, None); +// assert_eq!(result.kid, None); +// } + +// #[test] +// pub fn test_from_jwk_with_private_key() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let jwk = node.to_jwk(false).unwrap(); + +// let clone = Secp256k1::from_jwk(&jwk).unwrap(); + +// assert_eq!(clone.get_public_key(), node.get_public_key()); +// assert_eq!(clone.get_secret_key(), &[0; 32]); +// } + +// #[test] +// pub fn test_from_jwk_without_private_key() { +// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), +// PRIVATE_KEY.to_vec()).unwrap(); + +// let jwk = node.to_jwk(true).unwrap(); + +// let clone = Secp256k1::from_jwk(&jwk).unwrap(); + +// assert_eq!(node, clone); +// } +// } diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index aab4659..d7766ec 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -5,8 +5,8 @@ use thiserror::Error; use super::types::Proof; use crate::{ - common::{cipher::jws::Jws, utils}, - keyring::secp256k1::Secp256k1, + common::cipher::jws, + keyring::keypair::{K256KeyPair, KeyPair}, verifiable_credentials::types::VerifiableCredentials, }; @@ -19,7 +19,7 @@ pub struct ProofContext { pub struct CredentialSignerSuite<'a> { pub did: &'a str, pub key_id: &'a str, - pub context: &'a Secp256k1, + pub context: &'a K256KeyPair, } #[derive(Debug, Error)] @@ -48,12 +48,11 @@ impl CredentialSigner { suite: CredentialSignerSuite, created: DateTime, ) -> Result { - let jws = Jws::encode(&json!(object), suite.context)?; - + let jws = jws::sign(&json!(object), &suite.context.get_secret_key())?; let did = suite.did; let key_id = suite.key_id; - let proof: ProofContext = ProofContext { + Ok(VerifiableCredentials { proof: Some(Proof { r#type: "EcdsaSecp256k1Signature2019".to_string(), proof_purpose: "authentication".to_string(), @@ -64,28 +63,19 @@ impl CredentialSigner { controller: None, challenge: None, }), - }; - - // NOTE: sign - let mut signed_object = json!(object); - - utils::json::merge(&mut signed_object, json!(proof)); - - Ok(serde_json::from_value::(signed_object)?) + ..object.clone() + }) } pub fn verify( mut object: VerifiableCredentials, - context: &Secp256k1, - ) -> Result<(VerifiableCredentials, bool), CredentialSignerVerifyError> { + public_key: &k256::PublicKey, + ) -> Result { let proof = object.proof.take().ok_or(CredentialSignerVerifyError::ProofNotFound)?; - let jws = proof.jws; let payload = serde_json::to_value(&object)?; - - let verified = Jws::verify(&payload, &jws, context)?; - - Ok((object, verified)) + let _ = jws::verify(&payload, &jws, public_key)?; + Ok(object) } } @@ -93,10 +83,7 @@ impl CredentialSigner { pub mod tests { use super::*; - use crate::{ - keyring::{self}, - verifiable_credentials::types::{CredentialSubject, Issuer}, - }; + use crate::verifiable_credentials::types::{CredentialSubject, Issuer}; const PRIVATE_KEY: [u8; 32] = [ 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, 0x90, 0xac, 0xdf, 0x76, @@ -110,10 +97,20 @@ pub mod tests { 0x60, 0x5c, 0x44, ]; + #[test] + pub fn test_public_key() { + let sk = k256::SecretKey::from_slice(&PRIVATE_KEY).unwrap(); + let context = K256KeyPair::new(sk); + assert_eq!( + context.get_public_key(), + k256::PublicKey::from_sec1_bytes(&PUBLIC_KEY).unwrap() + ); + } + #[test] pub fn test_sign() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); + let sk = k256::SecretKey::from_slice(&PRIVATE_KEY).unwrap(); + let context = K256KeyPair::new(sk); let model = VerifiableCredentials { id: None, @@ -159,8 +156,8 @@ pub mod tests { #[test] pub fn test_verify() { - let context = - keyring::secp256k1::Secp256k1::new(PUBLIC_KEY.to_vec(), PRIVATE_KEY.to_vec()).unwrap(); + let sk = k256::SecretKey::from_slice(&PRIVATE_KEY).unwrap(); + let context = K256KeyPair::new(sk); let model = VerifiableCredentials { id: None, @@ -187,9 +184,8 @@ pub mod tests { ) .unwrap(); - let (verified_model, verified) = CredentialSigner::verify(vc, &context).unwrap(); + let verified_model = CredentialSigner::verify(vc, &context.get_public_key()).unwrap(); - assert!(verified); assert_eq!(model, verified_model); } } diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 7387540..3294528 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -1,11 +1,12 @@ -use anyhow::Context as _; +use std::marker::Sync; + use chrono::{DateTime, Utc}; use serde_json::Value; use thiserror::Error; use crate::{ - did::did_repository::{DidRepository, FindIdentifierError}, - keyring::{self, keypair}, + did::did_repository::{DidRepository, GetPublicKeyError}, + keyring::keypair, verifiable_credentials::{ credential_signer::{ CredentialSigner, CredentialSignerSignError, CredentialSignerSuite, @@ -15,22 +16,11 @@ use crate::{ }, }; -pub struct DIDVCService { +#[derive(Clone)] +pub struct DIDVCService { pub(crate) did_repository: R, } -impl DIDVCService { - pub fn new(did_repository: R) -> Self { - Self { did_repository } - } -} - -impl Clone for DIDVCService { - fn clone(&self) -> Self { - Self { did_repository: self.did_repository.clone() } - } -} - #[derive(Debug, Error)] pub enum DIDVCServiceGenerateError { #[error("credential signer error")] @@ -39,23 +29,16 @@ pub enum DIDVCServiceGenerateError { #[derive(Debug, Error)] pub enum DIDVCServiceVerifyError { - #[error("did not found : {0}")] - DIDNotFound(String), #[error("did public key not found. did: {0}")] - PublicKeyNotFound(String), + PublicKeyNotFound(#[from] GetPublicKeyError), #[error("credential signer error")] VerifyFailed(#[from] CredentialSignerVerifyError), - #[error("public_keys length must be 1")] - PublicKeyLengthMismatch, - #[error("sidetree find request failed")] - SidetreeFindRequestFailed(#[from] FindIdentifierError), - #[error("signature is not verified")] - SignatureNotVerified, - #[error(transparent)] - Other(#[from] anyhow::Error), } -impl DIDVCService { +impl DIDVCService { + pub fn new(did_repository: R) -> Self { + Self { did_repository } + } pub fn generate( &self, from_did: &str, @@ -94,33 +77,8 @@ impl DIDVCService { &self, model: VerifiableCredentials, ) -> Result { - let did_document = self - .did_repository - .find_identifier(&model.issuer.id) - .await? - .ok_or_else(|| DIDVCServiceVerifyError::DIDNotFound(model.issuer.id.clone()))?; - let public_keys = did_document - .did_document - .public_key - .ok_or_else(|| DIDVCServiceVerifyError::PublicKeyNotFound(model.issuer.id.clone()))?; - - // FIXME: workaround - if public_keys.len() != 1 { - return Err(DIDVCServiceVerifyError::PublicKeyLengthMismatch); - } - - let public_key = public_keys[0].clone(); - - let context = keyring::secp256k1::Secp256k1::from_jwk(&public_key.public_key_jwk) - .context("failed to convert key")?; - - let (verified_model, verified) = CredentialSigner::verify(model, &context)?; - - if verified { - Ok(verified_model) - } else { - Err(DIDVCServiceVerifyError::SignatureNotVerified) - } + let public_key = self.did_repository.get_sign_key(&model.issuer.id).await?; + Ok(CredentialSigner::verify(model, &public_key)?) } } @@ -128,20 +86,20 @@ impl DIDVCService { mod tests { use std::{collections::BTreeMap, iter::FromIterator as _}; + use rand_core::OsRng; use serde_json::json; use super::*; use crate::{ did::{did_repository::mocks::MockDidRepository, test_utils::create_random_did}, - keyring::{extension::trng::OSRandomNumberGenerator, keypair::KeyPairing}, + keyring::keypair::KeyPairing, }; #[actix_rt::test] async fn test_generate_and_verify() { let from_did = create_random_did(); - let trng = OSRandomNumberGenerator::default(); - let from_keyring = KeyPairing::create_keyring(&trng).unwrap(); + let from_keyring = KeyPairing::create_keyring(OsRng); let mock_repository = MockDidRepository::from_single(BTreeMap::from_iter([( from_did.clone(), @@ -161,35 +119,7 @@ mod tests { assert_eq!(verified.credential_subject.container, message); } - mod generate_failed { - use super::*; - use crate::keyring::secp256k1::Secp256k1; - - #[actix_rt::test] - async fn test_generate_sign_failed() { - let from_did = create_random_did(); - - let trng = OSRandomNumberGenerator::default(); - let mut illegal_keyring = KeyPairing::create_keyring(&trng).unwrap(); - illegal_keyring.sign = Secp256k1::new( - illegal_keyring.sign.get_public_key(), - vec![0; illegal_keyring.sign.get_secret_key().len()], - ) - .unwrap(); - - let mock_repository = MockDidRepository::from_single(BTreeMap::new()); - - let service = DIDVCService::new(mock_repository); - - let message = json!({"test": "0123456789abcdef"}); - let issuance_date = Utc::now(); - - let res = - service.generate(&from_did, &illegal_keyring, &message, issuance_date).unwrap_err(); - - assert!(matches!(res, DIDVCServiceGenerateError::SignFailed(_))); - } - } + mod generate_failed {} mod verify_failed { use super::*; @@ -218,14 +148,17 @@ mod tests { let model = create_did_vc( &from_did, - &KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + &KeyPairing::create_keyring(OsRng), &json!({}), Utc::now(), ); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::DIDNotFound(_) = res { + if let DIDVCServiceVerifyError::PublicKeyNotFound(GetPublicKeyError::DidDocNotFound( + _, + )) = res + { } else { panic!("unexpected error: {:?}", res); } @@ -237,7 +170,7 @@ mod tests { let model = create_did_vc( &from_did, - &KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + &KeyPairing::create_keyring(OsRng), &json!({}), Utc::now(), ); @@ -259,7 +192,7 @@ mod tests { let mut model = create_did_vc( &from_did, - &KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + &KeyPairing::create_keyring(OsRng), &json!({}), Utc::now(), ); @@ -268,7 +201,7 @@ mod tests { let mock_repository = MockDidRepository::from_single(BTreeMap::from_iter([( from_did.clone(), - KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + KeyPairing::create_keyring(OsRng), )])); let service = DIDVCService::new(mock_repository); @@ -286,7 +219,7 @@ mod tests { let model = create_did_vc( &from_did, - &KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + &KeyPairing::create_keyring(OsRng), &json!({}), Utc::now(), ); @@ -296,7 +229,7 @@ mod tests { let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::PublicKeyLengthMismatch = res { + if let DIDVCServiceVerifyError::PublicKeyNotFound(_) = res { } else { panic!("unexpected error: {:?}", res); } @@ -308,20 +241,20 @@ mod tests { let model = create_did_vc( &from_did, - &KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + &KeyPairing::create_keyring(OsRng), &json!({}), Utc::now(), ); let mock_repository = MockDidRepository::from_single(BTreeMap::from_iter([( from_did.clone(), - KeyPairing::create_keyring(&OSRandomNumberGenerator::default()).unwrap(), + KeyPairing::create_keyring(OsRng), )])); let service = DIDVCService::new(mock_repository); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::SignatureNotVerified = res { + if let DIDVCServiceVerifyError::VerifyFailed(_) = res { } else { panic!("unexpected error: {:?}", res); } From ab355283f7ad4dc27484563b87f44caa67efc83f Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 13 Jul 2024 22:57:22 +0900 Subject: [PATCH 02/58] Clean up --- src/keyring/secp256k1.rs | 390 --------------------------------------- 1 file changed, 390 deletions(-) delete mode 100644 src/keyring/secp256k1.rs diff --git a/src/keyring/secp256k1.rs b/src/keyring/secp256k1.rs deleted file mode 100644 index 28624d5..0000000 --- a/src/keyring/secp256k1.rs +++ /dev/null @@ -1,390 +0,0 @@ -// use std::{cmp::Ordering, convert::TryFrom}; - -// use hex; -// use ibig::{ibig, IBig}; -// use serde::{Deserialize, Serialize}; -// use thiserror::Error; - -// use crate::{ -// common::runtime::{self, base64_url::PaddingType, bip32::BIP32Container}, -// did::sidetree::payload::PublicKeyPayload, -// }; - -// #[derive(Clone, Debug, Serialize, Deserialize)] -// pub struct KeyPairSecp256K1 { -// #[serde(rename = "kty")] -// pub kty: String, - -// #[serde(rename = "crv")] -// pub crv: String, - -// #[serde(rename = "x")] -// pub x: String, - -// #[serde(rename = "y")] -// pub y: String, - -// #[serde(rename = "d", skip_serializing_if = "Option::is_none")] -// pub d: Option, - -// #[serde(rename = "kid", skip_serializing_if = "Option::is_none")] -// pub kid: Option, -// } - -// #[derive(Clone, Debug, Serialize, Deserialize)] -// pub struct Secp256k1HexKeyPair { -// // MEMO: Matching schema in NodeX config. -// pub public_key: String, -// pub secret_key: String, -// } - -// pub(crate) const PRIVATE_KEY_SIZE: usize = 32; // Buffer(PrivateKey (32 = 256 -// bit)) pub(crate) const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33; // Buffer(0x04 -// + PublicKey (32 = 256 bit)) pub(crate) const UNCOMPRESSED_PUBLIC_KEY_SIZE: -// usize = 65; // Buffer(0x04 + PublicKey (64 = 512 bit)) - -// #[derive(Clone, Debug, PartialEq)] -// pub struct Secp256k1 { -// public: [u8; UNCOMPRESSED_PUBLIC_KEY_SIZE], -// private: [u8; PRIVATE_KEY_SIZE], -// } - -// impl TryFrom<&Secp256k1HexKeyPair> for Secp256k1 { -// type Error = Secp256k1Error; - -// fn try_from(value: &Secp256k1HexKeyPair) -> Result { -// Self::from_hex_key_pair(value) -// } -// } - -// impl From<&Secp256k1> for Secp256k1HexKeyPair { -// fn from(value: &Secp256k1) -> Self { -// value.to_hex_key_pair() -// } -// } - -// #[derive(Error, Debug)] -// pub enum Secp256k1Error { -// #[error("Invalid public key size")] -// InvalidSecretKeySize, -// #[error("Secp256k1 runtime error : {0:?}")] -// Secp256k1RuntimeError(#[from] runtime::secp256k1::Secp256k1Error), -// #[error("Invalid public key size")] -// InvalidPublicKeySize, -// #[error("Base64 decode error")] -// Base64UrlError(#[from] runtime::base64_url::Base64UrlError), -// #[error("Invalid first bytes")] -// InvalidFirstBytes, -// #[error("Number parsing error")] -// NumberParsingError(#[from] ibig::error::ParseError), -// #[error("Validation Failed")] -// ValidationFailed, -// #[error("Hex Decode failed")] -// HexDecodeFailed(#[from] hex::FromHexError), -// #[error(transparent)] -// Other(#[from] anyhow::Error), -// } - -// impl Secp256k1 { -// pub fn from_bip32(container: BIP32Container) -> Result { let public = container.public_key; -// let private = container.private_key; - -// let public = Secp256k1::transform_uncompressed_public_key(&public)?; - -// let public = <[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) -// .map_err(|_| anyhow::anyhow!("Conversion failed"))?; - -// Ok(Secp256k1 { public, private }) -// } - -// pub fn new(public: Vec, private: Vec) -> Result { if private.len() != PRIVATE_KEY_SIZE { -// return Err(Secp256k1Error::InvalidSecretKeySize); -// } - -// if public.len() == COMPRESSED_PUBLIC_KEY_SIZE { -// let public = -// Secp256k1::transform_uncompressed_public_key(&public)?; - -// let public = <[u8; -// UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) .map_err(|_| -// anyhow::anyhow!("Conversion failed"))?; let private = <[u8; -// PRIVATE_KEY_SIZE]>::try_from(private) .map_err(|_| -// anyhow::anyhow!("Conversion failed"))?; - -// if public[0] != 0x04 { -// Err(Secp256k1Error::InvalidFirstBytes) -// } else { -// Ok(Secp256k1 { public, private }) -// } -// } else if public.len() == UNCOMPRESSED_PUBLIC_KEY_SIZE { -// let public = <[u8; -// UNCOMPRESSED_PUBLIC_KEY_SIZE]>::try_from(public) .map_err(|_| -// anyhow::anyhow!("Conversion failed"))?; let private = <[u8; -// PRIVATE_KEY_SIZE]>::try_from(private) .map_err(|_| -// anyhow::anyhow!("Conversion failed"))?; - -// if public[0] != 0x04 { -// Err(Secp256k1Error::InvalidFirstBytes) -// } else { -// Ok(Secp256k1 { public, private }) -// } -// } else { -// Err(Secp256k1Error::InvalidPublicKeySize) -// } -// } - -// pub fn get_public_key(&self) -> Vec { -// self.public.to_vec() -// } - -// pub fn get_secret_key(&self) -> Vec { -// self.private.to_vec() -// } - -// pub fn to_hex_key_pair(&self) -> Secp256k1HexKeyPair { -// Secp256k1HexKeyPair { -// public_key: hex::encode(self.get_public_key()), -// secret_key: hex::encode(self.get_secret_key()), -// } -// } - -// pub fn from_hex_key_pair(hex_key_pair: &Secp256k1HexKeyPair) -> -// Result { let public = -// hex::decode(&hex_key_pair.public_key)?; let private = -// hex::decode(&hex_key_pair.secret_key)?; - -// Self::new(public, private) -// } - -// pub fn from_jwk(jwk: &KeyPairSecp256K1) -> Result { -// let d = jwk.d.clone().unwrap_or_else(|| { -// const EMPTY_PRIVATE_KEY: [u8; PRIVATE_KEY_SIZE] = [0; -// PRIVATE_KEY_SIZE]; -// runtime::base64_url::Base64Url::encode(&EMPTY_PRIVATE_KEY, -// &PaddingType::NoPadding) }); - -// let x = runtime::base64_url::Base64Url::decode_as_bytes(&jwk.x, -// &PaddingType::NoPadding)?; let y = -// runtime::base64_url::Base64Url::decode_as_bytes(&jwk.y, -// &PaddingType::NoPadding)?; - -// let public = [&[0x04], &x[..], &y[..]].concat(); -// let private = runtime::base64_url::Base64Url::decode_as_bytes(&d, -// &PaddingType::NoPadding)?; - -// Self::new(public, private) -// } - -// pub fn to_jwk(&self, included_private_key: bool) -> -// Result { let validated = -// self.validate_point()?; - -// if !validated { -// return Err(Secp256k1Error::ValidationFailed); -// } - -// let d = if included_private_key { -// Some(runtime::base64_url::Base64Url::encode( -// &self.get_secret_key(), -// &PaddingType::NoPadding, -// )) -// } else { -// None -// }; - -// Ok(KeyPairSecp256K1 { -// kty: "EC".to_string(), -// crv: "secp256k1".to_string(), -// x: runtime::base64_url::Base64Url::encode(self.get_point_x(), -// &PaddingType::NoPadding), y: -// runtime::base64_url::Base64Url::encode(self.get_point_y(), -// &PaddingType::NoPadding), d, -// kid: None, -// }) -// } - -// pub fn to_public_key( -// &self, -// key_id: &str, -// purpose: &[&str], -// ) -> Result { -// let validated = self.validate_point()?; - -// if !validated { -// return Err(Secp256k1Error::ValidationFailed); -// } - -// let jwk = self.to_jwk(false)?; - -// Ok(PublicKeyPayload { -// id: key_id.to_string(), -// r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), -// jwk, -// purpose: purpose.to_vec().iter().map(|value| -// value.to_string()).collect(), }) -// } - -// fn get_point_x(&self) -> &[u8] { -// &self.public[1..33] -// } - -// fn get_point_y(&self) -> &[u8] { -// &self.public[33..] -// } - -// pub fn validate_point(&self) -> Result { -// let x = self.get_point_x(); -// let y = self.get_point_y(); - -// let nx = IBig::from_str_radix(&hex::encode(x), 16)?; -// let ny = IBig::from_str_radix(&hex::encode(y), 16)?; -// let np = IBig::from_str_radix( -// -// "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", -// 16, -// )?; - -// let verified: IBig = (&ny * &ny - &nx * &nx * &nx - 7) % &np; - -// Ok(verified.cmp(&ibig!(0)) == Ordering::Equal) -// } - -// pub fn transform_uncompressed_public_key(compressed: &[u8]) -> -// Result, Secp256k1Error> { -// runtime::secp256k1::Secp256k1::convert_public_key(compressed, -// false).map_err(|e| e.into()) } -// } - -// #[cfg(test)] -// pub mod tests { - -// use super::*; - -// const PRIVATE_KEY: [u8; 32] = [ -// 0xc7, 0x39, 0x80, 0x5a, 0xb0, 0x3d, 0xa6, 0x2d, 0xdb, 0xe0, 0x33, -// 0x90, 0xac, 0xdf, 0x76, 0x15, 0x64, 0x0a, 0xa6, 0xed, 0x31, 0xb8, -// 0xf1, 0x82, 0x43, 0xf0, 0x4a, 0x57, 0x2c, 0x52, 0x8e, 0xdb, -// ]; - -// const PUBLIC_KEY: [u8; 33] = [ -// 0x02, 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, -// 0xcc, 0xea, 0x96, 0xa2, 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, 0x6f, -// 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, 0x60, 0x5c, 0x44, -// ]; - -// #[test] -// pub fn test_to_hex_key_pair() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let result = node.to_hex_key_pair(); - -// assert_eq!( -// result.secret_key, -// -// "c739805ab03da62ddbe03390acdf7615640aa6ed31b8f18243f04a572c528edb" ); -// assert_eq!( -// result.public_key, -// -// "0470964532f083f45fe8e8ccea96a22f6018d46a406f583ab226b19283aa605c44851b9274e6a2ce2ad42b4169e37df5f6cb38e81604b3ca2ebe11dd085862b490" -// ); -// } - -// #[test] -// pub fn test_get_point_x() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// assert_eq!( -// node.get_point_x(), -// &[ -// 0x70, 0x96, 0x45, 0x32, 0xf0, 0x83, 0xf4, 0x5f, 0xe8, 0xe8, -// 0xcc, 0xea, 0x96, 0xa2, 0x2f, 0x60, 0x18, 0xd4, 0x6a, 0x40, -// 0x6f, 0x58, 0x3a, 0xb2, 0x26, 0xb1, 0x92, 0x83, 0xaa, 0x60, -// 0x5c, 0x44, ] -// ) -// } - -// #[test] -// pub fn test_get_point_y() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// assert_eq!( -// node.get_point_y(), -// &[ -// 0x85, 0x1b, 0x92, 0x74, 0xe6, 0xa2, 0xce, 0x2a, 0xd4, 0x2b, -// 0x41, 0x69, 0xe3, 0x7d, 0xf5, 0xf6, 0xcb, 0x38, 0xe8, 0x16, -// 0x04, 0xb3, 0xca, 0x2e, 0xbe, 0x11, 0xdd, 0x08, 0x58, 0x62, -// 0xb4, 0x90, ] -// ) -// } - -// #[test] -// pub fn test_validate_point() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let result = node.validate_point().unwrap(); - -// assert!(result) -// } - -// #[test] -// pub fn test_to_jwk_with_private_key() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let result = node.to_jwk(true).unwrap(); - -// assert_eq!(result.kty, "EC"); -// assert_eq!(result.crv, "secp256k1"); -// assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); -// assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); -// assert_eq!(result.d, -// Some("xzmAWrA9pi3b4DOQrN92FWQKpu0xuPGCQ_BKVyxSjts".to_string())); -// assert_eq!(result.kid, None); -// } - -// #[test] -// pub fn test_to_jwk_without_private_key() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let result = node.to_jwk(false).unwrap(); - -// assert_eq!(result.kty, "EC"); -// assert_eq!(result.crv, "secp256k1"); -// assert_eq!(result.x, "cJZFMvCD9F_o6MzqlqIvYBjUakBvWDqyJrGSg6pgXEQ"); -// assert_eq!(result.y, "hRuSdOaizirUK0Fp43319ss46BYEs8ouvhHdCFhitJA"); -// assert_eq!(result.d, None); -// assert_eq!(result.kid, None); -// } - -// #[test] -// pub fn test_from_jwk_with_private_key() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let jwk = node.to_jwk(false).unwrap(); - -// let clone = Secp256k1::from_jwk(&jwk).unwrap(); - -// assert_eq!(clone.get_public_key(), node.get_public_key()); -// assert_eq!(clone.get_secret_key(), &[0; 32]); -// } - -// #[test] -// pub fn test_from_jwk_without_private_key() { -// let node = Secp256k1::new(PUBLIC_KEY.to_vec(), -// PRIVATE_KEY.to_vec()).unwrap(); - -// let jwk = node.to_jwk(true).unwrap(); - -// let clone = Secp256k1::from_jwk(&jwk).unwrap(); - -// assert_eq!(node, clone); -// } -// } From ac1585980660ae7e8bc8a27386458c259afb1c67 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 13 Jul 2024 22:59:36 +0900 Subject: [PATCH 03/58] Clean up --- src/keyring/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/keyring/mod.rs b/src/keyring/mod.rs index 542af99..d1fd1a0 100644 --- a/src/keyring/mod.rs +++ b/src/keyring/mod.rs @@ -1,4 +1,3 @@ pub mod extension; pub mod jwk; pub mod keypair; -pub mod secp256k1; From d7253602ee8d325c55c285bf9fa2ec8a03d4931b Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 14 Jul 2024 00:13:38 +0900 Subject: [PATCH 04/58] Add TODO comment --- src/common/cipher/jws.rs | 2 ++ src/verifiable_credentials/credential_signer.rs | 7 ------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/common/cipher/jws.rs b/src/common/cipher/jws.rs index cba5384..ba46c4f 100644 --- a/src/common/cipher/jws.rs +++ b/src/common/cipher/jws.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use thiserror::Error; +// TODO: Design the interface to have an implementation with accelerators. + #[derive(Debug, Serialize, Deserialize)] struct JWSHeader { alg: String, diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index d7766ec..6d57c44 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -1,5 +1,4 @@ use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; use serde_json::json; use thiserror::Error; @@ -10,12 +9,6 @@ use crate::{ verifiable_credentials::types::VerifiableCredentials, }; -#[derive(Debug, Serialize, Deserialize)] -pub struct ProofContext { - #[serde(rename = "proof")] - pub proof: Option, -} - pub struct CredentialSignerSuite<'a> { pub did: &'a str, pub key_id: &'a str, From 689d7aca52adae762e9713f7ad9ec14b6c950892 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 14 Jul 2024 12:33:09 +0900 Subject: [PATCH 05/58] Try another interface --- src/did/did_repository.rs | 4 +- src/didcomm/encrypted.rs | 142 ++++++++++++++++++--------- src/didcomm/types.rs | 10 +- src/verifiable_credentials/did_vc.rs | 65 +++++++----- 4 files changed, 141 insertions(+), 80 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 510b0c0..76f4206 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -38,8 +38,6 @@ pub enum FindIdentifierError { SidetreeRequestFailed(anyhow::Error), #[error("Failed to parse body: {0}")] BodyParseError(#[from] serde_json::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), } impl From for FindIdentifierError { @@ -63,7 +61,7 @@ pub enum GetPublicKeyError { } #[async_trait::async_trait] -pub trait DidRepository { +pub trait DidRepository: Sync { async fn create_identifier( &self, keyring: KeyPairing, diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 7ae2187..73dc5be 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -1,5 +1,3 @@ -use std::marker::Sync; - use chrono::{DateTime, Utc}; use cuid; use didcomm_rs::{crypto::CryptoAlgorithm, AttachmentBuilder, AttachmentDataBuilder, Message}; @@ -10,26 +8,83 @@ use crate::{ did::did_repository::{ CreateIdentifierError, DidRepository, FindIdentifierError, GetPublicKeyError, }, - didcomm::types::{DIDCommMessage, FindSenderError}, + didcomm::types::{DidCommMessage, FindSenderError}, keyring::keypair::{KeyPair, KeyPairing}, verifiable_credentials::{ - did_vc::{DIDVCService, DIDVCServiceGenerateError, DIDVCServiceVerifyError}, + did_vc::{ + DidVcService, DidVcServiceGenerateError, DidVcServiceImpl, DidVcServiceVerifyError, + }, types::{VerifiableCredentials, VerifiedContainer}, }, }; +#[async_trait::async_trait] +pub trait DidCommEncryptedService { + async fn generate( + &self, + from_did: &str, + to_did: &str, + from_keyring: &KeyPairing, + message: &Value, + metadata: Option<&Value>, + issuance_date: DateTime, + ) -> Result; + async fn verify( + &self, + my_keyring: &KeyPairing, + message: &DidCommMessage, + ) -> Result; +} + #[derive(Clone)] -pub struct DIDCommEncryptedService { - vc_service: DIDVCService, +pub struct DidCommEncryptedServiceImpl +where + R: DidRepository, + V: DidVcService, +{ + did_repository: R, + vc_service: V, attachment_link: String, } +impl DidCommEncryptedServiceImpl +where + R: DidRepository, + V: DidVcService, +{ + pub fn new_with_vc(did_repository: R, vc_service: V, attachment_link: Option) -> Self { + fn default_attachment_link() -> String { + std::env::var("NODEX_Did_ATTACHMENT_LINK") + .unwrap_or("https://did.getnodex.io".to_string()) + } + + DidCommEncryptedServiceImpl { + did_repository, + vc_service, + attachment_link: attachment_link.unwrap_or(default_attachment_link()), + } + } +} + +impl DidCommEncryptedServiceImpl> +where + R: DidRepository + Clone, +{ + pub fn new(did_repository: R, attachment_link: Option) -> Self { + Self::new_with_vc( + did_repository.clone(), + DidVcServiceImpl::new(did_repository), + attachment_link, + ) + } +} + #[derive(Debug, Error)] -pub enum DIDCommEncryptedServiceGenerateError { +pub enum DidCommEncryptedServiceGenerateError { #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("something went wrong with vc service")] - VCServiceError(#[from] DIDVCServiceGenerateError), + VCServiceError(#[from] DidVcServiceGenerateError), #[error("failed to create identifier")] SidetreeCreateRequestFailed(#[from] CreateIdentifierError), #[error("failed to encrypt message with error: {0}")] @@ -39,9 +94,9 @@ pub enum DIDCommEncryptedServiceGenerateError { } #[derive(Debug, Error)] -pub enum DIDCommEncryptedServiceVerifyError { +pub enum DidCommEncryptedServiceVerifyError { #[error("something went wrong with vc service")] - VCServiceError(#[from] DIDVCServiceVerifyError), + VCServiceError(#[from] DidVcServiceVerifyError), #[error("failed to find identifier")] SidetreeFindRequestFailed(#[from] FindIdentifierError), #[error("did public key not found. did: {0}")] @@ -56,20 +111,13 @@ pub enum DIDCommEncryptedServiceVerifyError { FindSenderError(#[from] FindSenderError), } -impl DIDCommEncryptedService { - pub fn new(did_repository: R, attachment_link: Option) -> DIDCommEncryptedService { - fn default_attachment_link() -> String { - std::env::var("NODEX_DID_ATTACHMENT_LINK") - .unwrap_or("https://did.getnodex.io".to_string()) - } - - DIDCommEncryptedService { - vc_service: DIDVCService::new(did_repository), - attachment_link: attachment_link.unwrap_or(default_attachment_link()), - } - } - - pub async fn generate( +#[async_trait::async_trait] +impl DidCommEncryptedService for DidCommEncryptedServiceImpl +where + R: DidRepository, + V: DidVcService, +{ + async fn generate( &self, from_did: &str, to_did: &str, @@ -77,7 +125,7 @@ impl DIDCommEncryptedService { message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - ) -> Result { + ) -> Result { // NOTE: message let body = self.vc_service.generate(from_did, from_keyring, message, issuance_date)?; let body = serde_json::to_string(&body)?; @@ -99,8 +147,7 @@ impl DIDCommEncryptedService { } // NOTE: recipient to - let public_key = - self.vc_service.did_repository.get_encrypt_key(to_did).await?.as_bytes().to_vec(); + let public_key = self.did_repository.get_encrypt_key(to_did).await?.as_bytes().to_vec(); let public_key = Some(public_key); let seal_message = message.as_jwe(&CryptoAlgorithm::XC20P, public_key.clone()).seal( @@ -108,17 +155,16 @@ impl DIDCommEncryptedService { Some(vec![public_key]), )?; - Ok(serde_json::from_str::(&seal_message)?) + Ok(serde_json::from_str::(&seal_message)?) } - pub async fn verify( + async fn verify( &self, my_keyring: &KeyPairing, - message: &DIDCommMessage, - ) -> Result { + message: &DidCommMessage, + ) -> Result { let other_did = message.find_sender()?; - let public_key = - self.vc_service.did_repository.get_encrypt_key(&other_did).await?.as_bytes().to_vec(); + let public_key = self.did_repository.get_encrypt_key(&other_did).await?.as_bytes().to_vec(); let public_key = Some(public_key); let message = Message::receive( @@ -135,7 +181,7 @@ impl DIDCommEncryptedService { let body = message .get_body() - .map_err(|e| DIDCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; + .map_err(|e| DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; let body = serde_json::from_str::(&body)?; let body = self.vc_service.verify(body).await?; @@ -145,7 +191,7 @@ impl DIDCommEncryptedService { .data .json .as_ref() - .ok_or(DIDCommEncryptedServiceVerifyError::MetadataBodyNotFound(None))?; + .ok_or(DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(None))?; let metadata = serde_json::from_str::(metadata)?; Ok(VerifiedContainer { message: body, metadata: Some(metadata) }) } @@ -180,7 +226,7 @@ mod tests { (to_did.clone(), to_keyring.clone()), ])); - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -213,7 +259,7 @@ mod tests { from_keyring.clone(), )])); - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -223,7 +269,7 @@ mod tests { .await .unwrap_err(); - if let DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound( + if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( GetPublicKeyError::DidDocNotFound(did), ) = res { @@ -242,7 +288,7 @@ mod tests { let repo = NoPublicKeyDidRepository; - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -252,7 +298,7 @@ mod tests { .await .unwrap_err(); - if let DIDCommEncryptedServiceGenerateError::DidPublicKeyNotFound( + if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), ) = res { @@ -275,13 +321,13 @@ mod tests { message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - ) -> DIDCommMessage { + ) -> DidCommMessage { let repo = MockDidRepository::from_single(BTreeMap::from_iter([( to_did.to_string(), to_keyring.clone(), )])); - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); service .generate(from_did, to_did, from_keyring, message, metadata, issuance_date) @@ -316,11 +362,11 @@ mod tests { to_keyring.clone(), )])); - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let res = service.verify(&from_keyring, &res).await.unwrap_err(); - if let DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound( + if let DidCommEncryptedServiceVerifyError::DidPublicKeyNotFound( GetPublicKeyError::DidDocNotFound(did), ) = res { @@ -360,11 +406,11 @@ mod tests { (other_did.clone(), other_keyring.clone()), ])); - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let res = service.verify(&other_keyring, &res).await.unwrap_err(); - if let DIDCommEncryptedServiceVerifyError::DecryptFailed(_) = res { + if let DidCommEncryptedServiceVerifyError::DecryptFailed(_) = res { } else { panic!("unexpected result: {:?}", res); } @@ -394,11 +440,11 @@ mod tests { let repo = NoPublicKeyDidRepository; - let service = DIDCommEncryptedService::new(repo, None); + let service = DidCommEncryptedServiceImpl::new(repo, None); let res = service.verify(&from_keyring, &res).await.unwrap_err(); - if let DIDCommEncryptedServiceVerifyError::DidPublicKeyNotFound( + if let DidCommEncryptedServiceVerifyError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), ) = res { diff --git a/src/didcomm/types.rs b/src/didcomm/types.rs index 1732c31..38f297b 100644 --- a/src/didcomm/types.rs +++ b/src/didcomm/types.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; #[derive(Deserialize, Serialize, Clone, Debug)] -pub struct DIDCommMessage { +pub struct DidCommMessage { pub ciphertext: String, pub iv: String, pub protected: String, @@ -46,7 +46,7 @@ pub enum FindSenderError { SkidError, } -impl DIDCommMessage { +impl DidCommMessage { pub fn find_receivers(&self) -> Vec { self.recipients.iter().map(|v| v.header.kid.clone()).collect() } @@ -80,7 +80,7 @@ mod tests { #[test] fn extract_from_did() { - let message: DIDCommMessage = serde_json::from_str(MESSAGE).unwrap(); + let message: DidCommMessage = serde_json::from_str(MESSAGE).unwrap(); let result = message.find_sender().unwrap(); assert_eq!(&result, FROM_DID); } @@ -88,14 +88,14 @@ mod tests { #[test] fn extract_from_did_when_invalid_base64() { let message = include_str!("../../test_resources/invalid_didcomm_message.json"); - let message: DIDCommMessage = serde_json::from_str(message).unwrap(); + let message: DidCommMessage = serde_json::from_str(message).unwrap(); let result = message.find_sender(); assert!(result.is_err()); } #[test] fn extract_to_did() { - let message: DIDCommMessage = serde_json::from_str(MESSAGE).unwrap(); + let message: DidCommMessage = serde_json::from_str(MESSAGE).unwrap(); let result = message.find_receivers(); let expected_did = vec![TO_DID.to_string()]; assert_eq!(result, expected_did); diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 3294528..96bfafb 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -1,5 +1,3 @@ -use std::marker::Sync; - use chrono::{DateTime, Utc}; use serde_json::Value; use thiserror::Error; @@ -16,36 +14,55 @@ use crate::{ }, }; -#[derive(Clone)] -pub struct DIDVCService { - pub(crate) did_repository: R, +#[async_trait::async_trait] +pub trait DidVcService: Sync { + fn generate( + &self, + from_did: &str, + from_keyring: &keypair::KeyPairing, + message: &Value, + issuance_date: DateTime, + ) -> Result; + async fn verify( + &self, + model: VerifiableCredentials, + ) -> Result; } #[derive(Debug, Error)] -pub enum DIDVCServiceGenerateError { +pub enum DidVcServiceGenerateError { #[error("credential signer error")] SignFailed(#[from] CredentialSignerSignError), } #[derive(Debug, Error)] -pub enum DIDVCServiceVerifyError { +pub enum DidVcServiceVerifyError { #[error("did public key not found. did: {0}")] PublicKeyNotFound(#[from] GetPublicKeyError), #[error("credential signer error")] VerifyFailed(#[from] CredentialSignerVerifyError), } -impl DIDVCService { +#[derive(Clone)] +pub struct DidVcServiceImpl { + pub(crate) did_repository: R, +} + +impl DidVcServiceImpl { pub fn new(did_repository: R) -> Self { Self { did_repository } } - pub fn generate( +} + +#[async_trait::async_trait] +impl DidVcService for DidVcServiceImpl { + fn generate( &self, from_did: &str, from_keyring: &keypair::KeyPairing, message: &Value, issuance_date: DateTime, - ) -> Result { + ) -> Result { let r#type = "VerifiableCredential".to_string(); let context = "https://www.w3.org/2018/credentials/v1".to_string(); @@ -73,10 +90,10 @@ impl DIDVCService { Ok(signed) } - pub async fn verify( + async fn verify( &self, model: VerifiableCredentials, - ) -> Result { + ) -> Result { let public_key = self.did_repository.get_sign_key(&model.issuer.id).await?; Ok(CredentialSigner::verify(model, &public_key)?) } @@ -106,7 +123,7 @@ mod tests { from_keyring.clone(), )])); - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -133,7 +150,7 @@ mod tests { message: &Value, issuance_date: DateTime, ) -> VerifiableCredentials { - let service = DIDVCService::new(MockDidRepository::from_single(BTreeMap::new())); + let service = DidVcServiceImpl::new(MockDidRepository::from_single(BTreeMap::new())); service.generate(from_did, from_keyring, message, issuance_date).unwrap() } @@ -144,7 +161,7 @@ mod tests { let mock_repository = MockDidRepository::from_single(BTreeMap::new()); - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let model = create_did_vc( &from_did, @@ -155,7 +172,7 @@ mod tests { let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::PublicKeyNotFound(GetPublicKeyError::DidDocNotFound( + if let DidVcServiceVerifyError::PublicKeyNotFound(GetPublicKeyError::DidDocNotFound( _, )) = res { @@ -176,11 +193,11 @@ mod tests { ); let mock_repository = NoPublicKeyDidRepository; - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::PublicKeyNotFound(_) = res { + if let DidVcServiceVerifyError::PublicKeyNotFound(_) = res { } else { panic!("unexpected error: {:?}", res); } @@ -203,11 +220,11 @@ mod tests { from_did.clone(), KeyPairing::create_keyring(OsRng), )])); - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::VerifyFailed(_) = res { + if let DidVcServiceVerifyError::VerifyFailed(_) = res { } else { panic!("unexpected error: {:?}", res); } @@ -225,11 +242,11 @@ mod tests { ); let mock_repository = IllegalPublicKeyLengthDidRepository; - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::PublicKeyNotFound(_) = res { + if let DidVcServiceVerifyError::PublicKeyNotFound(_) = res { } else { panic!("unexpected error: {:?}", res); } @@ -250,11 +267,11 @@ mod tests { from_did.clone(), KeyPairing::create_keyring(OsRng), )])); - let service = DIDVCService::new(mock_repository); + let service = DidVcServiceImpl::new(mock_repository); let res = service.verify(model).await.unwrap_err(); - if let DIDVCServiceVerifyError::VerifyFailed(_) = res { + if let DidVcServiceVerifyError::VerifyFailed(_) = res { } else { panic!("unexpected error: {:?}", res); } From ca3c77934a11c288b4f5b8163220fdb9000abe13 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 00:07:35 +0900 Subject: [PATCH 06/58] Change interface --- src/common/runtime/multihash.rs | 2 +- src/did/did_repository.rs | 171 +++++++------ src/did/sidetree/client.rs | 9 +- src/did/sidetree/payload.rs | 46 ++-- src/didcomm/encrypted.rs | 344 ++++++++++++++------------- src/verifiable_credentials/did_vc.rs | 61 ++--- 6 files changed, 328 insertions(+), 305 deletions(-) diff --git a/src/common/runtime/multihash.rs b/src/common/runtime/multihash.rs index 99c0d27..03409c3 100644 --- a/src/common/runtime/multihash.rs +++ b/src/common/runtime/multihash.rs @@ -48,7 +48,7 @@ mod tests { } #[test] - fn test_canonicalize_then_double_hash_then_encode() { + fn test_double_hash_then_encode() { let result = double_hash_encode(message().as_bytes()); assert_eq!(result, String::from("EiC_GPLc6eWMwiyIuGr1oEWiSqrTufglVlDGco8oaQL_nQ")); } diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 76f4206..88cfc64 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -3,55 +3,42 @@ use std::convert::TryInto; use http::StatusCode; use super::sidetree::{ - client::{HttpError, SidetreeHttpClient}, - payload::{did_create_payload, DIDReplacePayload, ToPublicKey}, -}; -use crate::{ - did::sidetree::payload::DIDResolutionResponse, - keyring::{ - jwk::Jwk, - keypair::{KeyPair, KeyPairing}, + client::SidetreeHttpClient, + payload::{ + did_create_payload, DidDocument, DidReplacePayload, DidResolutionResponse, ToPublicKey, }, }; +use crate::keyring::{ + jwk::Jwk, + keypair::{KeyPair, KeyPairing}, +}; #[derive(Debug, thiserror::Error)] -pub enum CreateIdentifierError { +pub enum CreateIdentifierError { #[error("Failed to convert to JWK")] JwkError, #[error("Failed to build operation payload: {0}")] - PayloadBuildFailed(#[from] crate::did::sidetree::payload::DIDCreatePayloadError), + PayloadBuildFailed(#[from] crate::did::sidetree::payload::DidCreatePayloadError), #[error("Failed to parse body: {0}")] BodyParseError(#[from] serde_json::Error), - #[error("Failed to send request to sidetree: {0}")] - SidetreeRequestFailed(anyhow::Error), -} - -impl From for CreateIdentifierError { - fn from(HttpError::Inner(e): HttpError) -> Self { - Self::SidetreeRequestFailed(e) - } + #[error("Failed to create identifier. response: {0}")] + SidetreeRequestFailed(String), + #[error("Failed to send request: {0}")] + SidetreeHttpClientError(StudioClientError), } #[derive(Debug, thiserror::Error)] -pub enum FindIdentifierError { +pub enum FindIdentifierError { #[error("Failed to send request to sidetree: {0}")] - SidetreeRequestFailed(anyhow::Error), + SidetreeRequestFailed(String), #[error("Failed to parse body: {0}")] BodyParseError(#[from] serde_json::Error), -} - -impl From for FindIdentifierError { - fn from(HttpError::Inner(e): HttpError) -> Self { - Self::SidetreeRequestFailed(e) - } + #[error("Failed to send request: {0}")] + SidetreeHttpClientError(StudioClientError), } #[derive(Debug, thiserror::Error)] pub enum GetPublicKeyError { - #[error("Failed to find indentifier: {0}")] - FindIdentifierError(#[from] FindIdentifierError), - #[error("Failed to get did document: {0}")] - DidDocNotFound(String), #[error("Failed to get public key")] PublicKeyNotFound(String), #[error("Failed to convert from JWK: {0}")] @@ -60,47 +47,40 @@ pub enum GetPublicKeyError { JwkToX25519Error(#[from] crate::keyring::jwk::JwkToX25519Error), } +fn get_key(key_type: &str, did_document: &DidDocument) -> Result { + let did = &did_document.id; + let public_key = did_document + .public_key + .clone() + .and_then(|pks| pks.into_iter().find(|pk| pk.id == key_type)) + .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; + Ok(public_key.public_key_jwk) +} + +pub fn get_sign_key(did_document: &DidDocument) -> Result { + let public_key = get_key("signingKey", &did_document)?; + Ok(public_key.try_into()?) +} + +pub fn get_encrypt_key( + did_document: &DidDocument, +) -> Result { + let public_key = get_key("encryptionKey", &did_document)?; + Ok(public_key.try_into()?) +} + #[async_trait::async_trait] pub trait DidRepository: Sync { + type CreateIdentifierError: std::error::Error; + type FindIdentifierError: std::error::Error; async fn create_identifier( &self, keyring: KeyPairing, - ) -> Result; + ) -> Result; async fn find_identifier( &self, did: &str, - ) -> Result, FindIdentifierError>; - async fn get_sign_key(&self, did: &str) -> Result { - let did_document = self.find_identifier(did).await?; - let public_keys = did_document - .ok_or(GetPublicKeyError::DidDocNotFound(did.to_string()))? - .did_document - .public_key - .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; - let public_key = public_keys - .iter() - .find(|pk| pk.id == "signingKey") - .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; - let public_key: k256::PublicKey = public_key.public_key_jwk.clone().try_into()?; - Ok(public_key) - } - async fn get_encrypt_key( - &self, - did: &str, - ) -> Result { - let did_document = self.find_identifier(did).await?; - let public_keys = did_document - .ok_or(GetPublicKeyError::DidDocNotFound(did.to_string()))? - .did_document - .public_key - .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; - let public_key = public_keys - .iter() - .find(|pk| pk.id == "encryptionKey") - .ok_or(GetPublicKeyError::PublicKeyNotFound(did.to_string()))?; - let public_key: x25519_dalek::PublicKey = public_key.public_key_jwk.clone().try_into()?; - Ok(public_key) - } + ) -> Result, Self::FindIdentifierError>; } pub struct DidRepositoryImpl { @@ -124,10 +104,12 @@ impl DidRepositoryImpl { #[async_trait::async_trait] impl DidRepository for DidRepositoryImpl { + type CreateIdentifierError = CreateIdentifierError; + type FindIdentifierError = FindIdentifierError; async fn create_identifier( &self, keyring: KeyPairing, - ) -> Result { + ) -> Result> { // https://w3c.github.io/did-spec-registries/#assertionmethod let sign = keyring .sign @@ -158,34 +140,36 @@ impl DidRepository for DidRepositoryImpl .try_into() .map_err(|_| CreateIdentifierError::JwkError)?; let document = - DIDReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; + DidReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; let payload = did_create_payload(document, &update, &recovery)?; - let response = self.client.post_create_identifier(&payload).await?; + let response = self + .client + .post_create_identifier(&payload) + .await + .map_err(CreateIdentifierError::SidetreeHttpClientError)?; if response.status_code.is_success() { let response = serde_json::from_str(&response.body)?; Ok(response) } else { - Err(CreateIdentifierError::SidetreeRequestFailed(anyhow::anyhow!( - "Failed to create identifier. response: {:?}", - response - ))) + Err(CreateIdentifierError::SidetreeRequestFailed(format!("{:?}", response))) } } async fn find_identifier( &self, did: &str, - ) -> Result, FindIdentifierError> { - let response = self.client.get_find_identifier(did).await?; + ) -> Result, FindIdentifierError> { + let response = self + .client + .get_find_identifier(did) + .await + .map_err(FindIdentifierError::SidetreeHttpClientError)?; match response.status_code { StatusCode::OK => Ok(Some(serde_json::from_str(&response.body)?)), StatusCode::NOT_FOUND => Ok(None), - _ => Err(FindIdentifierError::SidetreeRequestFailed(anyhow::anyhow!( - "Failed to find identifier. response: {:?}", - response - ))), + _ => Err(FindIdentifierError::SidetreeRequestFailed(format!("{:?}", response))), } } } @@ -196,7 +180,7 @@ pub mod mocks { use super::*; use crate::{ - did::sidetree::payload::{DIDDocument, DidPublicKey, MethodMetadata}, + did::sidetree::payload::{DidDocument, DidPublicKey, MethodMetadata}, keyring::keypair::KeyPairing, }; @@ -215,18 +199,23 @@ pub mod mocks { } } + #[derive(Debug, thiserror::Error)] + pub enum DummyError {} + #[async_trait::async_trait] impl DidRepository for MockDidRepository { + type CreateIdentifierError = CreateIdentifierError; + type FindIdentifierError = FindIdentifierError; async fn create_identifier( &self, _keyring: KeyPairing, - ) -> Result { + ) -> Result { unimplemented!() } async fn find_identifier( &self, did: &str, - ) -> Result, FindIdentifierError> { + ) -> Result, Self::FindIdentifierError> { if let Some(keyrings) = self.map.get(did) { let public_keys = keyrings .iter() @@ -252,9 +241,9 @@ pub mod mocks { }) .collect(); - let response = DIDResolutionResponse { + let response = DidResolutionResponse { context: "https://www.w3.org/ns/did-resolution/v1".to_string(), - did_document: DIDDocument { + did_document: DidDocument { id: did.to_string(), public_key: Some(public_keys), service: None, @@ -278,19 +267,21 @@ pub mod mocks { #[async_trait::async_trait] impl DidRepository for NoPublicKeyDidRepository { + type CreateIdentifierError = CreateIdentifierError; + type FindIdentifierError = FindIdentifierError; async fn create_identifier( &self, _keyring: KeyPairing, - ) -> Result { + ) -> Result { unimplemented!() } async fn find_identifier( &self, did: &str, - ) -> Result, FindIdentifierError> { - Ok(Some(DIDResolutionResponse { + ) -> Result, Self::FindIdentifierError> { + Ok(Some(DidResolutionResponse { context: "https://www.w3.org/ns/did-resolution/v1".to_string(), - did_document: DIDDocument { + did_document: DidDocument { id: did.to_string(), public_key: None, service: None, @@ -310,19 +301,21 @@ pub mod mocks { #[async_trait::async_trait] impl DidRepository for IllegalPublicKeyLengthDidRepository { + type CreateIdentifierError = CreateIdentifierError; + type FindIdentifierError = FindIdentifierError; async fn create_identifier( &self, _keyring: KeyPairing, - ) -> Result { + ) -> Result { unimplemented!() } async fn find_identifier( &self, did: &str, - ) -> Result, FindIdentifierError> { - Ok(Some(DIDResolutionResponse { + ) -> Result, Self::FindIdentifierError> { + Ok(Some(DidResolutionResponse { context: "https://www.w3.org/ns/did-resolution/v1".to_string(), - did_document: DIDDocument { + did_document: DidDocument { id: did.to_string(), public_key: Some(vec![]), service: None, diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index cbc30f7..e483e80 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -41,10 +41,13 @@ impl SidetreeHttpClientResponse { #[async_trait::async_trait] pub trait SidetreeHttpClient { + type Error: std::error::Error; async fn post_create_identifier( &self, body: &str, - ) -> Result; - async fn get_find_identifier(&self, did: &str) - -> Result; + ) -> Result; + async fn get_find_identifier( + &self, + did: &str, + ) -> Result; } diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 15f8022..03b2030 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -47,7 +47,7 @@ struct Authentication { } #[derive(Debug, Serialize, Deserialize)] -pub struct DIDDocument { +pub struct DidDocument { // TODO: impl parser for mixed type // #[serde(rename = "@context")] // context: String, @@ -106,7 +106,7 @@ where // ACTION: replace #[derive(Debug, Serialize, Deserialize)] -pub struct DIDReplacePayload { +pub struct DidReplacePayload { #[serde(rename = "public_keys")] pub public_keys: Vec, @@ -115,32 +115,32 @@ pub struct DIDReplacePayload { } #[derive(Debug, Serialize, Deserialize)] -struct DIDReplaceAction { +struct DidReplaceAction { action: String, // 'replace', - document: DIDReplacePayload, + document: DidReplacePayload, } #[derive(Serialize, Deserialize, Debug)] -struct DIDReplaceDeltaObject { - patches: Vec, +struct DidReplaceDeltaObject { + patches: Vec, update_commitment: String, } #[derive(Debug, Serialize, Deserialize)] -struct DIDReplaceSuffixObject { +struct DidReplaceSuffixObject { delta_hash: String, recovery_commitment: String, } // ACTION: ietf-json-patch #[allow(dead_code)] -struct DIDIetfJsonPatchAction { +struct DidIetfJsonPatchAction { action: String, /* 'replace', * patches: Vec<> */ } #[allow(dead_code)] -struct DIDResolutionRequest { +struct DidResolutionRequest { did: String, } @@ -157,12 +157,12 @@ pub struct MethodMetadata { } #[derive(Debug, Serialize, Deserialize)] -pub struct DIDResolutionResponse { +pub struct DidResolutionResponse { #[serde(rename = "@context")] pub context: String, #[serde(rename = "didDocument")] - pub did_document: DIDDocument, + pub did_document: DidDocument, #[serde(rename = "methodMetadata")] pub method_metadata: MethodMetadata, @@ -178,7 +178,7 @@ pub struct CommitmentKeys { } #[derive(Clone, Serialize, Deserialize)] -pub struct DIDCreateRequest { +pub struct DidCreateRequest { #[serde(rename = "publicKeys")] pub public_keys: Vec, @@ -190,26 +190,26 @@ pub struct DIDCreateRequest { } #[derive(Debug, Serialize, Deserialize)] -struct DIDCreatePayload { +struct DidCreatePayload { r#type: String, // 'create', delta: String, suffix_data: String, } #[derive(Debug, Serialize, Deserialize)] -pub struct DIDCreateResponse { +pub struct DidCreateResponse { #[serde(rename = "@context")] pub context: String, #[serde(rename = "didDocument")] - pub did_document: DIDDocument, + pub did_document: DidDocument, #[serde(rename = "methodMetadata")] pub method_metadata: MethodMetadata, } #[derive(Debug, Error)] -pub enum DIDCreatePayloadError { +pub enum DidCreatePayloadError { #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), } @@ -223,25 +223,25 @@ where } pub fn did_create_payload( - replace_payload: DIDReplacePayload, + replace_payload: DidReplacePayload, update_key: &Jwk, recovery_key: &Jwk, -) -> Result { +) -> Result { let update = canon(update_key)?; let update_commitment = multihash::double_hash_encode(&update); let recovery = canon(recovery_key)?; let recovery_commitment = multihash::double_hash_encode(&recovery); - let patch = DIDReplaceAction { action: "replace".to_string(), document: replace_payload }; - let delta = DIDReplaceDeltaObject { patches: vec![patch], update_commitment }; + let patch = DidReplaceAction { action: "replace".to_string(), document: replace_payload }; + let delta = DidReplaceDeltaObject { patches: vec![patch], update_commitment }; let delta = canon(&delta)?; let delta_hash = multihash::hash_encode(&delta); - let suffix = DIDReplaceSuffixObject { delta_hash, recovery_commitment }; + let suffix = DidReplaceSuffixObject { delta_hash, recovery_commitment }; let suffix = canon(&suffix)?; let encoded_delta = BASE64URL_NOPAD.encode(&delta); let encoded_suffix = BASE64URL_NOPAD.encode(&suffix); - let payload = DIDCreatePayload { + let payload = DidCreatePayload { r#type: "create".to_string(), delta: encoded_delta, suffix_data: encoded_suffix, @@ -268,7 +268,7 @@ pub mod tests { let update: Jwk = keyring.recovery.get_public_key().try_into().unwrap(); let recovery: Jwk = keyring.update.get_public_key().try_into().unwrap(); - let document = DIDReplacePayload { public_keys: vec![public], service_endpoints: vec![] }; + let document = DidReplacePayload { public_keys: vec![public], service_endpoints: vec![] }; let _result = did_create_payload(document, &update, &recovery).unwrap(); } diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 73dc5be..084a671 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -5,21 +5,23 @@ use serde_json::Value; use thiserror::Error; use crate::{ - did::did_repository::{ - CreateIdentifierError, DidRepository, FindIdentifierError, GetPublicKeyError, + did::{ + did_repository::{get_encrypt_key, get_sign_key, DidRepository, GetPublicKeyError}, + sidetree::payload::DidDocument, }, didcomm::types::{DidCommMessage, FindSenderError}, keyring::keypair::{KeyPair, KeyPairing}, verifiable_credentials::{ - did_vc::{ - DidVcService, DidVcServiceGenerateError, DidVcServiceImpl, DidVcServiceVerifyError, - }, + credential_signer::{CredentialSigner, CredentialSignerVerifyError}, + did_vc::DidVcService, types::{VerifiableCredentials, VerifiedContainer}, }, }; #[async_trait::async_trait] -pub trait DidCommEncryptedService { +pub trait DidCommEncryptedService: Sync { + type GenerateError: std::error::Error; + type VerifyError: std::error::Error; async fn generate( &self, from_did: &str, @@ -28,65 +30,110 @@ pub trait DidCommEncryptedService { message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - ) -> Result; + attachment_link: &str, + ) -> Result; async fn verify( &self, my_keyring: &KeyPairing, message: &DidCommMessage, - ) -> Result; + ) -> Result; } -#[derive(Clone)] -pub struct DidCommEncryptedServiceImpl -where - R: DidRepository, - V: DidVcService, -{ - did_repository: R, - vc_service: V, - attachment_link: String, -} +fn generate( + from_did: &str, + to_doc: &DidDocument, + from_keyring: &KeyPairing, + metadata: Option<&Value>, + body: &VerifiableCredentials, + attachment_link: &str, +) -> Result< + DidCommMessage, + DidCommEncryptedServiceGenerateError, +> { + let to_did = &to_doc.id; + // NOTE: message + let body = serde_json::to_string(body)?; + + let mut message = Message::new().from(from_did).to(&[to_did]).body(&body)?; + + // NOTE: Has attachment + if let Some(value) = metadata { + let id = cuid::cuid2(); + + // let media_type = "application/json"; + let data = + AttachmentDataBuilder::new().with_link(attachment_link).with_json(&value.to_string()); + + message.append_attachment( + AttachmentBuilder::new(true).with_id(&id).with_format("metadata").with_data(data), + ) + } -impl DidCommEncryptedServiceImpl -where - R: DidRepository, - V: DidVcService, -{ - pub fn new_with_vc(did_repository: R, vc_service: V, attachment_link: Option) -> Self { - fn default_attachment_link() -> String { - std::env::var("NODEX_Did_ATTACHMENT_LINK") - .unwrap_or("https://did.getnodex.io".to_string()) - } + // NOTE: recipient to + let public_key = get_encrypt_key(to_doc)?.as_bytes().to_vec(); + let public_key = Some(public_key); - DidCommEncryptedServiceImpl { - did_repository, - vc_service, - attachment_link: attachment_link.unwrap_or(default_attachment_link()), - } - } + let seal_message = message + .as_jwe(&CryptoAlgorithm::XC20P, public_key.clone()) + .seal(&from_keyring.encrypt.get_secret_key().as_bytes().to_vec(), Some(vec![public_key]))?; + + Ok(serde_json::from_str::(&seal_message)?) } -impl DidCommEncryptedServiceImpl> -where - R: DidRepository + Clone, -{ - pub fn new(did_repository: R, attachment_link: Option) -> Self { - Self::new_with_vc( - did_repository.clone(), - DidVcServiceImpl::new(did_repository), - attachment_link, - ) +fn verify( + from_doc: &DidDocument, + my_keyring: &KeyPairing, + message: &DidCommMessage, +) -> Result> { + let public_key = get_encrypt_key(from_doc)?.as_bytes().to_vec(); + + let public_key = Some(public_key); + + let message = Message::receive( + &serde_json::to_string(&message)?, + Some(&my_keyring.encrypt.get_secret_key().as_bytes().to_vec()), + public_key, + None, + )?; + + let metadata = message.attachment_iter().find(|item| match &item.format { + Some(value) => value == "metadata", + None => false, + }); + + let body = message + .get_body() + .map_err(|e| DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; + let body = serde_json::from_str::(&body)?; + // let body = did_vc::verify(from_doc, body)?; + + match metadata { + Some(metadata) => { + let metadata = metadata + .data + .json + .as_ref() + .ok_or(DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(None))?; + let metadata = serde_json::from_str::(metadata)?; + Ok(VerifiedContainer { message: body, metadata: Some(metadata) }) + } + None => Ok(VerifiedContainer { message: body, metadata: None }), } } #[derive(Debug, Error)] -pub enum DidCommEncryptedServiceGenerateError { +pub enum DidCommEncryptedServiceGenerateError< + FindIdentifierError: std::error::Error, + DidVcServiceGenerateError: std::error::Error, +> { + #[error("failed to get did document: {0}")] + DidDocNotFound(String), #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("something went wrong with vc service")] - VCServiceError(#[from] DidVcServiceGenerateError), + VCServiceError(DidVcServiceGenerateError), #[error("failed to create identifier")] - SidetreeCreateRequestFailed(#[from] CreateIdentifierError), + SidetreeFindRequestFailed(FindIdentifierError), #[error("failed to encrypt message with error: {0}")] EncryptFailed(#[from] didcomm_rs::Error), #[error("failed serialize/deserialize : {0}")] @@ -94,11 +141,13 @@ pub enum DidCommEncryptedServiceGenerateError { } #[derive(Debug, Error)] -pub enum DidCommEncryptedServiceVerifyError { +pub enum DidCommEncryptedServiceVerifyError { + #[error("failed to get did document: {0}")] + DidDocNotFound(String), #[error("something went wrong with vc service")] - VCServiceError(#[from] DidVcServiceVerifyError), + VCServiceError(#[from] CredentialSignerVerifyError), #[error("failed to find identifier")] - SidetreeFindRequestFailed(#[from] FindIdentifierError), + SidetreeFindRequestFailed(FindIdentifierError), #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("failed to decrypt message : {0}")] @@ -112,11 +161,13 @@ pub enum DidCommEncryptedServiceVerifyError { } #[async_trait::async_trait] -impl DidCommEncryptedService for DidCommEncryptedServiceImpl +impl DidCommEncryptedService for R where - R: DidRepository, - V: DidVcService, + R: DidRepository + DidVcService, { + type GenerateError = + DidCommEncryptedServiceGenerateError; + type VerifyError = DidCommEncryptedServiceVerifyError; async fn generate( &self, from_did: &str, @@ -125,78 +176,39 @@ where message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - ) -> Result { + attachment_link: &str, + ) -> Result { // NOTE: message - let body = self.vc_service.generate(from_did, from_keyring, message, issuance_date)?; - let body = serde_json::to_string(&body)?; - - let mut message = Message::new().from(from_did).to(&[to_did]).body(&body)?; - - // NOTE: Has attachment - if let Some(value) = metadata { - let id = cuid::cuid2(); - - // let media_type = "application/json"; - let data = AttachmentDataBuilder::new() - .with_link(&self.attachment_link) - .with_json(&value.to_string()); - - message.append_attachment( - AttachmentBuilder::new(true).with_id(&id).with_format("metadata").with_data(data), - ) - } - - // NOTE: recipient to - let public_key = self.did_repository.get_encrypt_key(to_did).await?.as_bytes().to_vec(); - let public_key = Some(public_key); - - let seal_message = message.as_jwe(&CryptoAlgorithm::XC20P, public_key.clone()).seal( - &from_keyring.encrypt.get_secret_key().as_bytes().to_vec(), - Some(vec![public_key]), - )?; + let body = DidVcService::generate(self, from_did, from_keyring, message, issuance_date) + .map_err(Self::GenerateError::VCServiceError)?; + let to_doc = self + .find_identifier(to_did) + .await + .map_err(Self::GenerateError::SidetreeFindRequestFailed)? + .ok_or(Self::GenerateError::DidDocNotFound(to_did.to_string()))? + .did_document; - Ok(serde_json::from_str::(&seal_message)?) + Ok(generate::(from_did, &to_doc, from_keyring, metadata, &body, attachment_link)?) } async fn verify( &self, my_keyring: &KeyPairing, message: &DidCommMessage, - ) -> Result { + ) -> Result { let other_did = message.find_sender()?; - let public_key = self.did_repository.get_encrypt_key(&other_did).await?.as_bytes().to_vec(); - let public_key = Some(public_key); - - let message = Message::receive( - &serde_json::to_string(&message)?, - Some(&my_keyring.encrypt.get_secret_key().as_bytes().to_vec()), - public_key, - None, - )?; - - let metadata = message.attachment_iter().find(|item| match item.format.clone() { - Some(value) => value == "metadata", - None => false, - }); - - let body = message - .get_body() - .map_err(|e| DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; - let body = serde_json::from_str::(&body)?; - let body = self.vc_service.verify(body).await?; - - match metadata { - Some(metadata) => { - let metadata = metadata - .data - .json - .as_ref() - .ok_or(DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(None))?; - let metadata = serde_json::from_str::(metadata)?; - Ok(VerifiedContainer { message: body, metadata: Some(metadata) }) - } - None => Ok(VerifiedContainer { message: body, metadata: None }), - } + let other_doc = self + .find_identifier(&other_did) + .await + .map_err(Self::VerifyError::SidetreeFindRequestFailed)? + .ok_or(Self::VerifyError::DidDocNotFound(other_did))? + .did_document; + let mut container = verify::(&other_doc, my_keyring, message)?; + // For performance, call low level api + let public_key = get_sign_key(&other_doc)?; + let body = CredentialSigner::verify(container.message, &public_key)?; + container.message = body; + Ok(container) } } @@ -226,17 +238,23 @@ mod tests { (to_did.clone(), to_keyring.clone()), ])); - let service = DidCommEncryptedServiceImpl::new(repo, None); - let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = service - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap(); + let res = DidCommEncryptedService::generate( + &repo, + &from_did, + &to_did, + &from_keyring, + &message, + None, + issuance_date, + "", + ) + .await + .unwrap(); - let verified = service.verify(&to_keyring, &res).await.unwrap(); + let verified = DidCommEncryptedService::verify(&repo, &to_keyring, &res).await.unwrap(); let verified = verified.message; assert_eq!(verified.issuer.id, from_did); @@ -259,20 +277,23 @@ mod tests { from_keyring.clone(), )])); - let service = DidCommEncryptedServiceImpl::new(repo, None); - let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = service - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap_err(); + let res = DidCommEncryptedService::generate( + &repo, + &from_did, + &to_did, + &from_keyring, + &message, + None, + issuance_date, + "", + ) + .await + .unwrap_err(); - if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( - GetPublicKeyError::DidDocNotFound(did), - ) = res - { + if let DidCommEncryptedServiceGenerateError::DidDocNotFound(did) = res { assert_eq!(did, to_did); } else { panic!("unexpected result: {:?}", res); @@ -288,15 +309,21 @@ mod tests { let repo = NoPublicKeyDidRepository; - let service = DidCommEncryptedServiceImpl::new(repo, None); - let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = service - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap_err(); + let res = DidCommEncryptedService::generate( + &repo, + &from_did, + &to_did, + &from_keyring, + &message, + None, + issuance_date, + "", + ) + .await + .unwrap_err(); if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), @@ -327,12 +354,18 @@ mod tests { to_keyring.clone(), )])); - let service = DidCommEncryptedServiceImpl::new(repo, None); - - service - .generate(from_did, to_did, from_keyring, message, metadata, issuance_date) - .await - .unwrap() + DidCommEncryptedService::generate( + &repo, + &from_did, + &to_did, + &from_keyring, + &message, + metadata, + issuance_date, + "", + ) + .await + .unwrap() } #[actix_rt::test] @@ -361,15 +394,10 @@ mod tests { to_did.clone(), to_keyring.clone(), )])); + let res = + DidCommEncryptedService::verify(&repo, &from_keyring, &res).await.unwrap_err(); - let service = DidCommEncryptedServiceImpl::new(repo, None); - - let res = service.verify(&from_keyring, &res).await.unwrap_err(); - - if let DidCommEncryptedServiceVerifyError::DidPublicKeyNotFound( - GetPublicKeyError::DidDocNotFound(did), - ) = res - { + if let DidCommEncryptedServiceVerifyError::DidDocNotFound(did) = res { assert_eq!(did, from_did); } else { panic!("unexpected result: {:?}", res); @@ -406,9 +434,8 @@ mod tests { (other_did.clone(), other_keyring.clone()), ])); - let service = DidCommEncryptedServiceImpl::new(repo, None); - - let res = service.verify(&other_keyring, &res).await.unwrap_err(); + let res = + DidCommEncryptedService::verify(&repo, &other_keyring, &res).await.unwrap_err(); if let DidCommEncryptedServiceVerifyError::DecryptFailed(_) = res { } else { @@ -440,9 +467,8 @@ mod tests { let repo = NoPublicKeyDidRepository; - let service = DidCommEncryptedServiceImpl::new(repo, None); - - let res = service.verify(&from_keyring, &res).await.unwrap_err(); + let res = + DidCommEncryptedService::verify(&repo, &from_keyring, &res).await.unwrap_err(); if let DidCommEncryptedServiceVerifyError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 96bfafb..1914b0b 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -3,7 +3,7 @@ use serde_json::Value; use thiserror::Error; use crate::{ - did::did_repository::{DidRepository, GetPublicKeyError}, + did::did_repository::{get_sign_key, DidRepository, GetPublicKeyError}, keyring::keypair, verifiable_credentials::{ credential_signer::{ @@ -16,17 +16,19 @@ use crate::{ #[async_trait::async_trait] pub trait DidVcService: Sync { + type GenerateError: std::error::Error; + type VerifyError: std::error::Error; fn generate( &self, from_did: &str, from_keyring: &keypair::KeyPairing, message: &Value, issuance_date: DateTime, - ) -> Result; + ) -> Result; async fn verify( &self, model: VerifiableCredentials, - ) -> Result; + ) -> Result; } #[derive(Debug, Error)] @@ -36,33 +38,28 @@ pub enum DidVcServiceGenerateError { } #[derive(Debug, Error)] -pub enum DidVcServiceVerifyError { +pub enum DidVcServiceVerifyError { #[error("did public key not found. did: {0}")] PublicKeyNotFound(#[from] GetPublicKeyError), + #[error("failed to get did document: {0}")] + DidDocNotFound(String), + #[error("failed to find indentifier: {0}")] + FindIdentifierError(FindIdentifierError), #[error("credential signer error")] VerifyFailed(#[from] CredentialSignerVerifyError), } -#[derive(Clone)] -pub struct DidVcServiceImpl { - pub(crate) did_repository: R, -} - -impl DidVcServiceImpl { - pub fn new(did_repository: R) -> Self { - Self { did_repository } - } -} - #[async_trait::async_trait] -impl DidVcService for DidVcServiceImpl { +impl DidVcService for R { + type GenerateError = DidVcServiceGenerateError; + type VerifyError = DidVcServiceVerifyError; fn generate( &self, from_did: &str, from_keyring: &keypair::KeyPairing, message: &Value, issuance_date: DateTime, - ) -> Result { + ) -> Result { let r#type = "VerifiableCredential".to_string(); let context = "https://www.w3.org/2018/credentials/v1".to_string(); @@ -93,8 +90,15 @@ impl DidVcService for DidVcServiceImpl { async fn verify( &self, model: VerifiableCredentials, - ) -> Result { - let public_key = self.did_repository.get_sign_key(&model.issuer.id).await?; + ) -> Result { + let did_document = self + .find_identifier(&model.issuer.id) + .await + .map_err(Self::VerifyError::FindIdentifierError)?; + let did_document = did_document + .ok_or(DidVcServiceVerifyError::DidDocNotFound(model.issuer.id.clone()))? + .did_document; + let public_key = get_sign_key(&did_document)?; Ok(CredentialSigner::verify(model, &public_key)?) } } @@ -123,7 +127,7 @@ mod tests { from_keyring.clone(), )])); - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -150,7 +154,7 @@ mod tests { message: &Value, issuance_date: DateTime, ) -> VerifiableCredentials { - let service = DidVcServiceImpl::new(MockDidRepository::from_single(BTreeMap::new())); + let service = MockDidRepository::from_single(BTreeMap::new()); service.generate(from_did, from_keyring, message, issuance_date).unwrap() } @@ -161,7 +165,7 @@ mod tests { let mock_repository = MockDidRepository::from_single(BTreeMap::new()); - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let model = create_did_vc( &from_did, @@ -172,10 +176,7 @@ mod tests { let res = service.verify(model).await.unwrap_err(); - if let DidVcServiceVerifyError::PublicKeyNotFound(GetPublicKeyError::DidDocNotFound( - _, - )) = res - { + if let DidVcServiceVerifyError::DidDocNotFound(_) = res { } else { panic!("unexpected error: {:?}", res); } @@ -193,7 +194,7 @@ mod tests { ); let mock_repository = NoPublicKeyDidRepository; - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let res = service.verify(model).await.unwrap_err(); @@ -220,7 +221,7 @@ mod tests { from_did.clone(), KeyPairing::create_keyring(OsRng), )])); - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let res = service.verify(model).await.unwrap_err(); @@ -242,7 +243,7 @@ mod tests { ); let mock_repository = IllegalPublicKeyLengthDidRepository; - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let res = service.verify(model).await.unwrap_err(); @@ -267,7 +268,7 @@ mod tests { from_did.clone(), KeyPairing::create_keyring(OsRng), )])); - let service = DidVcServiceImpl::new(mock_repository); + let service = mock_repository; let res = service.verify(model).await.unwrap_err(); From 42bad2b0e0a670625b1ff0510c16e98cad3feafb Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 00:17:07 +0900 Subject: [PATCH 07/58] Get rid of anyhow --- Cargo.lock | 117 +------------------------------------ Cargo.toml | 1 - src/did/sidetree/client.rs | 16 ----- 3 files changed, 3 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cad5220..931201c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,12 +97,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anyhow" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" - [[package]] name = "arrayref" version = "0.3.7" @@ -817,19 +811,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "hdwallet" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7" -dependencies = [ - "lazy_static", - "rand_core 0.6.4", - "ring", - "secp256k1", - "thiserror", -] - [[package]] name = "hermit-abi" version = "0.1.19" @@ -927,18 +908,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ibig" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1fcc7f316b2c079dde77564a1360639c1a956a23fa96122732e416cb10717bb" -dependencies = [ - "cfg-if", - "num-traits", - "rand 0.8.5", - "static_assertions", -] - [[package]] name = "itoa" version = "1.0.10" @@ -990,12 +959,6 @@ dependencies = [ "cpufeatures 0.2.12", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libaes" version = "0.6.5" @@ -1008,16 +971,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets 0.52.4", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -1068,26 +1021,23 @@ dependencies = [ [[package]] name = "nodex-didcomm" -version = "0.1.0" +version = "0.1.2" dependencies = [ "actix-rt", - "anyhow", "arrayref", "async-trait", "chrono", "cuid", "data-encoding", "didcomm-rs", - "getrandom 0.2.12", - "hdwallet", + "elliptic-curve 0.13.8", "hex", "hmac 0.12.1", "http", - "ibig", "k256 0.13.3", - "libloading", "log", "rand 0.8.5", + "rand_core 0.6.4", "serde", "serde_jcs", "serde_json", @@ -1436,21 +1386,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1499,24 +1434,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - [[package]] name = "semver" version = "1.0.22" @@ -1666,12 +1583,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spki" version = "0.4.1" @@ -1691,12 +1602,6 @@ dependencies = [ "der 0.7.8", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "subtle" version = "2.4.1" @@ -1810,12 +1715,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "uuid" version = "1.8.0" @@ -1897,16 +1796,6 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 1b01f1c..6ed1ac1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ version = "0.1.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.82" arrayref = { version = "0.3.7" } async-trait = "0.1.79" chrono = { version = "0.4" } diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index e483e80..94a95ef 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -1,21 +1,5 @@ use http::StatusCode; -// This type isn't implement std::error::Error because of conflicting -// implementations -#[derive(Debug)] -pub enum HttpError { - Inner(anyhow::Error), -} - -impl From for HttpError -where - E: std::error::Error + Send + Sync + 'static, -{ - fn from(e: E) -> Self { - Self::Inner(anyhow::Error::new(e)) - } -} - #[derive(Clone, Debug)] pub struct SidetreeHttpClientResponse { pub(crate) status_code: StatusCode, From 492b63bdfbe626936b105320be3250ed2aa443c2 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 08:27:42 +0900 Subject: [PATCH 08/58] Fix error type --- src/keyring/keypair.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 57f7524..7d58e0c 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -22,10 +22,11 @@ pub enum KeyPairingError { } pub trait KeyPair: Sized { + type Error: std::error::Error; fn get_secret_key(&self) -> S; fn get_public_key(&self) -> P; fn to_hex_key_pair(&self) -> HexKeyPair; - fn from_hex_key_pair(kp: &HexKeyPair) -> Result; + fn from_hex_key_pair(kp: &HexKeyPair) -> Result; } #[derive(Clone)] @@ -42,6 +43,7 @@ impl K256KeyPair { } impl KeyPair for K256KeyPair { + type Error = KeyPairingError; fn get_secret_key(&self) -> k256::SecretKey { self.secret_key.clone() } @@ -80,6 +82,7 @@ impl X25519KeyPair { } impl KeyPair for X25519KeyPair { + type Error = KeyPairingError; fn get_secret_key(&self) -> x25519_dalek::StaticSecret { self.secret_key.clone() } From 647953e3ea1d44d824dc1aefcea6d705fa542876 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 09:02:38 +0900 Subject: [PATCH 09/58] Change minor code style --- src/didcomm/encrypted.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 084a671..11ee6bf 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -122,10 +122,11 @@ fn verify( } #[derive(Debug, Error)] -pub enum DidCommEncryptedServiceGenerateError< +pub enum DidCommEncryptedServiceGenerateError +where FindIdentifierError: std::error::Error, DidVcServiceGenerateError: std::error::Error, -> { +{ #[error("failed to get did document: {0}")] DidDocNotFound(String), #[error("did public key not found. did: {0}")] From dc9376a69b6aee37d896b3dacbd9ea11202ef3ed Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 10:41:20 +0900 Subject: [PATCH 10/58] Update didcomm-rs version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6ed1ac1..a5c0ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ async-trait = "0.1.79" chrono = { version = "0.4" } cuid = { version = "1.3.2" } data-encoding = { version = "2.5.0" } -didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.0", default-features = false, features = ["raw-crypto"] } +didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } hex = { version = "0.4.3" } hmac = { version = "0.12.1" } http = { version = "1.1.0" } From 474bef21acacaa52a2e0b042512e76e75ac8fcbb Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 10:43:49 +0900 Subject: [PATCH 11/58] Refactor sidetree client --- src/did/sidetree/client.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index 94a95ef..667a88d 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -6,20 +6,9 @@ pub struct SidetreeHttpClientResponse { pub(crate) body: String, } -#[derive(Debug, thiserror::Error)] -pub enum SidetreeResponseInitializationError { - #[error("Invalid status code: {0}")] - InvalidStatusCode(u16), -} - impl SidetreeHttpClientResponse { - pub fn new( - status_code: u16, - body: String, - ) -> Result { - let status_code = StatusCode::from_u16(status_code) - .map_err(|_| SidetreeResponseInitializationError::InvalidStatusCode(status_code))?; - Ok(Self { status_code, body }) + pub fn new(status_code: StatusCode, body: String) -> Self { + Self { status_code, body } } } From ca8e4adcd637dfe8775e009d13b0127ceccd6da0 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 16:08:24 +0900 Subject: [PATCH 12/58] Add Sync and Send bound --- src/did/did_repository.rs | 4 ++-- src/did/sidetree/client.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 88cfc64..7e837a7 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -71,8 +71,8 @@ pub fn get_encrypt_key( #[async_trait::async_trait] pub trait DidRepository: Sync { - type CreateIdentifierError: std::error::Error; - type FindIdentifierError: std::error::Error; + type CreateIdentifierError: std::error::Error + Send + Sync; + type FindIdentifierError: std::error::Error + Send + Sync; async fn create_identifier( &self, keyring: KeyPairing, diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index 667a88d..cdf17a0 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -14,7 +14,7 @@ impl SidetreeHttpClientResponse { #[async_trait::async_trait] pub trait SidetreeHttpClient { - type Error: std::error::Error; + type Error: std::error::Error + Send + Sync; async fn post_create_identifier( &self, body: &str, From e2b45f179d050788bda3dd148a6286c420380552 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 16:08:40 +0900 Subject: [PATCH 13/58] Refactor minor --- src/keyring/keypair.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 7d58e0c..35b69d2 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; #[derive(Clone, Serialize, Deserialize)] -pub struct HexKeyPair { +pub struct KeyPairHex { // MEMO: Matching schema in NodeX config. public_key: String, secret_key: String, @@ -25,8 +25,8 @@ pub trait KeyPair: Sized { type Error: std::error::Error; fn get_secret_key(&self) -> S; fn get_public_key(&self) -> P; - fn to_hex_key_pair(&self) -> HexKeyPair; - fn from_hex_key_pair(kp: &HexKeyPair) -> Result; + fn to_hex_key_pair(&self) -> KeyPairHex; + fn from_hex_key_pair(kp: &KeyPairHex) -> Result; } #[derive(Clone)] @@ -50,14 +50,14 @@ impl KeyPair for K256KeyPair { fn get_public_key(&self) -> k256::PublicKey { self.public_key.clone() } - fn to_hex_key_pair(&self) -> HexKeyPair { + fn to_hex_key_pair(&self) -> KeyPairHex { let sk = self.secret_key.to_bytes(); let secret_key = hex::encode(&sk); let pk = self.public_key.to_encoded_point(false); let public_key = hex::encode(pk.as_bytes()); - HexKeyPair { secret_key, public_key } + KeyPairHex { secret_key, public_key } } - fn from_hex_key_pair(kp: &HexKeyPair) -> Result { + fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; let secret_key = k256::SecretKey::from_slice(&secret_key).map_err(|_| KeyPairingError::CryptError)?; @@ -89,14 +89,14 @@ impl KeyPair for X25519KeyP fn get_public_key(&self) -> x25519_dalek::PublicKey { self.public_key.clone() } - fn to_hex_key_pair(&self) -> HexKeyPair { + fn to_hex_key_pair(&self) -> KeyPairHex { let sk = self.secret_key.as_bytes(); let secret_key = hex::encode(sk); let pk = self.public_key.as_bytes(); let public_key = hex::encode(pk); - HexKeyPair { secret_key, public_key } + KeyPairHex { secret_key, public_key } } - fn from_hex_key_pair(kp: &HexKeyPair) -> Result { + fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; let secret_key: [u8; 32] = secret_key.try_into().map_err(|_| KeyPairingError::CryptError)?; @@ -119,11 +119,11 @@ pub struct KeyPairing { #[derive(Clone, Serialize, Deserialize)] pub struct KeyPairingHex { - pub sign: HexKeyPair, - pub update: HexKeyPair, + pub sign: KeyPairHex, + pub update: KeyPairHex, // MEMO: Matching schema in NodeX config. - pub recover: HexKeyPair, - pub encrypt: HexKeyPair, + pub recover: KeyPairHex, + pub encrypt: KeyPairHex, } impl KeyPairing { From a9f5a761fcba9977ee717ac7761e3aec327c0a81 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 18:03:58 +0900 Subject: [PATCH 14/58] Workaround for sidetree --- src/did/did_repository.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 7e837a7..5869d89 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -117,7 +117,10 @@ impl DidRepository for DidRepositoryImpl .to_public_key( "EcdsaSecp256k1VerificationKey2019".to_string(), "signingKey".to_string(), - vec!["assertionMethod".to_string()], + vec!["auth".to_string(), "general".to_string()] + // TODO: This purpose property is strange... + // https://identity.foundation/sidetree/spec/#add-public-keys + // vec!["assertionMethod".to_string()], ) .map_err(|_| CreateIdentifierError::JwkError)?; let enc = keyring @@ -126,7 +129,10 @@ impl DidRepository for DidRepositoryImpl .to_public_key( "X25519KeyAgreementKey2019".to_string(), "encryptionKey".to_string(), - vec!["keyAgreement".to_string()], + vec!["auth".to_string(), "general".to_string()] + // TODO: This purpose property is strange... + // https://identity.foundation/sidetree/spec/#add-public-keys + // vec!["keyAgreement".to_string()] ) .map_err(|_| CreateIdentifierError::JwkError)?; let update: Jwk = keyring From 3dfb91251049efb0de7f26df13368aa40c52e156 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 15 Jul 2024 18:20:49 +0900 Subject: [PATCH 15/58] Fix key id name --- src/did/did_repository.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 5869d89..126d522 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -58,14 +58,14 @@ fn get_key(key_type: &str, did_document: &DidDocument) -> Result Result { - let public_key = get_key("signingKey", &did_document)?; + let public_key = get_key("#signingKey", &did_document)?; Ok(public_key.try_into()?) } pub fn get_encrypt_key( did_document: &DidDocument, ) -> Result { - let public_key = get_key("encryptionKey", &did_document)?; + let public_key = get_key("#encryptionKey", &did_document)?; Ok(public_key.try_into()?) } @@ -117,10 +117,10 @@ impl DidRepository for DidRepositoryImpl .to_public_key( "EcdsaSecp256k1VerificationKey2019".to_string(), "signingKey".to_string(), - vec!["auth".to_string(), "general".to_string()] - // TODO: This purpose property is strange... - // https://identity.foundation/sidetree/spec/#add-public-keys - // vec!["assertionMethod".to_string()], + vec!["auth".to_string(), "general".to_string()], /* TODO: This purpose property + * is strange... https://identity.foundation/sidetree/spec/#add-public-keys + * vec!["assertionMethod". + * to_string()], */ ) .map_err(|_| CreateIdentifierError::JwkError)?; let enc = keyring @@ -129,10 +129,10 @@ impl DidRepository for DidRepositoryImpl .to_public_key( "X25519KeyAgreementKey2019".to_string(), "encryptionKey".to_string(), - vec!["auth".to_string(), "general".to_string()] - // TODO: This purpose property is strange... - // https://identity.foundation/sidetree/spec/#add-public-keys - // vec!["keyAgreement".to_string()] + vec!["auth".to_string(), "general".to_string()], /* TODO: This purpose property + * is strange... https://identity.foundation/sidetree/spec/#add-public-keys + * vec!["keyAgreement". + * to_string()] */ ) .map_err(|_| CreateIdentifierError::JwkError)?; let update: Jwk = keyring @@ -228,13 +228,13 @@ pub mod mocks { .flat_map(|keyring| { vec![ DidPublicKey { - id: "signingKey".to_string(), + id: "#signingKey".to_string(), controller: String::new(), r#type: "EcdsaSecp256k1VerificationKey2019".to_string(), public_key_jwk: keyring.sign.get_public_key().try_into().unwrap(), }, DidPublicKey { - id: "encryptionKey".to_string(), + id: "#encryptionKey".to_string(), controller: String::new(), r#type: "X25519KeyAgreementKey2019".to_string(), public_key_jwk: keyring From 6f459ce0e8f750a413b6800f500176a5d3799b29 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 12:00:01 +0900 Subject: [PATCH 16/58] Fix comments --- src/did/did_repository.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 126d522..c658153 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -111,28 +111,26 @@ impl DidRepository for DidRepositoryImpl keyring: KeyPairing, ) -> Result> { // https://w3c.github.io/did-spec-registries/#assertionmethod + // TODO: This purpose property is strange... + // https://identity.foundation/sidetree/spec/#add-public-keys + // vec!["assertionMethod".to_string()], let sign = keyring .sign .get_public_key() .to_public_key( "EcdsaSecp256k1VerificationKey2019".to_string(), "signingKey".to_string(), - vec!["auth".to_string(), "general".to_string()], /* TODO: This purpose property - * is strange... https://identity.foundation/sidetree/spec/#add-public-keys - * vec!["assertionMethod". - * to_string()], */ + vec!["auth".to_string(), "general".to_string()], ) .map_err(|_| CreateIdentifierError::JwkError)?; + // vec!["keyAgreement".to_string()] let enc = keyring .encrypt .get_public_key() .to_public_key( "X25519KeyAgreementKey2019".to_string(), "encryptionKey".to_string(), - vec!["auth".to_string(), "general".to_string()], /* TODO: This purpose property - * is strange... https://identity.foundation/sidetree/spec/#add-public-keys - * vec!["keyAgreement". - * to_string()] */ + vec!["auth".to_string(), "general".to_string()], ) .map_err(|_| CreateIdentifierError::JwkError)?; let update: Jwk = keyring From 563526ed27b41827ed3a8e0ab0a824ec0ef3b319 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 12:00:32 +0900 Subject: [PATCH 17/58] Refactor didcomm service --- src/didcomm/encrypted.rs | 238 ++++++++++++++++++++++++--------------- 1 file changed, 148 insertions(+), 90 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 11ee6bf..abfc002 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -30,7 +30,6 @@ pub trait DidCommEncryptedService: Sync { message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - attachment_link: &str, ) -> Result; async fn verify( &self, @@ -39,24 +38,22 @@ pub trait DidCommEncryptedService: Sync { ) -> Result; } -fn generate( +fn didcomm_generate( from_did: &str, to_doc: &DidDocument, from_keyring: &KeyPairing, - metadata: Option<&Value>, body: &VerifiableCredentials, + metadata: Option<&Value>, attachment_link: &str, ) -> Result< DidCommMessage, DidCommEncryptedServiceGenerateError, > { let to_did = &to_doc.id; - // NOTE: message let body = serde_json::to_string(body)?; let mut message = Message::new().from(from_did).to(&[to_did]).body(&body)?; - // NOTE: Has attachment if let Some(value) = metadata { let id = cuid::cuid2(); @@ -69,7 +66,6 @@ fn generate( ) } - // NOTE: recipient to let public_key = get_encrypt_key(to_doc)?.as_bytes().to_vec(); let public_key = Some(public_key); @@ -80,7 +76,34 @@ fn generate( Ok(serde_json::from_str::(&seal_message)?) } -fn verify( +async fn generate( + did_repository: &R, + vc_service: &V, + from_did: &str, + to_did: &str, + from_keyring: &KeyPairing, + message: &Value, + metadata: Option<&Value>, + issuance_date: DateTime, + attachment_link: &str, +) -> Result< + DidCommMessage, + DidCommEncryptedServiceGenerateError, +> { + let body = vc_service + .generate(from_did, from_keyring, message, issuance_date) + .map_err(DidCommEncryptedServiceGenerateError::VCServiceError)?; + let to_doc = did_repository + .find_identifier(to_did) + .await + .map_err(DidCommEncryptedServiceGenerateError::SidetreeFindRequestFailed)? + .ok_or(DidCommEncryptedServiceGenerateError::DidDocNotFound(to_did.to_string()))? + .did_document; + + Ok(didcomm_generate::(from_did, &to_doc, from_keyring, &body, metadata, attachment_link)?) +} + +fn didcomm_verify( from_doc: &DidDocument, my_keyring: &KeyPairing, message: &DidCommMessage, @@ -105,7 +128,6 @@ fn verify( .get_body() .map_err(|e| DidCommEncryptedServiceVerifyError::MetadataBodyNotFound(Some(e)))?; let body = serde_json::from_str::(&body)?; - // let body = did_vc::verify(from_doc, body)?; match metadata { Some(metadata) => { @@ -121,6 +143,26 @@ fn verify( } } +async fn verify( + did_repository: &R, + my_keyring: &KeyPairing, + message: &DidCommMessage, +) -> Result> { + let other_did = message.find_sender()?; + let other_doc = did_repository + .find_identifier(&other_did) + .await + .map_err(DidCommEncryptedServiceVerifyError::SidetreeFindRequestFailed)? + .ok_or(DidCommEncryptedServiceVerifyError::DidDocNotFound(other_did))? + .did_document; + let mut container = didcomm_verify::(&other_doc, my_keyring, message)?; + // For performance, call low level api + let public_key = get_sign_key(&other_doc)?; + let body = CredentialSigner::verify(container.message, &public_key)?; + container.message = body; + Ok(container) +} + #[derive(Debug, Error)] pub enum DidCommEncryptedServiceGenerateError where @@ -177,19 +219,76 @@ where message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - attachment_link: &str, ) -> Result { - // NOTE: message - let body = DidVcService::generate(self, from_did, from_keyring, message, issuance_date) - .map_err(Self::GenerateError::VCServiceError)?; - let to_doc = self - .find_identifier(to_did) - .await - .map_err(Self::GenerateError::SidetreeFindRequestFailed)? - .ok_or(Self::GenerateError::DidDocNotFound(to_did.to_string()))? - .did_document; + generate::( + self, + self, + from_did, + to_did, + from_keyring, + message, + metadata, + issuance_date, + "", + ) + .await + } - Ok(generate::(from_did, &to_doc, from_keyring, metadata, &body, attachment_link)?) + async fn verify( + &self, + my_keyring: &KeyPairing, + message: &DidCommMessage, + ) -> Result { + verify(self, my_keyring, message).await + } +} + +pub struct DidCommServiceWithAttachment +where + R: DidRepository + DidVcService, +{ + vc_service: R, + attachment_link: String, +} + +impl DidCommServiceWithAttachment +where + R: DidRepository + DidVcService, +{ + pub fn new(did_repository: R, attachment_link: String) -> Self { + Self { vc_service: did_repository, attachment_link } + } +} + +#[async_trait::async_trait] +impl DidCommEncryptedService for DidCommServiceWithAttachment +where + R: DidRepository + DidVcService, +{ + type GenerateError = + DidCommEncryptedServiceGenerateError; + type VerifyError = DidCommEncryptedServiceVerifyError; + async fn generate( + &self, + from_did: &str, + to_did: &str, + from_keyring: &KeyPairing, + message: &Value, + metadata: Option<&Value>, + issuance_date: DateTime, + ) -> Result { + generate::( + &self.vc_service, + &self.vc_service, + from_did, + to_did, + from_keyring, + message, + metadata, + issuance_date, + &self.attachment_link, + ) + .await } async fn verify( @@ -197,19 +296,7 @@ where my_keyring: &KeyPairing, message: &DidCommMessage, ) -> Result { - let other_did = message.find_sender()?; - let other_doc = self - .find_identifier(&other_did) - .await - .map_err(Self::VerifyError::SidetreeFindRequestFailed)? - .ok_or(Self::VerifyError::DidDocNotFound(other_did))? - .did_document; - let mut container = verify::(&other_doc, my_keyring, message)?; - // For performance, call low level api - let public_key = get_sign_key(&other_doc)?; - let body = CredentialSigner::verify(container.message, &public_key)?; - container.message = body; - Ok(container) + verify(&self.vc_service, my_keyring, message).await } } @@ -217,12 +304,19 @@ where mod tests { use std::{collections::BTreeMap, iter::FromIterator as _}; + use chrono::{DateTime, Utc}; use rand_core::OsRng; - use serde_json::json; + use serde_json::{json, Value}; - use super::*; + // use super::*; + use super::DidCommEncryptedService; use crate::{ - did::did_repository::mocks::MockDidRepository, didcomm::test_utils::create_random_did, + did::did_repository::{mocks::MockDidRepository, GetPublicKeyError}, + didcomm::{ + encrypted::{DidCommEncryptedServiceGenerateError, DidCommEncryptedServiceVerifyError}, + test_utils::create_random_did, + types::DidCommMessage, + }, keyring::keypair::KeyPairing, }; @@ -242,20 +336,12 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = DidCommEncryptedService::generate( - &repo, - &from_did, - &to_did, - &from_keyring, - &message, - None, - issuance_date, - "", - ) - .await - .unwrap(); + let res = repo + .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) + .await + .unwrap(); - let verified = DidCommEncryptedService::verify(&repo, &to_keyring, &res).await.unwrap(); + let verified = repo.verify(&to_keyring, &res).await.unwrap(); let verified = verified.message; assert_eq!(verified.issuer.id, from_did); @@ -281,18 +367,10 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = DidCommEncryptedService::generate( - &repo, - &from_did, - &to_did, - &from_keyring, - &message, - None, - issuance_date, - "", - ) - .await - .unwrap_err(); + let res = repo + .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) + .await + .unwrap_err(); if let DidCommEncryptedServiceGenerateError::DidDocNotFound(did) = res { assert_eq!(did, to_did); @@ -313,18 +391,10 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = DidCommEncryptedService::generate( - &repo, - &from_did, - &to_did, - &from_keyring, - &message, - None, - issuance_date, - "", - ) - .await - .unwrap_err(); + let res = repo + .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) + .await + .unwrap_err(); if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), @@ -355,18 +425,9 @@ mod tests { to_keyring.clone(), )])); - DidCommEncryptedService::generate( - &repo, - &from_did, - &to_did, - &from_keyring, - &message, - metadata, - issuance_date, - "", - ) - .await - .unwrap() + repo.generate(&from_did, &to_did, &from_keyring, &message, metadata, issuance_date) + .await + .unwrap() } #[actix_rt::test] @@ -395,8 +456,7 @@ mod tests { to_did.clone(), to_keyring.clone(), )])); - let res = - DidCommEncryptedService::verify(&repo, &from_keyring, &res).await.unwrap_err(); + let res = repo.verify(&from_keyring, &res).await.unwrap_err(); if let DidCommEncryptedServiceVerifyError::DidDocNotFound(did) = res { assert_eq!(did, from_did); @@ -435,8 +495,7 @@ mod tests { (other_did.clone(), other_keyring.clone()), ])); - let res = - DidCommEncryptedService::verify(&repo, &other_keyring, &res).await.unwrap_err(); + let res = repo.verify(&other_keyring, &res).await.unwrap_err(); if let DidCommEncryptedServiceVerifyError::DecryptFailed(_) = res { } else { @@ -468,8 +527,7 @@ mod tests { let repo = NoPublicKeyDidRepository; - let res = - DidCommEncryptedService::verify(&repo, &from_keyring, &res).await.unwrap_err(); + let res = repo.verify(&from_keyring, &res).await.unwrap_err(); if let DidCommEncryptedServiceVerifyError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), From 27e19e0866f4bf68f6a7a78bb2d624fa22b0b272 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 12:23:01 +0900 Subject: [PATCH 18/58] Get rid of common --- src/common/cipher/mod.rs | 1 - src/common/mod.rs | 2 -- src/common/runtime/mod.rs | 1 - src/did/sidetree/mod.rs | 1 + src/{common/runtime => did/sidetree}/multihash.rs | 0 src/did/sidetree/payload.rs | 2 +- src/lib.rs | 1 - src/verifiable_credentials/credential_signer.rs | 7 +++---- src/{common/cipher => verifiable_credentials}/jws.rs | 0 src/verifiable_credentials/mod.rs | 1 + 10 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 src/common/cipher/mod.rs delete mode 100644 src/common/mod.rs delete mode 100644 src/common/runtime/mod.rs rename src/{common/runtime => did/sidetree}/multihash.rs (100%) rename src/{common/cipher => verifiable_credentials}/jws.rs (100%) diff --git a/src/common/cipher/mod.rs b/src/common/cipher/mod.rs deleted file mode 100644 index d2df536..0000000 --- a/src/common/cipher/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod jws; diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index cf2a8f9..0000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cipher; -pub mod runtime; diff --git a/src/common/runtime/mod.rs b/src/common/runtime/mod.rs deleted file mode 100644 index 02257ba..0000000 --- a/src/common/runtime/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod multihash; diff --git a/src/did/sidetree/mod.rs b/src/did/sidetree/mod.rs index 3ac7dcc..8f8b414 100644 --- a/src/did/sidetree/mod.rs +++ b/src/did/sidetree/mod.rs @@ -1,2 +1,3 @@ pub mod client; +mod multihash; pub mod payload; diff --git a/src/common/runtime/multihash.rs b/src/did/sidetree/multihash.rs similarity index 100% rename from src/common/runtime/multihash.rs rename to src/did/sidetree/multihash.rs diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 03b2030..24822c5 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use serde_jcs; use thiserror::Error; -use crate::{common::runtime::multihash, keyring::jwk::Jwk}; +use crate::{did::sidetree::multihash, keyring::jwk::Jwk}; #[derive(Debug, Serialize, Deserialize)] pub struct ServiceEndpoint { diff --git a/src/lib.rs b/src/lib.rs index c49cae6..b8a9f48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -pub(crate) mod common; pub mod did; pub mod didcomm; pub mod keyring; diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index 6d57c44..8a4168b 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -4,9 +4,8 @@ use thiserror::Error; use super::types::Proof; use crate::{ - common::cipher::jws, keyring::keypair::{K256KeyPair, KeyPair}, - verifiable_credentials::types::VerifiableCredentials, + verifiable_credentials::{jws, types::VerifiableCredentials}, }; pub struct CredentialSignerSuite<'a> { @@ -18,7 +17,7 @@ pub struct CredentialSignerSuite<'a> { #[derive(Debug, Error)] pub enum CredentialSignerSignError { #[error("jws error: {0:?}")] - JwsError(#[from] crate::common::cipher::jws::JwsEncodeError), + JwsError(#[from] jws::JwsEncodeError), #[error("json parse error: {0:?}")] JsonParseError(#[from] serde_json::Error), } @@ -26,7 +25,7 @@ pub enum CredentialSignerSignError { #[derive(Debug, Error)] pub enum CredentialSignerVerifyError { #[error("jws error: {0:?}")] - JwsError(#[from] crate::common::cipher::jws::JwsDecodeError), + JwsError(#[from] jws::JwsDecodeError), #[error("json parse error: {0:?}")] JsonParseError(#[from] serde_json::Error), #[error("proof not found")] diff --git a/src/common/cipher/jws.rs b/src/verifiable_credentials/jws.rs similarity index 100% rename from src/common/cipher/jws.rs rename to src/verifiable_credentials/jws.rs diff --git a/src/verifiable_credentials/mod.rs b/src/verifiable_credentials/mod.rs index 5886dd3..048112a 100644 --- a/src/verifiable_credentials/mod.rs +++ b/src/verifiable_credentials/mod.rs @@ -1,3 +1,4 @@ pub mod credential_signer; pub mod did_vc; +mod jws; pub mod types; From 83eb6aef3f0842c18c090372acd71c15d0946e34 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 15:30:17 +0900 Subject: [PATCH 19/58] Handle attachment link correctly --- src/didcomm/encrypted.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index abfc002..d3cdbb1 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -44,7 +44,7 @@ fn didcomm_generate( from_keyring: &KeyPairing, body: &VerifiableCredentials, metadata: Option<&Value>, - attachment_link: &str, + attachment_link: Option<&str>, ) -> Result< DidCommMessage, DidCommEncryptedServiceGenerateError, @@ -58,8 +58,13 @@ fn didcomm_generate( let id = cuid::cuid2(); // let media_type = "application/json"; - let data = - AttachmentDataBuilder::new().with_link(attachment_link).with_json(&value.to_string()); + let data = AttachmentDataBuilder::new().with_json(&value.to_string()); + + let data = if let Some(attachment_link) = attachment_link { + data.with_link(attachment_link) + } else { + data + }; message.append_attachment( AttachmentBuilder::new(true).with_id(&id).with_format("metadata").with_data(data), @@ -85,7 +90,7 @@ async fn generate( message: &Value, metadata: Option<&Value>, issuance_date: DateTime, - attachment_link: &str, + attachment_link: Option<&str>, ) -> Result< DidCommMessage, DidCommEncryptedServiceGenerateError, @@ -229,7 +234,7 @@ where message, metadata, issuance_date, - "", + None, ) .await } @@ -286,7 +291,7 @@ where message, metadata, issuance_date, - &self.attachment_link, + Some(&self.attachment_link), ) .await } From c8abdd5a107cec231ee677047de909268e12ae7b Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 15:36:55 +0900 Subject: [PATCH 20/58] Fix field name of recovery --- src/keyring/keypair.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 35b69d2..547a96c 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -121,8 +121,7 @@ pub struct KeyPairing { pub struct KeyPairingHex { pub sign: KeyPairHex, pub update: KeyPairHex, - // MEMO: Matching schema in NodeX config. - pub recover: KeyPairHex, + pub recovery: KeyPairHex, pub encrypt: KeyPairHex, } @@ -141,7 +140,7 @@ impl From<&KeyPairing> for KeyPairingHex { KeyPairingHex { sign: keypair.sign.to_hex_key_pair(), update: keypair.update.to_hex_key_pair(), - recover: keypair.recovery.to_hex_key_pair(), + recovery: keypair.recovery.to_hex_key_pair(), encrypt: keypair.encrypt.to_hex_key_pair(), } } @@ -153,7 +152,7 @@ impl TryFrom<&KeyPairingHex> for KeyPairing { fn try_from(hex: &KeyPairingHex) -> Result { let sign = K256KeyPair::from_hex_key_pair(&hex.sign)?; let update = K256KeyPair::from_hex_key_pair(&hex.update)?; - let recovery = K256KeyPair::from_hex_key_pair(&hex.recover)?; + let recovery = K256KeyPair::from_hex_key_pair(&hex.recovery)?; let encrypt = X25519KeyPair::from_hex_key_pair(&hex.encrypt)?; Ok(KeyPairing { sign, update, recovery, encrypt }) From d8f1e534ee80a1dea27f36b6ce3c60f871404c4f Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 16:05:24 +0900 Subject: [PATCH 21/58] Lint --- src/did/did_repository.rs | 4 ++-- src/did/sidetree/payload.rs | 2 +- src/didcomm/encrypted.rs | 19 ++++++++-------- src/didcomm/types.rs | 12 +++++----- src/keyring/jwk.rs | 6 ++--- src/keyring/keypair.rs | 22 +++++++++---------- .../credential_signer.rs | 10 ++++----- 7 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index c658153..ecba359 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -58,14 +58,14 @@ fn get_key(key_type: &str, did_document: &DidDocument) -> Result Result { - let public_key = get_key("#signingKey", &did_document)?; + let public_key = get_key("#signingKey", did_document)?; Ok(public_key.try_into()?) } pub fn get_encrypt_key( did_document: &DidDocument, ) -> Result { - let public_key = get_key("#encryptionKey", &did_document)?; + let public_key = get_key("#encryptionKey", did_document)?; Ok(public_key.try_into()?) } diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 24822c5..20b4733 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -100,7 +100,7 @@ where purpose: Vec, ) -> Result { let jwk: Jwk = self.try_into()?; - Ok(PublicKeyPayload { id: key_id.into(), r#type: key_type.into(), jwk, purpose }) + Ok(PublicKeyPayload { id: key_id, r#type: key_type, jwk, purpose }) } } diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index d3cdbb1..a1847de 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -76,11 +76,12 @@ fn didcomm_generate( let seal_message = message .as_jwe(&CryptoAlgorithm::XC20P, public_key.clone()) - .seal(&from_keyring.encrypt.get_secret_key().as_bytes().to_vec(), Some(vec![public_key]))?; + .seal(from_keyring.encrypt.get_secret_key().as_bytes(), Some(vec![public_key]))?; Ok(serde_json::from_str::(&seal_message)?) } +#[allow(clippy::too_many_arguments)] async fn generate( did_repository: &R, vc_service: &V, @@ -97,7 +98,7 @@ async fn generate( > { let body = vc_service .generate(from_did, from_keyring, message, issuance_date) - .map_err(DidCommEncryptedServiceGenerateError::VCServiceError)?; + .map_err(DidCommEncryptedServiceGenerateError::VcService)?; let to_doc = did_repository .find_identifier(to_did) .await @@ -105,7 +106,7 @@ async fn generate( .ok_or(DidCommEncryptedServiceGenerateError::DidDocNotFound(to_did.to_string()))? .did_document; - Ok(didcomm_generate::(from_did, &to_doc, from_keyring, &body, metadata, attachment_link)?) + didcomm_generate::(from_did, &to_doc, from_keyring, &body, metadata, attachment_link) } fn didcomm_verify( @@ -119,7 +120,7 @@ fn didcomm_verify( let message = Message::receive( &serde_json::to_string(&message)?, - Some(&my_keyring.encrypt.get_secret_key().as_bytes().to_vec()), + Some(my_keyring.encrypt.get_secret_key().as_bytes().as_ref()), public_key, None, )?; @@ -179,13 +180,13 @@ where #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("something went wrong with vc service")] - VCServiceError(DidVcServiceGenerateError), + VcService(DidVcServiceGenerateError), #[error("failed to create identifier")] SidetreeFindRequestFailed(FindIdentifierError), #[error("failed to encrypt message with error: {0}")] EncryptFailed(#[from] didcomm_rs::Error), #[error("failed serialize/deserialize : {0}")] - JsonError(#[from] serde_json::Error), + Json(#[from] serde_json::Error), } #[derive(Debug, Error)] @@ -193,7 +194,7 @@ pub enum DidCommEncryptedServiceVerifyError), #[error("failed serialize/deserialize : {0}")] - JsonError(#[from] serde_json::Error), + Json(#[from] serde_json::Error), #[error("failed to find sender did : {0}")] - FindSenderError(#[from] FindSenderError), + FindSender(#[from] FindSenderError), } #[async_trait::async_trait] diff --git a/src/didcomm/types.rs b/src/didcomm/types.rs index 38f297b..73c02ac 100644 --- a/src/didcomm/types.rs +++ b/src/didcomm/types.rs @@ -37,13 +37,13 @@ pub struct Epk { #[derive(Debug, Error)] pub enum FindSenderError { #[error("failed serialize/deserialize: {0}")] - JsonError(#[from] serde_json::Error), + Json(#[from] serde_json::Error), #[error("failed to base64 decode protected: {0}")] - FromUtf8Error(#[from] std::string::FromUtf8Error), + FromUtf8(#[from] std::string::FromUtf8Error), #[error("failed to base64 decode protected: {0}")] - DecodeError(#[from] data_encoding::DecodeError), + Decode(#[from] data_encoding::DecodeError), #[error("skid error")] - SkidError, + Skid, } impl DidCommMessage { @@ -60,9 +60,9 @@ impl DidCommMessage { let from_did = decoded .get("skid") - .ok_or(FindSenderError::SkidError)? + .ok_or(FindSenderError::Skid)? .as_str() - .ok_or(FindSenderError::SkidError)? + .ok_or(FindSenderError::Skid)? .to_string(); Ok(from_did) diff --git a/src/keyring/jwk.rs b/src/keyring/jwk.rs index a2b36b2..2ebc352 100644 --- a/src/keyring/jwk.rs +++ b/src/keyring/jwk.rs @@ -78,8 +78,8 @@ impl TryFrom for Jwk { let crv = "secp256k1".to_string(); match value.coordinates() { elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { - let x = BASE64URL_NOPAD.encode(&x); - let y = Some(BASE64URL_NOPAD.encode(&y)); + let x = BASE64URL_NOPAD.encode(x); + let y = Some(BASE64URL_NOPAD.encode(y)); Ok(Jwk { kty, crv, x, y }) } _ => Err(()), @@ -94,7 +94,7 @@ impl TryFrom for x25519_dalek::PublicKey { return Err(JwkToX25519Error::DifferentCrv); } let pk = BASE64URL_NOPAD - .decode(&value.x.as_bytes()) + .decode(value.x.as_bytes()) .map_err(|_| JwkToX25519Error::DecodeError)?; let pk: [u8; 32] = pk.try_into().map_err(|_| JwkToX25519Error::DecodeError)?; Ok(pk.into()) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 547a96c..abc38f1 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -16,9 +16,9 @@ pub struct KeyPairHex { #[derive(Error, Debug)] pub enum KeyPairingError { #[error("from hex error")] - FromHexError(#[from] FromHexError), + FromHex(#[from] FromHexError), #[error("crypt error")] - CryptError, + Crypt, } pub trait KeyPair: Sized { @@ -48,11 +48,11 @@ impl KeyPair for K256KeyPair { self.secret_key.clone() } fn get_public_key(&self) -> k256::PublicKey { - self.public_key.clone() + self.public_key } fn to_hex_key_pair(&self) -> KeyPairHex { let sk = self.secret_key.to_bytes(); - let secret_key = hex::encode(&sk); + let secret_key = hex::encode(sk); let pk = self.public_key.to_encoded_point(false); let public_key = hex::encode(pk.as_bytes()); KeyPairHex { secret_key, public_key } @@ -60,10 +60,10 @@ impl KeyPair for K256KeyPair { fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; let secret_key = - k256::SecretKey::from_slice(&secret_key).map_err(|_| KeyPairingError::CryptError)?; + k256::SecretKey::from_slice(&secret_key).map_err(|_| KeyPairingError::Crypt)?; let public_key = hex::decode(&kp.public_key)?; - let public_key = k256::PublicKey::from_sec1_bytes(&public_key) - .map_err(|_| KeyPairingError::CryptError)?; + let public_key = + k256::PublicKey::from_sec1_bytes(&public_key).map_err(|_| KeyPairingError::Crypt)?; Ok(K256KeyPair { public_key, secret_key }) } } @@ -87,7 +87,7 @@ impl KeyPair for X25519KeyP self.secret_key.clone() } fn get_public_key(&self) -> x25519_dalek::PublicKey { - self.public_key.clone() + self.public_key } fn to_hex_key_pair(&self) -> KeyPairHex { let sk = self.secret_key.as_bytes(); @@ -98,12 +98,10 @@ impl KeyPair for X25519KeyP } fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; - let secret_key: [u8; 32] = - secret_key.try_into().map_err(|_| KeyPairingError::CryptError)?; + let secret_key: [u8; 32] = secret_key.try_into().map_err(|_| KeyPairingError::Crypt)?; let secret_key = x25519_dalek::StaticSecret::from(secret_key); let public_key = hex::decode(&kp.public_key)?; - let public_key: [u8; 32] = - public_key.try_into().map_err(|_| KeyPairingError::CryptError)?; + let public_key: [u8; 32] = public_key.try_into().map_err(|_| KeyPairingError::Crypt)?; let public_key = x25519_dalek::PublicKey::from(public_key); Ok(X25519KeyPair { public_key, secret_key }) } diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index 8a4168b..60ba7e8 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -17,17 +17,17 @@ pub struct CredentialSignerSuite<'a> { #[derive(Debug, Error)] pub enum CredentialSignerSignError { #[error("jws error: {0:?}")] - JwsError(#[from] jws::JwsEncodeError), + Jws(#[from] jws::JwsEncodeError), #[error("json parse error: {0:?}")] - JsonParseError(#[from] serde_json::Error), + Json(#[from] serde_json::Error), } #[derive(Debug, Error)] pub enum CredentialSignerVerifyError { #[error("jws error: {0:?}")] - JwsError(#[from] jws::JwsDecodeError), + Jws(#[from] jws::JwsDecodeError), #[error("json parse error: {0:?}")] - JsonParseError(#[from] serde_json::Error), + Json(#[from] serde_json::Error), #[error("proof not found")] ProofNotFound, } @@ -66,7 +66,7 @@ impl CredentialSigner { let proof = object.proof.take().ok_or(CredentialSignerVerifyError::ProofNotFound)?; let jws = proof.jws; let payload = serde_json::to_value(&object)?; - let _ = jws::verify(&payload, &jws, public_key)?; + jws::verify(&payload, &jws, public_key)?; Ok(object) } } From 24b2c0df4e0ef3782849290c920185acffafb450 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 16:09:48 +0900 Subject: [PATCH 22/58] Lint --- src/did/did_repository.rs | 4 +--- src/didcomm/encrypted.rs | 24 ++++++++++++------------ src/verifiable_credentials/jws.rs | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index ecba359..ffe507d 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -237,9 +237,7 @@ pub mod mocks { r#type: "X25519KeyAgreementKey2019".to_string(), public_key_jwk: keyring .encrypt - .get_public_key() - .try_into() - .unwrap(), + .get_public_key().into(), }, ] }) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index a1847de..5bdf395 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -331,8 +331,8 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let to_keyring = KeyPairing::create_keyring(&mut OsRng); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let to_keyring = KeyPairing::create_keyring(OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); let repo = MockDidRepository::from_single(BTreeMap::from_iter([ (from_did.clone(), from_keyring.clone()), @@ -363,7 +363,7 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); let repo = MockDidRepository::from_single(BTreeMap::from_iter([( from_did.clone(), @@ -390,7 +390,7 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); let repo = NoPublicKeyDidRepository; @@ -431,7 +431,7 @@ mod tests { to_keyring.clone(), )])); - repo.generate(&from_did, &to_did, &from_keyring, &message, metadata, issuance_date) + repo.generate(from_did, to_did, from_keyring, message, metadata, issuance_date) .await .unwrap() } @@ -441,8 +441,8 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let to_keyring = KeyPairing::create_keyring(&mut OsRng); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let to_keyring = KeyPairing::create_keyring(OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -477,9 +477,9 @@ mod tests { let to_did = create_random_did(); let other_did = create_random_did(); - let to_keyring = KeyPairing::create_keyring(&mut OsRng); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); - let other_keyring = KeyPairing::create_keyring(&mut OsRng); + let to_keyring = KeyPairing::create_keyring(OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); + let other_keyring = KeyPairing::create_keyring(OsRng); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); @@ -514,8 +514,8 @@ mod tests { let from_did = create_random_did(); let to_did = create_random_did(); - let to_keyring = KeyPairing::create_keyring(&mut OsRng); - let from_keyring = KeyPairing::create_keyring(&mut OsRng); + let to_keyring = KeyPairing::create_keyring(OsRng); + let from_keyring = KeyPairing::create_keyring(OsRng); let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index ba46c4f..808b58a 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -159,6 +159,6 @@ pub mod tests { pub fn test_verify() { let pk = k256::PublicKey::from_sec1_bytes(&PUBLIC_KEY).unwrap(); let json: Value = serde_json::from_str(&message()).unwrap(); - let _ = verify(&json, &signature(), &pk).unwrap(); + verify(&json, &signature(), &pk).unwrap(); } } From 9b20ca02492967735dc4ebe64ad5e11f1917dd71 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 17:47:47 +0900 Subject: [PATCH 23/58] Refactor services --- src/did/did_repository.rs | 4 +- src/didcomm/encrypted.rs | 81 +++++++------------ .../credential_signer.rs | 36 ++++----- src/verifiable_credentials/did_vc.rs | 55 ++++--------- src/verifiable_credentials/types.rs | 19 +++++ 5 files changed, 78 insertions(+), 117 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index ffe507d..7bbdb26 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -235,9 +235,7 @@ pub mod mocks { id: "#encryptionKey".to_string(), controller: String::new(), r#type: "X25519KeyAgreementKey2019".to_string(), - public_key_jwk: keyring - .encrypt - .get_public_key().into(), + public_key_jwk: keyring.encrypt.get_public_key().into(), }, ] }) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 5bdf395..386c797 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -1,4 +1,3 @@ -use chrono::{DateTime, Utc}; use cuid; use didcomm_rs::{crypto::CryptoAlgorithm, AttachmentBuilder, AttachmentDataBuilder, Message}; use serde_json::Value; @@ -24,12 +23,10 @@ pub trait DidCommEncryptedService: Sync { type VerifyError: std::error::Error; async fn generate( &self, - from_did: &str, - to_did: &str, + model: VerifiableCredentials, from_keyring: &KeyPairing, - message: &Value, + to_did: &str, metadata: Option<&Value>, - issuance_date: DateTime, ) -> Result; async fn verify( &self, @@ -39,10 +36,9 @@ pub trait DidCommEncryptedService: Sync { } fn didcomm_generate( - from_did: &str, - to_doc: &DidDocument, - from_keyring: &KeyPairing, body: &VerifiableCredentials, + from_keyring: &KeyPairing, + to_doc: &DidDocument, metadata: Option<&Value>, attachment_link: Option<&str>, ) -> Result< @@ -50,6 +46,7 @@ fn didcomm_generate( DidCommEncryptedServiceGenerateError, > { let to_did = &to_doc.id; + let from_did = &body.issuer.id; let body = serde_json::to_string(body)?; let mut message = Message::new().from(from_did).to(&[to_did]).body(&body)?; @@ -81,23 +78,20 @@ fn didcomm_generate( Ok(serde_json::from_str::(&seal_message)?) } -#[allow(clippy::too_many_arguments)] async fn generate( did_repository: &R, vc_service: &V, - from_did: &str, - to_did: &str, + model: VerifiableCredentials, from_keyring: &KeyPairing, - message: &Value, + to_did: &str, metadata: Option<&Value>, - issuance_date: DateTime, attachment_link: Option<&str>, ) -> Result< DidCommMessage, DidCommEncryptedServiceGenerateError, > { let body = vc_service - .generate(from_did, from_keyring, message, issuance_date) + .generate(model, from_keyring) .map_err(DidCommEncryptedServiceGenerateError::VcService)?; let to_doc = did_repository .find_identifier(to_did) @@ -106,7 +100,7 @@ async fn generate( .ok_or(DidCommEncryptedServiceGenerateError::DidDocNotFound(to_did.to_string()))? .did_document; - didcomm_generate::(from_did, &to_doc, from_keyring, &body, metadata, attachment_link) + didcomm_generate::(&body, from_keyring, &to_doc, metadata, attachment_link) } fn didcomm_verify( @@ -219,25 +213,12 @@ where type VerifyError = DidCommEncryptedServiceVerifyError; async fn generate( &self, - from_did: &str, - to_did: &str, + model: VerifiableCredentials, from_keyring: &KeyPairing, - message: &Value, + to_did: &str, metadata: Option<&Value>, - issuance_date: DateTime, ) -> Result { - generate::( - self, - self, - from_did, - to_did, - from_keyring, - message, - metadata, - issuance_date, - None, - ) - .await + generate::(self, self, model, from_keyring, to_did, metadata, None).await } async fn verify( @@ -276,22 +257,18 @@ where type VerifyError = DidCommEncryptedServiceVerifyError; async fn generate( &self, - from_did: &str, - to_did: &str, + model: VerifiableCredentials, from_keyring: &KeyPairing, - message: &Value, + to_did: &str, metadata: Option<&Value>, - issuance_date: DateTime, ) -> Result { generate::( &self.vc_service, &self.vc_service, - from_did, - to_did, + model, from_keyring, - message, + to_did, metadata, - issuance_date, Some(&self.attachment_link), ) .await @@ -324,6 +301,7 @@ mod tests { types::DidCommMessage, }, keyring::keypair::KeyPairing, + verifiable_credentials::types::VerifiableCredentials, }; #[actix_rt::test] @@ -342,10 +320,8 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = repo - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap(); + let model = VerifiableCredentials::new(from_did.clone(), message.clone(), issuance_date); + let res = repo.generate(model, &from_keyring, &to_did, None).await.unwrap(); let verified = repo.verify(&to_keyring, &res).await.unwrap(); let verified = verified.message; @@ -373,10 +349,8 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = repo - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap_err(); + let model = VerifiableCredentials::new(from_did, message, issuance_date); + let res = repo.generate(model, &from_keyring, &to_did, None).await.unwrap_err(); if let DidCommEncryptedServiceGenerateError::DidDocNotFound(did) = res { assert_eq!(did, to_did); @@ -397,10 +371,8 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = repo - .generate(&from_did, &to_did, &from_keyring, &message, None, issuance_date) - .await - .unwrap_err(); + let model = VerifiableCredentials::new(from_did, message, issuance_date); + let res = repo.generate(model, &from_keyring, &to_did, None).await.unwrap_err(); if let DidCommEncryptedServiceGenerateError::DidPublicKeyNotFound( GetPublicKeyError::PublicKeyNotFound(did), @@ -431,9 +403,10 @@ mod tests { to_keyring.clone(), )])); - repo.generate(from_did, to_did, from_keyring, message, metadata, issuance_date) - .await - .unwrap() + let model = + VerifiableCredentials::new(from_did.to_string(), message.clone(), issuance_date); + + repo.generate(model, from_keyring, to_did, metadata).await.unwrap() } #[actix_rt::test] diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index 60ba7e8..c6b8a1f 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -1,4 +1,3 @@ -use chrono::{DateTime, Utc}; use serde_json::json; use thiserror::Error; @@ -36,27 +35,24 @@ pub struct CredentialSigner {} impl CredentialSigner { pub fn sign( - object: &VerifiableCredentials, + mut object: VerifiableCredentials, suite: CredentialSignerSuite, - created: DateTime, ) -> Result { let jws = jws::sign(&json!(object), &suite.context.get_secret_key())?; let did = suite.did; let key_id = suite.key_id; - - Ok(VerifiableCredentials { - proof: Some(Proof { - r#type: "EcdsaSecp256k1Signature2019".to_string(), - proof_purpose: "authentication".to_string(), - created: created.to_rfc3339(), - verification_method: format!("{}#{}", did, key_id), - jws, - domain: None, - controller: None, - challenge: None, - }), - ..object.clone() - }) + object.proof = Some(Proof { + r#type: "EcdsaSecp256k1Signature2019".to_string(), + proof_purpose: "authentication".to_string(), + // Assume that object.issuance_date is correct data + created: object.issuance_date.clone(), + verification_method: format!("{}#{}", did, key_id), + jws, + domain: None, + controller: None, + challenge: None, + }); + Ok(object) } pub fn verify( @@ -119,13 +115,12 @@ pub mod tests { }; let result = CredentialSigner::sign( - &model, + model, CredentialSignerSuite { did: "did:nodex:test:000000000000000000000000000000", key_id: "signingKey", context: &context, }, - Utc::now(), ) .unwrap(); @@ -166,13 +161,12 @@ pub mod tests { }; let vc = CredentialSigner::sign( - &model, + model.clone(), CredentialSignerSuite { did: "did:nodex:test:000000000000000000000000000000", key_id: "signingKey", context: &context, }, - Utc::now(), ) .unwrap(); diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 1914b0b..740a4ec 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -1,5 +1,3 @@ -use chrono::{DateTime, Utc}; -use serde_json::Value; use thiserror::Error; use crate::{ @@ -10,7 +8,7 @@ use crate::{ CredentialSigner, CredentialSignerSignError, CredentialSignerSuite, CredentialSignerVerifyError, }, - types::{CredentialSubject, Issuer, VerifiableCredentials}, + types::VerifiableCredentials, }, }; @@ -20,10 +18,8 @@ pub trait DidVcService: Sync { type VerifyError: std::error::Error; fn generate( &self, - from_did: &str, + model: VerifiableCredentials, from_keyring: &keypair::KeyPairing, - message: &Value, - issuance_date: DateTime, ) -> Result; async fn verify( &self, @@ -51,40 +47,18 @@ pub enum DidVcServiceVerifyError { #[async_trait::async_trait] impl DidVcService for R { - type GenerateError = DidVcServiceGenerateError; + type GenerateError = CredentialSignerSignError; type VerifyError = DidVcServiceVerifyError; fn generate( &self, - from_did: &str, + model: VerifiableCredentials, from_keyring: &keypair::KeyPairing, - message: &Value, - issuance_date: DateTime, ) -> Result { - let r#type = "VerifiableCredential".to_string(); - let context = "https://www.w3.org/2018/credentials/v1".to_string(); - - let model = VerifiableCredentials { - id: None, - issuer: Issuer { id: from_did.to_string() }, - r#type: vec![r#type], - context: vec![context], - issuance_date: issuance_date.to_rfc3339(), - credential_subject: CredentialSubject { id: None, container: message.clone() }, - expiration_date: None, - proof: None, - }; - - let signed: VerifiableCredentials = CredentialSigner::sign( - &model, - CredentialSignerSuite { - did: from_did, - key_id: "signingKey", - context: &from_keyring.sign, - }, - issuance_date, - )?; - - Ok(signed) + let did = &model.issuer.id.clone(); + CredentialSigner::sign( + model, + CredentialSignerSuite { did, key_id: "signingKey", context: &from_keyring.sign }, + ) } async fn verify( @@ -107,8 +81,9 @@ impl DidVcService for R { mod tests { use std::{collections::BTreeMap, iter::FromIterator as _}; + use chrono::{DateTime, Utc}; use rand_core::OsRng; - use serde_json::json; + use serde_json::{json, Value}; use super::*; use crate::{ @@ -132,7 +107,8 @@ mod tests { let message = json!({"test": "0123456789abcdef"}); let issuance_date = Utc::now(); - let res = service.generate(&from_did, &from_keyring, &message, issuance_date).unwrap(); + let model = VerifiableCredentials::new(from_did.clone(), message.clone(), issuance_date); + let res = service.generate(model, &from_keyring).unwrap(); let verified = service.verify(res).await.unwrap(); @@ -155,8 +131,9 @@ mod tests { issuance_date: DateTime, ) -> VerifiableCredentials { let service = MockDidRepository::from_single(BTreeMap::new()); - - service.generate(from_did, from_keyring, message, issuance_date).unwrap() + let model = + VerifiableCredentials::new(from_did.to_string(), message.clone(), issuance_date); + service.generate(model, from_keyring).unwrap() } #[actix_rt::test] diff --git a/src/verifiable_credentials/types.rs b/src/verifiable_credentials/types.rs index 5197183..bc1a915 100644 --- a/src/verifiable_credentials/types.rs +++ b/src/verifiable_credentials/types.rs @@ -1,3 +1,4 @@ +use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -77,3 +78,21 @@ pub struct VerifiableCredentials { #[serde(rename = "proof", skip_serializing_if = "Option::is_none")] pub proof: Option, } + +impl VerifiableCredentials { + pub fn new(from_did: String, message: Value, issuance_date: DateTime) -> Self { + let r#type = "VerifiableCredential".to_string(); + let context = "https://www.w3.org/2018/credentials/v1".to_string(); + + VerifiableCredentials { + id: None, + issuer: Issuer { id: from_did }, + r#type: vec![r#type], + context: vec![context], + issuance_date: issuance_date.to_rfc3339(), + credential_subject: CredentialSubject { id: None, container: message }, + expiration_date: None, + proof: None, + } + } +} From 6d8f419c6c64cc9eed7fe1beea5399c1d0805d0c Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 18:30:39 +0900 Subject: [PATCH 24/58] Remove unused struct --- src/didcomm/encrypted.rs | 6 +++--- src/verifiable_credentials/did_vc.rs | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 386c797..acab6ac 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -164,17 +164,17 @@ async fn verify( } #[derive(Debug, Error)] -pub enum DidCommEncryptedServiceGenerateError +pub enum DidCommEncryptedServiceGenerateError where FindIdentifierError: std::error::Error, - DidVcServiceGenerateError: std::error::Error, + CredentialSignerSignError: std::error::Error, { #[error("failed to get did document: {0}")] DidDocNotFound(String), #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), #[error("something went wrong with vc service")] - VcService(DidVcServiceGenerateError), + VcService(CredentialSignerSignError), #[error("failed to create identifier")] SidetreeFindRequestFailed(FindIdentifierError), #[error("failed to encrypt message with error: {0}")] diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 740a4ec..2969514 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -27,12 +27,6 @@ pub trait DidVcService: Sync { ) -> Result; } -#[derive(Debug, Error)] -pub enum DidVcServiceGenerateError { - #[error("credential signer error")] - SignFailed(#[from] CredentialSignerSignError), -} - #[derive(Debug, Error)] pub enum DidVcServiceVerifyError { #[error("did public key not found. did: {0}")] From c5ed1f35b91ec194abe54f03744aa924815ade2e Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 16 Jul 2024 21:03:11 +0900 Subject: [PATCH 25/58] Improve error message --- src/keyring/keypair.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index abc38f1..c3984dd 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -15,10 +15,10 @@ pub struct KeyPairHex { #[derive(Error, Debug)] pub enum KeyPairingError { - #[error("from hex error")] + #[error("from hex error: {0}")] FromHex(#[from] FromHexError), - #[error("crypt error")] - Crypt, + #[error("crypt error: {0}")] + Crypt(String), } pub trait KeyPair: Sized { @@ -59,11 +59,11 @@ impl KeyPair for K256KeyPair { } fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; - let secret_key = - k256::SecretKey::from_slice(&secret_key).map_err(|_| KeyPairingError::Crypt)?; + let secret_key = k256::SecretKey::from_slice(&secret_key) + .map_err(|e| KeyPairingError::Crypt(e.to_string()))?; let public_key = hex::decode(&kp.public_key)?; - let public_key = - k256::PublicKey::from_sec1_bytes(&public_key).map_err(|_| KeyPairingError::Crypt)?; + let public_key = k256::PublicKey::from_sec1_bytes(&public_key) + .map_err(|e| KeyPairingError::Crypt(e.to_string()))?; Ok(K256KeyPair { public_key, secret_key }) } } @@ -98,10 +98,14 @@ impl KeyPair for X25519KeyP } fn from_hex_key_pair(kp: &KeyPairHex) -> Result { let secret_key = hex::decode(&kp.secret_key)?; - let secret_key: [u8; 32] = secret_key.try_into().map_err(|_| KeyPairingError::Crypt)?; + let secret_key: [u8; 32] = secret_key.try_into().map_err(|e: Vec| { + KeyPairingError::Crypt(format!("array length mismatch: {}", e.len())) + })?; let secret_key = x25519_dalek::StaticSecret::from(secret_key); let public_key = hex::decode(&kp.public_key)?; - let public_key: [u8; 32] = public_key.try_into().map_err(|_| KeyPairingError::Crypt)?; + let public_key: [u8; 32] = public_key.try_into().map_err(|e: Vec| { + KeyPairingError::Crypt(format!("array length mismatch: {}", e.len())) + })?; let public_key = x25519_dalek::PublicKey::from(public_key); Ok(X25519KeyPair { public_key, secret_key }) } From f453c173f0419b44938a7b2ae99fdf09a4dfc2d4 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 16:13:15 +0900 Subject: [PATCH 26/58] Re export didcomm-rs --- src/didcomm/encrypted.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index acab6ac..f5bab45 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -16,6 +16,7 @@ use crate::{ types::{VerifiableCredentials, VerifiedContainer}, }, }; +pub use didcomm_rs; #[async_trait::async_trait] pub trait DidCommEncryptedService: Sync { From bb6a4d41df442ee7f627eac3924aac53dac9765d Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 17:41:58 +0900 Subject: [PATCH 27/58] Refactor minor --- src/didcomm/encrypted.rs | 2 +- src/verifiable_credentials/did_vc.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index f5bab45..d722056 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -1,4 +1,5 @@ use cuid; +pub use didcomm_rs; use didcomm_rs::{crypto::CryptoAlgorithm, AttachmentBuilder, AttachmentDataBuilder, Message}; use serde_json::Value; use thiserror::Error; @@ -16,7 +17,6 @@ use crate::{ types::{VerifiableCredentials, VerifiedContainer}, }, }; -pub use didcomm_rs; #[async_trait::async_trait] pub trait DidCommEncryptedService: Sync { diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 2969514..49ca86a 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -34,7 +34,7 @@ pub enum DidVcServiceVerifyError { #[error("failed to get did document: {0}")] DidDocNotFound(String), #[error("failed to find indentifier: {0}")] - FindIdentifierError(FindIdentifierError), + FindIdentifier(FindIdentifierError), #[error("credential signer error")] VerifyFailed(#[from] CredentialSignerVerifyError), } @@ -62,7 +62,7 @@ impl DidVcService for R { let did_document = self .find_identifier(&model.issuer.id) .await - .map_err(Self::VerifyError::FindIdentifierError)?; + .map_err(Self::VerifyError::FindIdentifier)?; let did_document = did_document .ok_or(DidVcServiceVerifyError::DidDocNotFound(model.issuer.id.clone()))? .did_document; From 6d73f4532623681ec92431f7611da6e19cea1950 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 21:56:15 +0900 Subject: [PATCH 28/58] Refator error message --- src/didcomm/encrypted.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index d722056..c46cc16 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -110,7 +110,6 @@ fn didcomm_verify( message: &DidCommMessage, ) -> Result> { let public_key = get_encrypt_key(from_doc)?.as_bytes().to_vec(); - let public_key = Some(public_key); let message = Message::receive( @@ -174,13 +173,13 @@ where DidDocNotFound(String), #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), - #[error("something went wrong with vc service")] + #[error("something went wrong with vc service: {0}")] VcService(CredentialSignerSignError), - #[error("failed to create identifier")] + #[error("failed to create identifier: {0}")] SidetreeFindRequestFailed(FindIdentifierError), #[error("failed to encrypt message with error: {0}")] EncryptFailed(#[from] didcomm_rs::Error), - #[error("failed serialize/deserialize : {0}")] + #[error("failed serialize/deserialize: {0}")] Json(#[from] serde_json::Error), } @@ -188,19 +187,19 @@ where pub enum DidCommEncryptedServiceVerifyError { #[error("failed to get did document: {0}")] DidDocNotFound(String), - #[error("something went wrong with vc service")] + #[error("something went wrong with vc service: {0}")] VcService(#[from] CredentialSignerVerifyError), - #[error("failed to find identifier")] + #[error("failed to find identifier: {0}")] SidetreeFindRequestFailed(FindIdentifierError), #[error("did public key not found. did: {0}")] DidPublicKeyNotFound(#[from] GetPublicKeyError), - #[error("failed to decrypt message : {0}")] + #[error("failed to decrypt message: {0}")] DecryptFailed(#[from] didcomm_rs::Error), - #[error("failed to get body : {0:?}")] + #[error("failed to get body: {0:?}")] MetadataBodyNotFound(Option), - #[error("failed serialize/deserialize : {0}")] + #[error("failed serialize/deserialize: {0}")] Json(#[from] serde_json::Error), - #[error("failed to find sender did : {0}")] + #[error("failed to find sender did: {0}")] FindSender(#[from] FindSenderError), } From 6ed93f27aa1015b43d4305c147112b8ed3266ccf Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 22:06:20 +0900 Subject: [PATCH 29/58] Export multihash --- src/did/sidetree/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/did/sidetree/mod.rs b/src/did/sidetree/mod.rs index 8f8b414..1915d43 100644 --- a/src/did/sidetree/mod.rs +++ b/src/did/sidetree/mod.rs @@ -1,3 +1,3 @@ pub mod client; -mod multihash; +pub mod multihash; pub mod payload; From 6a35646ec83b72614b7fb6eb3ac0ae616ff74786 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 22:10:01 +0900 Subject: [PATCH 30/58] Export EC libs --- src/keyring/keypair.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index c3984dd..c2501be 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -2,9 +2,11 @@ use std::convert::{TryFrom, TryInto}; use elliptic_curve::sec1::ToEncodedPoint; use hex::FromHexError; +pub use k256; use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; +pub use x25519_dalek; #[derive(Clone, Serialize, Deserialize)] pub struct KeyPairHex { From 6aecefb5a2f5b95232bbe828002ec420154c3b92 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 17 Jul 2024 22:49:34 +0900 Subject: [PATCH 31/58] Zeroize --- Cargo.toml | 1 + src/keyring/keypair.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a5c0ddc..d11273e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ sha2 = { version = "0.10.8" } thiserror = "1.0.59" x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } rand_core = "0.6.4" +zeroize = "1.8.1" [dev-dependencies] actix-rt = { version = "2.9.0" } diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index c2501be..1731bed 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -7,8 +7,9 @@ use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; pub use x25519_dalek; +use zeroize::{Zeroize, ZeroizeOnDrop}; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] pub struct KeyPairHex { // MEMO: Matching schema in NodeX config. public_key: String, From 0e5a5da730c5a7b7c86e08febf4de6203d1dbde5 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Thu, 18 Jul 2024 19:49:53 +0900 Subject: [PATCH 32/58] Export libs --- src/did/sidetree/payload.rs | 2 +- src/keyring/keypair.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 20b4733..d69d5b3 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -2,7 +2,7 @@ use core::convert::TryInto; use data_encoding::BASE64URL_NOPAD; use serde::{Deserialize, Serialize}; -use serde_jcs; +pub use serde_jcs; use thiserror::Error; use crate::{did::sidetree::multihash, keyring::jwk::Jwk}; diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 1731bed..b87d4cd 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; pub use x25519_dalek; use zeroize::{Zeroize, ZeroizeOnDrop}; +pub use rand_core; #[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] pub struct KeyPairHex { From e82869a94f5b35e4ed43354dd13cb1aaeb2d4cf3 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Thu, 18 Jul 2024 20:09:32 +0900 Subject: [PATCH 33/58] Gen rid of dead code --- src/did/sidetree/payload.rs | 9 --------- src/keyring/keypair.rs | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index d69d5b3..53a64d5 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -37,15 +37,6 @@ pub struct DidPublicKey { pub public_key_jwk: Jwk, } -#[derive(Debug, Serialize, Deserialize)] -struct Authentication { - #[serde(rename = "type")] - r#type: String, - - #[serde(rename = "publicKey")] - public_key: String, -} - #[derive(Debug, Serialize, Deserialize)] pub struct DidDocument { // TODO: impl parser for mixed type diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index b87d4cd..02ad132 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -3,12 +3,12 @@ use std::convert::{TryFrom, TryInto}; use elliptic_curve::sec1::ToEncodedPoint; use hex::FromHexError; pub use k256; +pub use rand_core; use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; pub use x25519_dalek; use zeroize::{Zeroize, ZeroizeOnDrop}; -pub use rand_core; #[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] pub struct KeyPairHex { From 6e14b79790e6841bdcbc95b029bf66b5a1d47fb3 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 16:04:09 +0900 Subject: [PATCH 34/58] Change to use datetime as json field type --- Cargo.toml | 2 +- src/verifiable_credentials/credential_signer.rs | 12 ++++++++---- src/verifiable_credentials/types.rs | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d11273e..2c9b89d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ version = "0.1.2" [dependencies] arrayref = { version = "0.3.7" } async-trait = "0.1.79" -chrono = { version = "0.4" } +chrono = { version = "0.4", features = ["serde"] } cuid = { version = "1.3.2" } data-encoding = { version = "2.5.0" } didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index c6b8a1f..fab8060 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -45,7 +45,7 @@ impl CredentialSigner { r#type: "EcdsaSecp256k1Signature2019".to_string(), proof_purpose: "authentication".to_string(), // Assume that object.issuance_date is correct data - created: object.issuance_date.clone(), + created: object.issuance_date, verification_method: format!("{}#{}", did, key_id), jws, domain: None, @@ -70,6 +70,8 @@ impl CredentialSigner { #[cfg(test)] pub mod tests { + use chrono::{DateTime, Local}; + use super::*; use crate::verifiable_credentials::types::{CredentialSubject, Issuer}; @@ -105,7 +107,9 @@ pub mod tests { r#type: vec!["type".to_string()], issuer: Issuer { id: "issuer".to_string() }, context: vec!["context".to_string()], - issuance_date: "issuance_date".to_string(), + issuance_date: DateTime::parse_from_rfc3339("2024-07-19T06:06:51.361316372Z") + .unwrap() + .to_utc(), credential_subject: CredentialSubject { id: None, container: json!(r#"{"k":"0123456789abcdef"}"#), @@ -128,7 +132,7 @@ pub mod tests { Some(proof) => { assert_eq!( proof.jws, - "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdfQ..Qc-NyzQu2v735_qPR72j1oqUDK1Ne4XQ7Lc66_x9tlMSeI9xmrgguEA8UmQyTM0cd13xkvpK4g-NEWJBp8_d_w" + "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdfQ..LK8OcOuMgWU4Y5Zpz9jeQ8b5UsgDmjKJTBpuxFepGlp-hGVHVgyZz8QkZseqQRdUXn6JouVYo1jFsQCq_7p7ig" ); assert_eq!(proof.proof_purpose, "authentication"); assert_eq!(proof.r#type, "EcdsaSecp256k1Signature2019"); @@ -151,7 +155,7 @@ pub mod tests { r#type: vec!["type".to_string()], issuer: Issuer { id: "issuer".to_string() }, context: vec!["context".to_string()], - issuance_date: "issuance_date".to_string(), + issuance_date: Local::now().to_utc(), credential_subject: CredentialSubject { id: None, container: json!(r#"{"k":"0123456789abcdef"}"#), diff --git a/src/verifiable_credentials/types.rs b/src/verifiable_credentials/types.rs index bc1a915..86ee7ce 100644 --- a/src/verifiable_credentials/types.rs +++ b/src/verifiable_credentials/types.rs @@ -33,7 +33,7 @@ pub struct Proof { pub proof_purpose: String, #[serde(rename = "created")] - pub created: String, + pub created: DateTime, #[serde(rename = "verificationMethod")] pub verification_method: String, @@ -61,7 +61,7 @@ pub struct VerifiableCredentials { pub issuer: Issuer, #[serde(rename = "issuanceDate")] - pub issuance_date: String, + pub issuance_date: DateTime, #[serde(rename = "expirationDate", skip_serializing_if = "Option::is_none")] pub expiration_date: Option, @@ -89,7 +89,7 @@ impl VerifiableCredentials { issuer: Issuer { id: from_did }, r#type: vec![r#type], context: vec![context], - issuance_date: issuance_date.to_rfc3339(), + issuance_date, credential_subject: CredentialSubject { id: None, container: message }, expiration_date: None, proof: None, From 6785cab3d818cf1997230b71e488f2ee303cecb9 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 16:44:43 +0900 Subject: [PATCH 35/58] Re export http crate --- src/did/sidetree/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index cdf17a0..a8c7d07 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -1,4 +1,4 @@ -use http::StatusCode; +pub use http::StatusCode; #[derive(Clone, Debug)] pub struct SidetreeHttpClientResponse { From 53a1b25f0440c407c4c52805904edf87837983cb Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 17:22:16 +0900 Subject: [PATCH 36/58] Clean up library --- Cargo.toml | 29 +++++++++++++---------------- src/did/sidetree/client.rs | 2 +- src/did/sidetree/payload.rs | 1 - src/keyring/jwk.rs | 8 ++++---- src/keyring/keypair.rs | 5 +---- src/lib.rs | 7 +++++++ src/verifiable_credentials/jws.rs | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c9b89d..1fa6bd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,31 +9,28 @@ version = "0.1.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -arrayref = { version = "0.3.7" } async-trait = "0.1.79" chrono = { version = "0.4", features = ["serde"] } -cuid = { version = "1.3.2" } -data-encoding = { version = "2.5.0" } -didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } -hex = { version = "0.4.3" } -hmac = { version = "0.12.1" } -http = { version = "1.1.0" } +data-encoding = "2.5.0" +cuid = "1.3.2" +hex = "0.4.3" +generic-array = "0.14.7" +http = "1.1.0" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.115" +serde_jcs = "0.1.0" +thiserror = "1.0.58" +sha2 = "0.10.8" +rand_core = "0.6.4" +zeroize = "1.8.1" k256 = { version = "0.13.3", features = [ "ecdh", "ecdsa", "serde", "sha256", ] } -elliptic-curve = { version = "0.13.8", features = ["sec1"] } -log = { version = "0.4.21" } -serde = { version = "1.0.204", features = ["derive"] } -serde_jcs = { version = "0.1.0" } -serde_json = { version = "1.0.116" } -sha2 = { version = "0.10.8" } -thiserror = "1.0.59" x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } -rand_core = "0.6.4" -zeroize = "1.8.1" +didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } [dev-dependencies] actix-rt = { version = "2.9.0" } diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index a8c7d07..cdf17a0 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -1,4 +1,4 @@ -pub use http::StatusCode; +use http::StatusCode; #[derive(Clone, Debug)] pub struct SidetreeHttpClientResponse { diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 53a64d5..5e06d23 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -2,7 +2,6 @@ use core::convert::TryInto; use data_encoding::BASE64URL_NOPAD; use serde::{Deserialize, Serialize}; -pub use serde_jcs; use thiserror::Error; use crate::{did::sidetree::multihash, keyring::jwk::Jwk}; diff --git a/src/keyring/jwk.rs b/src/keyring/jwk.rs index 2ebc352..b884755 100644 --- a/src/keyring/jwk.rs +++ b/src/keyring/jwk.rs @@ -1,7 +1,7 @@ use std::convert::{From, Into, TryFrom, TryInto}; use data_encoding::BASE64URL_NOPAD; -use elliptic_curve::sec1::ToEncodedPoint; +use k256::elliptic_curve::sec1::ToEncodedPoint; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -44,8 +44,8 @@ pub enum JwkToX25519Error { fn decode_base64url( s: &str, -) -> Result, JwkToK256Error> { - let mut result = elliptic_curve::FieldBytes::::default(); +) -> Result, JwkToK256Error> { + let mut result = k256::elliptic_curve::FieldBytes::::default(); BASE64URL_NOPAD .decode_mut(s.as_bytes(), &mut result) .map_err(|_| JwkToK256Error::DecodeError)?; @@ -77,7 +77,7 @@ impl TryFrom for Jwk { let kty = "EC".to_string(); let crv = "secp256k1".to_string(); match value.coordinates() { - elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { + k256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { let x = BASE64URL_NOPAD.encode(x); let y = Some(BASE64URL_NOPAD.encode(y)); Ok(Jwk { kty, crv, x, y }) diff --git a/src/keyring/keypair.rs b/src/keyring/keypair.rs index 02ad132..77b6ce0 100644 --- a/src/keyring/keypair.rs +++ b/src/keyring/keypair.rs @@ -1,13 +1,10 @@ use std::convert::{TryFrom, TryInto}; -use elliptic_curve::sec1::ToEncodedPoint; use hex::FromHexError; -pub use k256; -pub use rand_core; +use k256::elliptic_curve::sec1::ToEncodedPoint; use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use thiserror::Error; -pub use x25519_dalek; use zeroize::{Zeroize, ZeroizeOnDrop}; #[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] diff --git a/src/lib.rs b/src/lib.rs index b8a9f48..80d791e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,10 @@ pub mod did; pub mod didcomm; pub mod keyring; pub mod verifiable_credentials; +pub use hex; +pub use http; +pub use k256; +pub use rand_core; +pub use serde_jcs; +pub use sha2; +pub use x25519_dalek; diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index 808b58a..5250d9c 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -1,5 +1,5 @@ use data_encoding::BASE64URL_NOPAD; -use hmac::digest::generic_array::GenericArray; +use generic_array::GenericArray; use k256::ecdsa::{ signature::{Signer, Verifier}, Signature, SigningKey, VerifyingKey, From b375428a5d1278e09510fe46c5271c8fa28f4166 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 17:29:22 +0900 Subject: [PATCH 37/58] Re export again --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 80d791e..ea73f54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ pub mod did; pub mod didcomm; pub mod keyring; pub mod verifiable_credentials; +pub use chrono; +pub use cuid; pub use hex; pub use http; pub use k256; From ff2cd19b59308b2cf7955df940a10936ab0b2f10 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 17:32:29 +0900 Subject: [PATCH 38/58] Refactor minor --- src/verifiable_credentials/credential_signer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/verifiable_credentials/credential_signer.rs b/src/verifiable_credentials/credential_signer.rs index fab8060..30b8196 100644 --- a/src/verifiable_credentials/credential_signer.rs +++ b/src/verifiable_credentials/credential_signer.rs @@ -70,7 +70,7 @@ impl CredentialSigner { #[cfg(test)] pub mod tests { - use chrono::{DateTime, Local}; + use chrono::{DateTime, Utc}; use super::*; use crate::verifiable_credentials::types::{CredentialSubject, Issuer}; @@ -155,7 +155,7 @@ pub mod tests { r#type: vec!["type".to_string()], issuer: Issuer { id: "issuer".to_string() }, context: vec!["context".to_string()], - issuance_date: Local::now().to_utc(), + issuance_date: Utc::now(), credential_subject: CredentialSubject { id: None, container: json!(r#"{"k":"0123456789abcdef"}"#), From 6b4df751eea576f2c6c31b694c7a13b08dc1565e Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 17:50:36 +0900 Subject: [PATCH 39/58] Re export again --- src/didcomm/encrypted.rs | 1 + src/lib.rs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index c46cc16..c111507 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -1,6 +1,7 @@ use cuid; pub use didcomm_rs; use didcomm_rs::{crypto::CryptoAlgorithm, AttachmentBuilder, AttachmentDataBuilder, Message}; +pub use serde_json; use serde_json::Value; use thiserror::Error; diff --git a/src/lib.rs b/src/lib.rs index ea73f54..701f61c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,6 @@ pub mod did; pub mod didcomm; pub mod keyring; pub mod verifiable_credentials; -pub use chrono; -pub use cuid; -pub use hex; pub use http; pub use k256; pub use rand_core; From 8a2e1dbb39e2338ab6fcbc0cba41c3563ca56d82 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 19 Jul 2024 19:37:33 +0900 Subject: [PATCH 40/58] Refactor jwk --- src/keyring/jwk.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/keyring/jwk.rs b/src/keyring/jwk.rs index b884755..362afa4 100644 --- a/src/keyring/jwk.rs +++ b/src/keyring/jwk.rs @@ -1,6 +1,7 @@ use std::convert::{From, Into, TryFrom, TryInto}; -use data_encoding::BASE64URL_NOPAD; +pub use data_encoding; +use data_encoding::{DecodeError, DecodePartial, BASE64URL_NOPAD}; use k256::elliptic_curve::sec1::ToEncodedPoint; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -25,30 +26,32 @@ pub enum JwkToK256Error { #[error("missing y")] MissingY, #[error("decode error")] - DecodeError, + Decode(DecodePartial), #[error("different crv")] DifferentCrv, - #[error("crypt error")] - CryptError, + #[error("crypt error: {0}")] + Crypt(#[from] k256::elliptic_curve::Error), } #[derive(Error, Debug)] pub enum JwkToX25519Error { #[error("decode error")] - DecodeError, + Decode(Option), #[error("different crv")] DifferentCrv, - #[error("crypt error")] - CryptError, +} + +#[derive(Error, Debug)] +pub enum K256ToJwkError { + #[error("points are invalid")] + PointsInvalid, } fn decode_base64url( s: &str, ) -> Result, JwkToK256Error> { let mut result = k256::elliptic_curve::FieldBytes::::default(); - BASE64URL_NOPAD - .decode_mut(s.as_bytes(), &mut result) - .map_err(|_| JwkToK256Error::DecodeError)?; + BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut result).map_err(JwkToK256Error::Decode)?; Ok(result) } @@ -62,8 +65,8 @@ impl TryFrom for k256::PublicKey { let x = decode_base64url(&value.x)?; let y = decode_base64url(&y)?; let pk = k256::EncodedPoint::from_affine_coordinates(&x, &y, false); - let pk = k256::PublicKey::from_sec1_bytes(pk.as_bytes()); - pk.map_err(|_| JwkToK256Error::CryptError) + let pk = k256::PublicKey::from_sec1_bytes(pk.as_bytes())?; + Ok(pk) } else { Err(JwkToK256Error::MissingY) } @@ -71,7 +74,7 @@ impl TryFrom for k256::PublicKey { } impl TryFrom for Jwk { - type Error = (); + type Error = K256ToJwkError; fn try_from(value: k256::PublicKey) -> Result { let value = value.to_encoded_point(false); let kty = "EC".to_string(); @@ -82,7 +85,7 @@ impl TryFrom for Jwk { let y = Some(BASE64URL_NOPAD.encode(y)); Ok(Jwk { kty, crv, x, y }) } - _ => Err(()), + _ => Err(K256ToJwkError::PointsInvalid), } } } @@ -95,8 +98,8 @@ impl TryFrom for x25519_dalek::PublicKey { } let pk = BASE64URL_NOPAD .decode(value.x.as_bytes()) - .map_err(|_| JwkToX25519Error::DecodeError)?; - let pk: [u8; 32] = pk.try_into().map_err(|_| JwkToX25519Error::DecodeError)?; + .map_err(|e| JwkToX25519Error::Decode(Some(e)))?; + let pk: [u8; 32] = pk.try_into().map_err(|_| JwkToX25519Error::Decode(None))?; Ok(pk.into()) } } From 43d95d88d5f37d98598f2c0dd7bfbfd054a1d40d Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 20 Jul 2024 09:03:39 +0900 Subject: [PATCH 41/58] Refactor --- src/did/did_repository.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 7bbdb26..035367c 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -83,20 +83,12 @@ pub trait DidRepository: Sync { ) -> Result, Self::FindIdentifierError>; } -pub struct DidRepositoryImpl { +#[derive(Clone)] +pub struct DidRepositoryImpl { client: C, } -impl Clone for DidRepositoryImpl -where - C: SidetreeHttpClient + Send + Sync + Clone, -{ - fn clone(&self) -> Self { - Self { client: self.client.clone() } - } -} - -impl DidRepositoryImpl { +impl DidRepositoryImpl { pub fn new(client: C) -> Self { Self { client } } From b7e20be4529043c4b312fae7c7227c30f395d7c0 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 13:38:05 +0900 Subject: [PATCH 42/58] Refactor minor --- src/did/did_repository.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 035367c..a2190bf 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -16,7 +16,7 @@ use crate::keyring::{ #[derive(Debug, thiserror::Error)] pub enum CreateIdentifierError { #[error("Failed to convert to JWK")] - JwkError, + Jwk, #[error("Failed to build operation payload: {0}")] PayloadBuildFailed(#[from] crate::did::sidetree::payload::DidCreatePayloadError), #[error("Failed to parse body: {0}")] @@ -114,7 +114,7 @@ impl DidRepository for DidRepositoryImpl "signingKey".to_string(), vec!["auth".to_string(), "general".to_string()], ) - .map_err(|_| CreateIdentifierError::JwkError)?; + .map_err(|_| CreateIdentifierError::Jwk)?; // vec!["keyAgreement".to_string()] let enc = keyring .encrypt @@ -124,17 +124,11 @@ impl DidRepository for DidRepositoryImpl "encryptionKey".to_string(), vec!["auth".to_string(), "general".to_string()], ) - .map_err(|_| CreateIdentifierError::JwkError)?; - let update: Jwk = keyring - .update - .get_public_key() - .try_into() - .map_err(|_| CreateIdentifierError::JwkError)?; - let recovery: Jwk = keyring - .recovery - .get_public_key() - .try_into() - .map_err(|_| CreateIdentifierError::JwkError)?; + .map_err(|_| CreateIdentifierError::Jwk)?; + let update: Jwk = + keyring.update.get_public_key().try_into().map_err(|_| CreateIdentifierError::Jwk)?; + let recovery: Jwk = + keyring.recovery.get_public_key().try_into().map_err(|_| CreateIdentifierError::Jwk)?; let document = DidReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; let payload = did_create_payload(document, &update, &recovery)?; @@ -145,8 +139,7 @@ impl DidRepository for DidRepositoryImpl .await .map_err(CreateIdentifierError::SidetreeHttpClientError)?; if response.status_code.is_success() { - let response = serde_json::from_str(&response.body)?; - Ok(response) + Ok(serde_json::from_str(&response.body)?) } else { Err(CreateIdentifierError::SidetreeRequestFailed(format!("{:?}", response))) } From e6f59a5013f6bdd48f4f5b5616d521259e5c525b Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 14:16:07 +0900 Subject: [PATCH 43/58] Refactor minor --- src/did/did_repository.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index a2190bf..be9e6c0 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -20,11 +20,11 @@ pub enum CreateIdentifierError { #[error("Failed to build operation payload: {0}")] PayloadBuildFailed(#[from] crate::did::sidetree::payload::DidCreatePayloadError), #[error("Failed to parse body: {0}")] - BodyParseError(#[from] serde_json::Error), + BodyParse(#[from] serde_json::Error), #[error("Failed to create identifier. response: {0}")] SidetreeRequestFailed(String), #[error("Failed to send request: {0}")] - SidetreeHttpClientError(StudioClientError), + SidetreeHttpClient(StudioClientError), } #[derive(Debug, thiserror::Error)] @@ -32,9 +32,9 @@ pub enum FindIdentifierError { #[error("Failed to send request to sidetree: {0}")] SidetreeRequestFailed(String), #[error("Failed to parse body: {0}")] - BodyParseError(#[from] serde_json::Error), + BodyParse(#[from] serde_json::Error), #[error("Failed to send request: {0}")] - SidetreeHttpClientError(StudioClientError), + SidetreeHttpClient(StudioClientError), } #[derive(Debug, thiserror::Error)] @@ -42,9 +42,9 @@ pub enum GetPublicKeyError { #[error("Failed to get public key")] PublicKeyNotFound(String), #[error("Failed to convert from JWK: {0}")] - JwkToK256Error(#[from] crate::keyring::jwk::JwkToK256Error), + JwkToK256(#[from] crate::keyring::jwk::JwkToK256Error), #[error("Failed to convert from JWK: {0}")] - JwkToX25519Error(#[from] crate::keyring::jwk::JwkToX25519Error), + JwkToX25519(#[from] crate::keyring::jwk::JwkToX25519Error), } fn get_key(key_type: &str, did_document: &DidDocument) -> Result { @@ -137,7 +137,7 @@ impl DidRepository for DidRepositoryImpl .client .post_create_identifier(&payload) .await - .map_err(CreateIdentifierError::SidetreeHttpClientError)?; + .map_err(CreateIdentifierError::SidetreeHttpClient)?; if response.status_code.is_success() { Ok(serde_json::from_str(&response.body)?) } else { @@ -153,7 +153,7 @@ impl DidRepository for DidRepositoryImpl .client .get_find_identifier(did) .await - .map_err(FindIdentifierError::SidetreeHttpClientError)?; + .map_err(FindIdentifierError::SidetreeHttpClient)?; match response.status_code { StatusCode::OK => Ok(Some(serde_json::from_str(&response.body)?)), From e84f19f2049bcb151002c2808efd1648bbecb0d7 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 14:31:30 +0900 Subject: [PATCH 44/58] Format Cargo.toml --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1fa6bd7..c70f96b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,5 +33,5 @@ x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } [dev-dependencies] -actix-rt = { version = "2.9.0" } -rand = { version = "0.8.5" } +actix-rt = "2.9.0" +rand = "0.8.5" From e7d50c8f1b78175219b89b3937efc463f45cc133 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 14:46:31 +0900 Subject: [PATCH 45/58] Fix use generic_array --- Cargo.toml | 1 - src/verifiable_credentials/jws.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c70f96b..7eadfde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ chrono = { version = "0.4", features = ["serde"] } data-encoding = "2.5.0" cuid = "1.3.2" hex = "0.4.3" -generic-array = "0.14.7" http = "1.1.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index 5250d9c..ff88497 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -1,11 +1,11 @@ use data_encoding::BASE64URL_NOPAD; -use generic_array::GenericArray; use k256::ecdsa::{ signature::{Signer, Verifier}, Signature, SigningKey, VerifyingKey, }; use serde::{Deserialize, Serialize}; use serde_json::Value; +use sha2::digest::generic_array::GenericArray; use thiserror::Error; // TODO: Design the interface to have an implementation with accelerators. From 473298ffb8dd5fc66a1a56f48253607630c8792a Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 14:48:04 +0900 Subject: [PATCH 46/58] Fix again --- src/verifiable_credentials/jws.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index ff88497..f1775cf 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -1,11 +1,13 @@ use data_encoding::BASE64URL_NOPAD; -use k256::ecdsa::{ - signature::{Signer, Verifier}, - Signature, SigningKey, VerifyingKey, +use k256::{ + ecdsa::{ + signature::{Signer, Verifier}, + Signature, SigningKey, VerifyingKey, + }, + sha2::digest::generic_array::GenericArray, }; use serde::{Deserialize, Serialize}; use serde_json::Value; -use sha2::digest::generic_array::GenericArray; use thiserror::Error; // TODO: Design the interface to have an implementation with accelerators. From e0d4864234c47044efeb648ebcd8212f5e85cc08 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 19:15:38 +0900 Subject: [PATCH 47/58] Bump up major version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7eadfde..616bd89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2018" keywords = ["did", "embedded", "iot", "root-of-trust"] name = "nodex-didcomm" readme = "README.md" -version = "0.1.2" +version = "0.2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 61760bbad043b1991fbcf594eab5da44f40a1ed4 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sun, 21 Jul 2024 20:58:46 +0900 Subject: [PATCH 48/58] Remove GenericArray --- src/verifiable_credentials/jws.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index f1775cf..4639567 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -1,10 +1,9 @@ +use std::convert::TryInto; + use data_encoding::BASE64URL_NOPAD; -use k256::{ - ecdsa::{ - signature::{Signer, Verifier}, - Signature, SigningKey, VerifyingKey, - }, - sha2::digest::generic_array::GenericArray, +use k256::ecdsa::{ + signature::{Signer, Verifier}, + Signature, SigningKey, VerifyingKey, }; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -114,8 +113,8 @@ pub fn verify( if signature.len() != 64 { return Err(JwsDecodeError::InvalidSignatureLength(signature.len())); } - let r = GenericArray::from_slice(&signature[0..32]); - let s = GenericArray::from_slice(&signature[32..]); + let r: &[u8; 32] = &signature[0..32].try_into().unwrap(); + let s: &[u8; 32] = &signature[32..].try_into().unwrap(); let wrapped_signature = Signature::from_scalars(*r, *s)?; let verify_key = VerifyingKey::from(public_key); From c1e00336a82439e6b78a9565346c05955999bdb4 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 22 Jul 2024 10:28:39 +0900 Subject: [PATCH 49/58] Upgrade deps --- Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 616bd89..5ca89ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,16 +9,16 @@ version = "0.2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.79" +async-trait = "0.1.81" chrono = { version = "0.4", features = ["serde"] } -data-encoding = "2.5.0" +data-encoding = "2.6.0" cuid = "1.3.2" hex = "0.4.3" http = "1.1.0" -serde = { version = "1.0.197", features = ["derive"] } -serde_json = "1.0.115" +serde = { version = "1.0.204", features = ["derive"] } +serde_json = "1.0.120" serde_jcs = "0.1.0" -thiserror = "1.0.58" +thiserror = "1.0.63" sha2 = "0.10.8" rand_core = "0.6.4" zeroize = "1.8.1" @@ -32,5 +32,5 @@ x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } [dev-dependencies] -actix-rt = "2.9.0" +actix-rt = "2.10.0" rand = "0.8.5" From de2f0ec0b798ac28a78713fa8afc6c40c492c12e Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 22 Jul 2024 11:22:54 +0900 Subject: [PATCH 50/58] Fix typo --- src/verifiable_credentials/did_vc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 49ca86a..656e45b 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -33,7 +33,7 @@ pub enum DidVcServiceVerifyError { PublicKeyNotFound(#[from] GetPublicKeyError), #[error("failed to get did document: {0}")] DidDocNotFound(String), - #[error("failed to find indentifier: {0}")] + #[error("failed to find identifier: {0}")] FindIdentifier(FindIdentifierError), #[error("credential signer error")] VerifyFailed(#[from] CredentialSignerVerifyError), From e01487ec6f06cb8c53d05b40ea88855efe103b0b Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Mon, 22 Jul 2024 11:30:02 +0900 Subject: [PATCH 51/58] Format Cargo.toml --- Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ca89ec..e3591da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,25 +11,25 @@ version = "0.2.0" [dependencies] async-trait = "0.1.81" chrono = { version = "0.4", features = ["serde"] } -data-encoding = "2.6.0" cuid = "1.3.2" +data-encoding = "2.6.0" +didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } hex = "0.4.3" http = "1.1.0" -serde = { version = "1.0.204", features = ["derive"] } -serde_json = "1.0.120" -serde_jcs = "0.1.0" -thiserror = "1.0.63" -sha2 = "0.10.8" -rand_core = "0.6.4" -zeroize = "1.8.1" k256 = { version = "0.13.3", features = [ "ecdh", "ecdsa", "serde", "sha256", ] } +rand_core = "0.6.4" +serde = { version = "1.0.204", features = ["derive"] } +serde_jcs = "0.1.0" +serde_json = "1.0.120" +sha2 = "0.10.8" +thiserror = "1.0.63" x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } -didcomm-rs = { git = "https://github.com/nodecross/didcomm-rs.git", tag = "v0.8.1", default-features = false, features = ["raw-crypto"] } +zeroize = "1.8.1" [dev-dependencies] actix-rt = "2.10.0" From 1e6d8fb7d363a3fa18f1d025780748a2c5944b79 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Tue, 23 Jul 2024 14:55:05 +0900 Subject: [PATCH 52/58] Migrate async-trait to trait-variant --- Cargo.toml | 2 +- src/did/did_repository.rs | 8 ++------ src/did/sidetree/client.rs | 4 ++-- src/didcomm/encrypted.rs | 6 ++---- src/verifiable_credentials/did_vc.rs | 7 +++---- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3591da..cba2f45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ version = "0.2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.81" chrono = { version = "0.4", features = ["serde"] } cuid = "1.3.2" data-encoding = "2.6.0" @@ -28,6 +27,7 @@ serde_jcs = "0.1.0" serde_json = "1.0.120" sha2 = "0.10.8" thiserror = "1.0.63" +trait-variant = "0.1.2" x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } zeroize = "1.8.1" diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index be9e6c0..b3459c3 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -69,8 +69,8 @@ pub fn get_encrypt_key( Ok(public_key.try_into()?) } -#[async_trait::async_trait] -pub trait DidRepository: Sync { +#[trait_variant::make(DidRepository: Send)] +pub trait LocalDidRepository: Sync { type CreateIdentifierError: std::error::Error + Send + Sync; type FindIdentifierError: std::error::Error + Send + Sync; async fn create_identifier( @@ -94,7 +94,6 @@ impl DidRepositoryImpl { } } -#[async_trait::async_trait] impl DidRepository for DidRepositoryImpl { type CreateIdentifierError = CreateIdentifierError; type FindIdentifierError = FindIdentifierError; @@ -191,7 +190,6 @@ pub mod mocks { #[derive(Debug, thiserror::Error)] pub enum DummyError {} - #[async_trait::async_trait] impl DidRepository for MockDidRepository { type CreateIdentifierError = CreateIdentifierError; type FindIdentifierError = FindIdentifierError; @@ -250,7 +248,6 @@ pub mod mocks { #[derive(Clone, Copy)] pub struct NoPublicKeyDidRepository; - #[async_trait::async_trait] impl DidRepository for NoPublicKeyDidRepository { type CreateIdentifierError = CreateIdentifierError; type FindIdentifierError = FindIdentifierError; @@ -284,7 +281,6 @@ pub mod mocks { #[derive(Clone, Copy)] pub struct IllegalPublicKeyLengthDidRepository; - #[async_trait::async_trait] impl DidRepository for IllegalPublicKeyLengthDidRepository { type CreateIdentifierError = CreateIdentifierError; type FindIdentifierError = FindIdentifierError; diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index cdf17a0..196ded6 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -12,8 +12,8 @@ impl SidetreeHttpClientResponse { } } -#[async_trait::async_trait] -pub trait SidetreeHttpClient { +#[trait_variant::make(SidetreeHttpClient: Send)] +pub trait LocalSidetreeHttpClient { type Error: std::error::Error + Send + Sync; async fn post_create_identifier( &self, diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index c111507..262b577 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -19,8 +19,8 @@ use crate::{ }, }; -#[async_trait::async_trait] -pub trait DidCommEncryptedService: Sync { +#[trait_variant::make(DidCommEncryptedService: Send)] +pub trait LocalDidCommEncryptedService: Sync { type GenerateError: std::error::Error; type VerifyError: std::error::Error; async fn generate( @@ -204,7 +204,6 @@ pub enum DidCommEncryptedServiceVerifyError DidCommEncryptedService for R where R: DidRepository + DidVcService, @@ -248,7 +247,6 @@ where } } -#[async_trait::async_trait] impl DidCommEncryptedService for DidCommServiceWithAttachment where R: DidRepository + DidVcService, diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 656e45b..47e808d 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -12,8 +12,8 @@ use crate::{ }, }; -#[async_trait::async_trait] -pub trait DidVcService: Sync { +#[trait_variant::make(DidVcService: Send)] +pub trait LocalDidVcService: Sync { type GenerateError: std::error::Error; type VerifyError: std::error::Error; fn generate( @@ -39,7 +39,6 @@ pub enum DidVcServiceVerifyError { VerifyFailed(#[from] CredentialSignerVerifyError), } -#[async_trait::async_trait] impl DidVcService for R { type GenerateError = CredentialSignerSignError; type VerifyError = DidVcServiceVerifyError; @@ -79,7 +78,7 @@ mod tests { use rand_core::OsRng; use serde_json::{json, Value}; - use super::*; + use super::{DidVcService, DidVcServiceVerifyError, VerifiableCredentials}; use crate::{ did::{did_repository::mocks::MockDidRepository, test_utils::create_random_did}, keyring::keypair::KeyPairing, From a6e4b103e99360dd9945a98e43b800326bc0fa30 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 24 Jul 2024 16:05:08 +0900 Subject: [PATCH 53/58] Refactor --- src/did/did_repository.rs | 32 +++++++++++----------------- src/did/sidetree/client.rs | 4 ++-- src/didcomm/encrypted.rs | 10 ++++----- src/keyring/jwk.rs | 2 +- src/verifiable_credentials/did_vc.rs | 8 +++---- src/verifiable_credentials/jws.rs | 4 ++-- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index b3459c3..e7fa4e4 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -15,8 +15,8 @@ use crate::keyring::{ #[derive(Debug, thiserror::Error)] pub enum CreateIdentifierError { - #[error("Failed to convert to JWK")] - Jwk, + #[error("Failed to convert to JWK: {0}")] + Jwk(#[from] crate::keyring::jwk::K256ToJwkError), #[error("Failed to build operation payload: {0}")] PayloadBuildFailed(#[from] crate::did::sidetree::payload::DidCreatePayloadError), #[error("Failed to parse body: {0}")] @@ -39,7 +39,7 @@ pub enum FindIdentifierError { #[derive(Debug, thiserror::Error)] pub enum GetPublicKeyError { - #[error("Failed to get public key")] + #[error("Failed to get public key: {0}")] PublicKeyNotFound(String), #[error("Failed to convert from JWK: {0}")] JwkToK256(#[from] crate::keyring::jwk::JwkToK256Error), @@ -69,8 +69,8 @@ pub fn get_encrypt_key( Ok(public_key.try_into()?) } -#[trait_variant::make(DidRepository: Send)] -pub trait LocalDidRepository: Sync { +#[trait_variant::make(Send)] +pub trait DidRepository: Sync { type CreateIdentifierError: std::error::Error + Send + Sync; type FindIdentifierError: std::error::Error + Send + Sync; async fn create_identifier( @@ -105,15 +105,11 @@ impl DidRepository for DidRepositoryImpl // TODO: This purpose property is strange... // https://identity.foundation/sidetree/spec/#add-public-keys // vec!["assertionMethod".to_string()], - let sign = keyring - .sign - .get_public_key() - .to_public_key( - "EcdsaSecp256k1VerificationKey2019".to_string(), - "signingKey".to_string(), - vec!["auth".to_string(), "general".to_string()], - ) - .map_err(|_| CreateIdentifierError::Jwk)?; + let sign = keyring.sign.get_public_key().to_public_key( + "EcdsaSecp256k1VerificationKey2019".to_string(), + "signingKey".to_string(), + vec!["auth".to_string(), "general".to_string()], + )?; // vec!["keyAgreement".to_string()] let enc = keyring .encrypt @@ -123,11 +119,9 @@ impl DidRepository for DidRepositoryImpl "encryptionKey".to_string(), vec!["auth".to_string(), "general".to_string()], ) - .map_err(|_| CreateIdentifierError::Jwk)?; - let update: Jwk = - keyring.update.get_public_key().try_into().map_err(|_| CreateIdentifierError::Jwk)?; - let recovery: Jwk = - keyring.recovery.get_public_key().try_into().map_err(|_| CreateIdentifierError::Jwk)?; + .unwrap(); + let update: Jwk = keyring.update.get_public_key().try_into()?; + let recovery: Jwk = keyring.recovery.get_public_key().try_into()?; let document = DidReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; let payload = did_create_payload(document, &update, &recovery)?; diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index 196ded6..9863c45 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -12,8 +12,8 @@ impl SidetreeHttpClientResponse { } } -#[trait_variant::make(SidetreeHttpClient: Send)] -pub trait LocalSidetreeHttpClient { +#[trait_variant::make(Send)] +pub trait SidetreeHttpClient { type Error: std::error::Error + Send + Sync; async fn post_create_identifier( &self, diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 262b577..7e0133c 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -19,10 +19,10 @@ use crate::{ }, }; -#[trait_variant::make(DidCommEncryptedService: Send)] -pub trait LocalDidCommEncryptedService: Sync { - type GenerateError: std::error::Error; - type VerifyError: std::error::Error; +#[trait_variant::make(Send)] +pub trait DidCommEncryptedService: Sync { + type GenerateError: std::error::Error + Sync + Send; + type VerifyError: std::error::Error + Sync + Send; async fn generate( &self, model: VerifiableCredentials, @@ -194,7 +194,7 @@ pub enum DidCommEncryptedServiceVerifyError), diff --git a/src/keyring/jwk.rs b/src/keyring/jwk.rs index 362afa4..14b1da5 100644 --- a/src/keyring/jwk.rs +++ b/src/keyring/jwk.rs @@ -35,7 +35,7 @@ pub enum JwkToK256Error { #[derive(Error, Debug)] pub enum JwkToX25519Error { - #[error("decode error")] + #[error("decode error: {0:?}")] Decode(Option), #[error("different crv")] DifferentCrv, diff --git a/src/verifiable_credentials/did_vc.rs b/src/verifiable_credentials/did_vc.rs index 47e808d..52ab1b7 100644 --- a/src/verifiable_credentials/did_vc.rs +++ b/src/verifiable_credentials/did_vc.rs @@ -12,10 +12,10 @@ use crate::{ }, }; -#[trait_variant::make(DidVcService: Send)] -pub trait LocalDidVcService: Sync { - type GenerateError: std::error::Error; - type VerifyError: std::error::Error; +#[trait_variant::make(Send)] +pub trait DidVcService: Sync { + type GenerateError: std::error::Error + Send + Sync; + type VerifyError: std::error::Error + Send + Sync; fn generate( &self, model: VerifiableCredentials, diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index 4639567..b925ac7 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -28,7 +28,7 @@ pub enum JwsEncodeError { #[derive(Debug, Error)] pub enum JwsDecodeError { - #[error("DecodeError: {0}")] + #[error("DecodeError: {0:?}")] DecodeError(#[from] data_encoding::DecodeError), #[error(transparent)] JsonParseError(#[from] serde_json::Error), @@ -44,7 +44,7 @@ pub enum JwsDecodeError { EmptyPayload, #[error("InvalidJws : {0}")] InvalidJws(String), - #[error("CryptError: {0}")] + #[error("CryptError: {0:?}")] CryptError(#[from] k256::ecdsa::Error), #[error("FromUtf8Error: {0}")] FromUtf8Error(#[from] std::string::FromUtf8Error), From bb65964ec30e5bda20d1a27354d96f355b64da90 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Wed, 24 Jul 2024 16:19:53 +0900 Subject: [PATCH 54/58] Upgrade rust edition --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cba2f45..644dbff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] categories = ["cryptography", "embedded"] -edition = "2018" +edition = "2021" keywords = ["did", "embedded", "iot", "root-of-trust"] name = "nodex-didcomm" readme = "README.md" From f4cf6070f1f9b6208fee91ac94ec4cf240cb69d5 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Thu, 25 Jul 2024 17:49:02 +0900 Subject: [PATCH 55/58] Fix Sidetree Client --- src/did/did_repository.rs | 11 +- src/did/sidetree/multihash.rs | 2 +- src/did/sidetree/payload.rs | 190 ++++++++++++++++++++---------- src/verifiable_credentials/jws.rs | 6 +- src/verifiable_credentials/mod.rs | 2 +- 5 files changed, 135 insertions(+), 76 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index e7fa4e4..6d7fc09 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -5,7 +5,7 @@ use http::StatusCode; use super::sidetree::{ client::SidetreeHttpClient, payload::{ - did_create_payload, DidDocument, DidReplacePayload, DidResolutionResponse, ToPublicKey, + did_create_payload, DidDocument, DidPatchDocument, DidResolutionResponse, ToPublicKey, }, }; use crate::keyring::{ @@ -120,11 +120,10 @@ impl DidRepository for DidRepositoryImpl vec!["auth".to_string(), "general".to_string()], ) .unwrap(); - let update: Jwk = keyring.update.get_public_key().try_into()?; - let recovery: Jwk = keyring.recovery.get_public_key().try_into()?; - let document = - DidReplacePayload { public_keys: vec![sign, enc], service_endpoints: vec![] }; - let payload = did_create_payload(document, &update, &recovery)?; + let update = keyring.update.get_public_key(); + let recovery = keyring.recovery.get_public_key(); + let document = DidPatchDocument { public_keys: vec![sign, enc], service_endpoints: vec![] }; + let payload = did_create_payload(document, update, recovery)?; let response = self .client diff --git a/src/did/sidetree/multihash.rs b/src/did/sidetree/multihash.rs index 03409c3..61889b5 100644 --- a/src/did/sidetree/multihash.rs +++ b/src/did/sidetree/multihash.rs @@ -15,7 +15,7 @@ pub fn hash(message: &[u8]) -> Vec { } pub fn double_hash_encode(message: &[u8]) -> String { - let mes = hash(message); + let mes = Sha256::digest(message).to_vec(); let mes = hash(&mes); BASE64URL_NOPAD.encode(&mes) } diff --git a/src/did/sidetree/payload.rs b/src/did/sidetree/payload.rs index 5e06d23..18b2e6a 100644 --- a/src/did/sidetree/payload.rs +++ b/src/did/sidetree/payload.rs @@ -1,10 +1,15 @@ use core::convert::TryInto; use data_encoding::BASE64URL_NOPAD; +use k256::ecdsa::{signature::Signer, Signature, SigningKey}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::{did::sidetree::multihash, keyring::jwk::Jwk}; +use crate::{ + did::sidetree::multihash, keyring::jwk::Jwk, verifiable_credentials::jws::JwsEncodeError, +}; + +// TODO: Migrate Sidetree Version #[derive(Debug, Serialize, Deserialize)] pub struct ServiceEndpoint { @@ -94,46 +99,39 @@ where } } -// ACTION: replace #[derive(Debug, Serialize, Deserialize)] -pub struct DidReplacePayload { +pub struct DidPatchDocument { #[serde(rename = "public_keys")] pub public_keys: Vec, #[serde(rename = "service_endpoints")] - pub service_endpoints: Vec, + pub service_endpoints: Vec, } #[derive(Debug, Serialize, Deserialize)] -struct DidReplaceAction { - action: String, // 'replace', - document: DidReplacePayload, +#[serde(tag = "action")] +pub enum DidAction { + #[serde(rename = "replace")] + Replace { document: DidPatchDocument }, + #[serde(rename = "add-public-keys")] + AddPublicKeys { + #[serde(rename = "public_keys")] + public_keys: Vec, + }, } #[derive(Serialize, Deserialize, Debug)] -struct DidReplaceDeltaObject { - patches: Vec, +struct DidDeltaObject { + patches: Vec, update_commitment: String, } #[derive(Debug, Serialize, Deserialize)] -struct DidReplaceSuffixObject { +struct DidSuffixObject { delta_hash: String, recovery_commitment: String, } -// ACTION: ietf-json-patch -#[allow(dead_code)] -struct DidIetfJsonPatchAction { - action: String, /* 'replace', - * patches: Vec<> */ -} - -#[allow(dead_code)] -struct DidResolutionRequest { - did: String, -} - #[derive(Debug, Serialize, Deserialize)] pub struct MethodMetadata { #[serde(rename = "published")] @@ -158,32 +156,21 @@ pub struct DidResolutionResponse { pub method_metadata: MethodMetadata, } -#[derive(Clone, Serialize, Deserialize)] -pub struct CommitmentKeys { - #[serde(rename = "recovery")] - pub recovery: Jwk, - - #[serde(rename = "update")] - pub update: Jwk, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct DidCreateRequest { - #[serde(rename = "publicKeys")] - pub public_keys: Vec, - - #[serde(rename = "commitmentKeys")] - pub commitment_keys: CommitmentKeys, - - #[serde(rename = "serviceEndpoints")] - pub service_endpoints: Vec, -} - #[derive(Debug, Serialize, Deserialize)] -struct DidCreatePayload { - r#type: String, // 'create', - delta: String, - suffix_data: String, +#[serde(tag = "type")] +enum DidPayload { + #[serde(rename = "create")] + Create { delta: String, suffix_data: String }, + #[serde(rename = "update")] + Update { + delta: String, + // #[serde(rename = "revealValue")] + // reveal_value: String, + #[serde(rename = "did_suffix")] + did_suffix: String, + #[serde(rename = "signed_data")] + signed_data: String, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -202,6 +189,8 @@ pub struct DidCreateResponse { pub enum DidCreatePayloadError { #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), + #[error("Failed to convert to JWK: {0}")] + Jwk(#[from] crate::keyring::jwk::K256ToJwkError), } #[inline] @@ -212,31 +201,102 @@ where Ok(serde_jcs::to_string(value)?.into_bytes()) } +#[inline] +fn commitment_scheme(value: &Jwk) -> Result { + Ok(multihash::double_hash_encode(&canon(value)?)) +} + pub fn did_create_payload( - replace_payload: DidReplacePayload, - update_key: &Jwk, - recovery_key: &Jwk, + replace_payload: DidPatchDocument, + update_key: k256::PublicKey, + recovery_key: k256::PublicKey, ) -> Result { - let update = canon(update_key)?; - let update_commitment = multihash::double_hash_encode(&update); - let recovery = canon(recovery_key)?; - let recovery_commitment = multihash::double_hash_encode(&recovery); - let patch = DidReplaceAction { action: "replace".to_string(), document: replace_payload }; - let delta = DidReplaceDeltaObject { patches: vec![patch], update_commitment }; + let update_commitment = commitment_scheme(&update_key.try_into()?)?; + let recovery_commitment = commitment_scheme(&recovery_key.try_into()?)?; + let patch = DidAction::Replace { document: replace_payload }; + let delta = DidDeltaObject { patches: vec![patch], update_commitment }; let delta = canon(&delta)?; let delta_hash = multihash::hash_encode(&delta); - let suffix = DidReplaceSuffixObject { delta_hash, recovery_commitment }; + let suffix = DidSuffixObject { delta_hash, recovery_commitment }; let suffix = canon(&suffix)?; let encoded_delta = BASE64URL_NOPAD.encode(&delta); let encoded_suffix = BASE64URL_NOPAD.encode(&suffix); - let payload = DidCreatePayload { - r#type: "create".to_string(), + let payload = DidPayload::Create { delta: encoded_delta, suffix_data: encoded_suffix }; + + Ok(serde_jcs::to_string(&payload)?) +} + +pub fn parse_did(did: &str) -> Option<(String, String)> { + let ret: Vec<&str> = did.splitn(3, ':').collect(); + if ret.len() == 3 { Some((ret[1].to_string(), ret[2].to_string())) } else { None } +} + +pub fn get_did_suffix(method_specific_id: &str) -> Option { + let ret: Vec<&str> = method_specific_id.splitn(2, ':').collect(); + if ret.len() == 2 || ret.len() == 1 { Some(ret[0].to_string()) } else { None } +} + +fn sign( + delta_hash: String, + old_public_key: Jwk, + old_secret_key: &k256::SecretKey, +) -> Result { + // NOTE: header + let header = serde_json::json!({ "alg": "ES256K".to_string() }); + let header = serde_jcs::to_string(&header)?; + let header = BASE64URL_NOPAD.encode(header.as_bytes()); + // NOTE: payload + let object = serde_json::json!({"delta_hash": delta_hash, "update_key": old_public_key}); + let payload = BASE64URL_NOPAD.encode(object.to_string().as_bytes()); + // NOTE: message + let message = [header.clone(), payload.clone()].join("."); + let message: &[u8] = message.as_bytes(); + + // NOTE: signature + let signing_key: SigningKey = old_secret_key.into(); + let signature: Signature = signing_key.try_sign(message)?; + let signature = BASE64URL_NOPAD.encode(&signature.to_vec()); + + Ok([header, payload, signature].join(".")) +} + +#[derive(Debug, Error)] +pub enum DidUpdatePayloadError { + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + #[error("Failed to convert to JWK: {0}")] + Jwk(#[from] crate::keyring::jwk::K256ToJwkError), + #[error("Failed to parse did")] + DidParse, + #[error("Failed to sign: {0}")] + Sign(#[from] JwsEncodeError), +} + +// TODO: Not yet tested because sidetree is broken. +pub fn did_update_payload( + update_payload: Vec, + my_did: &str, + old_update: k256::PublicKey, + old_update_secret: &k256::SecretKey, + new_update: k256::PublicKey, +) -> Result { + let old_update: Jwk = old_update.try_into()?; + let new_update = commitment_scheme(&new_update.try_into()?)?; + let delta = DidDeltaObject { patches: update_payload, update_commitment: new_update }; + let delta = canon(&delta)?; + let delta_hash = multihash::hash_encode(&delta); + let encoded_delta = BASE64URL_NOPAD.encode(&delta); + let (_, suff) = parse_did(my_did).ok_or(DidUpdatePayloadError::DidParse)?; + let suff = get_did_suffix(&suff).ok_or(DidUpdatePayloadError::DidParse)?; + + let payload = DidPayload::Update { delta: encoded_delta, - suffix_data: encoded_suffix, + did_suffix: suff, + // reveal_value: multihash::hash_encode(&canon(&old_update)?), + signed_data: sign(delta_hash, old_update, old_update_secret)?, }; - Ok(serde_jcs::to_string(&payload)?) } @@ -255,11 +315,11 @@ pub mod tests { .get_public_key() .to_public_key("".to_string(), "key_id".to_string(), vec!["".to_string()]) .unwrap(); - let update: Jwk = keyring.recovery.get_public_key().try_into().unwrap(); - let recovery: Jwk = keyring.update.get_public_key().try_into().unwrap(); + let update = keyring.recovery.get_public_key(); + let recovery = keyring.update.get_public_key(); - let document = DidReplacePayload { public_keys: vec![public], service_endpoints: vec![] }; + let document = DidPatchDocument { public_keys: vec![public], service_endpoints: vec![] }; - let _result = did_create_payload(document, &update, &recovery).unwrap(); + let _result = did_create_payload(document, update, recovery).unwrap(); } } diff --git a/src/verifiable_credentials/jws.rs b/src/verifiable_credentials/jws.rs index b925ac7..be6249d 100644 --- a/src/verifiable_credentials/jws.rs +++ b/src/verifiable_credentials/jws.rs @@ -12,7 +12,7 @@ use thiserror::Error; // TODO: Design the interface to have an implementation with accelerators. #[derive(Debug, Serialize, Deserialize)] -struct JWSHeader { +struct JwsHeader { alg: String, b64: bool, crit: Vec, @@ -52,7 +52,7 @@ pub enum JwsDecodeError { pub fn sign(object: &Value, secret_key: &k256::SecretKey) -> Result { // NOTE: header - let header = JWSHeader { alg: "ES256K".to_string(), b64: false, crit: vec!["b64".to_string()] }; + let header = JwsHeader { alg: "ES256K".to_string(), b64: false, crit: vec!["b64".to_string()] }; let header = serde_jcs::to_string(&header)?; let header = BASE64URL_NOPAD.encode(header.as_bytes()); // NOTE: payload @@ -87,7 +87,7 @@ pub fn verify( // NOTE: header let decoded = BASE64URL_NOPAD.decode(_header.as_bytes())?; let decoded = String::from_utf8(decoded)?; - let header = serde_json::from_str::(&decoded)?; + let header = serde_json::from_str::(&decoded)?; if header.alg != *"ES256K" { return Err(JwsDecodeError::InvalidAlgorithm(header.alg)); diff --git a/src/verifiable_credentials/mod.rs b/src/verifiable_credentials/mod.rs index 048112a..28fd00b 100644 --- a/src/verifiable_credentials/mod.rs +++ b/src/verifiable_credentials/mod.rs @@ -1,4 +1,4 @@ pub mod credential_signer; pub mod did_vc; -mod jws; +pub mod jws; pub mod types; From 91c606321df3059b38fc6d37f2587f1bc6cc44af Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Fri, 26 Jul 2024 11:17:57 +0900 Subject: [PATCH 56/58] Fix multihash test --- src/did/sidetree/multihash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/did/sidetree/multihash.rs b/src/did/sidetree/multihash.rs index 61889b5..2cde0b8 100644 --- a/src/did/sidetree/multihash.rs +++ b/src/did/sidetree/multihash.rs @@ -50,7 +50,7 @@ mod tests { #[test] fn test_double_hash_then_encode() { let result = double_hash_encode(message().as_bytes()); - assert_eq!(result, String::from("EiC_GPLc6eWMwiyIuGr1oEWiSqrTufglVlDGco8oaQL_nQ")); + assert_eq!(result, String::from("EiAEX1W46vVid7IjJyFY5ibjmyrgepTjW0rYrw-wo4xLCw")); } #[test] From e079a4c03d964c0a30d1f6ea0db7c81dc5e0d04e Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 3 Aug 2024 17:28:52 +0900 Subject: [PATCH 57/58] Refactor minor --- src/did/did_repository.rs | 9 +++++++-- src/did/sidetree/client.rs | 2 +- src/didcomm/encrypted.rs | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/did/did_repository.rs b/src/did/did_repository.rs index 6d7fc09..9e48f21 100644 --- a/src/did/did_repository.rs +++ b/src/did/did_repository.rs @@ -94,7 +94,11 @@ impl DidRepositoryImpl { } } -impl DidRepository for DidRepositoryImpl { +impl DidRepository for DidRepositoryImpl +where + C: SidetreeHttpClient + Send + Sync, + C::Error: Send + Sync, +{ type CreateIdentifierError = CreateIdentifierError; type FindIdentifierError = FindIdentifierError; async fn create_identifier( @@ -102,7 +106,8 @@ impl DidRepository for DidRepositoryImpl keyring: KeyPairing, ) -> Result> { // https://w3c.github.io/did-spec-registries/#assertionmethod - // TODO: This purpose property is strange... + // FIXME: This purpose property is strange... + // I guess the sidetree protocol this impl uses is too old. // https://identity.foundation/sidetree/spec/#add-public-keys // vec!["assertionMethod".to_string()], let sign = keyring.sign.get_public_key().to_public_key( diff --git a/src/did/sidetree/client.rs b/src/did/sidetree/client.rs index 9863c45..c8f2d21 100644 --- a/src/did/sidetree/client.rs +++ b/src/did/sidetree/client.rs @@ -14,7 +14,7 @@ impl SidetreeHttpClientResponse { #[trait_variant::make(Send)] pub trait SidetreeHttpClient { - type Error: std::error::Error + Send + Sync; + type Error: std::error::Error; async fn post_create_identifier( &self, body: &str, diff --git a/src/didcomm/encrypted.rs b/src/didcomm/encrypted.rs index 7e0133c..a904963 100644 --- a/src/didcomm/encrypted.rs +++ b/src/didcomm/encrypted.rs @@ -21,8 +21,8 @@ use crate::{ #[trait_variant::make(Send)] pub trait DidCommEncryptedService: Sync { - type GenerateError: std::error::Error + Sync + Send; - type VerifyError: std::error::Error + Sync + Send; + type GenerateError: std::error::Error; + type VerifyError: std::error::Error; async fn generate( &self, model: VerifiableCredentials, From 659f0d1b2a9b31e237ad309ad030356005782113 Mon Sep 17 00:00:00 2001 From: Kazunari Tanaka Date: Sat, 3 Aug 2024 17:28:59 +0900 Subject: [PATCH 58/58] Update Cargo.lock --- Cargo.lock | 61 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 931201c..bb75185 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "actix-macros", "futures-core", @@ -103,17 +103,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "async-trait" -version = "0.1.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.53", -] - [[package]] name = "atty" version = "0.2.14" @@ -305,6 +294,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.4", ] @@ -482,9 +472,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -507,8 +497,8 @@ dependencies = [ [[package]] name = "didcomm-rs" -version = "0.8.0" -source = "git+https://github.com/nodecross/didcomm-rs.git?tag=v0.8.0#b206c57b85165dec6e4e5a8f734b27ad5ef7befe" +version = "0.8.1" +source = "git+https://github.com/nodecross/didcomm-rs.git?tag=v0.8.1#45729172897436d177d6965113997efa0d1d094b" dependencies = [ "aes-gcm", "arrayref", @@ -1021,21 +1011,16 @@ dependencies = [ [[package]] name = "nodex-didcomm" -version = "0.1.2" +version = "0.2.0" dependencies = [ "actix-rt", - "arrayref", - "async-trait", "chrono", "cuid", "data-encoding", "didcomm-rs", - "elliptic-curve 0.13.8", "hex", - "hmac 0.12.1", "http", "k256 0.13.3", - "log", "rand 0.8.5", "rand_core 0.6.4", "serde", @@ -1043,7 +1028,9 @@ dependencies = [ "serde_json", "sha2 0.10.8", "thiserror", + "trait-variant", "x25519-dalek 2.0.1", + "zeroize", ] [[package]] @@ -1473,11 +1460,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1653,18 +1641,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -1687,6 +1675,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1993,9 +1992,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ]