From e5dfc869e331fd45ce64d350b195f15a31f4a040 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 14 Sep 2023 14:25:26 -0700 Subject: [PATCH 1/7] merge functionality into indy-data-types; update dalek crates Signed-off-by: Andrew Whitehead --- README.md | 4 - indy-credx/Cargo.toml | 6 +- indy-credx/src/error.rs | 4 +- indy-credx/src/ffi/cred_def.rs | 2 +- indy-credx/src/ffi/cred_offer.rs | 2 +- indy-credx/src/ffi/cred_req.rs | 2 +- indy-credx/src/ffi/object.rs | 42 +- indy-credx/src/ffi/revocation.rs | 2 +- indy-credx/src/ffi/schema.rs | 2 +- indy-credx/src/services/issuer.rs | 2 +- indy-credx/src/services/prover.rs | 2 +- indy-credx/src/services/tails.rs | 2 +- indy-credx/src/services/types.rs | 5 +- indy-credx/tests/utils/anoncreds.rs | 2 +- indy-data-types/Cargo.toml | 21 +- indy-data-types/src/anoncreds/cred_def.rs | 3 +- indy-data-types/src/anoncreds/cred_offer.rs | 3 +- indy-data-types/src/anoncreds/cred_request.rs | 5 +- indy-data-types/src/anoncreds/pres_request.rs | 5 +- indy-data-types/src/anoncreds/rev_reg_def.rs | 3 +- indy-data-types/src/anoncreds/schema.rs | 3 +- indy-data-types/src/did.rs | 161 +++++ indy-data-types/src/error.rs | 176 ++++++ indy-data-types/src/identifiers/cred_def.rs | 5 +- indy-data-types/src/identifiers/rev_reg.rs | 5 +- .../src/identifiers/rich_schema.rs | 3 +- indy-data-types/src/identifiers/schema.rs | 7 +- indy-data-types/src/keys/mod.rs | 551 ++++++++++++++++++ indy-data-types/src/keys/types.rs | 122 ++++ indy-data-types/src/lib.rs | 24 +- indy-data-types/src/macros.rs | 9 - indy-data-types/src/merkle_tree/mod.rs | 9 + indy-data-types/src/qualifiable.rs | 139 +++++ indy-data-types/src/utils/base58.rs | 13 + indy-data-types/src/utils/mod.rs | 1 + indy-data-types/src/validation.rs | 17 + 36 files changed, 1286 insertions(+), 78 deletions(-) create mode 100644 indy-data-types/src/did.rs create mode 100644 indy-data-types/src/error.rs create mode 100644 indy-data-types/src/keys/mod.rs create mode 100644 indy-data-types/src/keys/types.rs delete mode 100644 indy-data-types/src/macros.rs create mode 100644 indy-data-types/src/qualifiable.rs create mode 100644 indy-data-types/src/utils/base58.rs create mode 100644 indy-data-types/src/utils/mod.rs create mode 100644 indy-data-types/src/validation.rs diff --git a/README.md b/README.md index ea5f3ab..f6209e3 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ Shared Rust libraries for Hyperledger Indy. - `indy-data-types`: Data type definitions for Schemas, Credential Definitions and other types related to credential issuance and processing. -- `indy-test-utils`: Utilities for use in integration tests. - -- `indy-utils`: Standard wrappers around binary data encodings. Includes support for normalizing transactions for signing, deriving DIDs and verification keys. - ## Credit The initial implementation of `indy-shared-rs` was developed by the Verifiable Organizations Network (VON) team based at the Province of British Columbia, and derives largely from the implementations within [Hyperledger Indy-SDK](https://github.com/hyperledger/indy-sdk). To learn more about VON and what's happening with decentralized identity in British Columbia, please go to [https://vonx.io](https://vonx.io). diff --git a/indy-credx/Cargo.toml b/indy-credx/Cargo.toml index 50692e8..8bc9d94 100644 --- a/indy-credx/Cargo.toml +++ b/indy-credx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indy-credx" -version = "1.0.3" +version = "1.1.0" authors = ["Hyperledger Indy Contributors "] description = "Verifiable credential issuance and presentation for Hyperledger Indy (https://www.hyperledger.org/projects), which provides a distributed-ledger-based foundation for self-sovereign identity (https://sovrin.org)." edition = "2021" @@ -25,10 +25,9 @@ vendored = ["indy-data-types/vendored"] [dependencies] env_logger = { version = "0.10", optional = true } ffi-support = { version = "0.4.0", optional = true } -indy-data-types = { version = "0.6.1", features = [ +indy-data-types = { version = "0.7", features = [ "cl_native", ], path = "../indy-data-types" } -indy-utils = { version = "0.6.0", default-features = false, path = "../indy-utils" } log = "0.4" once_cell = "1" rand = "0.8" @@ -36,5 +35,4 @@ regex = "1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.10" -thiserror = "1.0" zeroize = { version = "1", optional = true } diff --git a/indy-credx/src/error.rs b/indy-credx/src/error.rs index 059e2c9..ecf76ed 100644 --- a/indy-credx/src/error.rs +++ b/indy-credx/src/error.rs @@ -116,8 +116,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: indy_utils::ValidationError) -> Self { +impl From for Error { + fn from(err: indy_data_types::ValidationError) -> Self { Error::from_opt_msg(ErrorKind::Input, err.context) } } diff --git a/indy-credx/src/ffi/cred_def.rs b/indy-credx/src/ffi/cred_def.rs index bd99f82..6b6a4e0 100644 --- a/indy-credx/src/ffi/cred_def.rs +++ b/indy-credx/src/ffi/cred_def.rs @@ -1,7 +1,7 @@ use std::os::raw::c_char; use ffi_support::{rust_string_to_c, FfiStr}; -use indy_utils::Qualifiable; +use indy_data_types::Qualifiable; use super::error::{catch_error, ErrorCode}; use super::object::{IndyObjectId, ObjectHandle}; diff --git a/indy-credx/src/ffi/cred_offer.rs b/indy-credx/src/ffi/cred_offer.rs index 295ff1d..f648470 100644 --- a/indy-credx/src/ffi/cred_offer.rs +++ b/indy-credx/src/ffi/cred_offer.rs @@ -1,5 +1,5 @@ use ffi_support::FfiStr; -use indy_utils::Qualifiable; +use indy_data_types::Qualifiable; use super::error::{catch_error, ErrorCode}; use super::object::ObjectHandle; diff --git a/indy-credx/src/ffi/cred_req.rs b/indy-credx/src/ffi/cred_req.rs index f442628..0f35873 100644 --- a/indy-credx/src/ffi/cred_req.rs +++ b/indy-credx/src/ffi/cred_req.rs @@ -1,5 +1,5 @@ use ffi_support::FfiStr; -use indy_utils::Qualifiable; +use indy_data_types::Qualifiable; use super::error::{catch_error, ErrorCode}; use super::object::ObjectHandle; diff --git a/indy-credx/src/ffi/object.rs b/indy-credx/src/ffi/object.rs index 436c064..f67abf0 100644 --- a/indy-credx/src/ffi/object.rs +++ b/indy-credx/src/ffi/object.rs @@ -4,9 +4,10 @@ use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; use std::os::raw::c_char; -use std::sync::{Arc, Mutex}; +use std::sync::{atomic::AtomicUsize, Arc, Mutex}; use ffi_support::{rust_string_to_c, ByteBuffer}; +use indy_data_types::{Validatable, ValidationError}; use once_cell::sync::Lazy; use serde::Serialize; @@ -16,9 +17,17 @@ use crate::error::Result; pub(crate) static FFI_OBJECTS: Lazy>> = Lazy::new(|| Mutex::new(BTreeMap::new())); -indy_utils::new_handle_type!(ObjectHandle, FFI_OBJECT_COUNTER); +static FFI_OBJECT_COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(transparent)] +pub struct ObjectHandle(pub usize); impl ObjectHandle { + pub fn next() -> Self { + Self(FFI_OBJECT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1) + } + pub(crate) fn create(value: O) -> Result { let handle = Self::next(); FFI_OBJECTS @@ -68,6 +77,35 @@ impl Default for ObjectHandle { } } +impl std::fmt::Display for ObjectHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}({})", stringify!($newtype), self.0) + } +} + +impl std::ops::Deref for ObjectHandle { + type Target = usize; + fn deref(&self) -> &usize { + &self.0 + } +} + +impl PartialEq for ObjectHandle { + fn eq(&self, other: &usize) -> bool { + self.0 == *other + } +} + +impl Validatable for ObjectHandle { + fn validate(&self) -> std::result::Result<(), ValidationError> { + if **self == 0 { + Err("Invalid handle: zero".into()) + } else { + Ok(()) + } + } +} + #[derive(Clone, Debug)] #[repr(transparent)] pub(crate) struct IndyObject(Arc); diff --git a/indy-credx/src/ffi/revocation.rs b/indy-credx/src/ffi/revocation.rs index 65305be..886fb4b 100644 --- a/indy-credx/src/ffi/revocation.rs +++ b/indy-credx/src/ffi/revocation.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; use std::os::raw::c_char; use ffi_support::{rust_string_to_c, FfiStr}; -use indy_utils::Qualifiable; +use indy_data_types::Qualifiable; use super::error::{catch_error, ErrorCode}; use super::object::{IndyObject, IndyObjectId, ObjectHandle}; diff --git a/indy-credx/src/ffi/schema.rs b/indy-credx/src/ffi/schema.rs index 845f6a7..d95e35b 100644 --- a/indy-credx/src/ffi/schema.rs +++ b/indy-credx/src/ffi/schema.rs @@ -1,7 +1,7 @@ use std::os::raw::c_char; use ffi_support::{rust_string_to_c, FfiStr}; -use indy_utils::Qualifiable; +use indy_data_types::Qualifiable; use super::error::{catch_error, ErrorCode}; use super::object::{IndyObjectId, ObjectHandle}; diff --git a/indy-credx/src/services/issuer.rs b/indy-credx/src/services/issuer.rs index f9bc9b4..82f7bc0 100644 --- a/indy-credx/src/services/issuer.rs +++ b/indy-credx/src/services/issuer.rs @@ -14,7 +14,7 @@ use indy_data_types::anoncreds::{ }, schema::SchemaV1, }; -use indy_utils::{Qualifiable, Validatable}; +use indy_data_types::{Qualifiable, Validatable}; use super::tails::TailsWriter; diff --git a/indy-credx/src/services/prover.rs b/indy-credx/src/services/prover.rs index ab72f33..252998d 100644 --- a/indy-credx/src/services/prover.rs +++ b/indy-credx/src/services/prover.rs @@ -16,7 +16,7 @@ use indy_data_types::anoncreds::{ RevealedAttributeInfo, SubProofReferent, }, }; -use indy_utils::{Qualifiable, Validatable}; +use indy_data_types::{Qualifiable, Validatable}; use super::tails::TailsFileReader; diff --git a/indy-credx/src/services/tails.rs b/indy-credx/src/services/tails.rs index 4594efa..0fecd74 100644 --- a/indy-credx/src/services/tails.rs +++ b/indy-credx/src/services/tails.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::{self, BufReader, BufWriter, Read, Seek, Write}; use std::path::{Path, PathBuf}; -use indy_utils::base58; +use indy_data_types::utils::base58; use rand::random; use sha2::{Digest, Sha256}; diff --git a/indy-credx/src/services/types.rs b/indy-credx/src/services/types.rs index 2f476f9..d15d9eb 100644 --- a/indy-credx/src/services/types.rs +++ b/indy-credx/src/services/types.rs @@ -19,10 +19,9 @@ pub use indy_data_types::{ }, schema::{AttributeNames, Schema}, }, - CredentialDefinitionId, RevocationRegistryId, SchemaId, + did::DidValue, + invalid, CredentialDefinitionId, RevocationRegistryId, SchemaId, Validatable, ValidationError, }; -pub use indy_utils::did::DidValue; -use indy_utils::{invalid, Validatable, ValidationError}; use crate::anoncreds_clsignatures::{RevocationRegistry as CryptoRevocationRegistry, Witness}; use crate::error::Error; diff --git a/indy-credx/tests/utils/anoncreds.rs b/indy-credx/tests/utils/anoncreds.rs index 4cbf60e..139a5f9 100644 --- a/indy-credx/tests/utils/anoncreds.rs +++ b/indy-credx/tests/utils/anoncreds.rs @@ -3,7 +3,7 @@ use indy_credx::types::{CredentialDefinitionPrivate, CredentialKeyCorrectnessPro use indy_data_types::anoncreds::cred_def::CredentialDefinition; use indy_data_types::anoncreds::credential::Credential; use indy_data_types::anoncreds::link_secret::LinkSecret; -use indy_utils::did::DidValue; +use indy_data_types::did::DidValue; pub const ISSUER_DID: &'static str = "NcYxiDXkpYi6ov5FcYDi1e"; pub const PROVER_DID: &'static str = "VsKV7grR1BUE29mG2Fm2kX"; diff --git a/indy-data-types/Cargo.toml b/indy-data-types/Cargo.toml index d4bfa4a..50813d5 100644 --- a/indy-data-types/Cargo.toml +++ b/indy-data-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indy-data-types" -version = "0.6.1" +version = "0.7.0" authors = ["Hyperledger Indy Contributors "] description = "Common data types for Hyperledger Indy (https://www.hyperledger.org/projects), which provides a distributed-ledger-based foundation for self-sovereign identity (https://sovrin.org)." edition = "2021" @@ -16,29 +16,32 @@ path = "src/lib.rs" crate-type = ["rlib"] [features] -default = ["anoncreds", "merkle_tree"] +default = ["anoncreds", "ed25519", "merkle_tree"] anoncreds = ["serde_support"] cl = ["anoncreds", "anoncreds-clsignatures", "serde_support"] cl_native = ["anoncreds", "anoncreds-clsignatures/openssl_bn", "serde_support"] +ed25519 = ["curve25519-dalek", "ed25519-dalek", "rand", "sha2", "x25519-dalek"] merkle_tree = ["hex", "sha2"] rich_schema = [] -serde_support = [ - "serde", - "serde_json", - "anoncreds-clsignatures?/serde", - "indy-utils/serde", -] +serde_support = ["serde", "serde_json", "anoncreds-clsignatures?/serde"] vendored = ["anoncreds-clsignatures?/openssl_vendored"] [dependencies] anoncreds-clsignatures = { version = "0.2", optional = true } -indy-utils = { version = "0.6.0", default-features = false, path = "../indy-utils" } +bs58 = "0.5" +curve25519-dalek = { version = "4.1", default-features = false, optional = true } +ed25519-dalek = { version = "2.0", default-features = false, optional = true } hex = { version = "0.4", optional = true } once_cell = "1" +rand = { version = "0.8", optional = true } regex = "1" serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true, features = ["raw_value"] } sha2 = { version = "0.10", optional = true } +thiserror = "1" +x25519-dalek = { version = "2.0", default-features = false, optional = true, features = [ + "static_secrets", +] } zeroize = { version = "1", features = ["zeroize_derive"] } [dev-dependencies] diff --git a/indy-data-types/src/anoncreds/cred_def.rs b/indy-data-types/src/anoncreds/cred_def.rs index 17a6ba0..1de6ea2 100644 --- a/indy-data-types/src/anoncreds/cred_def.rs +++ b/indy-data-types/src/anoncreds/cred_def.rs @@ -2,8 +2,7 @@ use crate::anoncreds_clsignatures::CredentialPublicKey; use crate::identifiers::cred_def::CredentialDefinitionId; use crate::identifiers::schema::SchemaId; -use crate::utils::Qualifiable; -use crate::{ConversionError, Validatable, ValidationError}; +use crate::{ConversionError, Qualifiable, Validatable, ValidationError}; pub const CL_SIGNATURE_TYPE: &str = "CL"; diff --git a/indy-data-types/src/anoncreds/cred_offer.rs b/indy-data-types/src/anoncreds/cred_offer.rs index c37400b..5a2abcf 100644 --- a/indy-data-types/src/anoncreds/cred_offer.rs +++ b/indy-data-types/src/anoncreds/cred_offer.rs @@ -1,8 +1,7 @@ use super::nonce::Nonce; use crate::identifiers::cred_def::CredentialDefinitionId; use crate::identifiers::schema::SchemaId; -use crate::utils::Qualifiable; -use crate::{Validatable, ValidationError}; +use crate::{Qualifiable, Validatable, ValidationError}; #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] diff --git a/indy-data-types/src/anoncreds/cred_request.rs b/indy-data-types/src/anoncreds/cred_request.rs index e99e1b5..385b030 100644 --- a/indy-data-types/src/anoncreds/cred_request.rs +++ b/indy-data-types/src/anoncreds/cred_request.rs @@ -1,8 +1,7 @@ use super::nonce::Nonce; +use crate::did::DidValue; use crate::identifiers::cred_def::CredentialDefinitionId; -use crate::utils::Qualifiable; -use crate::{Validatable, ValidationError}; -use indy_utils::did::DidValue; +use crate::{Qualifiable, Validatable, ValidationError}; #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] diff --git a/indy-data-types/src/anoncreds/pres_request.rs b/indy-data-types/src/anoncreds/pres_request.rs index 951d512..89baf5b 100644 --- a/indy-data-types/src/anoncreds/pres_request.rs +++ b/indy-data-types/src/anoncreds/pres_request.rs @@ -13,9 +13,8 @@ use crate::did::DidValue; use crate::identifiers::cred_def::CredentialDefinitionId; use crate::identifiers::rev_reg::RevocationRegistryId; use crate::identifiers::schema::SchemaId; -use crate::invalid; -use crate::utils::{qualifiable, Qualifiable}; -use crate::{Validatable, ValidationError}; +use crate::qualifiable::{self, Qualifiable}; +use crate::{invalid, Validatable, ValidationError}; #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] diff --git a/indy-data-types/src/anoncreds/rev_reg_def.rs b/indy-data-types/src/anoncreds/rev_reg_def.rs index 98cc04c..158514d 100644 --- a/indy-data-types/src/anoncreds/rev_reg_def.rs +++ b/indy-data-types/src/anoncreds/rev_reg_def.rs @@ -1,7 +1,6 @@ use crate::identifiers::cred_def::CredentialDefinitionId; use crate::identifiers::rev_reg::RevocationRegistryId; -use crate::utils::Qualifiable; -use crate::{invalid, ConversionError, Validatable, ValidationError}; +use crate::{invalid, ConversionError, Qualifiable, Validatable, ValidationError}; pub const CL_ACCUM: &str = "CL_ACCUM"; diff --git a/indy-data-types/src/anoncreds/schema.rs b/indy-data-types/src/anoncreds/schema.rs index 308412a..7ba0d42 100644 --- a/indy-data-types/src/anoncreds/schema.rs +++ b/indy-data-types/src/anoncreds/schema.rs @@ -1,6 +1,5 @@ use crate::identifiers::schema::SchemaId; -use crate::utils::Qualifiable; -use crate::{Validatable, ValidationError}; +use crate::{Qualifiable, Validatable, ValidationError}; use std::collections::HashSet; use std::iter::FromIterator; diff --git a/indy-data-types/src/did.rs b/indy-data-types/src/did.rs new file mode 100644 index 0000000..3396a49 --- /dev/null +++ b/indy-data-types/src/did.rs @@ -0,0 +1,161 @@ +use once_cell::sync::Lazy; + +use regex::Regex; +#[cfg(feature = "ed25519")] +use sha2::{Digest, Sha256}; + +#[cfg(feature = "ed25519")] +use crate::keys::{KeyType, PrivateKey, VerKey}; +use crate::qualifiable::{qualifiable_type, Qualifiable}; +use crate::utils::base58; +use crate::{Validatable, ValidationError}; + +/// The default identifier DID used when submitting ledger read requests +pub static DEFAULT_LIBINDY_DID: Lazy = + Lazy::new(|| DidValue::new("LibindyDid111111111111", None)); + +/// Create a new DID with an optional seed value +/// Version determines version of self-certification to be used +/// 1 (default) = did:sov +/// 2 = did:indy +#[cfg(feature = "ed25519")] +pub fn generate_did( + seed: Option<&[u8]>, + version: Option, +) -> Result<(ShortDidValue, PrivateKey, VerKey), crate::ConversionError> { + let sk = match seed { + Some(seed) => PrivateKey::from_seed(seed)?, + None => PrivateKey::generate(Some(KeyType::ED25519))?, + }; + + let pk = sk.public_key()?; + let did = match version { + Some(1) | None => Ok(base58::encode(&pk.as_ref()[..16])), + Some(2) => { + let mut hasher = Sha256::new(); + Digest::update(&mut hasher, &pk.as_ref()); + let hash = hasher.finalize(); + Ok(base58::encode(&hash[..16])) + } + _ => Err("Version must be one of 1,2"), + }?; + Ok((ShortDidValue::from(did), sk, pk)) +} + +/// A wrapper providing validation for DID methods +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DidMethod(pub String); + +impl Validatable for DidMethod { + fn validate(&self) -> Result<(), ValidationError> { + static REGEX_METHOD_NAME: Lazy = Lazy::new(|| Regex::new("^[a-z0-9]+$").unwrap()); + + if !REGEX_METHOD_NAME.is_match(&self.0) { + return Err(invalid!( + "Invalid default name: {}. It does not match the DID method name format.", + self.0 + )); + } + Ok(()) + } +} + +qualifiable_type!(DidValue, "A qualifiable DID type"); + +impl Qualifiable for DidValue { + fn prefix() -> &'static str { + "did" + } +} + +impl DidValue { + pub fn new(did: &str, method: Option<&str>) -> DidValue { + DidValue::combine(method, did) + } + + pub fn to_short(&self) -> ShortDidValue { + ShortDidValue(self.to_unqualified().0) + } + + pub fn is_abbreviatable(&self) -> bool { + match self.get_method() { + Some(ref method) if method.starts_with("sov") => true, + Some(_) => false, + None => true, + } + } +} + +impl Validatable for DidValue { + fn validate(&self) -> Result<(), ValidationError> { + if self.is_fully_qualified() { + // pass + } else { + let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?; + if did.len() != 16 && did.len() != 32 { + return Err(invalid!( + "Trying to use DID with unexpected length: {}. \ + The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len() + )); + } + } + Ok(()) + } +} + +/// A short DID with no prefix or method +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ShortDidValue(pub String); + +impl From for ShortDidValue { + fn from(val: String) -> Self { + Self(val) + } +} + +impl std::ops::Deref for ShortDidValue { + type Target = str; + fn deref(&self) -> &str { + &self.0 + } +} + +impl ShortDidValue { + /// Convert a short DID value to a qualified DID + pub fn qualify(&self, method: Option) -> DidValue { + DidValue::combine(method.as_ref().map(String::as_str), &self) + } +} + +impl Validatable for ShortDidValue { + fn validate(&self) -> Result<(), ValidationError> { + let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?; + if did.len() != 16 && did.len() != 32 { + return Err(invalid!( + "Trying to use DID with unexpected length: {}. \ + The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len() + )); + } + Ok(()) + } +} + +#[cfg(all(test, feature = "ed25519"))] +mod tests { + use super::*; + use crate::keys::EncodedVerKey; + + #[test] + fn generate_abbreviate() { + let (did, _sk, vk) = generate_did(None, None).unwrap(); + let vk_b58 = vk.as_base58().unwrap(); + let vk_short = vk_b58.abbreviated_for_did(&did).unwrap(); + assert_eq!(vk_short.chars().next(), Some('~')); + let vk_long = EncodedVerKey::from_did_and_verkey(&did, &vk_short).unwrap(); + assert_eq!(vk_long, vk_b58); + let cmp_vk = vk_long.decode().unwrap(); + assert_eq!(vk, cmp_vk); + } +} diff --git a/indy-data-types/src/error.rs b/indy-data-types/src/error.rs new file mode 100644 index 0000000..22dfc82 --- /dev/null +++ b/indy-data-types/src/error.rs @@ -0,0 +1,176 @@ +use std::error::Error as StdError; +use thiserror::Error; + +type DynError = Box; + +macro_rules! define_error { + ($name:tt, $short:expr, $doc:tt) => { + #[derive(Debug, Error)] + #[doc=$doc] + pub struct $name { + pub context: Option, + pub source: Option, + } + + impl $name { + #[allow(unused)] + pub fn from_msg>(msg: T) -> Self { + Self::from(msg.into()) + } + + #[allow(unused)] + pub fn from_err(err: E) -> Self + where + E: StdError + Send + Sync + 'static, + { + Self { + context: None, + source: Some(Box::new(err) as DynError), + } + } + + pub fn from_msg_err(msg: M, err: E) -> Self + where + M: Into, + E: StdError + Send + Sync + 'static, + { + Self { + context: Some(msg.into()), + source: Some(Box::new(err) as DynError), + } + } + } + + impl From<&str> for $name { + fn from(context: &str) -> Self { + Self { + context: Some(context.to_owned()), + source: None, + } + } + } + + impl From for $name { + fn from(context: String) -> Self { + Self { + context: Some(context), + source: None, + } + } + } + + impl From> for $name { + fn from(context: Option) -> Self { + Self { + context, + source: None, + } + } + } + + impl From<(M, E)> for $name + where + M: Into, + E: StdError + Send + Sync + 'static, + { + fn from((context, err): (M, E)) -> Self { + Self::from_msg_err(context, err) + } + } + + impl Into for $name { + fn into(self) -> String { + self.to_string() + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $short)?; + match self.context { + Some(ref context) => write!(f, ": {}", context), + None => Ok(()), + } + } + } + }; +} + +define_error!( + ConversionError, + "Conversion error", + "Error type for general data conversion errors" +); + +define_error!( + EncryptionError, + "Encryption error", + "Error type for failure of encryption and decryption operations" +); + +define_error!( + UnexpectedError, + "Unexpected error", + "Error type for eventualities that shouldn't normally occur" +); + +define_error!( + ValidationError, + "Validation error", + "Error type for failures of `Validatable::validate`" +); + +#[cfg(feature = "serde_json")] +impl From for ConversionError { + fn from(err: serde_json::error::Error) -> Self { + Self::from_msg(err.to_string()) + } +} + +impl From for ConversionError { + fn from(_err: std::str::Utf8Error) -> Self { + Self::from("UTF-8 decoding error") + } +} + +impl From for ConversionError { + fn from(_err: std::string::FromUtf8Error) -> Self { + Self::from("UTF-8 decoding error") + } +} + +impl From for ConversionError { + fn from(err: ValidationError) -> Self { + Self { + context: err.context, + source: err.source, + } + } +} + +impl From for ValidationError { + fn from(err: ConversionError) -> Self { + Self { + context: err.context, + source: err.source, + } + } +} + +impl From for ConversionError { + fn from(err: UnexpectedError) -> Self { + Self { + context: err.context, + source: err.source, + } + } +} + +impl From for EncryptionError { + fn from(err: UnexpectedError) -> Self { + Self { + context: err.context, + source: err.source, + } + } +} diff --git a/indy-data-types/src/identifiers/cred_def.rs b/indy-data-types/src/identifiers/cred_def.rs index e8266f4..c205db7 100644 --- a/indy-data-types/src/identifiers/cred_def.rs +++ b/indy-data-types/src/identifiers/cred_def.rs @@ -1,8 +1,7 @@ use super::schema::SchemaId; -use crate::utils::{qualifiable, Qualifiable}; +use crate::did::DidValue; +use crate::qualifiable::{self, qualifiable_type, Qualifiable}; use crate::{Validatable, ValidationError}; -use indy_utils::did::DidValue; -use indy_utils::qualifiable_type; use super::DELIMITER; diff --git a/indy-data-types/src/identifiers/rev_reg.rs b/indy-data-types/src/identifiers/rev_reg.rs index 77fa442..edf0e8c 100644 --- a/indy-data-types/src/identifiers/rev_reg.rs +++ b/indy-data-types/src/identifiers/rev_reg.rs @@ -4,10 +4,9 @@ use regex::Regex; use super::cred_def::CredentialDefinitionId; use super::DELIMITER; -use crate::utils::{qualifiable, Qualifiable}; +use crate::did::DidValue; +use crate::qualifiable::{self, qualifiable_type, Qualifiable}; use crate::{Validatable, ValidationError}; -use indy_utils::did::DidValue; -use indy_utils::qualifiable_type; static QUALIFIED_REV_REG_ID: Lazy = Lazy::new(|| { Regex::new("(^revreg:(?P[a-z0-9]+):)?(?P.+):4:(?P.+):(?P.+):(?P.+)$").unwrap() diff --git a/indy-data-types/src/identifiers/rich_schema.rs b/indy-data-types/src/identifiers/rich_schema.rs index 42fbc45..596b466 100644 --- a/indy-data-types/src/identifiers/rich_schema.rs +++ b/indy-data-types/src/identifiers/rich_schema.rs @@ -1,6 +1,5 @@ -use crate::utils::Qualifiable; +use crate::qualifiable::{qualifiable_type, Qualifiable}; use crate::{Validatable, ValidationError}; -use indy_utils::qualifiable_type; qualifiable_type!(RichSchemaId, "A rich schema identifier"); diff --git a/indy-data-types/src/identifiers/schema.rs b/indy-data-types/src/identifiers/schema.rs index b4938e4..50b070d 100644 --- a/indy-data-types/src/identifiers/schema.rs +++ b/indy-data-types/src/identifiers/schema.rs @@ -1,7 +1,6 @@ -use crate::utils::{qualifiable, Qualifiable}; -use crate::{Validatable, ValidationError}; -use indy_utils::did::DidValue; -use indy_utils::qualifiable_type; +use crate::did::DidValue; +use crate::qualifiable::{self, qualifiable_type}; +use crate::{Qualifiable, Validatable, ValidationError}; use super::DELIMITER; diff --git a/indy-data-types/src/keys/mod.rs b/indy-data-types/src/keys/mod.rs new file mode 100644 index 0000000..69a30d1 --- /dev/null +++ b/indy-data-types/src/keys/mod.rs @@ -0,0 +1,551 @@ +#[cfg(feature = "ed25519")] +use curve25519_dalek::edwards::CompressedEdwardsY; +#[cfg(feature = "ed25519")] +use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; +#[cfg(feature = "ed25519")] +use rand::{thread_rng, RngCore}; +#[cfg(feature = "ed25519")] +use sha2::digest::Digest; + +#[cfg(feature = "ed25519")] +use std::convert::TryFrom; + +use zeroize::Zeroize; + +use crate::utils::base58; +use crate::{ConversionError, Validatable, ValidationError}; + +mod types; +pub use types::{KeyEncoding, KeyType}; + +/// Build an encoded verkey +pub fn build_full_verkey(dest: &str, key: &str) -> Result { + EncodedVerKey::from_str_qualified(key, Some(dest), None, None) +} + +/// A raw signing key used for generating transaction signatures +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PrivateKey { + pub key: Box<[u8]>, + pub alg: KeyType, +} + +impl PrivateKey { + pub fn new>(key: K, alg: Option) -> Self { + Self { + key: key.as_ref().into(), + alg: alg.unwrap_or_default(), + } + } + + #[cfg(feature = "ed25519")] + pub fn generate(alg: Option) -> Result { + let alg = alg.unwrap_or_default(); + match alg { + KeyType::ED25519 => { + let mut sk = [0u8; 32]; + thread_rng().fill_bytes(&mut sk[..]); + Self::from_seed(&sk[..]) + } + _ => Err("Unsupported key type".into()), + } + } + + #[cfg(feature = "ed25519")] + pub fn from_seed(seed: &[u8]) -> Result { + let sk = SigningKey::from_bytes( + seed.try_into() + .map_err(|_| "Invalid length for secret key")?, + ); + Ok(Self::new(sk.to_keypair_bytes(), Some(KeyType::ED25519))) + } + + pub fn public_key(&self) -> Result { + match self.alg { + KeyType::ED25519 => Ok(VerKey::new(&self.key[32..], Some(self.alg.clone()))), + _ => Err("Unsupported key type".into()), + } + } + + pub fn key_bytes(&self) -> Vec { + Vec::from(self.key.as_ref()) + } + + #[cfg(feature = "ed25519")] + pub fn key_exchange(&self) -> Result { + match self.alg { + KeyType::ED25519 => { + let mut hash = sha2::Sha512::digest(&self.key[..32]); + let x_sk = + x25519_dalek::StaticSecret::from(<[u8; 32]>::try_from(&hash[..32]).unwrap()); + hash.zeroize(); + Ok(Self::new(&x_sk.to_bytes(), Some(KeyType::X25519))) + } + _ => Err("Unsupported key format for key exchange".into()), + } + } + + #[cfg(feature = "ed25519")] + pub fn sign>(&self, message: M) -> Result, ConversionError> { + match self.alg { + KeyType::ED25519 => { + let esk = SigningKey::from_keypair_bytes((&*self.key).try_into().unwrap()).unwrap(); + let sig = esk.sign(message.as_ref()); + Ok(sig.to_bytes().into()) + } + _ => Err("Unsupported key format for signing".into()), + } + } +} + +impl AsRef<[u8]> for PrivateKey { + fn as_ref(&self) -> &[u8] { + self.key.as_ref() + } +} + +impl Zeroize for PrivateKey { + fn zeroize(&mut self) { + self.key.zeroize(); + self.alg = KeyType::from("") + } +} + +impl Drop for PrivateKey { + fn drop(&mut self) { + self.zeroize() + } +} + +impl Validatable for PrivateKey { + fn validate(&self) -> Result<(), ValidationError> { + if self.alg == KeyType::ED25519 { + if self.key.len() == 64 { + Ok(()) + } else { + Err("Invalid signing key length".into()) + } + } else { + Err("Unsupported signing key type".into()) + } + } +} + +/// A raw verkey used in verifying signatures +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VerKey { + pub key: Box<[u8]>, + pub alg: KeyType, +} + +impl VerKey { + pub fn new>(key: K, alg: Option) -> Self { + let alg = alg.unwrap_or_default(); + Self { + key: key.as_ref().into(), + alg, + } + } + + pub fn as_base58(&self) -> Result { + self.encode(&KeyEncoding::BASE58) + } + + pub fn encode(&self, enc: &KeyEncoding) -> Result { + match enc { + KeyEncoding::BASE58 => { + let key = base58::encode(&self.key); + Ok(EncodedVerKey::new( + key.as_str(), + Some(self.alg.clone()), + Some(enc.clone()), + )) + } + _ => Err("Unsupported key encoding".into()), + } + } + + pub fn key_bytes(&self) -> Vec { + Vec::from(self.key.as_ref()) + } + + #[cfg(feature = "ed25519")] + pub fn key_exchange(&self) -> Result { + match self.alg { + KeyType::ED25519 => { + let vky = CompressedEdwardsY::from_slice(&self.key).unwrap(); + if let Some(x_vk) = vky.decompress() { + Ok(Self::new( + x_vk.to_montgomery().as_bytes().to_vec(), + Some(KeyType::X25519), + )) + } else { + Err("Error converting to x25519 key".into()) + } + } + _ => Err("Unsupported verkey type".into()), + } + } + + #[cfg(feature = "ed25519")] + pub fn verify_signature, S: AsRef<[u8]>>( + &self, + message: M, + signature: S, + ) -> Result { + match self.alg { + KeyType::ED25519 => { + let vk = VerifyingKey::try_from(&*self.key).unwrap(); + if let Ok(sig) = Signature::try_from(signature.as_ref()) { + Ok(vk.verify_strict(message.as_ref(), &sig).is_ok()) + } else { + Err("Error validating message signature".into()) + } + } + _ => Err("Unsupported verkey type".into()), + } + } +} + +impl AsRef<[u8]> for VerKey { + fn as_ref(&self) -> &[u8] { + self.key.as_ref() + } +} + +impl std::fmt::Display for VerKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.as_base58() { + Ok(k) => k.fmt(f), + Err(err) => write!(f, "", err), + } + } +} + +impl Validatable for VerKey { + fn validate(&self) -> Result<(), ValidationError> { + if self.alg == KeyType::ED25519 { + let bytes = self.key_bytes(); + if bytes.len() == 32 { + Ok(()) + } else { + Err("Invalid verkey length".into()) + } + } else { + Err("Unsupported verkey type".into()) + } + } +} + +impl Zeroize for VerKey { + fn zeroize(&mut self) { + self.key.zeroize(); + self.alg = KeyType::from(""); + } +} + +impl Drop for VerKey { + fn drop(&mut self) { + self.zeroize() + } +} + +/// An encoded verkey appropriate for storing and transmitting +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EncodedVerKey { + pub key: String, + pub alg: KeyType, + pub enc: KeyEncoding, +} + +impl EncodedVerKey { + pub fn new>(key: K, alg: Option, enc: Option) -> Self { + let alg = alg.unwrap_or_default(); + let enc = enc.unwrap_or_default(); + Self { + key: key.as_ref().to_owned(), + alg, + enc, + } + } + + pub fn from_did_and_verkey(did: &str, key: &str) -> Result { + if key.chars().next() == Some('~') { + let mut vk_bytes = base58::decode(&key[1..])?; + if vk_bytes.len() != 16 { + return Err("Expected 16-byte abbreviated verkey".into()); + } + let mut did_bytes = base58::decode(did)?; + if did_bytes.len() != 16 { + return Err("DID must be 16 bytes in length".into()); + } + did_bytes.append(&mut vk_bytes); + Ok(Self::new( + &base58::encode(did_bytes), + Some(KeyType::ED25519), + Some(KeyEncoding::BASE58), + )) + } else { + Ok(Self::new( + key, + Some(KeyType::ED25519), + Some(KeyEncoding::BASE58), + )) + } + } + + pub fn abbreviated_for_did(&self, did: &str) -> Result { + let did_bytes = base58::decode(did)?; + if did_bytes.len() != 16 { + return Err("DID must be 16 bytes in length".into()); + } + let vk = self.key_bytes()?; + if vk.len() != 32 { + return Err("Expected 32-byte verkey".into()); + } + if &vk[..16] == did_bytes.as_slice() { + let mut result = "~".to_string(); + result.push_str(&base58::encode(&vk[16..])); + Ok(result) + } else { + Ok(base58::encode(vk)) + } + } + + pub fn decode(&self) -> Result { + let mut vk = self.key_bytes()?; + let result = VerKey::new(&vk, Some(self.alg.clone())); + vk.zeroize(); + Ok(result) + } + + pub fn from_slice>(key: K) -> Result { + let key = std::str::from_utf8(key.as_ref())?; + Self::from_str_qualified(key, None, None, None) + } + + pub fn from_str(key: &str) -> Result { + Self::from_str_qualified(key, None, None, None) + } + + pub fn from_str_qualified( + key: &str, + dest: Option<&str>, + alg: Option, + enc: Option, + ) -> Result { + let (key, alg) = if key.contains(':') { + let splits: Vec<&str> = key.splitn(2, ':').collect(); + let alg = match splits[1] { + "" => alg, + _ => Some(splits[1].into()), + }; + (splits[0], alg) + } else { + (key, alg) + }; + + if key.starts_with('~') { + let dest = dest.ok_or_else(|| "Destination required for short verkey")?; + let mut result = base58::decode(dest)?; + let mut end = base58::decode(&key[1..])?; + result.append(&mut end); + Ok(Self::new(&base58::encode(result), alg, enc)) + } else { + Ok(Self::new(key, alg, enc)) + } + } + + pub fn long_form(&self) -> String { + let mut result = self.key.clone(); + result.push(':'); + result.push_str(&self.alg); + result + } + + pub fn as_base58(self) -> Result { + match self.enc { + KeyEncoding::BASE58 => Ok(self), + _ => { + let key = base58::encode(self.key_bytes()?); + Ok(Self::new( + key.as_str(), + Some(self.alg.clone()), + Some(KeyEncoding::BASE58), + )) + } + } + } + + pub fn key_bytes(&self) -> Result, ConversionError> { + match self.enc { + KeyEncoding::BASE58 => Ok(base58::decode(&self.key)?), + _ => Err("Unsupported verkey encoding".into()), + } + } + + pub fn encoded_key_bytes(&self) -> &[u8] { + self.key.as_bytes() + } + + #[cfg(feature = "ed25519")] + pub fn key_exchange(&self) -> Result { + let vk = self.decode()?; + vk.key_exchange() + } + + #[cfg(feature = "ed25519")] + pub fn key_exchange_encoded(&self) -> Result { + let x_vk = self.key_exchange()?; + x_vk.encode(&self.enc) + } + + #[cfg(feature = "ed25519")] + pub fn verify_signature, S: AsRef<[u8]>>( + &self, + message: M, + signature: S, + ) -> Result { + let vk = self.decode()?; + vk.verify_signature(message, signature) + } +} + +impl std::fmt::Display for EncodedVerKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let out = if self.alg == KeyType::default() { + self.key.clone() + } else { + self.long_form() + }; + f.write_str(out.as_str()) + } +} + +impl Validatable for EncodedVerKey { + fn validate(&self) -> Result<(), ValidationError> { + let verkey = self.decode()?; + verkey.validate() + } +} + +impl Zeroize for EncodedVerKey { + fn zeroize(&mut self) { + self.key.zeroize(); + self.alg = KeyType::from(""); + self.enc = KeyEncoding::from("") + } +} + +impl Drop for EncodedVerKey { + fn drop(&mut self) { + self.zeroize() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_str_empty() { + assert_eq!( + EncodedVerKey::from_str("").unwrap(), + EncodedVerKey::new("", Some(KeyType::default()), Some(KeyEncoding::default())) + ) + } + + #[test] + fn from_str_single_colon() { + assert_eq!( + EncodedVerKey::from_str(":").unwrap(), + EncodedVerKey::new("", Some(KeyType::default()), Some(KeyEncoding::default())) + ) + } + + #[test] + fn from_str_ends_with_colon() { + assert_eq!( + EncodedVerKey::from_str("foo:").unwrap(), + EncodedVerKey::new( + "foo", + Some(KeyType::default()), + Some(KeyEncoding::default()) + ) + ) + } + + #[test] + fn from_key_starts_with_colon() { + assert_eq!( + EncodedVerKey::from_str(":bar").unwrap(), + EncodedVerKey::new("", Some("bar".into()), Some(KeyEncoding::default())) + ) + } + + #[test] + fn from_key_works() { + assert_eq!( + EncodedVerKey::from_str("foo:bar:baz").unwrap(), + EncodedVerKey::new("foo", Some("bar:baz".into()), Some(KeyEncoding::default())) + ) + } + + #[test] + fn round_trip_verkey() { + assert_eq!( + EncodedVerKey::from_str("foo:bar:baz").unwrap().long_form(), + "foo:bar:baz" + ) + } + + #[cfg(feature = "ed25519")] + #[test] + fn key_from_seed() { + const SEED: &[u8; 32] = b"aaaabbbbccccddddeeeeffffgggghhhh"; + let sk = PrivateKey::from_seed(&SEED[..]).unwrap(); + assert_eq!( + &sk.as_ref()[..], + &[ + 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 100, 101, 101, 101, + 101, 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 113, 22, 13, 44, + 71, 184, 166, 148, 196, 234, 85, 148, 234, 22, 204, 148, 187, 247, 77, 119, 250, + 28, 37, 255, 29, 31, 159, 159, 245, 68, 107, 235 + ] + ); + let xk = sk.key_exchange().unwrap(); + assert_eq!( + &xk.as_ref(), + &[ + 208, 235, 232, 147, 241, 214, 250, 182, 45, 157, 20, 202, 31, 184, 226, 115, 149, + 82, 210, 89, 50, 100, 22, 67, 21, 8, 124, 198, 100, 252, 237, 107 + ] + ); + } + + #[cfg(feature = "ed25519")] + #[test] + fn sign_and_verify() { + let message = b"hello there"; + let sk = PrivateKey::generate(None).unwrap(); + let sig = sk.sign(&message).unwrap(); + let vk = sk.public_key().unwrap(); + assert!(vk.verify_signature(&message, &sig).unwrap()); + assert!(vk.verify_signature(&message, &[]).is_err()); + assert!(!vk.verify_signature(&"goodbye", &sig).unwrap()); + } + + #[cfg(feature = "ed25519")] + #[test] + fn validate_keys() { + let sk = PrivateKey::generate(None).unwrap(); + sk.validate().unwrap(); + let vk = sk.public_key().unwrap(); + vk.validate().unwrap(); + + let sk = PrivateKey::new(b"bad key", Some(KeyType::ED25519)); + assert_eq!(sk.validate().is_ok(), false); + let vk = VerKey::new(b"bad key", Some(KeyType::ED25519)); + assert_eq!(vk.validate().is_ok(), false); + } +} diff --git a/indy-data-types/src/keys/types.rs b/indy-data-types/src/keys/types.rs new file mode 100644 index 0000000..2bd0dd2 --- /dev/null +++ b/indy-data-types/src/keys/types.rs @@ -0,0 +1,122 @@ +pub const KEY_ENC_BASE58: &'static str = "base58"; + +pub const KEY_TYPE_ED25519: &'static str = "ed25519"; +pub const KEY_TYPE_X25519: &'static str = "x25519"; + +/// Enum of known and unknown key types +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum KeyType { + ED25519, + X25519, + Other(String), +} + +impl KeyType { + pub fn from_str(keytype: &str) -> KeyType { + match keytype.to_ascii_lowercase().as_str() { + KEY_TYPE_ED25519 => KeyType::ED25519, + KEY_TYPE_X25519 => KeyType::X25519, + _ => KeyType::Other(keytype.to_owned()), + } + } + + pub fn is_known(&self) -> bool { + match self { + Self::Other(_) => false, + _ => true, + } + } + + pub fn as_str(&self) -> &str { + match self { + Self::ED25519 => KEY_TYPE_ED25519, + Self::X25519 => KEY_TYPE_X25519, + Self::Other(t) => t.as_str(), + } + } +} + +impl std::string::ToString for KeyType { + fn to_string(&self) -> String { + self.as_str().to_owned() + } +} + +impl Default for KeyType { + fn default() -> Self { + KeyType::ED25519 + } +} + +impl std::ops::Deref for KeyType { + type Target = str; + fn deref(&self) -> &str { + self.as_str() + } +} + +impl From<&str> for KeyType { + fn from(value: &str) -> Self { + Self::from_str(value) + } +} + +impl From for KeyType { + fn from(value: String) -> Self { + Self::from_str(&value) + } +} + +/// Enum of known and unknown key encodings +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum KeyEncoding { + BASE58, + Other(String), +} + +impl KeyEncoding { + pub fn from_str(keyenc: &str) -> KeyEncoding { + match keyenc.to_ascii_lowercase().as_str() { + KEY_ENC_BASE58 => KeyEncoding::BASE58, + _ => KeyEncoding::Other(keyenc.to_owned()), + } + } + + pub fn as_str(&self) -> &str { + match self { + Self::BASE58 => KEY_ENC_BASE58, + Self::Other(e) => e.as_str(), + } + } +} + +impl std::string::ToString for KeyEncoding { + fn to_string(&self) -> String { + self.as_str().to_owned() + } +} + +impl Default for KeyEncoding { + fn default() -> Self { + KeyEncoding::BASE58 + } +} + +impl std::ops::Deref for KeyEncoding { + type Target = str; + fn deref(&self) -> &str { + self.as_str() + } +} + +impl From<&str> for KeyEncoding { + fn from(value: &str) -> Self { + Self::from_str(value) + } +} + +impl From for KeyEncoding { + fn from(value: String) -> Self { + Self::from_str(&value) + } +} diff --git a/indy-data-types/src/lib.rs b/indy-data-types/src/lib.rs index e69b922..077b9a5 100644 --- a/indy-data-types/src/lib.rs +++ b/indy-data-types/src/lib.rs @@ -6,17 +6,21 @@ extern crate serde; #[macro_use] extern crate serde_json; -#[macro_use] -mod macros; - -mod utils { - pub use indy_utils::base58; - pub use indy_utils::{qualifiable, Qualifiable}; -} +mod error; -pub use indy_utils::did; -pub use indy_utils::keys; -pub use indy_utils::{invalid, ConversionError, Validatable, ValidationError}; +#[macro_use] +mod validation; + +pub mod did; +pub mod keys; +pub mod qualifiable; +pub mod utils; + +pub use self::{ + error::{ConversionError, ValidationError}, + qualifiable::Qualifiable, + validation::Validatable, +}; #[cfg(any(feature = "cl", feature = "cl_native"))] pub use anoncreds_clsignatures; diff --git a/indy-data-types/src/macros.rs b/indy-data-types/src/macros.rs deleted file mode 100644 index 0759b37..0000000 --- a/indy-data-types/src/macros.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(feature = "merkle_tree")] -macro_rules! unwrap_opt_or_return { - ($opt:expr, $err:expr) => { - match $opt { - Some(val) => val, - None => return $err, - } - }; -} diff --git a/indy-data-types/src/merkle_tree/mod.rs b/indy-data-types/src/merkle_tree/mod.rs index b2669a1..8fcfe7c 100644 --- a/indy-data-types/src/merkle_tree/mod.rs +++ b/indy-data-types/src/merkle_tree/mod.rs @@ -10,6 +10,15 @@ mod merkletree; mod proof; mod tree; +macro_rules! unwrap_opt_or_return { + ($opt:expr, $err:expr) => { + match $opt { + Some(val) => val, + None => return $err, + } + }; +} + impl MerkleTree { fn count_bits(v: usize) -> usize { let mut ret = 0; diff --git a/indy-data-types/src/qualifiable.rs b/indy-data-types/src/qualifiable.rs new file mode 100644 index 0000000..91f2787 --- /dev/null +++ b/indy-data-types/src/qualifiable.rs @@ -0,0 +1,139 @@ +use once_cell::sync::Lazy; + +use regex::Regex; + +use crate::{invalid, Validatable, ValidationError}; + +pub(crate) static REGEX: Lazy = + Lazy::new(|| Regex::new("^([a-z0-9]+):([a-z0-9]+):(.*)$").unwrap()); + +/// Combine a prefix, method, and value into a qualified identifier +pub fn combine(prefix: &str, method: Option<&str>, entity: &str) -> String { + match method { + Some(method) => format!("{}:{}:{}", prefix, method, entity), + _ => entity.to_owned(), + } +} + +/// Split a qualifiable identifier into its method and value components +pub fn split<'a>(prefix: &str, val: &'a str) -> (Option<&'a str>, &'a str) { + match REGEX.captures(&val) { + None => (None, val), + Some(caps) => { + if caps.get(1).map(|m| m.as_str()) == Some(prefix) { + ( + Some(caps.get(2).unwrap().as_str()), + caps.get(3).unwrap().as_str(), + ) + } else { + (None, val) + } + } + } +} + +/// Check if an identifier is qualified by a prefix and method +pub fn is_fully_qualified(entity: &str) -> bool { + REGEX.captures(entity).is_some() +} + +/// An identifier which can be qualified with a prefix and method +pub trait Qualifiable: From + std::ops::Deref + Validatable { + fn prefix() -> &'static str; + + fn combine(method: Option<&str>, entity: &str) -> Self { + Self::from(combine(Self::prefix(), method, entity)) + } + + fn split<'a>(&'a self) -> (Option<&'a str>, &'a str) { + split(Self::prefix(), self.deref()) + } + + fn get_method<'a>(&'a self) -> Option<&'a str> { + let (method, _rest) = self.split(); + method + } + + fn default_method(&self, method: Option<&str>) -> Self { + let (prev_method, rest) = self.split(); + match prev_method { + Some(_) => Self::from(self.to_string()), + None => Self::combine(method, rest), + } + } + + fn replace_method(&self, method: Option<&str>) -> Self { + let (_method, rest) = self.split(); + Self::combine(method, rest) + } + + fn remove_method(&self, method: &str) -> Self { + let (prev_method, rest) = self.split(); + if prev_method == Some(method) { + Self::combine(None, rest) + } else { + Self::from(self.to_string()) + } + } + + fn from_str(entity: &str) -> Result { + let result = Self::from(entity.to_owned()); + result.validate()?; + Ok(result) + } + + fn is_fully_qualified(&self) -> bool { + self.get_method().is_some() + } + + fn to_qualified(&self, method: &str) -> Result { + match self.split() { + (None, rest) => Ok(Self::combine(Some(method), rest)), + (Some(prev_method), rest) if prev_method == method => { + Ok(Self::combine(Some(method), rest)) + } + _ => Err(invalid!( + "Identifier is already qualified with another method", + )), + } + } + + fn to_unqualified(&self) -> Self { + let (_, rest) = self.split(); + Self::from(rest.to_owned()) + } +} + +/// Derive a new `Qualifiable` string type +macro_rules! qualifiable_type { + ($newtype:ident, $doc:expr) => { + #[doc=$doc] + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $newtype(pub String); + + impl From for $newtype { + fn from(val: String) -> Self { + Self(val) + } + } + + impl std::ops::Deref for $newtype { + type Target = str; + fn deref(&self) -> &str { + &self.0 + } + } + + impl std::fmt::Display for $newtype { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.0.as_str()) + } + } + }; + ($newtype:ident) => { + qualifiable_type!($newtype, ""); + }; +} + +pub(crate) use qualifiable_type; diff --git a/indy-data-types/src/utils/base58.rs b/indy-data-types/src/utils/base58.rs new file mode 100644 index 0000000..ebbc3b2 --- /dev/null +++ b/indy-data-types/src/utils/base58.rs @@ -0,0 +1,13 @@ +use bs58; + +use crate::error::ConversionError; + +pub fn decode>(val: T) -> Result, ConversionError> { + Ok(bs58::decode(val) + .into_vec() + .map_err(|err| ("Error decoding base58 data", err))?) +} + +pub fn encode>(val: T) -> String { + bs58::encode(val).into_string() +} diff --git a/indy-data-types/src/utils/mod.rs b/indy-data-types/src/utils/mod.rs new file mode 100644 index 0000000..4f32be0 --- /dev/null +++ b/indy-data-types/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod base58; diff --git a/indy-data-types/src/validation.rs b/indy-data-types/src/validation.rs new file mode 100644 index 0000000..dfa41a0 --- /dev/null +++ b/indy-data-types/src/validation.rs @@ -0,0 +1,17 @@ +use crate::error::ValidationError; + +/// Macro to return a new `ValidationError` with an optional message +#[macro_export] +macro_rules! invalid { + () => { $crate::ValidationError::from(None) }; + ($($arg:tt)+) => { + $crate::ValidationError::from(format!($($arg)+)) + }; +} + +/// Trait for data types which need validation after being loaded from external sources +pub trait Validatable { + fn validate(&self) -> Result<(), ValidationError> { + Ok(()) + } +} From f363f17bd32e5b69be61b06b36a37c1740bed649 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 14 Sep 2023 14:25:44 -0700 Subject: [PATCH 2/7] remove unused crates Signed-off-by: Andrew Whitehead --- Cargo.toml | 7 +- indy-test-utils/Cargo.toml | 22 -- indy-test-utils/src/environment.rs | 48 --- indy-test-utils/src/genesis.rs | 64 ---- indy-test-utils/src/lib.rs | 2 - indy-utils/Cargo.toml | 45 --- indy-utils/src/base58.rs | 13 - indy-utils/src/did.rs | 160 --------- indy-utils/src/error.rs | 174 --------- indy-utils/src/keys/mod.rs | 555 ----------------------------- indy-utils/src/keys/types.rs | 122 ------- indy-utils/src/lib.rs | 29 -- indy-utils/src/macros.rs | 88 ----- indy-utils/src/qualifiable.rs | 139 -------- indy-utils/src/validation.rs | 17 - 15 files changed, 1 insertion(+), 1484 deletions(-) delete mode 100644 indy-test-utils/Cargo.toml delete mode 100644 indy-test-utils/src/environment.rs delete mode 100644 indy-test-utils/src/genesis.rs delete mode 100644 indy-test-utils/src/lib.rs delete mode 100644 indy-utils/Cargo.toml delete mode 100644 indy-utils/src/base58.rs delete mode 100644 indy-utils/src/did.rs delete mode 100644 indy-utils/src/error.rs delete mode 100644 indy-utils/src/keys/mod.rs delete mode 100644 indy-utils/src/keys/types.rs delete mode 100644 indy-utils/src/lib.rs delete mode 100644 indy-utils/src/macros.rs delete mode 100644 indy-utils/src/qualifiable.rs delete mode 100644 indy-utils/src/validation.rs diff --git a/Cargo.toml b/Cargo.toml index 89e9422..43bb688 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,6 @@ [workspace] -members = [ - "indy-credx", - "indy-data-types", - "indy-test-utils", - "indy-utils" -] +members = ["indy-credx", "indy-data-types"] [profile.release] panic = "abort" diff --git a/indy-test-utils/Cargo.toml b/indy-test-utils/Cargo.toml deleted file mode 100644 index 3db11af..0000000 --- a/indy-test-utils/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "indy-test-utils" -version = "0.1.0" -authors = ["Hyperledger Indy Contributors "] -description = "Utilities for testing Hyperledger Indy (https://www.hyperledger.org/projects), which provides a distributed-ledger-based foundation for self-sovereign identity (https://sovrin.org)." -edition = "2018" -license = "Apache-2.0" -readme = "../README.md" -repository = "https://github.com/hyperledger/indy-shared-rs/" -categories = ["authentication", "cryptography"] -keywords = ["hyperledger", "indy", "ssi", "verifiable", "credentials"] - -[lib] -name = "indy_test_utils" -path = "src/lib.rs" -crate-type = ["rlib"] - -[features] -default = [] - -[dependencies] -tempfile = "3.1" diff --git a/indy-test-utils/src/environment.rs b/indy-test-utils/src/environment.rs deleted file mode 100644 index 283f692..0000000 --- a/indy-test-utils/src/environment.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::env; -use std::path::PathBuf; - -pub fn tmp_path() -> PathBuf { - let mut path = env::temp_dir(); - path.push("indy_ledger_client"); - path -} - -pub fn tmp_file_path(file_name: &str) -> PathBuf { - let mut path = tmp_path(); - path.push(file_name); - path -} - -pub fn test_pool_ip() -> String { - env::var("TEST_POOL_IP").unwrap_or("127.0.0.1".to_string()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn tmp_path_works() { - let path = tmp_path(); - - assert!(path.is_absolute()); - assert!(path.has_root()); - assert!(path.to_string_lossy().contains("indy_ledger_client")); - } - - #[test] - fn tmp_file_path_works() { - let path = tmp_file_path("test.txt"); - - assert!(path.is_absolute()); - assert!(path.has_root()); - assert!(path.to_string_lossy().contains("indy_ledger_client")); - assert!(path.to_string_lossy().contains("test.txt")); - } - - #[test] - fn test_pool_ip_works() { - let pool_ip = test_pool_ip(); - assert!(!pool_ip.is_empty()); - } -} diff --git a/indy-test-utils/src/genesis.rs b/indy-test-utils/src/genesis.rs deleted file mode 100644 index 7ba2b05..0000000 --- a/indy-test-utils/src/genesis.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::environment; - -use std::io::Write; -use std::path::PathBuf; -use tempfile::NamedTempFile; - -pub struct GenesisTransactions { - pub transactions: Vec, - pub file: Option, -} - -impl GenesisTransactions { - pub fn default_transactions() -> Vec { - let test_pool_ip = environment::test_pool_ip(); - - vec![ - format!( - r#"{{"reqSignature":{{}},"txn":{{"data":{{"data":{{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"{}","client_port":9702,"node_ip":"{}","node_port":9701,"services":["VALIDATOR"]}},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"}},"metadata":{{"from":"Th7MpTaRZVRYnPiabds81Y"}},"type":"0"}},"txnMetadata":{{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"}},"ver":"1"}}"#, - test_pool_ip, test_pool_ip - ), - format!( - r#"{{"reqSignature":{{}},"txn":{{"data":{{"data":{{"alias":"Node2","blskey":"37rAPpXVoxzKhz7d9gkUe52XuXryuLXoM6P6LbWDB7LSbG62Lsb33sfG7zqS8TK1MXwuCHj1FKNzVpsnafmqLG1vXN88rt38mNFs9TENzm4QHdBzsvCuoBnPH7rpYYDo9DZNJePaDvRvqJKByCabubJz3XXKbEeshzpz4Ma5QYpJqjk","blskey_pop":"Qr658mWZ2YC8JXGXwMDQTzuZCWF7NK9EwxphGmcBvCh6ybUuLxbG65nsX4JvD4SPNtkJ2w9ug1yLTj6fgmuDg41TgECXjLCij3RMsV8CwewBVgVN67wsA45DFWvqvLtu4rjNnE9JbdFTc1Z4WCPA3Xan44K1HoHAq9EVeaRYs8zoF5","client_ip":"{}","client_port":9704,"node_ip":"{}","node_port":9703,"services":["VALIDATOR"]}},"dest":"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb"}},"metadata":{{"from":"EbP4aYNeTHL6q385GuVpRV"}},"type":"0"}},"txnMetadata":{{"seqNo":2,"txnId":"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc"}},"ver":"1"}}"#, - test_pool_ip, test_pool_ip - ), - format!( - r#"{{"reqSignature":{{}},"txn":{{"data":{{"data":{{"alias":"Node3","blskey":"3WFpdbg7C5cnLYZwFZevJqhubkFALBfCBBok15GdrKMUhUjGsk3jV6QKj6MZgEubF7oqCafxNdkm7eswgA4sdKTRc82tLGzZBd6vNqU8dupzup6uYUf32KTHTPQbuUM8Yk4QFXjEf2Usu2TJcNkdgpyeUSX42u5LqdDDpNSWUK5deC5","blskey_pop":"QwDeb2CkNSx6r8QC8vGQK3GRv7Yndn84TGNijX8YXHPiagXajyfTjoR87rXUu4G4QLk2cF8NNyqWiYMus1623dELWwx57rLCFqGh7N4ZRbGDRP4fnVcaKg1BcUxQ866Ven4gw8y4N56S5HzxXNBZtLYmhGHvDtk6PFkFwCvxYrNYjh","client_ip":"{}","client_port":9706,"node_ip":"{}","node_port":9705,"services":["VALIDATOR"]}},"dest":"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya"}},"metadata":{{"from":"4cU41vWW82ArfxJxHkzXPG"}},"type":"0"}},"txnMetadata":{{"seqNo":3,"txnId":"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4"}},"ver":"1"}}"#, - test_pool_ip, test_pool_ip - ), - format!( - r#"{{"reqSignature":{{}},"txn":{{"data":{{"data":{{"alias":"Node4","blskey":"2zN3bHM1m4rLz54MJHYSwvqzPchYp8jkHswveCLAEJVcX6Mm1wHQD1SkPYMzUDTZvWvhuE6VNAkK3KxVeEmsanSmvjVkReDeBEMxeDaayjcZjFGPydyey1qxBHmTvAnBKoPydvuTAqx5f7YNNRAdeLmUi99gERUU7TD8KfAa6MpQ9bw","blskey_pop":"RPLagxaR5xdimFzwmzYnz4ZhWtYQEj8iR5ZU53T2gitPCyCHQneUn2Huc4oeLd2B2HzkGnjAff4hWTJT6C7qHYB1Mv2wU5iHHGFWkhnTX9WsEAbunJCV2qcaXScKj4tTfvdDKfLiVuU2av6hbsMztirRze7LvYBkRHV3tGwyCptsrP","client_ip":"{}","client_port":9708,"node_ip":"{}","node_port":9707,"services":["VALIDATOR"]}},"dest":"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA"}},"metadata":{{"from":"TWwCRQRZ2ZHMJFn9TzLp7W"}},"type":"0"}},"txnMetadata":{{"seqNo":4,"txnId":"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008"}},"ver":"1"}}"#, - test_pool_ip, test_pool_ip - ), - ] - } - - pub fn new(count: Option) -> GenesisTransactions { - let count = count.unwrap_or(4); - let transactions = Self::default_transactions()[0..count].to_vec(); - GenesisTransactions { - transactions, - file: None, - } - } - - pub fn from_transactions(transactions: T) -> GenesisTransactions - where - T: IntoIterator, - T::Item: ToString, - { - GenesisTransactions { - transactions: transactions.into_iter().map(|v| v.to_string()).collect(), - file: None, - } - } - - pub fn store_to_file(&mut self) -> PathBuf { - let data = self.transactions.join("\n"); - let mut file = NamedTempFile::new().unwrap(); - let path = file.path().to_owned(); - file.as_file_mut().write_all(data.as_bytes()).unwrap(); - self.file = Some(file); - path - } -} diff --git a/indy-test-utils/src/lib.rs b/indy-test-utils/src/lib.rs deleted file mode 100644 index 7ccbf57..0000000 --- a/indy-test-utils/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod environment; -pub mod genesis; diff --git a/indy-utils/Cargo.toml b/indy-utils/Cargo.toml deleted file mode 100644 index 2a1e33d..0000000 --- a/indy-utils/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "indy-utils" -version = "0.6.0" -authors = ["Hyperledger Indy Contributors "] -description = "Utilities for Hyperledger Indy (https://www.hyperledger.org/projects), which provides a distributed-ledger-based foundation for self-sovereign identity (https://sovrin.org)." -edition = "2018" -license = "Apache-2.0" -readme = "../README.md" -repository = "https://github.com/hyperledger/indy-shared-rs/" -categories = ["authentication", "cryptography"] -keywords = ["hyperledger", "indy", "ssi", "verifiable", "credentials"] - -[lib] -name = "indy_utils" -path = "src/lib.rs" -crate-type = ["rlib"] - -[features] -default = ["ed25519"] -ed25519 = ["curve25519-dalek", "ed25519-dalek", "rand", "sha2", "x25519-dalek"] - -[dependencies] -bs58 = "0.5" -curve25519-dalek = { version = "3.1", default-features = false, features = [ - "u64_backend", -], optional = true } -ed25519-dalek = { version = "1.0", default-features = false, features = [ - "u64_backend", -], optional = true } -once_cell = "1.9" -rand = { version = "0.8", optional = true } -regex = "1.3" -serde = { version = "1.0", optional = true, features = ["derive"] } -serde_json = { version = "1.0", optional = true } -sha2 = { version = "0.10", optional = true } -thiserror = "1.0" -x25519-dalek = { version = "1.1", default-features = false, features = [ - "u64_backend", -], optional = true } -zeroize = { version = "1.3" } - -[dev-dependencies] -async-global-executor = "2.3" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" diff --git a/indy-utils/src/base58.rs b/indy-utils/src/base58.rs deleted file mode 100644 index 59b5570..0000000 --- a/indy-utils/src/base58.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bs58; - -use super::error::ConversionError; - -pub fn decode>(val: T) -> Result, ConversionError> { - Ok(bs58::decode(val) - .into_vec() - .map_err(|err| ("Error decoding base58 data", err))?) -} - -pub fn encode>(val: T) -> String { - bs58::encode(val).into_string() -} diff --git a/indy-utils/src/did.rs b/indy-utils/src/did.rs deleted file mode 100644 index 059d45b..0000000 --- a/indy-utils/src/did.rs +++ /dev/null @@ -1,160 +0,0 @@ -use once_cell::sync::Lazy; - -use regex::Regex; -#[cfg(feature = "ed25519")] -use sha2::{Digest, Sha256}; - -use crate::base58; -#[cfg(feature = "ed25519")] -use crate::keys::{KeyType, PrivateKey, VerKey}; -use crate::{Qualifiable, Validatable, ValidationError}; - -/// The default identifier DID used when submitting ledger read requests -pub static DEFAULT_LIBINDY_DID: Lazy = - Lazy::new(|| DidValue::new("LibindyDid111111111111", None)); - -/// Create a new DID with an optional seed value -/// Version determines version of self-certification to be used -/// 1 (default) = did:sov -/// 2 = did:indy -#[cfg(feature = "ed25519")] -pub fn generate_did( - seed: Option<&[u8]>, - version: Option, -) -> Result<(ShortDidValue, PrivateKey, VerKey), crate::ConversionError> { - let sk = match seed { - Some(seed) => PrivateKey::from_seed(seed)?, - None => PrivateKey::generate(Some(KeyType::ED25519))?, - }; - - let pk = sk.public_key()?; - let did = match version { - Some(1) | None => Ok(base58::encode(&pk.as_ref()[..16])), - Some(2) => { - let mut hasher = Sha256::new(); - Digest::update(&mut hasher, &pk.as_ref()); - let hash = hasher.finalize(); - Ok(base58::encode(&hash[..16])) - } - _ => Err("Version must be one of 1,2"), - }?; - Ok((ShortDidValue::from(did), sk, pk)) -} - -/// A wrapper providing validation for DID methods -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct DidMethod(pub String); - -impl Validatable for DidMethod { - fn validate(&self) -> Result<(), ValidationError> { - static REGEX_METHOD_NAME: Lazy = Lazy::new(|| Regex::new("^[a-z0-9]+$").unwrap()); - - if !REGEX_METHOD_NAME.is_match(&self.0) { - return Err(invalid!( - "Invalid default name: {}. It does not match the DID method name format.", - self.0 - )); - } - Ok(()) - } -} - -qualifiable_type!(DidValue, "A qualifiable DID type"); - -impl Qualifiable for DidValue { - fn prefix() -> &'static str { - "did" - } -} - -impl DidValue { - pub fn new(did: &str, method: Option<&str>) -> DidValue { - DidValue::combine(method, did) - } - - pub fn to_short(&self) -> ShortDidValue { - ShortDidValue(self.to_unqualified().0) - } - - pub fn is_abbreviatable(&self) -> bool { - match self.get_method() { - Some(ref method) if method.starts_with("sov") => true, - Some(_) => false, - None => true, - } - } -} - -impl Validatable for DidValue { - fn validate(&self) -> Result<(), ValidationError> { - if self.is_fully_qualified() { - // pass - } else { - let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?; - if did.len() != 16 && did.len() != 32 { - return Err(invalid!( - "Trying to use DID with unexpected length: {}. \ - The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len() - )); - } - } - Ok(()) - } -} - -/// A short DID with no prefix or method -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct ShortDidValue(pub String); - -impl From for ShortDidValue { - fn from(val: String) -> Self { - Self(val) - } -} - -impl std::ops::Deref for ShortDidValue { - type Target = str; - fn deref(&self) -> &str { - &self.0 - } -} - -impl ShortDidValue { - /// Convert a short DID value to a qualified DID - pub fn qualify(&self, method: Option) -> DidValue { - DidValue::combine(method.as_ref().map(String::as_str), &self) - } -} - -impl Validatable for ShortDidValue { - fn validate(&self) -> Result<(), ValidationError> { - let did = base58::decode(&self.0).map_err(ValidationError::from_msg)?; - if did.len() != 16 && did.len() != 32 { - return Err(invalid!( - "Trying to use DID with unexpected length: {}. \ - The 16- or 32-byte number upon which a DID is based should be 22/23 or 44/45 bytes when encoded as base58.", did.len() - )); - } - Ok(()) - } -} - -#[cfg(all(test, feature = "ed25519"))] -mod tests { - use super::*; - use crate::keys::EncodedVerKey; - - #[test] - fn generate_abbreviate() { - let (did, _sk, vk) = generate_did(None, None).unwrap(); - let vk_b58 = vk.as_base58().unwrap(); - let vk_short = vk_b58.abbreviated_for_did(&did).unwrap(); - assert_eq!(vk_short.chars().next(), Some('~')); - let vk_long = EncodedVerKey::from_did_and_verkey(&did, &vk_short).unwrap(); - assert_eq!(vk_long, vk_b58); - let cmp_vk = vk_long.decode().unwrap(); - assert_eq!(vk, cmp_vk); - } -} diff --git a/indy-utils/src/error.rs b/indy-utils/src/error.rs deleted file mode 100644 index 898f8d1..0000000 --- a/indy-utils/src/error.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::error::Error as StdError; -use thiserror::Error; - -type DynError = Box; - -macro_rules! define_error { - ($name:tt, $short:expr, $doc:tt) => { - #[derive(Debug, Error)] - #[doc=$doc] - pub struct $name { - pub context: Option, - pub source: Option, - } - - impl $name { - pub fn from_msg>(msg: T) -> Self { - Self::from(msg.into()) - } - - pub fn from_err(err: E) -> Self - where - E: StdError + Send + Sync + 'static, - { - Self { - context: None, - source: Some(Box::new(err) as DynError), - } - } - - pub fn from_msg_err(msg: M, err: E) -> Self - where - M: Into, - E: StdError + Send + Sync + 'static, - { - Self { - context: Some(msg.into()), - source: Some(Box::new(err) as DynError), - } - } - } - - impl From<&str> for $name { - fn from(context: &str) -> Self { - Self { - context: Some(context.to_owned()), - source: None, - } - } - } - - impl From for $name { - fn from(context: String) -> Self { - Self { - context: Some(context), - source: None, - } - } - } - - impl From> for $name { - fn from(context: Option) -> Self { - Self { - context, - source: None, - } - } - } - - impl From<(M, E)> for $name - where - M: Into, - E: StdError + Send + Sync + 'static, - { - fn from((context, err): (M, E)) -> Self { - Self::from_msg_err(context, err) - } - } - - impl Into for $name { - fn into(self) -> String { - self.to_string() - } - } - - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $short)?; - match self.context { - Some(ref context) => write!(f, ": {}", context), - None => Ok(()), - } - } - } - }; -} - -define_error!( - ConversionError, - "Conversion error", - "Error type for general data conversion errors" -); - -define_error!( - EncryptionError, - "Encryption error", - "Error type for failure of encryption and decryption operations" -); - -define_error!( - UnexpectedError, - "Unexpected error", - "Error type for eventualities that shouldn't normally occur" -); - -define_error!( - ValidationError, - "Validation error", - "Error type for failures of `Validatable::validate`" -); - -#[cfg(feature = "serde_json")] -impl From for ConversionError { - fn from(err: serde_json::error::Error) -> Self { - Self::from_msg(err.to_string()) - } -} - -impl From for ConversionError { - fn from(_err: std::str::Utf8Error) -> Self { - Self::from("UTF-8 decoding error") - } -} - -impl From for ConversionError { - fn from(_err: std::string::FromUtf8Error) -> Self { - Self::from("UTF-8 decoding error") - } -} - -impl From for ConversionError { - fn from(err: ValidationError) -> Self { - Self { - context: err.context, - source: err.source, - } - } -} - -impl From for ValidationError { - fn from(err: ConversionError) -> Self { - Self { - context: err.context, - source: err.source, - } - } -} - -impl From for ConversionError { - fn from(err: UnexpectedError) -> Self { - Self { - context: err.context, - source: err.source, - } - } -} - -impl From for EncryptionError { - fn from(err: UnexpectedError) -> Self { - Self { - context: err.context, - source: err.source, - } - } -} diff --git a/indy-utils/src/keys/mod.rs b/indy-utils/src/keys/mod.rs deleted file mode 100644 index 511ddae..0000000 --- a/indy-utils/src/keys/mod.rs +++ /dev/null @@ -1,555 +0,0 @@ -#[cfg(feature = "ed25519")] -use curve25519_dalek::edwards::CompressedEdwardsY; -#[cfg(feature = "ed25519")] -use ed25519_dalek::{ExpandedSecretKey, PublicKey, SecretKey, Signature}; -#[cfg(feature = "ed25519")] -use rand::{thread_rng, RngCore}; -#[cfg(feature = "ed25519")] -use sha2::digest::Digest; - -#[cfg(feature = "ed25519")] -use std::convert::TryFrom; - -use zeroize::Zeroize; - -use super::base58; -use super::error::ConversionError; -use super::{Validatable, ValidationError}; - -mod types; -pub use types::{KeyEncoding, KeyType}; - -/// Build an encoded verkey -pub fn build_full_verkey(dest: &str, key: &str) -> Result { - EncodedVerKey::from_str_qualified(key, Some(dest), None, None) -} - -/// A raw signing key used for generating transaction signatures -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PrivateKey { - pub key: Vec, - pub alg: KeyType, -} - -impl PrivateKey { - pub fn new>(key: K, alg: Option) -> Self { - Self { - key: key.as_ref().to_vec(), - alg: alg.unwrap_or_default(), - } - } - - #[cfg(feature = "ed25519")] - pub fn generate(alg: Option) -> Result { - let alg = alg.unwrap_or_default(); - match alg { - KeyType::ED25519 => { - let mut sk = [0u8; 32]; - thread_rng().fill_bytes(&mut sk[..]); - Self::from_seed(&sk[..]) - } - _ => Err("Unsupported key type".into()), - } - } - - #[cfg(feature = "ed25519")] - pub fn from_seed(seed: &[u8]) -> Result { - let sk = SecretKey::from_bytes(seed) - .map_err(|err| format!("Error creating signing key: {}", err))?; - let mut esk = [0u8; 64]; - esk[..32].copy_from_slice(sk.as_bytes()); - esk[32..].copy_from_slice(PublicKey::from(&sk).as_bytes()); - Ok(Self::new(esk, Some(KeyType::ED25519))) - } - - pub fn public_key(&self) -> Result { - match self.alg { - KeyType::ED25519 => Ok(VerKey::new(&self.key[32..], Some(self.alg.clone()))), - _ => Err("Unsupported key type".into()), - } - } - - pub fn key_bytes(&self) -> Vec { - self.key.clone() - } - - #[cfg(feature = "ed25519")] - pub fn key_exchange(&self) -> Result { - match self.alg { - KeyType::ED25519 => { - let mut hash = sha2::Sha512::digest(&self.key[..32]); - let x_sk = - x25519_dalek::StaticSecret::from(<[u8; 32]>::try_from(&hash[..32]).unwrap()); - hash.zeroize(); - Ok(Self::new(&x_sk.to_bytes(), Some(KeyType::X25519))) - } - _ => Err("Unsupported key format for key exchange".into()), - } - } - - #[cfg(feature = "ed25519")] - pub fn sign>(&self, message: M) -> Result, ConversionError> { - match self.alg { - KeyType::ED25519 => { - let esk = ExpandedSecretKey::from(&SecretKey::from_bytes(&self.key[..32]).unwrap()); - let pk = PublicKey::from_bytes(&self.key[32..]).unwrap(); - let sig = esk.sign(message.as_ref(), &pk); - Ok(sig.to_bytes().into()) - } - _ => Err("Unsupported key format for signing".into()), - } - } -} - -impl AsRef<[u8]> for PrivateKey { - fn as_ref(&self) -> &[u8] { - self.key.as_ref() - } -} - -impl Zeroize for PrivateKey { - fn zeroize(&mut self) { - self.key.zeroize(); - self.alg = KeyType::from("") - } -} - -impl Drop for PrivateKey { - fn drop(&mut self) { - self.zeroize() - } -} - -impl Validatable for PrivateKey { - fn validate(&self) -> Result<(), ValidationError> { - if self.alg == KeyType::ED25519 { - if self.key.len() == 64 { - Ok(()) - } else { - Err("Invalid signing key length".into()) - } - } else { - Err("Unsupported signing key type".into()) - } - } -} - -/// A raw verkey used in verifying signatures -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct VerKey { - pub key: Vec, - pub alg: KeyType, -} - -impl VerKey { - pub fn new>(key: K, alg: Option) -> Self { - let alg = alg.unwrap_or_default(); - Self { - key: key.as_ref().to_vec(), - alg, - } - } - - pub fn as_base58(&self) -> Result { - self.encode(&KeyEncoding::BASE58) - } - - pub fn encode(&self, enc: &KeyEncoding) -> Result { - match enc { - KeyEncoding::BASE58 => { - let key = base58::encode(&self.key); - Ok(EncodedVerKey::new( - key.as_str(), - Some(self.alg.clone()), - Some(enc.clone()), - )) - } - _ => Err("Unsupported key encoding".into()), - } - } - - pub fn key_bytes(&self) -> Vec { - self.key.clone() - } - - #[cfg(feature = "ed25519")] - pub fn key_exchange(&self) -> Result { - match self.alg { - KeyType::ED25519 => { - let vky = CompressedEdwardsY::from_slice(&self.key[..]); - if let Some(x_vk) = vky.decompress() { - Ok(Self::new( - x_vk.to_montgomery().as_bytes().to_vec(), - Some(KeyType::X25519), - )) - } else { - Err("Error converting to x25519 key".into()) - } - } - _ => Err("Unsupported verkey type".into()), - } - } - - #[cfg(feature = "ed25519")] - pub fn verify_signature, S: AsRef<[u8]>>( - &self, - message: M, - signature: S, - ) -> Result { - match self.alg { - KeyType::ED25519 => { - let vk = PublicKey::from_bytes(&self.key[..]).unwrap(); - if let Ok(sig) = Signature::try_from(signature.as_ref()) { - Ok(vk.verify_strict(message.as_ref(), &sig).is_ok()) - } else { - Err("Error validating message signature".into()) - } - } - _ => Err("Unsupported verkey type".into()), - } - } -} - -impl AsRef<[u8]> for VerKey { - fn as_ref(&self) -> &[u8] { - self.key.as_ref() - } -} - -impl std::fmt::Display for VerKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.as_base58() { - Ok(k) => k.fmt(f), - Err(err) => write!(f, "", err), - } - } -} - -impl Validatable for VerKey { - fn validate(&self) -> Result<(), ValidationError> { - if self.alg == KeyType::ED25519 { - let bytes = self.key_bytes(); - if bytes.len() == 32 { - Ok(()) - } else { - Err("Invalid verkey length".into()) - } - } else { - Err("Unsupported verkey type".into()) - } - } -} - -impl Zeroize for VerKey { - fn zeroize(&mut self) { - self.key.zeroize(); - self.alg = KeyType::from(""); - } -} - -impl Drop for VerKey { - fn drop(&mut self) { - self.zeroize() - } -} - -/// An encoded verkey appropriate for storing and transmitting -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct EncodedVerKey { - pub key: String, - pub alg: KeyType, - pub enc: KeyEncoding, -} - -impl EncodedVerKey { - pub fn new>(key: K, alg: Option, enc: Option) -> Self { - let alg = alg.unwrap_or_default(); - let enc = enc.unwrap_or_default(); - Self { - key: key.as_ref().to_owned(), - alg, - enc, - } - } - - pub fn from_did_and_verkey(did: &str, key: &str) -> Result { - if key.chars().next() == Some('~') { - let mut vk_bytes = base58::decode(&key[1..])?; - if vk_bytes.len() != 16 { - return Err("Expected 16-byte abbreviated verkey".into()); - } - let mut did_bytes = base58::decode(did)?; - if did_bytes.len() != 16 { - return Err("DID must be 16 bytes in length".into()); - } - did_bytes.append(&mut vk_bytes); - Ok(Self::new( - &base58::encode(did_bytes), - Some(KeyType::ED25519), - Some(KeyEncoding::BASE58), - )) - } else { - Ok(Self::new( - key, - Some(KeyType::ED25519), - Some(KeyEncoding::BASE58), - )) - } - } - - pub fn abbreviated_for_did(&self, did: &str) -> Result { - let did_bytes = base58::decode(did)?; - if did_bytes.len() != 16 { - return Err("DID must be 16 bytes in length".into()); - } - let vk = self.key_bytes()?; - if vk.len() != 32 { - return Err("Expected 32-byte verkey".into()); - } - if &vk[..16] == did_bytes.as_slice() { - let mut result = "~".to_string(); - result.push_str(&base58::encode(&vk[16..])); - Ok(result) - } else { - Ok(base58::encode(vk)) - } - } - - pub fn decode(&self) -> Result { - let mut vk = self.key_bytes()?; - let result = VerKey::new(&vk, Some(self.alg.clone())); - vk.zeroize(); - Ok(result) - } - - pub fn from_slice>(key: K) -> Result { - let key = std::str::from_utf8(key.as_ref())?; - Self::from_str_qualified(key, None, None, None) - } - - pub fn from_str(key: &str) -> Result { - Self::from_str_qualified(key, None, None, None) - } - - pub fn from_str_qualified( - key: &str, - dest: Option<&str>, - alg: Option, - enc: Option, - ) -> Result { - let (key, alg) = if key.contains(':') { - let splits: Vec<&str> = key.splitn(2, ':').collect(); - let alg = match splits[1] { - "" => alg, - _ => Some(splits[1].into()), - }; - (splits[0], alg) - } else { - (key, alg) - }; - - if key.starts_with('~') { - let dest = - unwrap_opt_or_return!(dest, Err("Destination required for short verkey".into())); - let mut result = base58::decode(dest)?; - let mut end = base58::decode(&key[1..])?; - result.append(&mut end); - Ok(Self::new(&base58::encode(result), alg, enc)) - } else { - Ok(Self::new(key, alg, enc)) - } - } - - pub fn long_form(&self) -> String { - let mut result = self.key.clone(); - result.push(':'); - result.push_str(&self.alg); - result - } - - pub fn as_base58(self) -> Result { - match self.enc { - KeyEncoding::BASE58 => Ok(self), - _ => { - let key = base58::encode(self.key_bytes()?); - Ok(Self::new( - key.as_str(), - Some(self.alg.clone()), - Some(KeyEncoding::BASE58), - )) - } - } - } - - pub fn key_bytes(&self) -> Result, ConversionError> { - match self.enc { - KeyEncoding::BASE58 => Ok(base58::decode(&self.key)?), - _ => Err("Unsupported verkey encoding".into()), - } - } - - pub fn encoded_key_bytes(&self) -> &[u8] { - self.key.as_bytes() - } - - #[cfg(feature = "ed25519")] - pub fn key_exchange(&self) -> Result { - let vk = self.decode()?; - vk.key_exchange() - } - - #[cfg(feature = "ed25519")] - pub fn key_exchange_encoded(&self) -> Result { - let x_vk = self.key_exchange()?; - x_vk.encode(&self.enc) - } - - #[cfg(feature = "ed25519")] - pub fn verify_signature, S: AsRef<[u8]>>( - &self, - message: M, - signature: S, - ) -> Result { - let vk = self.decode()?; - vk.verify_signature(message, signature) - } -} - -impl std::fmt::Display for EncodedVerKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let out = if self.alg == KeyType::default() { - self.key.clone() - } else { - self.long_form() - }; - f.write_str(out.as_str()) - } -} - -impl Validatable for EncodedVerKey { - fn validate(&self) -> Result<(), ValidationError> { - let verkey = self.decode()?; - verkey.validate() - } -} - -impl Zeroize for EncodedVerKey { - fn zeroize(&mut self) { - self.key.zeroize(); - self.alg = KeyType::from(""); - self.enc = KeyEncoding::from("") - } -} - -impl Drop for EncodedVerKey { - fn drop(&mut self) { - self.zeroize() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn from_str_empty() { - assert_eq!( - EncodedVerKey::from_str("").unwrap(), - EncodedVerKey::new("", Some(KeyType::default()), Some(KeyEncoding::default())) - ) - } - - #[test] - fn from_str_single_colon() { - assert_eq!( - EncodedVerKey::from_str(":").unwrap(), - EncodedVerKey::new("", Some(KeyType::default()), Some(KeyEncoding::default())) - ) - } - - #[test] - fn from_str_ends_with_colon() { - assert_eq!( - EncodedVerKey::from_str("foo:").unwrap(), - EncodedVerKey::new( - "foo", - Some(KeyType::default()), - Some(KeyEncoding::default()) - ) - ) - } - - #[test] - fn from_key_starts_with_colon() { - assert_eq!( - EncodedVerKey::from_str(":bar").unwrap(), - EncodedVerKey::new("", Some("bar".into()), Some(KeyEncoding::default())) - ) - } - - #[test] - fn from_key_works() { - assert_eq!( - EncodedVerKey::from_str("foo:bar:baz").unwrap(), - EncodedVerKey::new("foo", Some("bar:baz".into()), Some(KeyEncoding::default())) - ) - } - - #[test] - fn round_trip_verkey() { - assert_eq!( - EncodedVerKey::from_str("foo:bar:baz").unwrap().long_form(), - "foo:bar:baz" - ) - } - - #[cfg(feature = "ed25519")] - #[test] - fn key_from_seed() { - const SEED: &[u8; 32] = b"aaaabbbbccccddddeeeeffffgggghhhh"; - let sk = PrivateKey::from_seed(&SEED[..]).unwrap(); - assert_eq!( - &sk.as_ref()[..], - &[ - 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 100, 101, 101, 101, - 101, 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 113, 22, 13, 44, - 71, 184, 166, 148, 196, 234, 85, 148, 234, 22, 204, 148, 187, 247, 77, 119, 250, - 28, 37, 255, 29, 31, 159, 159, 245, 68, 107, 235 - ] - ); - let xk = sk.key_exchange().unwrap(); - assert_eq!( - &xk.as_ref(), - &[ - 208, 235, 232, 147, 241, 214, 250, 182, 45, 157, 20, 202, 31, 184, 226, 115, 149, - 82, 210, 89, 50, 100, 22, 67, 21, 8, 124, 198, 100, 252, 237, 107 - ] - ); - } - - #[cfg(feature = "ed25519")] - #[test] - fn sign_and_verify() { - let message = b"hello there"; - let sk = PrivateKey::generate(None).unwrap(); - let sig = sk.sign(&message).unwrap(); - let vk = sk.public_key().unwrap(); - assert!(vk.verify_signature(&message, &sig).unwrap()); - assert!(vk.verify_signature(&message, &[]).is_err()); - assert!(!vk.verify_signature(&"goodbye", &sig).unwrap()); - } - - #[cfg(feature = "ed25519")] - #[test] - fn validate_keys() { - let sk = PrivateKey::generate(None).unwrap(); - sk.validate().unwrap(); - let vk = sk.public_key().unwrap(); - vk.validate().unwrap(); - - let sk = PrivateKey::new(b"bad key", Some(KeyType::ED25519)); - assert_eq!(sk.validate().is_ok(), false); - let vk = VerKey::new(b"bad key", Some(KeyType::ED25519)); - assert_eq!(vk.validate().is_ok(), false); - } -} diff --git a/indy-utils/src/keys/types.rs b/indy-utils/src/keys/types.rs deleted file mode 100644 index 2bd0dd2..0000000 --- a/indy-utils/src/keys/types.rs +++ /dev/null @@ -1,122 +0,0 @@ -pub const KEY_ENC_BASE58: &'static str = "base58"; - -pub const KEY_TYPE_ED25519: &'static str = "ed25519"; -pub const KEY_TYPE_X25519: &'static str = "x25519"; - -/// Enum of known and unknown key types -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum KeyType { - ED25519, - X25519, - Other(String), -} - -impl KeyType { - pub fn from_str(keytype: &str) -> KeyType { - match keytype.to_ascii_lowercase().as_str() { - KEY_TYPE_ED25519 => KeyType::ED25519, - KEY_TYPE_X25519 => KeyType::X25519, - _ => KeyType::Other(keytype.to_owned()), - } - } - - pub fn is_known(&self) -> bool { - match self { - Self::Other(_) => false, - _ => true, - } - } - - pub fn as_str(&self) -> &str { - match self { - Self::ED25519 => KEY_TYPE_ED25519, - Self::X25519 => KEY_TYPE_X25519, - Self::Other(t) => t.as_str(), - } - } -} - -impl std::string::ToString for KeyType { - fn to_string(&self) -> String { - self.as_str().to_owned() - } -} - -impl Default for KeyType { - fn default() -> Self { - KeyType::ED25519 - } -} - -impl std::ops::Deref for KeyType { - type Target = str; - fn deref(&self) -> &str { - self.as_str() - } -} - -impl From<&str> for KeyType { - fn from(value: &str) -> Self { - Self::from_str(value) - } -} - -impl From for KeyType { - fn from(value: String) -> Self { - Self::from_str(&value) - } -} - -/// Enum of known and unknown key encodings -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum KeyEncoding { - BASE58, - Other(String), -} - -impl KeyEncoding { - pub fn from_str(keyenc: &str) -> KeyEncoding { - match keyenc.to_ascii_lowercase().as_str() { - KEY_ENC_BASE58 => KeyEncoding::BASE58, - _ => KeyEncoding::Other(keyenc.to_owned()), - } - } - - pub fn as_str(&self) -> &str { - match self { - Self::BASE58 => KEY_ENC_BASE58, - Self::Other(e) => e.as_str(), - } - } -} - -impl std::string::ToString for KeyEncoding { - fn to_string(&self) -> String { - self.as_str().to_owned() - } -} - -impl Default for KeyEncoding { - fn default() -> Self { - KeyEncoding::BASE58 - } -} - -impl std::ops::Deref for KeyEncoding { - type Target = str; - fn deref(&self) -> &str { - self.as_str() - } -} - -impl From<&str> for KeyEncoding { - fn from(value: &str) -> Self { - Self::from_str(value) - } -} - -impl From for KeyEncoding { - fn from(value: String) -> Self { - Self::from_str(&value) - } -} diff --git a/indy-utils/src/lib.rs b/indy-utils/src/lib.rs deleted file mode 100644 index aed6b2b..0000000 --- a/indy-utils/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[cfg(any(feature = "serde", test))] -#[macro_use] -pub extern crate serde; - -/// Common macros -#[macro_use] -mod macros; - -mod error; -pub use error::{ConversionError, EncryptionError, UnexpectedError, ValidationError}; - -/// Trait for qualifiable identifier types, having an optional prefix and method -#[macro_use] -pub mod qualifiable; -pub use qualifiable::Qualifiable; - -/// Trait definition for validatable data types -#[macro_use] -mod validation; -pub use validation::Validatable; - -/// base58 encoding and decoding -pub mod base58; - -/// Indy DID representation and derivation -pub mod did; - -/// Indy signing keys and verification keys -pub mod keys; diff --git a/indy-utils/src/macros.rs b/indy-utils/src/macros.rs deleted file mode 100644 index 7c06770..0000000 --- a/indy-utils/src/macros.rs +++ /dev/null @@ -1,88 +0,0 @@ -macro_rules! unwrap_opt_or_return { - ($opt:expr, $err:expr) => { - match $opt { - Some(val) => val, - None => return $err, - } - }; -} - -/// Used to optionally add Serialize and Deserialize traits to Qualifiable types -#[cfg(feature = "serde")] -#[macro_export] -macro_rules! serde_derive_impl { - ($def:item) => { - #[derive(Serialize, Deserialize)] - $def - }; -} - -#[cfg(not(feature = "serde"))] -#[macro_export] -macro_rules! serde_derive_impl { - ($def:item) => { - $def - }; -} - -/// Derive a new handle type having an atomically increasing sequence number -#[macro_export] -macro_rules! new_handle_type (($newtype:ident, $counter:ident) => ( - static $counter: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); - - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] - #[repr(transparent)] - pub struct $newtype(pub usize); - - impl $newtype { - #[allow(dead_code)] - pub fn invalid() -> $newtype { - $newtype(0) - } - - #[allow(dead_code)] - pub fn next() -> $newtype { - $newtype($counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1) - } - } - - impl std::fmt::Display for $newtype { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}({})", stringify!($newtype), self.0) - } - } - - impl std::ops::Deref for $newtype { - type Target = usize; - fn deref(&self) -> &usize { - &self.0 - } - } - - impl PartialEq for $newtype { - fn eq(&self, other: &usize) -> bool { - self.0 == *other - } - } - - impl $crate::Validatable for $newtype { - fn validate(&self) -> std::result::Result<(), $crate::ValidationError> { - if(**self == 0) { - Err("Invalid handle: zero".into()) - } else { - Ok(()) - } - } - } -)); - -#[cfg(test)] -mod tests { - new_handle_type!(TestHandle, TEST_HANDLE_CTR); - - #[test] - fn test_handle_seq() { - assert_eq!(TestHandle::next(), 1); - assert_eq!(TestHandle::next(), 2); - } -} diff --git a/indy-utils/src/qualifiable.rs b/indy-utils/src/qualifiable.rs deleted file mode 100644 index 195f3b6..0000000 --- a/indy-utils/src/qualifiable.rs +++ /dev/null @@ -1,139 +0,0 @@ -use once_cell::sync::Lazy; - -use regex::Regex; - -use super::{invalid, Validatable, ValidationError}; - -pub(crate) static REGEX: Lazy = - Lazy::new(|| Regex::new("^([a-z0-9]+):([a-z0-9]+):(.*)$").unwrap()); - -/// Combine a prefix, method, and value into a qualified identifier -pub fn combine(prefix: &str, method: Option<&str>, entity: &str) -> String { - match method { - Some(method) => format!("{}:{}:{}", prefix, method, entity), - _ => entity.to_owned(), - } -} - -/// Split a qualifiable identifier into its method and value components -pub fn split<'a>(prefix: &str, val: &'a str) -> (Option<&'a str>, &'a str) { - match REGEX.captures(&val) { - None => (None, val), - Some(caps) => { - if caps.get(1).map(|m| m.as_str()) == Some(prefix) { - ( - Some(caps.get(2).unwrap().as_str()), - caps.get(3).unwrap().as_str(), - ) - } else { - (None, val) - } - } - } -} - -/// Check if an identifier is qualified by a prefix and method -pub fn is_fully_qualified(entity: &str) -> bool { - REGEX.captures(entity).is_some() -} - -/// An identifier which can be qualified with a prefix and method -pub trait Qualifiable: From + std::ops::Deref + Validatable { - fn prefix() -> &'static str; - - fn combine(method: Option<&str>, entity: &str) -> Self { - Self::from(combine(Self::prefix(), method, entity)) - } - - fn split<'a>(&'a self) -> (Option<&'a str>, &'a str) { - split(Self::prefix(), self.deref()) - } - - fn get_method<'a>(&'a self) -> Option<&'a str> { - let (method, _rest) = self.split(); - method - } - - fn default_method(&self, method: Option<&str>) -> Self { - let (prev_method, rest) = self.split(); - match prev_method { - Some(_) => Self::from(self.to_string()), - None => Self::combine(method, rest), - } - } - - fn replace_method(&self, method: Option<&str>) -> Self { - let (_method, rest) = self.split(); - Self::combine(method, rest) - } - - fn remove_method(&self, method: &str) -> Self { - let (prev_method, rest) = self.split(); - if prev_method == Some(method) { - Self::combine(None, rest) - } else { - Self::from(self.to_string()) - } - } - - fn from_str(entity: &str) -> Result { - let result = Self::from(entity.to_owned()); - result.validate()?; - Ok(result) - } - - fn is_fully_qualified(&self) -> bool { - self.get_method().is_some() - } - - fn to_qualified(&self, method: &str) -> Result { - match self.split() { - (None, rest) => Ok(Self::combine(Some(method), rest)), - (Some(prev_method), rest) if prev_method == method => { - Ok(Self::combine(Some(method), rest)) - } - _ => Err(invalid!( - "Identifier is already qualified with another method", - )), - } - } - - fn to_unqualified(&self) -> Self { - let (_, rest) = self.split(); - Self::from(rest.to_owned()) - } -} - -/// Derive a new `Qualifiable` string type -#[macro_export] -macro_rules! qualifiable_type { - ($newtype:ident, $doc:expr) => { - $crate::serde_derive_impl! { - #[doc=$doc] - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $newtype(pub String); - } - - impl From for $newtype { - fn from(val: String) -> Self { - Self(val) - } - } - - impl std::ops::Deref for $newtype { - type Target = str; - fn deref(&self) -> &str { - &self.0 - } - } - - impl std::fmt::Display for $newtype { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.0.as_str()) - } - } - }; - ($newtype:ident) => { - qualifiable_type!($newtype, ""); - }; -} diff --git a/indy-utils/src/validation.rs b/indy-utils/src/validation.rs deleted file mode 100644 index dfa41a0..0000000 --- a/indy-utils/src/validation.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::error::ValidationError; - -/// Macro to return a new `ValidationError` with an optional message -#[macro_export] -macro_rules! invalid { - () => { $crate::ValidationError::from(None) }; - ($($arg:tt)+) => { - $crate::ValidationError::from(format!($($arg)+)) - }; -} - -/// Trait for data types which need validation after being loaded from external sources -pub trait Validatable { - fn validate(&self) -> Result<(), ValidationError> { - Ok(()) - } -} From be7729563a886bdc9d271647faf23c155157e885 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 14 Sep 2023 14:28:53 -0700 Subject: [PATCH 3/7] set resolver version Signed-off-by: Andrew Whitehead --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 43bb688..220cac6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = ["indy-credx", "indy-data-types"] From ee4b2fb4ca0effcd592d63693fd721c0168c1b39 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Thu, 14 Sep 2023 15:24:02 -0700 Subject: [PATCH 4/7] clean up clippy warnings Signed-off-by: Andrew Whitehead --- indy-credx/src/ffi/cred_def.rs | 1 + indy-credx/src/ffi/object.rs | 15 ++-- indy-credx/src/ffi/presentation.rs | 1 + indy-credx/src/ffi/revocation.rs | 1 + indy-credx/src/services/helpers.rs | 2 +- indy-credx/src/services/issuer.rs | 38 ++++----- indy-credx/src/services/prover.rs | 38 +++------ indy-credx/src/services/types.rs | 16 +--- indy-credx/src/services/verifier.rs | 72 +++++++--------- indy-credx/tests/anoncreds_demos.rs | 28 +++---- indy-credx/tests/utils/anoncreds.rs | 4 +- indy-data-types/src/anoncreds/cred_def.rs | 20 +++-- indy-data-types/src/anoncreds/nonce.rs | 10 +-- indy-data-types/src/anoncreds/pres_request.rs | 14 ++-- indy-data-types/src/anoncreds/presentation.rs | 14 +--- indy-data-types/src/anoncreds/rev_reg_def.rs | 48 ++++++----- indy-data-types/src/anoncreds/rich_schema.rs | 4 +- indy-data-types/src/anoncreds/schema.rs | 8 +- indy-data-types/src/anoncreds/wql.rs | 20 ++--- indy-data-types/src/did.rs | 6 +- indy-data-types/src/error.rs | 6 +- indy-data-types/src/identifiers/cred_def.rs | 2 +- indy-data-types/src/identifiers/mod.rs | 2 +- indy-data-types/src/identifiers/rev_reg.rs | 9 +- .../src/identifiers/rich_schema.rs | 6 +- indy-data-types/src/keys/mod.rs | 54 ++++++------ indy-data-types/src/keys/types.rs | 83 ++++++++++--------- indy-data-types/src/merkle_tree/hash.rs | 7 +- indy-data-types/src/merkle_tree/mod.rs | 36 ++++---- indy-data-types/src/merkle_tree/proof.rs | 43 +++++----- indy-data-types/src/qualifiable.rs | 6 +- 31 files changed, 287 insertions(+), 327 deletions(-) diff --git a/indy-credx/src/ffi/cred_def.rs b/indy-credx/src/ffi/cred_def.rs index 6b6a4e0..021c62e 100644 --- a/indy-credx/src/ffi/cred_def.rs +++ b/indy-credx/src/ffi/cred_def.rs @@ -1,4 +1,5 @@ use std::os::raw::c_char; +use std::str::FromStr; use ffi_support::{rust_string_to_c, FfiStr}; use indy_data_types::Qualifiable; diff --git a/indy-credx/src/ffi/object.rs b/indy-credx/src/ffi/object.rs index f67abf0..ab7d570 100644 --- a/indy-credx/src/ffi/object.rs +++ b/indy-credx/src/ffi/object.rs @@ -19,7 +19,7 @@ pub(crate) static FFI_OBJECTS: Lazy>> = static FFI_OBJECT_COUNTER: AtomicUsize = AtomicUsize::new(0); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(transparent)] pub struct ObjectHandle(pub usize); @@ -71,12 +71,6 @@ impl ObjectHandle { } } -impl Default for ObjectHandle { - fn default() -> Self { - Self(0) - } -} - impl std::fmt::Display for ObjectHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}({})", stringify!($newtype), self.0) @@ -112,6 +106,7 @@ pub(crate) struct IndyObject(Arc); impl IndyObject { pub fn new(value: O) -> Self { + assert!(std::mem::size_of::() != 0); Self(Arc::new(value)) } @@ -135,6 +130,10 @@ impl IndyObject { impl PartialEq for IndyObject { fn eq(&self, other: &IndyObject) -> bool { + #[allow(clippy::vtable_address_comparisons)] + // this is allowed only because we create all such objects + // in one place (the `new` method) and ensure they are not + // zero-sized. Arc::ptr_eq(&self.0, &other.0) } } @@ -252,7 +251,7 @@ pub(crate) struct IndyObjectList(Vec); impl IndyObjectList { pub fn load(handles: &[ObjectHandle]) -> Result { let loaded = handles - .into_iter() + .iter() .map(ObjectHandle::load) .collect::>()?; Ok(Self(loaded)) diff --git a/indy-credx/src/ffi/presentation.rs b/indy-credx/src/ffi/presentation.rs index a2e5c5f..9763f9d 100644 --- a/indy-credx/src/ffi/presentation.rs +++ b/indy-credx/src/ffi/presentation.rs @@ -218,6 +218,7 @@ pub extern "C" fn credx_verify_presentation_legacy( ) } +#[allow(clippy::too_many_arguments)] fn _credx_verify_presentation( presentation: ObjectHandle, pres_req: ObjectHandle, diff --git a/indy-credx/src/ffi/revocation.rs b/indy-credx/src/ffi/revocation.rs index 886fb4b..e17daf3 100644 --- a/indy-credx/src/ffi/revocation.rs +++ b/indy-credx/src/ffi/revocation.rs @@ -1,6 +1,7 @@ use std::collections::BTreeSet; use std::convert::TryInto; use std::os::raw::c_char; +use std::str::FromStr; use ffi_support::{rust_string_to_c, FfiStr}; use indy_data_types::Qualifiable; diff --git a/indy-credx/src/services/helpers.rs b/indy-credx/src/services/helpers.rs index 8d28c25..c808d10 100644 --- a/indy-credx/src/services/helpers.rs +++ b/indy-credx/src/services/helpers.rs @@ -14,7 +14,7 @@ use crate::anoncreds_clsignatures::{ use crate::error::Result; pub fn attr_common_view(attr: &str) -> String { - attr.replace(" ", "").to_lowercase() + attr.replace(' ', "").to_lowercase() } pub fn build_credential_schema(attrs: &HashSet) -> Result { diff --git a/indy-credx/src/services/issuer.rs b/indy-credx/src/services/issuer.rs index 82f7bc0..5c65d16 100644 --- a/indy-credx/src/services/issuer.rs +++ b/indy-credx/src/services/issuer.rs @@ -29,7 +29,7 @@ pub fn create_schema( origin_did, schema_name, schema_version, attr_names); origin_did.validate()?; - let schema_id = SchemaId::new(&origin_did, schema_name, schema_version); + let schema_id = SchemaId::new(origin_did, schema_name, schema_version); let schema = SchemaV1 { id: schema_id, name: schema_name.to_string(), @@ -57,12 +57,12 @@ pub fn make_credential_definition_id( }; let schema_infix_id = schema_seq_no .map(|n| SchemaId(n.to_string())) - .unwrap_or(schema_id.clone()); + .unwrap_or(schema_id); Ok(CredentialDefinitionId::new( origin_did, &schema_infix_id, - &signature_type.to_str(), + signature_type.to_str(), tag, )) } @@ -84,9 +84,7 @@ pub fn create_credential_definition( config ); - let schema = match schema { - Schema::SchemaV1(s) => s, - }; + let Schema::SchemaV1(schema) = schema; let cred_def_id = make_credential_definition_id(origin_did, &schema.id, schema.seq_no, tag, signature_type)?; @@ -142,9 +140,7 @@ pub fn make_revocation_registry_id( tag: &str, rev_reg_type: RegistryType, ) -> Result { - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(c) => c, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let origin_did = match (origin_did.get_method(), cred_def.id.get_method()) { (None, Some(_)) => { @@ -157,9 +153,9 @@ pub fn make_revocation_registry_id( }; Ok(RevocationRegistryId::new( - &origin_did, + origin_did, &cred_def.id, - &rev_reg_type.to_str(), + rev_reg_type.to_str(), tag, )) } @@ -186,9 +182,7 @@ where let rev_reg_id = make_revocation_registry_id(origin_did, cred_def, tag, rev_reg_type)?; - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(c) => c, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let credential_pub_key = cred_def.get_public_key().map_err(err_map!( Unexpected, "Error fetching public key from credential definition" @@ -211,13 +205,13 @@ where max_cred_num, issuance_type, public_keys: rev_keys_pub, - tails_location: tails_location.clone(), + tails_location, tails_hash, }; let revoc_reg_def = RevocationRegistryDefinition::RevocationRegistryDefinitionV1( RevocationRegistryDefinitionV1 { - id: rev_reg_id.clone(), + id: rev_reg_id, revoc_def_type: rev_reg_type, tag: tag.to_string(), cred_def_id: cred_def.id.clone(), @@ -260,9 +254,7 @@ pub fn update_revocation_registry( ))? } }; - let rev_reg_def = match rev_reg_def { - RevocationRegistryDefinition::RevocationRegistryDefinitionV1(v1) => v1, - }; + let RevocationRegistryDefinition::RevocationRegistryDefinitionV1(rev_reg_def) = rev_reg_def; let mut rev_reg = match rev_reg { RevocationRegistry::RevocationRegistryV1(v1) => v1.value.clone(), }; @@ -292,9 +284,7 @@ pub fn create_credential_offer( let nonce = Nonce::new().map_err(err_map!(Unexpected, "Error creating nonce"))?; - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(c) => c, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let key_correctness_proof = correctness_proof .try_clone() @@ -370,8 +360,8 @@ pub fn create_credential( )?; let cred_rev_reg_id = match cred_offer.method_name.as_ref() { - Some(ref _method_name) => Some(reg_reg_id.to_unqualified()), - _ => Some(reg_reg_id.clone()), + Some(_method_name) => Some(reg_reg_id.to_unqualified()), + _ => Some(reg_reg_id), }; ( credential_signature, diff --git a/indy-credx/src/services/prover.rs b/indy-credx/src/services/prover.rs index 252998d..c3e6c05 100644 --- a/indy-credx/src/services/prover.rs +++ b/indy-credx/src/services/prover.rs @@ -38,9 +38,7 @@ pub fn create_credential_request( credential_offer ); - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(cd) => cd, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let credential_pub_key = CredentialPublicKey::build_from_parts( &cred_def.value.primary, cred_def.value.revocation.as_ref(), @@ -93,9 +91,7 @@ pub fn process_credential( trace!("process_credential >>> credential: {:?}, cred_request_metadata: {:?}, link_secret: {:?}, cred_def: {:?}, rev_reg_def: {:?}", credential, cred_request_metadata, secret!(&link_secret), cred_def, rev_reg_def); - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(cd) => cd, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let credential_pub_key = CredentialPublicKey::build_from_parts( &cred_def.value.primary, cred_def.value.revocation.as_ref(), @@ -154,9 +150,10 @@ pub fn create_presentation( let mut proof_builder = ClProver::new_proof_builder()?; proof_builder.add_common_attribute("master_secret")?; - let mut requested_proof = RequestedProof::default(); - - requested_proof.self_attested_attrs = self_attested.unwrap_or_default(); + let mut requested_proof = RequestedProof { + self_attested_attrs: self_attested.unwrap_or_default(), + ..Default::default() + }; let mut sub_proof_index = 0; let non_credential_schema = build_non_credential_schema()?; @@ -168,12 +165,9 @@ pub fn create_presentation( } let credential = present.cred; - let schema = *schemas + let Schema::SchemaV1(schema) = *schemas .get(&credential.schema_id) .ok_or_else(|| err_msg!("Schema not provided for ID: {}", credential.schema_id))?; - let schema = match schema { - Schema::SchemaV1(schema) => schema, - }; let cred_def = *cred_defs.get(&credential.cred_def_id).ok_or_else(|| { err_msg!( @@ -181,9 +175,7 @@ pub fn create_presentation( credential.cred_def_id ) })?; - let cred_def = match cred_def { - CredentialDefinition::CredentialDefinitionV1(cd) => cd, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = cred_def; let credential_pub_key = CredentialPublicKey::build_from_parts( &cred_def.value.primary, @@ -272,12 +264,8 @@ rev_reg_delta: {:?}, rev_reg_idx: {}, timestamp: {:?}, rev_state: {:?}", rev_state ); - let revoc_reg_def = match revoc_reg_def { - RevocationRegistryDefinition::RevocationRegistryDefinitionV1(v1) => v1, - }; - let rev_reg_delta = match rev_reg_delta { - RevocationRegistryDelta::RevocationRegistryDeltaV1(v1) => v1, - }; + let RevocationRegistryDefinition::RevocationRegistryDefinitionV1(revoc_reg_def) = revoc_reg_def; + let RevocationRegistryDelta::RevocationRegistryDeltaV1(rev_reg_delta) = rev_reg_delta; let witness = match rev_state { None => Witness::new( @@ -377,7 +365,7 @@ fn get_credential_values_for_attribute( let res = credential_attrs .iter() - .find(|&(ref key, _)| attr_common_view(key) == attr_common_view(&requested_attr)) + .find(|(key, _)| attr_common_view(key) == attr_common_view(requested_attr)) .map(|(_, values)| values.clone()); trace!( @@ -406,7 +394,7 @@ fn update_requested_proof( if let Some(name) = &attribute.name { let attribute_values = - get_credential_values_for_attribute(&credential.values.0, &name).ok_or_else( + get_credential_values_for_attribute(&credential.values.0, name).ok_or_else( || err_msg!("Credential value not found for attribute {:?}", name), )?; @@ -422,7 +410,7 @@ fn update_requested_proof( let mut value_map: HashMap = HashMap::new(); for name in names { let attr_value = - get_credential_values_for_attribute(&credential.values.0, &name) + get_credential_values_for_attribute(&credential.values.0, name) .ok_or_else(|| { err_msg!("Credential value not found for attribute {:?}", name) })?; diff --git a/indy-credx/src/services/types.rs b/indy-credx/src/services/types.rs index d15d9eb..be5d2eb 100644 --- a/indy-credx/src/services/types.rs +++ b/indy-credx/src/services/types.rs @@ -27,7 +27,7 @@ use crate::anoncreds_clsignatures::{RevocationRegistry as CryptoRevocationRegist use crate::error::Error; use crate::services::helpers::encode_credential_attribute; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct CredentialDefinitionConfig { pub support_revocation: bool, } @@ -38,14 +38,6 @@ impl CredentialDefinitionConfig { } } -impl Default for CredentialDefinitionConfig { - fn default() -> Self { - Self { - support_revocation: false, - } - } -} - impl Validatable for CredentialDefinitionConfig {} #[derive(Debug, Default)] @@ -81,9 +73,9 @@ impl MakeCredentialValues { } } -impl Into for MakeCredentialValues { - fn into(self) -> CredentialValues { - self.0 +impl From for CredentialValues { + fn from(val: MakeCredentialValues) -> CredentialValues { + val.0 } } diff --git a/indy-credx/src/services/verifier.rs b/indy-credx/src/services/verifier.rs index 12b8526..17bc76f 100644 --- a/indy-credx/src/services/verifier.rs +++ b/indy-credx/src/services/verifier.rs @@ -80,11 +80,11 @@ pub(crate) fn _verify_presentation( let pres_req = pres_req.value(); let received_revealed_attrs: HashMap = - received_revealed_attrs(&presentation)?; + received_revealed_attrs(presentation)?; let received_unrevealed_attrs: HashMap = - received_unrevealed_attrs(&presentation)?; - let received_predicates: HashMap = received_predicates(&presentation)?; - let received_self_attested_attrs: HashSet = received_self_attested_attrs(&presentation); + received_unrevealed_attrs(presentation)?; + let received_predicates: HashMap = received_predicates(presentation)?; + let received_self_attested_attrs: HashSet = received_self_attested_attrs(presentation); compare_attr_from_proof_and_request( pres_req, @@ -94,10 +94,10 @@ pub(crate) fn _verify_presentation( &received_predicates, )?; - verify_revealed_attribute_values(&pres_req, &presentation)?; + verify_revealed_attribute_values(pres_req, presentation)?; verify_requested_restrictions( - &pres_req, + pres_req, &presentation.requested_proof, &received_revealed_attrs, &received_unrevealed_attrs, @@ -120,21 +120,17 @@ pub(crate) fn _verify_presentation( for sub_proof_index in 0..presentation.identifiers.len() { let identifier = presentation.identifiers[sub_proof_index].clone(); - let schema = match schemas + let Schema::SchemaV1(schema) = schemas .get(&identifier.schema_id) - .ok_or_else(|| err_msg!("Schema not provided for ID: {:?}", identifier.schema_id))? - { - Schema::SchemaV1(schema) => schema, - }; + .ok_or_else(|| err_msg!("Schema not provided for ID: {:?}", identifier.schema_id))?; - let cred_def = match cred_defs.get(&identifier.cred_def_id).ok_or_else(|| { - err_msg!( - "Credential Definition not provided for ID: {:?}", - identifier.cred_def_id - ) - })? { - CredentialDefinition::CredentialDefinitionV1(cred_def) => cred_def, - }; + let CredentialDefinition::CredentialDefinitionV1(cred_def) = + cred_defs.get(&identifier.cred_def_id).ok_or_else(|| { + err_msg!( + "Credential Definition not provided for ID: {:?}", + identifier.cred_def_id + ) + })?; let (rev_reg_def, rev_reg) = if let Some(timestamp) = identifier.timestamp { let rev_reg_id = identifier.rev_reg_id.clone().ok_or_else(|| { @@ -241,7 +237,7 @@ fn get_revealed_attributes_for_credential( let mut revealed_attrs_for_credential = requested_proof .revealed_attrs .iter() - .filter(|&(attr_referent, ref revealed_attr_info)| { + .filter(|&(attr_referent, revealed_attr_info)| { sub_proof_index == revealed_attr_info.sub_proof_index as usize && pres_req.requested_attributes.contains_key(attr_referent) }) @@ -252,7 +248,7 @@ fn get_revealed_attributes_for_credential( &mut requested_proof .revealed_attr_groups .iter() - .filter(|&(attr_referent, ref revealed_attr_info)| { + .filter(|&(attr_referent, revealed_attr_info)| { sub_proof_index == revealed_attr_info.sub_proof_index as usize && pres_req.requested_attributes.contains_key(attr_referent) }) @@ -310,7 +306,7 @@ fn compare_attr_from_proof_and_request( .chain(received_unrevealed_attrs) .map(|(r, _)| r.to_string()) .collect::>() - .union(&received_self_attested_attrs) + .union(received_self_attested_attrs) .cloned() .collect(); @@ -349,14 +345,14 @@ fn compare_timestamps_from_proof_and_request( .iter() .map(|(referent, info)| { validate_timestamp( - &received_revealed_attrs, + received_revealed_attrs, referent, &pres_req.non_revoked, &info.non_revoked, ) .or_else(|_| { validate_timestamp( - &received_unrevealed_attrs, + received_unrevealed_attrs, referent, &pres_req.non_revoked, &info.non_revoked, @@ -489,7 +485,7 @@ fn verify_revealed_attribute_values( attr_referent, ) })?; - verify_revealed_attribute_value(attr_name.as_str(), proof, &attr_info)?; + verify_revealed_attribute_value(attr_name.as_str(), proof, attr_info)?; } for (attr_referent, attr_infos) in proof.requested_proof.revealed_attr_groups.iter() { @@ -542,11 +538,7 @@ fn verify_revealed_attribute_value( proof: &Presentation, attr_info: &RevealedAttributeInfo, ) -> Result<()> { - let reveal_attr_encoded = attr_info.encoded.to_string(); - let reveal_attr_encoded = Regex::new("^0*") - .unwrap() - .replace_all(&reveal_attr_encoded, "") - .to_owned(); + let reveal_attr_encoded = attr_info.encoded.trim_start_matches('0'); let sub_proof_index = attr_info.sub_proof_index as usize; let crypto_proof_encoded = proof @@ -562,7 +554,7 @@ fn verify_revealed_attribute_value( })? .revealed_attrs()? .iter() - .find(|(key, _)| attr_common_view(attr_name) == attr_common_view(&key)) + .find(|(key, _)| attr_common_view(attr_name) == attr_common_view(key)) .map(|(_, val)| val.to_string()) .ok_or_else(|| { err_msg!( @@ -597,13 +589,13 @@ fn verify_requested_restrictions( let requested_attrs: HashMap = pres_req .requested_attributes .iter() - .filter(|&(referent, info)| !is_self_attested(&referent, &info, self_attested_attrs)) + .filter(|&(referent, info)| !is_self_attested(referent, info, self_attested_attrs)) .map(|(referent, info)| (referent.to_string(), info.clone())) .collect(); for (referent, info) in requested_attrs.iter() { if let Some(ref query) = info.restrictions { - let filter = gather_filter_info(&referent, &proof_attr_identifiers)?; + let filter = gather_filter_info(referent, &proof_attr_identifiers)?; let attr_value_map: HashMap> = if let Some(name) = info.name.as_ref() @@ -638,7 +630,7 @@ fn verify_requested_restrictions( )); }; - process_operator(&attr_value_map, &query, &filter).map_err(err_map!( + process_operator(&attr_value_map, query, &filter).map_err(err_map!( "Requested restriction validation failed for \"{:?}\" attributes", &attr_value_map ))?; @@ -647,7 +639,7 @@ fn verify_requested_restrictions( for (referent, info) in pres_req.requested_predicates.iter() { if let Some(ref query) = info.restrictions { - let filter = gather_filter_info(&referent, received_predicates)?; + let filter = gather_filter_info(referent, received_predicates)?; // start with the predicate requested attribute, which is un-revealed let mut attr_value_map = HashMap::new(); @@ -683,7 +675,7 @@ fn verify_requested_restrictions( } } - process_operator(&attr_value_map, &query, &filter).map_err(err_map!( + process_operator(&attr_value_map, query, &filter).map_err(err_map!( "Requested restriction validation failed for \"{}\" predicate", &info.name ))?; @@ -748,14 +740,14 @@ fn process_operator( ) -> Result<()> { match restriction_op { Query::Eq(ref tag_name, ref tag_value) => { - process_filter(attr_value_map, &tag_name, &tag_value, filter).map_err(err_map!( + process_filter(attr_value_map, tag_name, tag_value, filter).map_err(err_map!( "$eq operator validation failed for tag: \"{}\", value: \"{}\"", tag_name, tag_value )) } Query::Neq(ref tag_name, ref tag_value) => { - if process_filter(attr_value_map, &tag_name, &tag_value, filter).is_err() { + if process_filter(attr_value_map, tag_name, tag_value, filter).is_err() { Ok(()) } else { Err(err_msg!(ProofRejected, @@ -765,7 +757,7 @@ fn process_operator( Query::In(ref tag_name, ref tag_values) => { let res = tag_values .iter() - .any(|val| process_filter(attr_value_map, &tag_name, &val, filter).is_ok()); + .any(|val| process_filter(attr_value_map, tag_name, val, filter).is_ok()); if res { Ok(()) } else { @@ -797,7 +789,7 @@ fn process_operator( } } Query::Not(ref operator) => { - if process_operator(attr_value_map, &*operator, filter).is_err() { + if process_operator(attr_value_map, operator, filter).is_err() { Ok(()) } else { Err(err_msg!( diff --git a/indy-credx/tests/anoncreds_demos.rs b/indy-credx/tests/anoncreds_demos.rs index e41f395..a539731 100644 --- a/indy-credx/tests/anoncreds_demos.rs +++ b/indy-credx/tests/anoncreds_demos.rs @@ -16,8 +16,8 @@ use self::utils::anoncreds::{IssuerWallet, ProverWallet}; mod utils; -pub static GVT_SCHEMA_NAME: &'static str = "gvt"; -pub static GVT_SCHEMA_ATTRIBUTES: &[&'static str; 4] = &["name", "age", "sex", "height"]; +pub static GVT_SCHEMA_NAME: &str = "gvt"; +pub static GVT_SCHEMA_ATTRIBUTES: &[&str; 4] = &["name", "age", "sex", "height"]; #[test] fn anoncreds_works_for_single_issuer_single_prover() { @@ -56,7 +56,7 @@ fn anoncreds_works_for_single_issuer_single_prover() { // Issuer creates a Credential Offer let cred_offer = issuer::create_credential_offer( gvt_schema.id(), - &gvt_cred_def, + gvt_cred_def, &issuer_wallet.cred_defs[0].key_proof, ) .expect("Error creating credential offer"); @@ -64,7 +64,7 @@ fn anoncreds_works_for_single_issuer_single_prover() { // Prover creates a Credential Request let (cred_request, cred_request_metadata) = prover::create_credential_request( &prover_wallet.did, - &*gvt_cred_def, + gvt_cred_def, &prover_wallet.link_secret, "default", &cred_offer, @@ -86,7 +86,7 @@ fn anoncreds_works_for_single_issuer_single_prover() { .add_raw("age", "28") .expect("Error encoding attribute"); let (issue_cred, _, _) = issuer::create_credential( - &*gvt_cred_def, + gvt_cred_def, &issuer_wallet.cred_defs[0].private, &cred_offer, &cred_request, @@ -101,7 +101,7 @@ fn anoncreds_works_for_single_issuer_single_prover() { &mut recv_cred, &cred_request_metadata, &prover_wallet.link_secret, - &*gvt_cred_def, + gvt_cred_def, None, ) .expect("Error processing credential"); @@ -154,7 +154,7 @@ fn anoncreds_works_for_single_issuer_single_prover() { schemas.insert(gvt_schema.id().clone(), &gvt_schema); let mut cred_defs = HashMap::new(); - cred_defs.insert(gvt_cred_def.id().clone(), &*gvt_cred_def); + cred_defs.insert(gvt_cred_def.id().clone(), gvt_cred_def); let presentation = prover::create_presentation( &pres_request, @@ -254,7 +254,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { let (rev_reg_def, rev_reg_def_private, rev_reg, rev_reg_delta) = issuer::create_revocation_registry( &issuer_wallet.did, - &gvt_cred_def, + gvt_cred_def, "tag", RegistryType::CL_ACCUM, IssuanceType::ISSUANCE_BY_DEFAULT, @@ -271,7 +271,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { // Issuer creates a Credential Offer let cred_offer = issuer::create_credential_offer( gvt_schema.id(), - &gvt_cred_def, + gvt_cred_def, &issuer_wallet.cred_defs[0].key_proof, ) .expect("Error creating credential offer"); @@ -279,7 +279,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { // Prover creates a Credential Request let (cred_request, cred_request_metadata) = prover::create_credential_request( &prover_wallet.did, - &gvt_cred_def, + gvt_cred_def, &prover_wallet.link_secret, "default", &cred_offer, @@ -301,7 +301,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { .add_raw("age", "28") .expect("Error encoding attribute"); let (issue_cred, _rev_reg, _delta) = issuer::create_credential( - &*gvt_cred_def, + gvt_cred_def, &issuer_wallet.cred_defs[0].private, &cred_offer, &cred_request, @@ -322,7 +322,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { &mut recv_cred, &cred_request_metadata, &prover_wallet.link_secret, - &*gvt_cred_def, + gvt_cred_def, Some(&rev_reg_def), ) .expect("Error processing credential"); @@ -354,7 +354,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { .expect("Error creating proof request"); // Prover creates revocation state - let tails_reader = TailsFileReader::new(&tails_path); + let tails_reader = TailsFileReader::new(tails_path); let rev_state = prover::create_or_update_revocation_state( tails_reader, &rev_reg_def, @@ -387,7 +387,7 @@ fn anoncreds_works_for_single_issuer_single_prover_unrevoked() { schemas.insert(gvt_schema.id().clone(), &gvt_schema); let mut cred_defs = HashMap::new(); - cred_defs.insert(gvt_cred_def.id().clone(), &*gvt_cred_def); + cred_defs.insert(gvt_cred_def.id().clone(), gvt_cred_def); let presentation = prover::create_presentation( &pres_request, diff --git a/indy-credx/tests/utils/anoncreds.rs b/indy-credx/tests/utils/anoncreds.rs index 139a5f9..de702aa 100644 --- a/indy-credx/tests/utils/anoncreds.rs +++ b/indy-credx/tests/utils/anoncreds.rs @@ -5,8 +5,8 @@ use indy_data_types::anoncreds::credential::Credential; use indy_data_types::anoncreds::link_secret::LinkSecret; use indy_data_types::did::DidValue; -pub const ISSUER_DID: &'static str = "NcYxiDXkpYi6ov5FcYDi1e"; -pub const PROVER_DID: &'static str = "VsKV7grR1BUE29mG2Fm2kX"; +pub const ISSUER_DID: &str = "NcYxiDXkpYi6ov5FcYDi1e"; +pub const PROVER_DID: &str = "VsKV7grR1BUE29mG2Fm2kX"; pub struct StoredCredDef { pub public: CredentialDefinition, diff --git a/indy-data-types/src/anoncreds/cred_def.rs b/indy-data-types/src/anoncreds/cred_def.rs index 1de6ea2..3d06eb9 100644 --- a/indy-data-types/src/anoncreds/cred_def.rs +++ b/indy-data-types/src/anoncreds/cred_def.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + #[cfg(any(feature = "cl", feature = "cl_native"))] use crate::anoncreds_clsignatures::CredentialPublicKey; use crate::identifiers::cred_def::CredentialDefinitionId; @@ -13,13 +15,6 @@ pub enum SignatureType { } impl SignatureType { - pub fn from_str(value: &str) -> Result { - match value { - CL_SIGNATURE_TYPE => Ok(Self::CL), - _ => Err(ConversionError::from_msg("Invalid signature type")), - } - } - pub fn to_str(&self) -> &'static str { match *self { SignatureType::CL => CL_SIGNATURE_TYPE, @@ -27,6 +22,17 @@ impl SignatureType { } } +impl FromStr for SignatureType { + type Err = ConversionError; + + fn from_str(value: &str) -> Result { + match value { + CL_SIGNATURE_TYPE => Ok(Self::CL), + _ => Err(ConversionError::from_msg("Invalid signature type")), + } + } +} + #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct CredentialDefinitionData { diff --git a/indy-data-types/src/anoncreds/nonce.rs b/indy-data-types/src/anoncreds/nonce.rs index fc3d3d1..123feaa 100644 --- a/indy-data-types/src/anoncreds/nonce.rs +++ b/indy-data-types/src/anoncreds/nonce.rs @@ -49,7 +49,7 @@ impl Nonce { return Err("Invalid bignum: empty value".into()); } for c in strval.chars() { - if c < '0' || c > '9' { + if !c.is_ascii_digit() { return Err("Invalid bignum value".into()); } } @@ -176,28 +176,28 @@ impl<'a> Deserialize<'a> for Nonce { where E: serde::de::Error, { - Ok(Nonce::try_from(value).map_err(E::custom)?) + Nonce::try_from(value).map_err(E::custom) } fn visit_u64(self, value: u64) -> Result where E: serde::de::Error, { - Ok(Nonce::try_from(value).map_err(E::custom)?) + Nonce::try_from(value).map_err(E::custom) } fn visit_u128(self, value: u128) -> Result where E: serde::de::Error, { - Ok(Nonce::try_from(value).map_err(E::custom)?) + Nonce::try_from(value).map_err(E::custom) } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { - Ok(Nonce::from_dec(value).map_err(E::custom)?) + Nonce::from_dec(value).map_err(E::custom) } } diff --git a/indy-data-types/src/anoncreds/pres_request.rs b/indy-data-types/src/anoncreds/pres_request.rs index 89baf5b..c1adc66 100644 --- a/indy-data-types/src/anoncreds/pres_request.rs +++ b/indy-data-types/src/anoncreds/pres_request.rs @@ -42,7 +42,7 @@ pub enum PresentationRequestVersion { } impl PresentationRequest { - pub fn value<'a>(&'a self) -> &'a PresentationRequestPayload { + pub fn value(&self) -> &PresentationRequestPayload { match self { PresentationRequest::PresentationRequestV1(req) => req, PresentationRequest::PresentationRequestV2(req) => req, @@ -227,7 +227,7 @@ impl Validatable for PresentationRequest { } if let Some(ref restrictions) = requested_attribute.restrictions { - _process_operator(&restrictions, &version)?; + _process_operator(restrictions, &version)?; } } @@ -239,7 +239,7 @@ impl Validatable for PresentationRequest { )); } if let Some(ref restrictions) = requested_predicate.restrictions { - _process_operator(&restrictions, &version)?; + _process_operator(restrictions, &version)?; } } @@ -255,13 +255,13 @@ impl PresentationRequest { requested_attribute.restrictions = requested_attribute .restrictions .as_mut() - .map(|ref mut restrictions| _convert_query_to_unqualified(&restrictions)); + .map(|ref mut restrictions| _convert_query_to_unqualified(restrictions)); } for (_, requested_predicate) in request.requested_predicates.iter_mut() { requested_predicate.restrictions = requested_predicate .restrictions .as_mut() - .map(|ref mut restrictions| _convert_query_to_unqualified(&restrictions)); + .map(|ref mut restrictions| _convert_query_to_unqualified(restrictions)); } }; @@ -298,13 +298,13 @@ fn _convert_query_to_unqualified(query: &Query) -> Query { Query::And(ref queries) => Query::And( queries .iter() - .map(|query| _convert_query_to_unqualified(query)) + .map(_convert_query_to_unqualified) .collect::>(), ), Query::Or(ref queries) => Query::Or( queries .iter() - .map(|query| _convert_query_to_unqualified(query)) + .map(_convert_query_to_unqualified) .collect::>(), ), Query::Not(ref query) => _convert_query_to_unqualified(query), diff --git a/indy-data-types/src/anoncreds/presentation.rs b/indy-data-types/src/anoncreds/presentation.rs index 064cb1f..4a64a76 100644 --- a/indy-data-types/src/anoncreds/presentation.rs +++ b/indy-data-types/src/anoncreds/presentation.rs @@ -13,7 +13,7 @@ pub struct Presentation { pub identifiers: Vec, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct RequestedProof { pub revealed_attrs: HashMap, @@ -28,18 +28,6 @@ pub struct RequestedProof { pub predicates: HashMap, } -impl Default for RequestedProof { - fn default() -> Self { - RequestedProof { - revealed_attrs: HashMap::new(), - revealed_attr_groups: HashMap::new(), - self_attested_attrs: HashMap::new(), - unrevealed_attrs: HashMap::new(), - predicates: HashMap::new(), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct SubProofReferent { diff --git a/indy-data-types/src/anoncreds/rev_reg_def.rs b/indy-data-types/src/anoncreds/rev_reg_def.rs index 158514d..b41b975 100644 --- a/indy-data-types/src/anoncreds/rev_reg_def.rs +++ b/indy-data-types/src/anoncreds/rev_reg_def.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::identifiers::cred_def::CredentialDefinitionId; use crate::identifiers::rev_reg::RevocationRegistryId; use crate::{invalid, ConversionError, Qualifiable, Validatable, ValidationError}; @@ -8,24 +10,17 @@ pub const ISSUANCE_BY_DEFAULT: &str = "ISSUANCE_BY_DEFAULT"; pub const ISSUANCE_ON_DEMAND: &str = "ISSUANCE_ON_DEMAND"; #[allow(non_camel_case_types)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub enum IssuanceType { + #[default] ISSUANCE_BY_DEFAULT, ISSUANCE_ON_DEMAND, } impl IssuanceType { - pub fn from_str(value: &str) -> Result { - match value { - ISSUANCE_BY_DEFAULT => Ok(Self::ISSUANCE_BY_DEFAULT), - ISSUANCE_ON_DEMAND => Ok(Self::ISSUANCE_ON_DEMAND), - _ => Err(ConversionError::from_msg("Invalid issuance type")), - } - } - pub fn to_bool(&self) -> bool { - self.clone() == IssuanceType::ISSUANCE_BY_DEFAULT + matches!(self, IssuanceType::ISSUANCE_BY_DEFAULT) } pub fn to_str(&self) -> &'static str { @@ -36,27 +31,27 @@ impl IssuanceType { } } -impl Default for IssuanceType { - fn default() -> Self { - Self::ISSUANCE_BY_DEFAULT +impl FromStr for IssuanceType { + type Err = ConversionError; + + fn from_str(value: &str) -> Result { + match value { + ISSUANCE_BY_DEFAULT => Ok(Self::ISSUANCE_BY_DEFAULT), + ISSUANCE_ON_DEMAND => Ok(Self::ISSUANCE_ON_DEMAND), + _ => Err(ConversionError::from_msg("Invalid issuance type")), + } } } #[allow(non_camel_case_types)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub enum RegistryType { + #[default] CL_ACCUM, } impl RegistryType { - pub fn from_str(value: &str) -> Result { - match value { - CL_ACCUM => Ok(Self::CL_ACCUM), - _ => Err(ConversionError::from_msg("Invalid registry type")), - } - } - pub fn to_str(&self) -> &'static str { match *self { Self::CL_ACCUM => CL_ACCUM, @@ -64,6 +59,17 @@ impl RegistryType { } } +impl FromStr for RegistryType { + type Err = ConversionError; + + fn from_str(value: &str) -> Result { + match value { + CL_ACCUM => Ok(Self::CL_ACCUM), + _ => Err(ConversionError::from_msg("Invalid registry type")), + } + } +} + #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] diff --git a/indy-data-types/src/anoncreds/rich_schema.rs b/indy-data-types/src/anoncreds/rich_schema.rs index fa268f8..88abc57 100644 --- a/indy-data-types/src/anoncreds/rich_schema.rs +++ b/indy-data-types/src/anoncreds/rich_schema.rs @@ -20,7 +20,7 @@ pub struct RSContent(pub String); impl Validatable for RSContent { fn validate(&self) -> Result<(), ValidationError> { // ToDo: Add JSON-LD validation if needed - return Ok(()); + Ok(()) } } @@ -59,7 +59,7 @@ impl Validatable for RichSchema { let _rs_type: RSType = serde_json::from_value(serde_json::value::Value::String(self.rs_type.clone())) .map_err(|_err| _err.to_string())?; - return self.id.validate(); + self.id.validate() } } diff --git a/indy-data-types/src/anoncreds/schema.rs b/indy-data-types/src/anoncreds/schema.rs index 7ba0d42..6a4c817 100644 --- a/indy-data-types/src/anoncreds/schema.rs +++ b/indy-data-types/src/anoncreds/schema.rs @@ -54,7 +54,7 @@ pub struct SchemaV1 { pub seq_no: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AttributeNames(pub HashSet); @@ -86,9 +86,9 @@ impl From> for AttributeNames { } } -impl Into> for AttributeNames { - fn into(self) -> HashSet { - self.0 +impl From for HashSet { + fn from(val: AttributeNames) -> HashSet { + val.0 } } diff --git a/indy-data-types/src/anoncreds/wql.rs b/indy-data-types/src/anoncreds/wql.rs index f49e37a..20a10a2 100644 --- a/indy-data-types/src/anoncreds/wql.rs +++ b/indy-data-types/src/anoncreds/wql.rs @@ -198,9 +198,7 @@ mod serde_support { let v = JsonValue::deserialize(deserializer)?; match v { - JsonValue::Object(map) => { - parse_query(map).map_err(|err| de::Error::missing_field(err)) - } + JsonValue::Object(map) => parse_query(map).map_err(de::Error::missing_field), JsonValue::Array(array) => { // cast old restrictions format to wql let mut res: Vec = Vec::new(); @@ -210,7 +208,7 @@ mod serde_support { .ok_or_else(|| de::Error::custom("Restriction is invalid"))? .clone() .into_iter() - .filter(|&(_, ref v)| !v.is_null()) + .filter(|(_, v)| !v.is_null()) .collect(); if !sub_query.is_empty() { @@ -221,7 +219,7 @@ mod serde_support { let mut map = serde_json::Map::new(); map.insert("$or".to_string(), JsonValue::Array(res)); - parse_query(map).map_err(|err| de::Error::custom(err)) + parse_query(map).map_err(de::Error::custom) } _ => Err(de::Error::missing_field( "Restriction must be either object or array", @@ -341,7 +339,7 @@ mod serde_support { (_, JsonValue::Object(map)) => { if map.len() == 1 { let (operator_name, value) = map.into_iter().next().unwrap(); - parse_single_operator(operator_name, key, value).map(|operator| Some(operator)) + parse_single_operator(operator_name, key, value).map(Some) } else { Err("value must be JSON object of length 1") } @@ -2852,9 +2850,9 @@ mod tests { #[test] fn test_old_format_empty() { - let json = format!(r#"[]"#); + let json = r#"[]"#; - let query: Query = ::serde_json::from_str(&json).unwrap(); + let query: Query = ::serde_json::from_str(json).unwrap(); let expected = Query::And(vec![]); @@ -2868,8 +2866,8 @@ mod tests { let value1 = _random_string(10); let json = json!(vec![ - json ! ({name1.clone(): value1.clone()}), - json!({ name2.clone(): ::serde_json::Value::Null }) + json!({ name1.clone(): value1 }), + json!({ name2: ::serde_json::Value::Null }) ]) .to_string(); @@ -2893,7 +2891,7 @@ mod tests { fn test_optimise_or() { let json = r#"[]"#; - let query: Query = ::serde_json::from_str(&json).unwrap(); + let query: Query = ::serde_json::from_str(json).unwrap(); assert_eq!(query.optimise(), None); } diff --git a/indy-data-types/src/did.rs b/indy-data-types/src/did.rs index 3396a49..31139e2 100644 --- a/indy-data-types/src/did.rs +++ b/indy-data-types/src/did.rs @@ -33,7 +33,7 @@ pub fn generate_did( Some(1) | None => Ok(base58::encode(&pk.as_ref()[..16])), Some(2) => { let mut hasher = Sha256::new(); - Digest::update(&mut hasher, &pk.as_ref()); + Digest::update(&mut hasher, pk.as_ref()); let hash = hasher.finalize(); Ok(base58::encode(&hash[..16])) } @@ -80,7 +80,7 @@ impl DidValue { pub fn is_abbreviatable(&self) -> bool { match self.get_method() { - Some(ref method) if method.starts_with("sov") => true, + Some(method) if method.starts_with("sov") => true, Some(_) => false, None => true, } @@ -125,7 +125,7 @@ impl std::ops::Deref for ShortDidValue { impl ShortDidValue { /// Convert a short DID value to a qualified DID pub fn qualify(&self, method: Option) -> DidValue { - DidValue::combine(method.as_ref().map(String::as_str), &self) + DidValue::combine(method.as_deref(), self) } } diff --git a/indy-data-types/src/error.rs b/indy-data-types/src/error.rs index 22dfc82..5e2a01d 100644 --- a/indy-data-types/src/error.rs +++ b/indy-data-types/src/error.rs @@ -78,9 +78,9 @@ macro_rules! define_error { } } - impl Into for $name { - fn into(self) -> String { - self.to_string() + impl From<$name> for String { + fn from(val: $name) -> String { + val.to_string() } } diff --git a/indy-data-types/src/identifiers/cred_def.rs b/indy-data-types/src/identifiers/cred_def.rs index c205db7..78928b1 100644 --- a/indy-data-types/src/identifiers/cred_def.rs +++ b/indy-data-types/src/identifiers/cred_def.rs @@ -18,7 +18,7 @@ impl CredentialDefinitionId { tag: &str, ) -> CredentialDefinitionId { let tag = if tag.is_empty() { - format!("") + String::new() } else { format!("{}{}", DELIMITER, tag) }; diff --git a/indy-data-types/src/identifiers/mod.rs b/indy-data-types/src/identifiers/mod.rs index e6a7467..2a5fc0c 100644 --- a/indy-data-types/src/identifiers/mod.rs +++ b/indy-data-types/src/identifiers/mod.rs @@ -10,4 +10,4 @@ pub mod schema; pub mod rich_schema; /// The standard delimiter used in identifier strings -pub const DELIMITER: &'static str = ":"; +pub const DELIMITER: &str = ":"; diff --git a/indy-data-types/src/identifiers/rev_reg.rs b/indy-data-types/src/identifiers/rev_reg.rs index edf0e8c..93c6b96 100644 --- a/indy-data-types/src/identifiers/rev_reg.rs +++ b/indy-data-types/src/identifiers/rev_reg.rs @@ -44,15 +44,14 @@ impl RevocationRegistryId { } pub fn parts(&self) -> Option<(DidValue, CredentialDefinitionId, String, String)> { - match QUALIFIED_REV_REG_ID.captures(&self.0) { - Some(caps) => Some(( + QUALIFIED_REV_REG_ID.captures(&self.0).map(|caps| { + ( DidValue(caps["did"].to_string()), CredentialDefinitionId(caps["cred_def_id"].to_string()), caps["rev_reg_type"].to_string(), caps["tag"].to_string(), - )), - None => None, - } + ) + }) } } diff --git a/indy-data-types/src/identifiers/rich_schema.rs b/indy-data-types/src/identifiers/rich_schema.rs index 596b466..3e11012 100644 --- a/indy-data-types/src/identifiers/rich_schema.rs +++ b/indy-data-types/src/identifiers/rich_schema.rs @@ -7,14 +7,14 @@ impl RichSchemaId { pub const PREFIX: &'static str = "rich_schema"; pub fn new(did_string: String) -> RichSchemaId { // ToDo: add RichSchema specific id forming if needed - return RichSchemaId(did_string); + RichSchemaId(did_string) } } impl Validatable for RichSchemaId { fn validate(&self) -> Result<(), ValidationError> { // ToDO: add RichSchema ID specific validation - return Ok(()); + Ok(()) } } @@ -38,7 +38,7 @@ mod tests { #[test] fn _validate_qualified_rs_id() { - assert_eq!(_rs_id_qualified().validate().unwrap(), ()) + _rs_id_qualified().validate().unwrap(); } // #[test] diff --git a/indy-data-types/src/keys/mod.rs b/indy-data-types/src/keys/mod.rs index 69a30d1..8ebd410 100644 --- a/indy-data-types/src/keys/mod.rs +++ b/indy-data-types/src/keys/mod.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "ed25519")] +use std::convert::TryFrom; +use std::str::FromStr; + #[cfg(feature = "ed25519")] use curve25519_dalek::edwards::CompressedEdwardsY; #[cfg(feature = "ed25519")] @@ -6,10 +10,6 @@ use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; use rand::{thread_rng, RngCore}; #[cfg(feature = "ed25519")] use sha2::digest::Digest; - -#[cfg(feature = "ed25519")] -use std::convert::TryFrom; - use zeroize::Zeroize; use crate::utils::base58; @@ -79,7 +79,7 @@ impl PrivateKey { let x_sk = x25519_dalek::StaticSecret::from(<[u8; 32]>::try_from(&hash[..32]).unwrap()); hash.zeroize(); - Ok(Self::new(&x_sk.to_bytes(), Some(KeyType::X25519))) + Ok(Self::new(x_sk.to_bytes(), Some(KeyType::X25519))) } _ => Err("Unsupported key format for key exchange".into()), } @@ -176,7 +176,7 @@ impl VerKey { let vky = CompressedEdwardsY::from_slice(&self.key).unwrap(); if let Some(x_vk) = vky.decompress() { Ok(Self::new( - x_vk.to_montgomery().as_bytes().to_vec(), + x_vk.to_montgomery().as_bytes(), Some(KeyType::X25519), )) } else { @@ -270,8 +270,8 @@ impl EncodedVerKey { } pub fn from_did_and_verkey(did: &str, key: &str) -> Result { - if key.chars().next() == Some('~') { - let mut vk_bytes = base58::decode(&key[1..])?; + if let Some(key) = key.strip_prefix('~') { + let mut vk_bytes = base58::decode(key)?; if vk_bytes.len() != 16 { return Err("Expected 16-byte abbreviated verkey".into()); } @@ -281,7 +281,7 @@ impl EncodedVerKey { } did_bytes.append(&mut vk_bytes); Ok(Self::new( - &base58::encode(did_bytes), + base58::encode(did_bytes), Some(KeyType::ED25519), Some(KeyEncoding::BASE58), )) @@ -324,10 +324,6 @@ impl EncodedVerKey { Self::from_str_qualified(key, None, None, None) } - pub fn from_str(key: &str) -> Result { - Self::from_str_qualified(key, None, None, None) - } - pub fn from_str_qualified( key: &str, dest: Option<&str>, @@ -345,12 +341,12 @@ impl EncodedVerKey { (key, alg) }; - if key.starts_with('~') { - let dest = dest.ok_or_else(|| "Destination required for short verkey")?; + if let Some(key) = key.strip_prefix('~') { + let dest = dest.ok_or("Destination required for short verkey")?; let mut result = base58::decode(dest)?; - let mut end = base58::decode(&key[1..])?; + let mut end = base58::decode(key)?; result.append(&mut end); - Ok(Self::new(&base58::encode(result), alg, enc)) + Ok(Self::new(base58::encode(result), alg, enc)) } else { Ok(Self::new(key, alg, enc)) } @@ -422,6 +418,14 @@ impl std::fmt::Display for EncodedVerKey { } } +impl FromStr for EncodedVerKey { + type Err = ConversionError; + + fn from_str(key: &str) -> Result { + Self::from_str_qualified(key, None, None, None) + } +} + impl Validatable for EncodedVerKey { fn validate(&self) -> Result<(), ValidationError> { let verkey = self.decode()?; @@ -505,7 +509,7 @@ mod tests { const SEED: &[u8; 32] = b"aaaabbbbccccddddeeeeffffgggghhhh"; let sk = PrivateKey::from_seed(&SEED[..]).unwrap(); assert_eq!( - &sk.as_ref()[..], + sk.as_ref(), &[ 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 100, 101, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 113, 22, 13, 44, @@ -515,7 +519,7 @@ mod tests { ); let xk = sk.key_exchange().unwrap(); assert_eq!( - &xk.as_ref(), + xk.as_ref(), &[ 208, 235, 232, 147, 241, 214, 250, 182, 45, 157, 20, 202, 31, 184, 226, 115, 149, 82, 210, 89, 50, 100, 22, 67, 21, 8, 124, 198, 100, 252, 237, 107 @@ -528,11 +532,11 @@ mod tests { fn sign_and_verify() { let message = b"hello there"; let sk = PrivateKey::generate(None).unwrap(); - let sig = sk.sign(&message).unwrap(); + let sig = sk.sign(message).unwrap(); let vk = sk.public_key().unwrap(); - assert!(vk.verify_signature(&message, &sig).unwrap()); - assert!(vk.verify_signature(&message, &[]).is_err()); - assert!(!vk.verify_signature(&"goodbye", &sig).unwrap()); + assert!(vk.verify_signature(message, &sig).unwrap()); + assert!(vk.verify_signature(message, b"").is_err()); + assert!(!vk.verify_signature("goodbye", &sig).unwrap()); } #[cfg(feature = "ed25519")] @@ -544,8 +548,8 @@ mod tests { vk.validate().unwrap(); let sk = PrivateKey::new(b"bad key", Some(KeyType::ED25519)); - assert_eq!(sk.validate().is_ok(), false); + assert!(sk.validate().is_err()); let vk = VerKey::new(b"bad key", Some(KeyType::ED25519)); - assert_eq!(vk.validate().is_ok(), false); + assert!(vk.validate().is_err()); } } diff --git a/indy-data-types/src/keys/types.rs b/indy-data-types/src/keys/types.rs index 2bd0dd2..0551fda 100644 --- a/indy-data-types/src/keys/types.rs +++ b/indy-data-types/src/keys/types.rs @@ -1,30 +1,26 @@ -pub const KEY_ENC_BASE58: &'static str = "base58"; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str::FromStr; -pub const KEY_TYPE_ED25519: &'static str = "ed25519"; -pub const KEY_TYPE_X25519: &'static str = "x25519"; +use crate::ConversionError; + +pub const KEY_ENC_BASE58: &str = "base58"; + +pub const KEY_TYPE_ED25519: &str = "ed25519"; +pub const KEY_TYPE_X25519: &str = "x25519"; /// Enum of known and unknown key types -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum KeyType { + #[default] ED25519, X25519, Other(String), } impl KeyType { - pub fn from_str(keytype: &str) -> KeyType { - match keytype.to_ascii_lowercase().as_str() { - KEY_TYPE_ED25519 => KeyType::ED25519, - KEY_TYPE_X25519 => KeyType::X25519, - _ => KeyType::Other(keytype.to_owned()), - } - } - pub fn is_known(&self) -> bool { - match self { - Self::Other(_) => false, - _ => true, - } + !matches!(self, Self::Other(_)) } pub fn as_str(&self) -> &str { @@ -36,15 +32,21 @@ impl KeyType { } } -impl std::string::ToString for KeyType { - fn to_string(&self) -> String { - self.as_str().to_owned() +impl FromStr for KeyType { + type Err = ConversionError; + + fn from_str(keytype: &str) -> Result { + Ok(match keytype.to_ascii_lowercase().as_str() { + KEY_TYPE_ED25519 => KeyType::ED25519, + KEY_TYPE_X25519 => KeyType::X25519, + _ => KeyType::Other(keytype.to_owned()), + }) } } -impl Default for KeyType { - fn default() -> Self { - KeyType::ED25519 +impl Display for KeyType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } } @@ -57,31 +59,25 @@ impl std::ops::Deref for KeyType { impl From<&str> for KeyType { fn from(value: &str) -> Self { - Self::from_str(value) + Self::from_str(value).unwrap() } } impl From for KeyType { fn from(value: String) -> Self { - Self::from_str(&value) + Self::from_str(&value).unwrap() } } /// Enum of known and unknown key encodings -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum KeyEncoding { + #[default] BASE58, Other(String), } impl KeyEncoding { - pub fn from_str(keyenc: &str) -> KeyEncoding { - match keyenc.to_ascii_lowercase().as_str() { - KEY_ENC_BASE58 => KeyEncoding::BASE58, - _ => KeyEncoding::Other(keyenc.to_owned()), - } - } - pub fn as_str(&self) -> &str { match self { Self::BASE58 => KEY_ENC_BASE58, @@ -90,19 +86,24 @@ impl KeyEncoding { } } -impl std::string::ToString for KeyEncoding { - fn to_string(&self) -> String { - self.as_str().to_owned() +impl FromStr for KeyEncoding { + type Err = ConversionError; + + fn from_str(keyenc: &str) -> Result { + Ok(match keyenc.to_ascii_lowercase().as_str() { + KEY_ENC_BASE58 => KeyEncoding::BASE58, + _ => KeyEncoding::Other(keyenc.to_owned()), + }) } } -impl Default for KeyEncoding { - fn default() -> Self { - KeyEncoding::BASE58 +impl Display for KeyEncoding { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) } } -impl std::ops::Deref for KeyEncoding { +impl Deref for KeyEncoding { type Target = str; fn deref(&self) -> &str { self.as_str() @@ -111,12 +112,12 @@ impl std::ops::Deref for KeyEncoding { impl From<&str> for KeyEncoding { fn from(value: &str) -> Self { - Self::from_str(value) + Self::from_str(value).unwrap() } } impl From for KeyEncoding { fn from(value: String) -> Self { - Self::from_str(&value) + Self::from_str(&value).unwrap() } } diff --git a/indy-data-types/src/merkle_tree/hash.rs b/indy-data-types/src/merkle_tree/hash.rs index 3e18e8c..75816c4 100644 --- a/indy-data-types/src/merkle_tree/hash.rs +++ b/indy-data-types/src/merkle_tree/hash.rs @@ -49,7 +49,7 @@ impl TreeHash for H { T: Hashable, { let mut ctx = Self::new(); - ctx.update(&[0x00]); + ctx.update([0x00]); leaf.update_context(&mut ctx)?; Ok(ctx.finalize().to_vec()) } @@ -59,7 +59,7 @@ impl TreeHash for H { T: Hashable, { let mut ctx = Self::new(); - ctx.update(&[0x01]); + ctx.update([0x01]); left.update_context(&mut ctx)?; right.update_context(&mut ctx)?; Ok(ctx.finalize().to_vec()) @@ -95,7 +95,8 @@ pub trait Hashable { impl> Hashable for T { fn update_context(&self, context: &mut D) -> Result<(), ValidationError> { - Ok(context.update(self.as_ref())) + context.update(self.as_ref()); + Ok(()) } } diff --git a/indy-data-types/src/merkle_tree/mod.rs b/indy-data-types/src/merkle_tree/mod.rs index 8fcfe7c..2a5138b 100644 --- a/indy-data-types/src/merkle_tree/mod.rs +++ b/indy-data-types/src/merkle_tree/mod.rs @@ -32,10 +32,7 @@ impl MerkleTree { pub fn find_hash<'a>(from: &'a Tree, required_hash: &Vec) -> Option<&'a Tree> { match *from { - Tree::Empty { .. } => { - assert!(false); - None - } + Tree::Empty { .. } => None, Tree::Node { ref left, ref right, @@ -72,7 +69,7 @@ impl MerkleTree { &self, new_root_hash: &Vec, new_size: usize, - proof: &Vec>, + proof: &[Vec], ) -> Result { if self.count == 0 { // empty old tree @@ -84,7 +81,6 @@ impl MerkleTree { } if self.count > new_size { // old tree is bigger! - assert!(false); return Ok(false); } @@ -171,7 +167,7 @@ impl MerkleTree { self.nodes_count += 1; } _ => { - assert!(false); + unreachable!(); } } } else { @@ -202,7 +198,7 @@ impl MerkleTree { self.nodes_count += 1; } _ => { - assert!(false); + unreachable!(); } } self.height += 1; @@ -321,9 +317,9 @@ mod tests { assert!(mt .consistency_proof( &vec![ - 0x77 as u8, 0xf1, 0x5a, 0x58, 0x07, 0xfd, 0xaa, 0x56, 0x51, 0x28, 0xc5, 0x8f, - 0x59, 0x1f, 0x4f, 0x03, 0x25, 0x81, 0xfe, 0xe7, 0xd8, 0x61, 0x99, 0xae, 0xf8, - 0xae, 0xac, 0x7b, 0x05, 0x80, 0xbe, 0x0a + 0x77u8, 0xf1, 0x5a, 0x58, 0x07, 0xfd, 0xaa, 0x56, 0x51, 0x28, 0xc5, 0x8f, 0x59, + 0x1f, 0x4f, 0x03, 0x25, 0x81, 0xfe, 0xe7, 0xd8, 0x61, 0x99, 0xae, 0xf8, 0xae, + 0xac, 0x7b, 0x05, 0x80, 0xbe, 0x0a ], 4, &proofs @@ -344,11 +340,9 @@ mod tests { let proofs: Vec> = vec![]; - assert_eq!( - false, - mt.consistency_proof(&vec![0x77 as u8, 0xf1, 0x5a], 4, &proofs) - .unwrap() - ); + assert!(!mt + .consistency_proof(&vec![0x77u8, 0xf1, 0x5a], 4, &proofs) + .unwrap()); } #[test] @@ -364,7 +358,7 @@ mod tests { for value in values { let proof = tree.gen_proof(value).unwrap(); let is_valid = proof - .map(|p| p.validate(&root_hash).unwrap()) + .map(|p| p.validate(root_hash).unwrap()) .unwrap_or(false); assert!(is_valid); @@ -428,7 +422,7 @@ mod tests { //add 5th node mt.append(all_values[5 - 1].clone()).unwrap(); assert!(mt - .consistency_proof(&full_root_hash, 8, &proofs_for_5) + .consistency_proof(full_root_hash, 8, &proofs_for_5) .unwrap()); //try to add 6th node @@ -444,7 +438,7 @@ mod tests { //add 6th node mt.append(all_values[6 - 1].clone()).unwrap(); assert!(mt - .consistency_proof(&full_root_hash, 8, &proofs_for_6) + .consistency_proof(full_root_hash, 8, &proofs_for_6) .unwrap()); //try to add 7th node @@ -461,7 +455,7 @@ mod tests { //add 7th node mt.append(all_values[7 - 1].clone()).unwrap(); assert!(mt - .consistency_proof(&full_root_hash, 8, &proofs_for_7) + .consistency_proof(full_root_hash, 8, &proofs_for_7) .unwrap()); //try to add 8th node, empty proof @@ -469,7 +463,7 @@ mod tests { //add 7th node mt.append(all_values[8 - 1].clone()).unwrap(); assert!(mt - .consistency_proof(&full_root_hash, 8, &proofs_for_8) + .consistency_proof(full_root_hash, 8, &proofs_for_8) .unwrap()); } diff --git a/indy-data-types/src/merkle_tree/proof.rs b/indy-data-types/src/merkle_tree/proof.rs index f2c38a5..aeda1b7 100644 --- a/indy-data-types/src/merkle_tree/proof.rs +++ b/indy-data-types/src/merkle_tree/proof.rs @@ -32,30 +32,29 @@ impl Proof { if self.root_hash != root_hash || self.lemma.node_hash != root_hash { return Ok(false); } - - Ok(self.validate_lemma(&self.lemma)?) + _validate_lemma(&self.lemma) } +} - fn validate_lemma(&self, lemma: &Lemma) -> Result { - match lemma.sub_lemma { - None => Ok(lemma.sibling_hash.is_none()), - - Some(ref sub) => match lemma.sibling_hash { - None => Ok(false), - - Some(Positioned::Left(ref hash)) => { - let combined = Hash::hash_nodes(hash, &sub.node_hash)?; - let hashes_match = combined == lemma.node_hash; - Ok(hashes_match && self.validate_lemma(sub)?) - } - - Some(Positioned::Right(ref hash)) => { - let combined = Hash::hash_nodes(&sub.node_hash, hash)?; - let hashes_match = combined == lemma.node_hash; - Ok(hashes_match && self.validate_lemma(sub)?) - } - }, - } +fn _validate_lemma(lemma: &Lemma) -> Result { + match lemma.sub_lemma { + None => Ok(lemma.sibling_hash.is_none()), + + Some(ref sub) => match lemma.sibling_hash { + None => Ok(false), + + Some(Positioned::Left(ref hash)) => { + let combined = Hash::hash_nodes(hash, &sub.node_hash)?; + let hashes_match = combined == lemma.node_hash; + Ok(hashes_match && _validate_lemma(sub)?) + } + + Some(Positioned::Right(ref hash)) => { + let combined = Hash::hash_nodes(&sub.node_hash, hash)?; + let hashes_match = combined == lemma.node_hash; + Ok(hashes_match && _validate_lemma(sub)?) + } + }, } } diff --git a/indy-data-types/src/qualifiable.rs b/indy-data-types/src/qualifiable.rs index 91f2787..b8b3536 100644 --- a/indy-data-types/src/qualifiable.rs +++ b/indy-data-types/src/qualifiable.rs @@ -17,7 +17,7 @@ pub fn combine(prefix: &str, method: Option<&str>, entity: &str) -> String { /// Split a qualifiable identifier into its method and value components pub fn split<'a>(prefix: &str, val: &'a str) -> (Option<&'a str>, &'a str) { - match REGEX.captures(&val) { + match REGEX.captures(val) { None => (None, val), Some(caps) => { if caps.get(1).map(|m| m.as_str()) == Some(prefix) { @@ -45,11 +45,11 @@ pub trait Qualifiable: From + std::ops::Deref + Validatabl Self::from(combine(Self::prefix(), method, entity)) } - fn split<'a>(&'a self) -> (Option<&'a str>, &'a str) { + fn split(&self) -> (Option<&str>, &str) { split(Self::prefix(), self.deref()) } - fn get_method<'a>(&'a self) -> Option<&'a str> { + fn get_method(&self) -> Option<&str> { let (method, _rest) = self.split(); method } From 733682397763dee048b817caa1544f0421e37822 Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 15 Sep 2023 13:18:12 -0700 Subject: [PATCH 5/7] enable zeroize for dalek crates Signed-off-by: Andrew Whitehead --- indy-data-types/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indy-data-types/Cargo.toml b/indy-data-types/Cargo.toml index 50813d5..2d680e5 100644 --- a/indy-data-types/Cargo.toml +++ b/indy-data-types/Cargo.toml @@ -30,7 +30,9 @@ vendored = ["anoncreds-clsignatures?/openssl_vendored"] anoncreds-clsignatures = { version = "0.2", optional = true } bs58 = "0.5" curve25519-dalek = { version = "4.1", default-features = false, optional = true } -ed25519-dalek = { version = "2.0", default-features = false, optional = true } +ed25519-dalek = { version = "2.0", default-features = false, features = [ + "zeroize", +], optional = true } hex = { version = "0.4", optional = true } once_cell = "1" rand = { version = "0.8", optional = true } @@ -41,6 +43,7 @@ sha2 = { version = "0.10", optional = true } thiserror = "1" x25519-dalek = { version = "2.0", default-features = false, optional = true, features = [ "static_secrets", + "zeroize", ] } zeroize = { version = "1", features = ["zeroize_derive"] } From bff120c85c7e1d304ccc7683dc671a160fdf65be Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 15 Sep 2023 13:53:28 -0700 Subject: [PATCH 6/7] remove test from workflow Signed-off-by: Andrew Whitehead --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcf8622..9b3c773 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -91,9 +91,6 @@ jobs: - name: Debug build run: cargo build --all-targets --features vendored - - name: Test indy-utils - run: cargo test --manifest-path indy-utils/Cargo.toml - # - name: Test indy-data-types (CL) # run: cargo test --manifest-path indy-data-types/Cargo.toml --features cl From 684cafa10c8252585477095330bc1d372f66333e Mon Sep 17 00:00:00 2001 From: Andrew Whitehead Date: Fri, 15 Sep 2023 14:19:07 -0700 Subject: [PATCH 7/7] manually clamp x25519 scalar for consistency with old version Signed-off-by: Andrew Whitehead --- indy-data-types/src/keys/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indy-data-types/src/keys/mod.rs b/indy-data-types/src/keys/mod.rs index 8ebd410..22867e3 100644 --- a/indy-data-types/src/keys/mod.rs +++ b/indy-data-types/src/keys/mod.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use std::str::FromStr; #[cfg(feature = "ed25519")] -use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::{edwards::CompressedEdwardsY, scalar::clamp_integer}; #[cfg(feature = "ed25519")] use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; #[cfg(feature = "ed25519")] @@ -77,7 +77,7 @@ impl PrivateKey { KeyType::ED25519 => { let mut hash = sha2::Sha512::digest(&self.key[..32]); let x_sk = - x25519_dalek::StaticSecret::from(<[u8; 32]>::try_from(&hash[..32]).unwrap()); + x25519_dalek::StaticSecret::from(clamp_integer(hash[..32].try_into().unwrap())); hash.zeroize(); Ok(Self::new(x_sk.to_bytes(), Some(KeyType::X25519))) }