diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68ce6ba..1bf48db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,10 +55,10 @@ jobs: strategy: matrix: include: - - os: macos-latest # macos-11.0 for universal + - os: macos-11 lib: libindy_credx.dylib - # target: apple-darwin - toolchain: stable # beta for universal + target: apple-darwin + toolchain: beta # beta required for Darwin build - os: windows-latest lib: indy_credx.dll toolchain: stable @@ -109,13 +109,13 @@ jobs: strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-11, windows-latest] python-version: [3.6] include: - os: ubuntu-latest plat-name: manylinux2014_x86_64 - - os: macos-latest - plat-name: macosx_10_9_x86_64 # macosx_10_9_universal2 + - os: macos-11 + plat-name: macosx_10_9_universal2 # macosx_10_9_x86_64 - os: windows-latest plat-name: win_amd64 diff --git a/indy-credx/Cargo.toml b/indy-credx/Cargo.toml index 0a56b00..c98fb87 100644 --- a/indy-credx/Cargo.toml +++ b/indy-credx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indy-credx" -version = "0.3.0" +version = "0.3.1" 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 = "2018" @@ -24,16 +24,16 @@ vendored = ["indy-data-types/vendored"] [dependencies] env_logger = { version = "0.7.1", optional = true } ffi-support = { version = "0.4.0", optional = true } -log = "0.4.8" -once_cell = "1.4" +log = "0.4" +once_cell = "1.9" rand = "0.7" regex = "1.2.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.9" tempfile = "3.1.0" -thiserror = "1.0.9" -zeroize = { version = "1.1", optional = true } +thiserror = "1.0" +zeroize = { version = "1.3", optional = true } [dependencies.indy-data-types] version = "0.5" @@ -41,7 +41,7 @@ path = "../indy-data-types" features = ["cl_native"] [dependencies.indy-utils] -version = "0.4" +version = "0.5" path = "../indy-utils" default-features = false features = ["wql"] diff --git a/indy-data-types/Cargo.toml b/indy-data-types/Cargo.toml index 7cba54a..b6d0a45 100644 --- a/indy-data-types/Cargo.toml +++ b/indy-data-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indy-data-types" -version = "0.5.0" +version = "0.5.1" 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 = "2018" @@ -27,15 +27,15 @@ vendored = ["openssl", "openssl/vendored"] [dependencies] hex = { version = "0.4", optional = true } openssl = { version = "0.10", optional = true } -once_cell = "1.4" +once_cell = "1.9" regex = "1.3" serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true, features = ["raw_value"] } -ursa = { version = "0.3.5", default-features = false, optional = true } +ursa = { version = "=0.3.6", default-features = false, optional = true } zeroize = { version = "1.1", features = ["zeroize_derive"] } [dependencies.indy-utils] -version = "0.4" +version = "0.5" path = "../indy-utils" default-features = false features = ["wql"] diff --git a/indy-data-types/src/macros.rs b/indy-data-types/src/macros.rs index 374eb4e..dfcf4a5 100644 --- a/indy-data-types/src/macros.rs +++ b/indy-data-types/src/macros.rs @@ -3,6 +3,6 @@ macro_rules! unwrap_opt_or_return { match $opt { Some(val) => val, None => return $err, - }; + } }; } diff --git a/indy-utils/Cargo.toml b/indy-utils/Cargo.toml index 15b2756..a100111 100644 --- a/indy-utils/Cargo.toml +++ b/indy-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "indy-utils" -version = "0.4.0" +version = "0.5.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" @@ -18,25 +18,27 @@ crate-type = ["rlib"] [features] default = ["ed25519", "hash", "txn_signature", "wql"] base64 = ["base64_rs"] -ed25519 = ["ursa", "ursa/ed25519", "ursa/x25519"] +ed25519 = ["curve25519-dalek", "ed25519-dalek", "rand", "sha2", "x25519-dalek"] hash = ["sha2"] txn_signature = ["hex", "sha2", "serde", "serde_json"] wql = ["indy-wql", "serde", "serde_json"] [dependencies] -aead = "0.3" base64_rs = { package = "base64", version = "0.12", optional = true } bs58 = "0.3" +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 } hex = { version = "0.4", optional = true } indy-wql = { version = "0.4", optional = true, path = "../indy-wql" } -once_cell = "1.4" +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.9", optional = true } thiserror = "1.0" -ursa = { version = "0.3.5", default-features = false, optional = true } -zeroize = { version = "1.1" } +x25519-dalek = { version = "=1.1", default-features = false, features = ["u64_backend"], optional = true } +zeroize = { version = "1.3" } [dev-dependencies] async-global-executor = "1.2" diff --git a/indy-utils/src/keys/mod.rs b/indy-utils/src/keys/mod.rs index 469ec83..511ddae 100644 --- a/indy-utils/src/keys/mod.rs +++ b/indy-utils/src/keys/mod.rs @@ -1,5 +1,14 @@ #[cfg(feature = "ed25519")] -use ursa::signatures::{ed25519::Ed25519Sha512, SignatureScheme}; +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; @@ -35,10 +44,9 @@ impl PrivateKey { let alg = alg.unwrap_or_default(); match alg { KeyType::ED25519 => { - let (_pk, sk) = Ed25519Sha512 - .keypair(None) - .map_err(|_| "Error creating signing key")?; - Ok(Self::new(sk, Some(KeyType::ED25519))) + let mut sk = [0u8; 32]; + thread_rng().fill_bytes(&mut sk[..]); + Self::from_seed(&sk[..]) } _ => Err("Unsupported key type".into()), } @@ -46,9 +54,12 @@ impl PrivateKey { #[cfg(feature = "ed25519")] pub fn from_seed(seed: &[u8]) -> Result { - let (_pk, sk) = Ed25519Sha512::expand_keypair(seed) + let sk = SecretKey::from_bytes(seed) .map_err(|err| format!("Error creating signing key: {}", err))?; - Ok(Self::new(sk, Some(KeyType::ED25519))) + 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 { @@ -66,10 +77,11 @@ impl PrivateKey { pub fn key_exchange(&self) -> Result { match self.alg { KeyType::ED25519 => { - let sk = ursa::keys::PrivateKey(self.key_bytes()); - let x_sk = Ed25519Sha512::sign_key_to_key_exchange(&sk) - .map_err(|err| format!("Error converting to x25519 key: {}", err))?; - Ok(Self::new(&x_sk, Some(KeyType::X25519))) + 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()), } @@ -79,10 +91,10 @@ impl PrivateKey { pub fn sign>(&self, message: M) -> Result, ConversionError> { match self.alg { KeyType::ED25519 => { - let sk = ursa::keys::PrivateKey(self.key_bytes()); - Ok(Ed25519Sha512 - .sign(message.as_ref(), &sk) - .map_err(|err| format!("Error signing payload: {}", err))?) + 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()), } @@ -164,11 +176,15 @@ impl VerKey { pub fn key_exchange(&self) -> Result { match self.alg { KeyType::ED25519 => { - let vk = ursa::keys::PublicKey(self.key_bytes()); - let x_vk = Ed25519Sha512::ver_key_to_key_exchange(&vk).map_err(|err| { - format!("Error converting to x25519 key: {}", err.to_string()) - })?; - Ok(Self::new(&x_vk, Some(KeyType::X25519))) + 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()), } @@ -182,10 +198,12 @@ impl VerKey { ) -> Result { match self.alg { KeyType::ED25519 => { - let vk = ursa::keys::PublicKey(self.key_bytes()); - Ok(Ed25519Sha512 - .verify(message.as_ref(), signature.as_ref(), &vk) - .map_err(|err| format!("Error validating message signature: {}", err))?) + 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()), } @@ -258,13 +276,11 @@ impl EncodedVerKey { if key.chars().next() == Some('~') { let mut vk_bytes = base58::decode(&key[1..])?; if vk_bytes.len() != 16 { - return Err(ConversionError::from_msg( - "Expected 16-byte abbreviated verkey", - )); + return Err("Expected 16-byte abbreviated verkey".into()); } let mut did_bytes = base58::decode(did)?; if did_bytes.len() != 16 { - return Err(ConversionError::from_msg("DID must be 16 bytes in length")); + return Err("DID must be 16 bytes in length".into()); } did_bytes.append(&mut vk_bytes); Ok(Self::new( @@ -284,11 +300,11 @@ impl EncodedVerKey { pub fn abbreviated_for_did(&self, did: &str) -> Result { let did_bytes = base58::decode(did)?; if did_bytes.len() != 16 { - return Err(ConversionError::from_msg("DID must be 16 bytes in length")); + return Err("DID must be 16 bytes in length".into()); } let vk = self.key_bytes()?; if vk.len() != 32 { - return Err(ConversionError::from_msg("Expected 32-byte verkey")); + return Err("Expected 32-byte verkey".into()); } if &vk[..16] == did_bytes.as_slice() { let mut result = "~".to_string(); @@ -487,6 +503,30 @@ mod tests { ) } + #[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() { @@ -495,6 +535,8 @@ mod tests { 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")] diff --git a/indy-utils/src/lib.rs b/indy-utils/src/lib.rs index 4c54f36..1ec949c 100644 --- a/indy-utils/src/lib.rs +++ b/indy-utils/src/lib.rs @@ -1,5 +1,3 @@ -pub extern crate aead; - #[cfg(any(feature = "serde", test))] #[macro_use] pub extern crate serde; diff --git a/indy-utils/src/macros.rs b/indy-utils/src/macros.rs index 1c99d25..7c06770 100644 --- a/indy-utils/src/macros.rs +++ b/indy-utils/src/macros.rs @@ -3,7 +3,7 @@ macro_rules! unwrap_opt_or_return { match $opt { Some(val) => val, None => return $err, - }; + } }; } diff --git a/wrappers/python/indy_credx/version.py b/wrappers/python/indy_credx/version.py index ee03c5f..2506faa 100644 --- a/wrappers/python/indy_credx/version.py +++ b/wrappers/python/indy_credx/version.py @@ -1,3 +1,3 @@ """indy_credx library wrapper version.""" -__version__ = "0.3.0" +__version__ = "0.3.1"