From e0fd12b9f28e0a6382881786b5ce7b98da2c48f0 Mon Sep 17 00:00:00 2001 From: Nikita Strygin Date: Wed, 8 Nov 2023 14:07:22 +0300 Subject: [PATCH] [refactor] #3422: Clean up the API & resurrect ursa tests Clean up the API a bit: - hide the implementation details of signatures (they are only used through the PublicKey, PrivateKey and Signature types) - remove even more unused API functions - add missing documentation items Signed-off-by: Nikita Strygin --- Cargo.lock | 9 +- crypto/Cargo.toml | 2 - crypto/src/encryption/chacha20poly1305.rs | 8 +- crypto/src/encryption/mod.rs | 64 ++- crypto/src/kex/mod.rs | 15 +- crypto/src/kex/x25519.rs | 26 +- crypto/src/lib.rs | 43 +- crypto/src/signature/bls/implementation.rs | 16 +- crypto/src/signature/bls/mod.rs | 9 - crypto/src/signature/bls/tests.rs | 7 +- crypto/src/signature/ed25519.rs | 257 +++++------- crypto/src/signature/mod.rs | 35 +- crypto/src/signature/secp256k1.rs | 456 +++++++++------------ 13 files changed, 415 insertions(+), 532 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b26d6602228..94472582823 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2865,6 +2865,7 @@ dependencies = [ "iroha_schema", "k256", "libsodium-sys-stable", + "openssl", "openssl-sys", "parity-scale-codec", "rand 0.8.5", @@ -3881,9 +3882,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" dependencies = [ "bitflags 2.4.0", "cfg-if", @@ -3922,9 +3923,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 8d472157fa2..e0c69cccc74 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -26,7 +26,6 @@ std = [ "dep:x25519-dalek", "dep:rand", "dep:rand_chacha", - "dep:secp256k1", "dep:zeroize", "dep:arrayref", "dep:aead", @@ -72,7 +71,6 @@ x25519-dalek = { version = "2.0.0", optional = true, features = ["static_secrets rand = { workspace = true, optional = true } rand_chacha = { version = "0.3.1", optional = true } -secp256k1 = { version = "0.28.0", features = ["rand", "serde"], optional = true } zeroize = { version = "1.6.0", optional = true } arrayref = { version = "0.3.7", optional = true } diff --git a/crypto/src/encryption/chacha20poly1305.rs b/crypto/src/encryption/chacha20poly1305.rs index 9e8b771d409..f58597032f2 100644 --- a/crypto/src/encryption/chacha20poly1305.rs +++ b/crypto/src/encryption/chacha20poly1305.rs @@ -7,9 +7,9 @@ use aead::{ }; use chacha20poly1305::ChaCha20Poly1305 as SysChaCha20Poly1305; -// use zeroize::Zeroize; use super::Encryptor; +/// ChaCha20Poly1305 is a symmetric encryption algorithm that uses the ChaCha20 stream cipher #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ChaCha20Poly1305 { key: GenericArray, @@ -35,6 +35,8 @@ impl AeadCore for ChaCha20Poly1305 { type CiphertextOverhead = U0; } +// false positives: eliding lifetimes here requires an unstable feature `anonymous_lifetime_in_impl_trait` +#[allow(single_use_lifetimes)] impl Aead for ChaCha20Poly1305 { fn encrypt<'msg, 'aad>( &self, @@ -57,9 +59,6 @@ impl Aead for ChaCha20Poly1305 { } } -// default_impl!(ChaCha20Poly1305); -// drop_impl!(ChaCha20Poly1305); - #[cfg(test)] mod tests { use std::io::Cursor; @@ -138,6 +137,7 @@ mod tests { assert_eq!(dummytext.to_vec(), plaintext); } + // TODO: this should be tested for, but only after we integrate with secrecy/zeroize // #[test] // fn zeroed_on_drop() { // let mut aes = ChaCha20Poly1305::new(&ChaCha20Poly1305::key_gen().unwrap()); diff --git a/crypto/src/encryption/mod.rs b/crypto/src/encryption/mod.rs index 5ed425c2832..ff4b082d38b 100644 --- a/crypto/src/encryption/mod.rs +++ b/crypto/src/encryption/mod.rs @@ -1,5 +1,19 @@ -// TODO: clean up & remove -#![allow(unused, missing_docs)] +//! A suite of Authenticated Encryption with Associated Data (AEAD) cryptographic ciphers. +//! +//! Each AEAD algorithm provides [`SymmetricEncryptor::encrypt_easy`] and [`SymmetricEncryptor::decrypt_easy`] methods which hides the complexity +//! of generating a secure nonce of appropriate size with the ciphertext. +//! The [`SymmetricEncryptor::encrypt_easy`] prepends the nonce to the front of the ciphertext and [`SymmetricEncryptor::decrypt_easy`] expects +//! the nonce to be prepended to the front of the ciphertext. +//! +//! More advanced users may use [`SymmetricEncryptor::encrypt`] and [`SymmetricEncryptor::decrypt`] directly. These two methods require the +//! caller to supply a nonce with sufficient entropy and should never be reused when encrypting +//! with the same `key`. +//! +//! The convenience struct [`SymmetricEncryptor`] exists to allow users to easily switch between +//! algorithms by using any algorithm that implements the [`Encryptor`] trait. +//! +//! [`ChaCha20Poly1305`] is the only algorithm currently supported, +//! as it is the only one used by the iroha p2p transport protocol. mod chacha20poly1305; @@ -15,13 +29,13 @@ pub use self::chacha20poly1305::ChaCha20Poly1305; use crate::SessionKey; // Helpful for generating bytes using the operating system random number generator -pub fn random_vec(bytes: usize) -> Result, Error> { +fn random_vec(bytes: usize) -> Result, Error> { let mut value = vec![0u8; bytes]; OsRng.fill_bytes(value.as_mut_slice()); Ok(value) } -pub fn random_bytes>() -> Result, Error> { +fn random_bytes>() -> Result, Error> { Ok(GenericArray::clone_from_slice( random_vec(T::to_usize())?.as_slice(), )) @@ -59,27 +73,29 @@ pub struct SymmetricEncryptor { } impl SymmetricEncryptor { + /// Create a new [`SymmetricEncryptor`] using the provided `encryptor` pub fn new(encryptor: E) -> Self { Self { encryptor } } + /// Create a new [`SymmetricEncryptor`] from a [`SessionKey`] pub fn new_from_session_key(key: SessionKey) -> Self { Self::new(::new(GenericArray::from_slice(&key.0))) } - + /// Create a new [`SymmetricEncryptor`] from key bytes pub fn new_with_key>(key: A) -> Result { Ok(Self { encryptor: ::new(GenericArray::from_slice(key.as_ref())), }) } - // Encrypt `plaintext` and integrity protect `aad`. The result is the ciphertext. - // This method handles safely generating a `nonce` and prepends it to the ciphertext + /// Encrypt `plaintext` and integrity protect `aad`. The result is the ciphertext. + /// This method handles safely generating a `nonce` and prepends it to the ciphertext pub fn encrypt_easy>(&self, aad: A, plaintext: A) -> Result, Error> { self.encryptor.encrypt_easy(aad, plaintext) } - // Encrypt `plaintext` and integrity protect `aad`. The result is the ciphertext. + /// Encrypt `plaintext` and integrity protect `aad`. The result is the ciphertext. pub fn encrypt>( &self, nonce: A, @@ -94,19 +110,19 @@ impl SymmetricEncryptor { self.encryptor.encrypt(nonce, payload) } - // Decrypt `ciphertext` using integrity protected `aad`. The result is the plaintext if successful - // or an error if the `ciphetext` cannot be decrypted due to tampering, an incorrect `aad` value, - // or incorrect key. - // `aad` must be the same value used in `encrypt_easy`. Expects the nonce to be prepended to - // the `ciphertext` + /// Decrypt `ciphertext` using integrity protected `aad`. The result is the plaintext if successful + /// or an error if the `ciphetext` cannot be decrypted due to tampering, an incorrect `aad` value, + /// or incorrect key. + /// `aad` must be the same value used in `encrypt_easy`. Expects the nonce to be prepended to + /// the `ciphertext` pub fn decrypt_easy>(&self, aad: A, ciphertext: A) -> Result, Error> { self.encryptor.decrypt_easy(aad, ciphertext) } - // Decrypt `ciphertext` using integrity protected `aad`. The result is the plaintext if successful - // or an error if the `ciphetext` cannot be decrypted due to tampering, an incorrect `aad` value, - // or incorrect key. - // `aad` must be the same value used in `encrypt_easy`. + /// Decrypt `ciphertext` using integrity protected `aad`. The result is the plaintext if successful + /// or an error if the `ciphetext` cannot be decrypted due to tampering, an incorrect `aad` value, + /// or incorrect key. + /// `aad` must be the same value used in `encrypt_easy`. pub fn decrypt>( &self, nonce: A, @@ -121,7 +137,7 @@ impl SymmetricEncryptor { self.encryptor.decrypt(nonce, payload) } - // Similar to `encrypt_easy` but reads from a stream instead of a slice + /// Similar to `encrypt_easy` but reads from a stream instead of a slice pub fn encrypt_buffer, I: Read, O: Write>( &self, aad: A, @@ -131,7 +147,7 @@ impl SymmetricEncryptor { self.encryptor.encrypt_buffer(aad, plaintext, ciphertext) } - // Similar to `decrypt_easy` but reads from a stream instead of a slice + /// Similar to `decrypt_easy` but reads from a stream instead of a slice pub fn decrypt_buffer, I: Read, O: Write>( &self, aad: A, @@ -155,6 +171,9 @@ pub trait Encryptor: Aead + KeyInit { /// The minimum size that the ciphertext will yield from plaintext type MinSize: ArrayLength; + /// A simple API to encrypt a message with authenticated associated data. + /// + /// This API handles nonce generation for you and prepends it in front of the ciphertext. Use [`Encryptor::decrypt_easy`] to decrypt the message encrypted this way. fn encrypt_easy>(&self, aad: M, plaintext: M) -> Result, Error> { let nonce = Self::nonce_gen()?; let payload = Payload { @@ -167,6 +186,9 @@ pub trait Encryptor: Aead + KeyInit { Ok(result) } + /// A simple API to decrypt a message with authenticated associated data. + /// + /// This API expects the nonce to be prepended to the ciphertext. Use [`Encryptor::encrypt_easy`] to encrypt the message this way. fn decrypt_easy>(&self, aad: M, ciphertext: M) -> Result, Error> { let ciphertext = ciphertext.as_ref(); if ciphertext.len() < Self::MinSize::to_usize() { @@ -182,6 +204,7 @@ pub trait Encryptor: Aead + KeyInit { Ok(plaintext) } + /// Same as [`Encryptor::encrypt_easy`] but works with [`std::io`] streams instead of slices fn encrypt_buffer, I: Read, O: Write>( &self, aad: M, @@ -194,6 +217,7 @@ pub trait Encryptor: Aead + KeyInit { Ok(()) } + /// Same as [`Encryptor::decrypt_easy`] but works with [`std::io`] streams instead of slices fn decrypt_buffer, I: Read, O: Write>( &self, aad: M, @@ -206,10 +230,12 @@ pub trait Encryptor: Aead + KeyInit { Ok(()) } + /// Generate a new key for this encryptor fn key_gen() -> Result, Error> { random_bytes() } + /// Generate a new nonce for this encryptor fn nonce_gen() -> Result, Error> { random_bytes() } diff --git a/crypto/src/kex/mod.rs b/crypto/src/kex/mod.rs index 23de4536689..0096d167d09 100644 --- a/crypto/src/kex/mod.rs +++ b/crypto/src/kex/mod.rs @@ -1,5 +1,7 @@ -// TODO: clean up & remove -#![allow(unused, missing_docs)] +//! A suite of Diffie-Hellman key exchange methods. +//! +//! [`X25519Sha256`] is the only key exchange scheme currently supported, +//! as it is the only one used by the iroha p2p transport protocol. mod x25519; @@ -28,7 +30,10 @@ pub trait KeyExchangeScheme { remote_public_key: &PublicKey, ) -> Result; - fn shared_secret_size() -> usize; - fn public_key_size() -> usize; - fn private_key_size() -> usize; + /// Size of the shared secret in bytes. + const SHARED_SECRET_SIZE: usize; + /// Size of the public key in bytes. + const PUBLIC_KEY_SIZE: usize; + /// Size of the private key in bytes. + const PRIVATE_KEY_SIZE: usize; } diff --git a/crypto/src/kex/x25519.rs b/crypto/src/kex/x25519.rs index 42367d8c7a0..e269842fb52 100644 --- a/crypto/src/kex/x25519.rs +++ b/crypto/src/kex/x25519.rs @@ -11,6 +11,7 @@ const ALGORITHM: Algorithm = Algorithm::Ed25519; use super::KeyExchangeScheme; use crate::{Algorithm, Error, KeyGenOption, PrivateKey, PublicKey, SessionKey}; +/// Implements the [`KeyExchangeScheme`] using X25519 key exchange and SHA256 hash function. #[derive(Copy, Clone)] pub struct X25519Sha256; @@ -70,34 +71,15 @@ impl KeyExchangeScheme for X25519Sha256 { Ok(SessionKey(ConstVec::new(hash.as_slice().to_vec()))) } - fn public_key_size() -> usize { - 32 - } - fn private_key_size() -> usize { - 32 - } - fn shared_secret_size() -> usize { - 32 - } + const SHARED_SECRET_SIZE: usize = 32; + const PUBLIC_KEY_SIZE: usize = 32; + const PRIVATE_KEY_SIZE: usize = 32; } #[cfg(test)] mod tests { use super::*; - // #[test] - // fn convert_from_sig_keys() { - // use crate::{Ed25519Sha512, SignatureScheme}; - // let sig_scheme = Ed25519Sha512::new(); - // let (pk, sk) = sig_scheme.keypair(None).unwrap(); - // let res = Ed25519Sha512::ver_key_to_key_exchange(&pk); - // assert!(res.is_ok()); - // let pk1 = res.unwrap(); - // let kex_scheme = X25519Sha256::new(); - // let res = kex_scheme.compute_shared_secret(&sk, &pk1); - // assert!(res.is_ok()); - // } - #[test] fn key_exchange() { let scheme = X25519Sha256::new(); diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index 2e5859f6c1d..9b33032e095 100755 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -233,10 +233,12 @@ impl KeyPair { }; let (public_key, private_key) = match configuration.algorithm { - Algorithm::Ed25519 => Ed25519Sha512.keypair(key_gen_option), - Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().keypair(key_gen_option), - Algorithm::BlsNormal => BlsNormal::new().keypair(key_gen_option), - Algorithm::BlsSmall => BlsSmall::new().keypair(key_gen_option), + Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512.keypair(key_gen_option), + Algorithm::Secp256k1 => { + signature::secp256k1::EcdsaSecp256k1Sha256::new().keypair(key_gen_option) + } + Algorithm::BlsNormal => signature::bls::BlsNormal::new().keypair(key_gen_option), + Algorithm::BlsSmall => signature::bls::BlsSmall::new().keypair(key_gen_option), }?; Ok(Self { @@ -317,14 +319,37 @@ impl PublicKey { let key_gen_option = Some(KeyGenOption::FromPrivateKey(private_key)); let (public_key, _) = match digest_function { - Algorithm::Ed25519 => Ed25519Sha512.keypair(key_gen_option), - Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().keypair(key_gen_option), - Algorithm::BlsNormal => BlsNormal::new().keypair(key_gen_option), - Algorithm::BlsSmall => BlsSmall::new().keypair(key_gen_option), + Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512.keypair(key_gen_option), + Algorithm::Secp256k1 => { + signature::secp256k1::EcdsaSecp256k1Sha256::new().keypair(key_gen_option) + } + Algorithm::BlsNormal => signature::bls::BlsNormal::new().keypair(key_gen_option), + Algorithm::BlsSmall => signature::bls::BlsSmall::new().keypair(key_gen_option), }?; Ok(public_key) } + + /// Construct `PrivateKey` from hex encoded string + /// + /// # Errors + /// + /// - If the given payload is not hex encoded + /// - If the given payload is not a valid private key + #[cfg(feature = "std")] + pub fn from_hex(digest_function: Algorithm, payload: &str) -> Result { + let payload = hex_decode(payload)?; + let payload = ConstVec::new(payload); + + // NOTE: PrivateKey does some validation by generating a public key from the provided bytes + // we can't really do this for PublicKey + // this can be solved if the keys used here would be actually aware of the underlying crypto primitive types + // instead of just being raw bytes + Ok(Self { + digest_function, + payload, + }) + } } impl FromStr for PublicKey { @@ -415,7 +440,7 @@ impl PrivateKey { /// - If the given payload is not hex encoded /// - If the given payload is not a valid private key #[cfg(feature = "std")] - pub fn from_hex(digest_function: Algorithm, payload: &[u8]) -> Result { + pub fn from_hex(digest_function: Algorithm, payload: &str) -> Result { let payload = hex_decode(payload)?; let payload = ConstVec::new(payload); diff --git a/crypto/src/signature/bls/implementation.rs b/crypto/src/signature/bls/implementation.rs index 70089997e9b..4cef0d569b4 100644 --- a/crypto/src/signature/bls/implementation.rs +++ b/crypto/src/signature/bls/implementation.rs @@ -8,7 +8,13 @@ use amcl_wrapper::{ }; use sha2::Sha256; -use super::{MESSAGE_CONTEXT, PRIVATE_KEY_SIZE, PUBLICKEY_CONTEXT}; +pub(super) const MESSAGE_CONTEXT: &[u8; 20] = b"for signing messages"; + +// it is not unused? Why am I getting the unused lint here? +#[allow(dead_code)] +const PUBLICKEY_CONTEXT: &[u8; 47] = b"for signing public keys for proof of possession"; + +use super::PRIVATE_KEY_SIZE; use crate::{ Algorithm, ConstVec, Error, KeyGenOption, PrivateKey as IrohaPrivateKey, PublicKey as IrohaPublicKey, @@ -69,14 +75,6 @@ impl PublicKey { // Self(g * sk) } - // Create an combined public key without rogue key mitigation - pub fn combine(&mut self, pks: &[PublicKey]) { - for pk in pks { - self.0.add_assign_(&pk.0) - // self.0 += &; - } - } - pub fn to_bytes(&self) -> Vec { self.0.to_bytes(false) } diff --git a/crypto/src/signature/bls/mod.rs b/crypto/src/signature/bls/mod.rs index f4c56fa1a00..4a7c228a75e 100644 --- a/crypto/src/signature/bls/mod.rs +++ b/crypto/src/signature/bls/mod.rs @@ -1,16 +1,7 @@ -// TODO: clean up & remove -#![allow(missing_docs)] - // Do not expose the [implementation] module & the [implementation::BlsConfiguration] trait mod implementation; pub const PRIVATE_KEY_SIZE: usize = amcl_wrapper::constants::MODBYTES; -/// This is a simple alias so the consumer can just use PrivateKey::random() to generate a new one -/// instead of wrapping it as a private field -// pub type PrivateKey = FieldElement; - -const MESSAGE_CONTEXT: &[u8; 20] = b"for signing messages"; -const PUBLICKEY_CONTEXT: &[u8; 47] = b"for signing public keys for proof of possession"; /// This version is the "normal" BLS signature scheme /// with the public key group in G1 and signature group in G2. diff --git a/crypto/src/signature/bls/tests.rs b/crypto/src/signature/bls/tests.rs index ad9aab07046..72c42f192ee 100644 --- a/crypto/src/signature/bls/tests.rs +++ b/crypto/src/signature/bls/tests.rs @@ -6,14 +6,11 @@ use amcl_wrapper::{ }; use super::{ + implementation::{BlsConfiguration, BlsImpl, Signature, MESSAGE_CONTEXT}, normal::{normal_generate, NormalConfiguration, NormalGenerator, NormalSignature}, small::{small_generate, SmallConfiguration, SmallGenerator, SmallSignature}, - MESSAGE_CONTEXT, -}; -use crate::{ - signature::bls::implementation::{BlsConfiguration, BlsImpl, Signature}, - KeyGenOption, }; +use crate::KeyGenOption; const MESSAGE_1: &[u8; 22] = b"This is a test message"; const MESSAGE_2: &[u8; 20] = b"Another test message"; diff --git a/crypto/src/signature/ed25519.rs b/crypto/src/signature/ed25519.rs index 1fb1ba492fc..b546d635f64 100644 --- a/crypto/src/signature/ed25519.rs +++ b/crypto/src/signature/ed25519.rs @@ -1,6 +1,3 @@ -// TODO: clean up & remove -#![allow(missing_docs)] - use std::convert::TryFrom; use arrayref::array_ref; @@ -77,158 +74,110 @@ impl Ed25519Sha512 { .map_err(|e| Error::Signing(e.to_string()))?; Ok(true) } - pub const fn signature_size() -> usize { - SIGNATURE_SIZE +} + +#[cfg(test)] +// unsafe code is needed to check consistency with libsodium, which is a C library +#[allow(unsafe_code)] +mod test { + use libsodium_sys as ffi; + + use self::Ed25519Sha512; + use super::*; + use crate::{KeyGenOption, PrivateKey, PublicKey}; + + const MESSAGE_1: &[u8] = b"This is a dummy message for use with tests"; + const SIGNATURE_1: &str = "451b5b8e8725321541954997781de51f4142e4a56bab68d24f6a6b92615de5eefb74134138315859a32c7cf5fe5a488bc545e2e08e5eedfd1fb10188d532d808"; + const PRIVATE_KEY: &str = "1c1179a560d092b90458fe6ab8291215a427fcd6b3927cb240701778ef55201927c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf"; + const PUBLIC_KEY: &str = "27c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf"; + + #[test] + #[ignore] + fn create_new_keys() { + let scheme = Ed25519Sha512::new(); + let (p, s) = scheme.keypair(None).unwrap(); + + println!("{:?}", s); + println!("{:?}", p); + } + + #[test] + fn ed25519_load_keys() { + let scheme = Ed25519Sha512::new(); + let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); + let sres = scheme.keypair(Some(KeyGenOption::FromPrivateKey(secret))); + assert!(sres.is_ok()); + let (p1, s1) = sres.unwrap(); + + assert_eq!( + s1, + PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap() + ); + assert_eq!( + p1, + PublicKey::from_hex(Algorithm::Ed25519, PUBLIC_KEY).unwrap() + ); } - pub const fn private_key_size() -> usize { - PRIVATE_KEY_SIZE + + #[test] + fn ed25519_verify() { + let scheme = Ed25519Sha512::new(); + let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); + let (p, _) = scheme + .keypair(Some(KeyGenOption::FromPrivateKey(secret))) + .unwrap(); + + let result = scheme.verify(&MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + + //Check if signatures produced here can be verified by libsodium + let signature = hex::decode(SIGNATURE_1).unwrap(); + let res = unsafe { + ffi::crypto_sign_ed25519_verify_detached( + signature.as_slice().as_ptr(), + MESSAGE_1.as_ptr(), + MESSAGE_1.len() as u64, + p.payload().as_ptr(), + ) + }; + assert_eq!(res, 0); } - pub const fn public_key_size() -> usize { - PUBLIC_KEY_SIZE + + #[test] + fn ed25519_sign() { + let scheme = Ed25519Sha512::new(); + let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); + let (p, s) = scheme + .keypair(Some(KeyGenOption::FromPrivateKey(secret))) + .unwrap(); + + match scheme.sign(&MESSAGE_1, &s) { + Ok(sig) => { + let result = scheme.verify(&MESSAGE_1, &sig, &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + + assert_eq!(sig.len(), SIGNATURE_SIZE); + assert_eq!(hex::encode(sig.as_slice()), SIGNATURE_1); + + //Check if libsodium signs the message and this module still can verify it + //And that private keys can sign with other libraries + let mut signature = [0u8; ffi::crypto_sign_ed25519_BYTES as usize]; + unsafe { + ffi::crypto_sign_ed25519_detached( + signature.as_mut_ptr(), + std::ptr::null_mut(), + MESSAGE_1.as_ptr(), + MESSAGE_1.len() as u64, + s.payload().as_ptr(), + ) + }; + let result = scheme.verify(&MESSAGE_1, &signature, &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + } + Err(e) => assert!(false, "{}", e), + } } } - -// #[cfg(test)] -// mod test { -// use keys::{KeyGenOption, PrivateKey, PublicKey}; -// use libsodium_sys_stable as ffi; -// -// use self::Ed25519Sha512; -// use super::{ -// super::{SignatureScheme, Signer}, -// *, -// }; -// -// const MESSAGE_1: &[u8] = b"This is a dummy message for use with tests"; -// const SIGNATURE_1: &str = "451b5b8e8725321541954997781de51f4142e4a56bab68d24f6a6b92615de5eefb74134138315859a32c7cf5fe5a488bc545e2e08e5eedfd1fb10188d532d808"; -// const PRIVATE_KEY: &str = "1c1179a560d092b90458fe6ab8291215a427fcd6b3927cb240701778ef55201927c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf"; -// const PUBLIC_KEY: &str = "27c96646f2d4632d4fc241f84cbc427fbc3ecaa95becba55088d6c7b81fc5bbf"; -// const PRIVATE_KEY_X25519: &str = -// "08e7286c232ec71b37918533ea0229bf0c75d3db4731df1c5c03c45bc909475f"; -// const PUBLIC_KEY_X25519: &str = -// "9b4260484c889158c128796103dc8d8b883977f2ef7efb0facb12b6ca9b2ae3d"; -// -// #[test] -// #[ignore] -// fn create_new_keys() { -// let scheme = Ed25519Sha512::new(); -// let (p, s) = scheme.keypair(None).unwrap(); -// -// println!("{:?}", s); -// println!("{:?}", p); -// } -// -// #[test] -// fn ed25519_load_keys() { -// let scheme = Ed25519Sha512::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let sres = scheme.keypair(Some(KeyGenOption::FromSecretKey(secret))); -// assert!(sres.is_ok()); -// let (p1, s1) = sres.unwrap(); -// assert_eq!(s1, PrivateKey(hex::decode(PRIVATE_KEY).unwrap())); -// assert_eq!(p1, PublicKey(hex::decode(PUBLIC_KEY).unwrap())); -// } -// -// #[test] -// fn ed25519_verify() { -// let scheme = Ed25519Sha512::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let (p, _) = scheme -// .keypair(Some(KeyGenOption::FromSecretKey(secret))) -// .unwrap(); -// -// let result = scheme.verify(&MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// //Check if signatures produced here can be verified by libsodium -// let signature = hex::decode(SIGNATURE_1).unwrap(); -// let res = unsafe { -// ffi::crypto_sign_ed25519_verify_detached( -// signature.as_slice().as_ptr() as *const u8, -// MESSAGE_1.as_ptr() as *const u8, -// MESSAGE_1.len() as u64, -// p.as_ptr() as *const u8, -// ) -// }; -// assert_eq!(res, 0); -// } -// -// #[test] -// fn ed25519_sign() { -// let scheme = Ed25519Sha512::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let (p, s) = scheme -// .keypair(Some(KeyGenOption::FromSecretKey(secret))) -// .unwrap(); -// -// match scheme.sign(&MESSAGE_1, &s) { -// Ok(sig) => { -// let result = scheme.verify(&MESSAGE_1, &sig, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// assert_eq!(sig.len(), SIGNATURE_SIZE); -// assert_eq!(hex::encode(sig.as_slice()), SIGNATURE_1); -// -// //Check if libsodium signs the message and this module still can verify it -// //And that private keys can sign with other libraries -// let mut signature = [0u8; ffi::crypto_sign_ed25519_BYTES as usize]; -// unsafe { -// ffi::crypto_sign_ed25519_detached( -// signature.as_mut_ptr() as *mut u8, -// 0u64 as *mut u64, -// MESSAGE_1.as_ptr() as *const u8, -// MESSAGE_1.len() as u64, -// s.as_ptr() as *const u8, -// ) -// }; -// let result = scheme.verify(&MESSAGE_1, &signature, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// } -// Err(e) => assert!(false, "{}", e), -// } -// let signer = Signer::new(&scheme, &s); -// match signer.sign(&MESSAGE_1) { -// Ok(signed) => { -// let result = scheme.verify(&MESSAGE_1, &signed, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// } -// Err(er) => assert!(false, "{}", er), -// } -// } -// -// #[test] -// fn ed25519_to_x25519_default() { -// let scheme = Ed25519Sha512::new(); -// let (p, _) = scheme.keypair(None).unwrap(); -// -// let res = Ed25519Sha512::ver_key_to_key_exchange(&p); -// assert!(res.is_ok()); -// } -// -// #[test] -// fn ed25519_to_x25519_verify() { -// let sk = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let pk = PublicKey(hex::decode(PUBLIC_KEY).unwrap()); -// -// let x_pk = Ed25519Sha512::ver_key_to_key_exchange(&pk).unwrap(); -// assert_eq!(hex::encode(&x_pk), PUBLIC_KEY_X25519); -// -// let x_sk = Ed25519Sha512::sign_key_to_key_exchange(&sk).unwrap(); -// assert_eq!(hex::encode(&x_sk), PRIVATE_KEY_X25519); -// } -// -// #[test] -// fn nacl_derive_from_seed() { -// let seed = b"000000000000000000000000Trustee1"; -// let test_sk = hex::decode("3030303030303030303030303030303030303030303030305472757374656531e33aaf381fffa6109ad591fdc38717945f8fabf7abf02086ae401c63e9913097").unwrap(); -// let test_pk = &test_sk[32..]; -// -// let (pk, sk) = Ed25519Sha512::expand_keypair(seed).unwrap(); -// assert_eq!(pk.0, test_pk); -// assert_eq!(sk.0, test_sk); -// } -// } diff --git a/crypto/src/signature/mod.rs b/crypto/src/signature/mod.rs index 0278c345c8a..f8a2d1b9a8e 100644 --- a/crypto/src/signature/mod.rs +++ b/crypto/src/signature/mod.rs @@ -1,14 +1,14 @@ #[cfg(feature = "std")] #[cfg(not(feature = "ffi_import"))] -mod bls; +pub(crate) mod bls; #[cfg(feature = "std")] #[cfg(not(feature = "ffi_import"))] -mod ed25519; +pub(crate) mod ed25519; #[cfg(feature = "std")] #[cfg(not(feature = "ffi_import"))] -mod secp256k1; +pub(crate) mod secp256k1; #[cfg(not(feature = "std"))] use alloc::{boxed::Box, collections::btree_set, format, string::String, vec, vec::Vec}; @@ -25,11 +25,6 @@ use parity_scale_codec::{Decode, Encode}; #[cfg(not(feature = "ffi_import"))] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -#[cfg(not(feature = "ffi_import"))] -pub use self::{ - bls::BlsNormal, bls::BlsSmall, ed25519::Ed25519Sha512, secp256k1::EcdsaSecp256k1Sha256, -}; #[cfg(any(feature = "std", feature = "import_ffi"))] use crate::Error; use crate::{ffi, PublicKey}; @@ -72,10 +67,12 @@ impl Signature { let algorithm: crate::Algorithm = private_key.digest_function(); let signature = match algorithm { - crate::Algorithm::Ed25519 => Ed25519Sha512::new().sign(payload, &private_key), - crate::Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().sign(payload, &private_key), - crate::Algorithm::BlsSmall => BlsSmall::new().sign(payload, &private_key), - crate::Algorithm::BlsNormal => BlsNormal::new().sign(payload, &private_key), + crate::Algorithm::Ed25519 => ed25519::Ed25519Sha512::new().sign(payload, &private_key), + crate::Algorithm::Secp256k1 => { + secp256k1::EcdsaSecp256k1Sha256::new().sign(payload, &private_key) + } + crate::Algorithm::BlsSmall => bls::BlsSmall::new().sign(payload, &private_key), + crate::Algorithm::BlsNormal => bls::BlsNormal::new().sign(payload, &private_key), }?; Ok(Self { public_key, @@ -93,16 +90,18 @@ impl Signature { match algorithm { crate::Algorithm::Ed25519 => { - Ed25519Sha512::new().verify(payload, self.payload(), &self.public_key) - } - crate::Algorithm::Secp256k1 => { - EcdsaSecp256k1Sha256::new().verify(payload, self.payload(), &self.public_key) + ed25519::Ed25519Sha512::new().verify(payload, self.payload(), &self.public_key) } + crate::Algorithm::Secp256k1 => secp256k1::EcdsaSecp256k1Sha256::new().verify( + payload, + self.payload(), + &self.public_key, + ), crate::Algorithm::BlsSmall => { - BlsSmall::new().verify(payload, self.payload(), &self.public_key) + bls::BlsSmall::new().verify(payload, self.payload(), &self.public_key) } crate::Algorithm::BlsNormal => { - BlsNormal::new().verify(payload, self.payload(), &self.public_key) + bls::BlsNormal::new().verify(payload, self.payload(), &self.public_key) } }?; diff --git a/crypto/src/signature/secp256k1.rs b/crypto/src/signature/secp256k1.rs index d7256f46801..77fb6cf6104 100644 --- a/crypto/src/signature/secp256k1.rs +++ b/crypto/src/signature/secp256k1.rs @@ -1,23 +1,12 @@ -// TODO: clean up & remove -#![allow(missing_docs, unused)] - use crate::{Algorithm, Error, KeyGenOption, PrivateKey, PublicKey}; pub const PRIVATE_KEY_SIZE: usize = 32; pub const PUBLIC_KEY_SIZE: usize = 33; -pub const SIGNATURE_SIZE: usize = 64; -pub const PUBLIC_UNCOMPRESSED_KEY_SIZE: usize = 65; const ALGORITHM: Algorithm = Algorithm::Secp256k1; pub struct EcdsaSecp256k1Sha256(ecdsa_secp256k1::EcdsaSecp256k1Impl); -impl EcdsaSecp256k1Sha256 { - pub fn normalize_s(&self, signature: &mut [u8]) -> Result<(), Error> { - self.0.normalize_s(signature) - } -} - impl EcdsaSecp256k1Sha256 { pub fn new() -> Self { EcdsaSecp256k1Sha256(ecdsa_secp256k1::EcdsaSecp256k1Impl::new()) @@ -31,15 +20,6 @@ impl EcdsaSecp256k1Sha256 { pub fn verify(&self, message: &[u8], signature: &[u8], pk: &PublicKey) -> Result { self.0.verify::(message, signature, pk) } - pub const fn signature_size() -> usize { - SIGNATURE_SIZE - } - pub const fn private_key_size() -> usize { - PRIVATE_KEY_SIZE - } - pub const fn public_key_size() -> usize { - PUBLIC_KEY_SIZE - } } mod ecdsa_secp256k1 { @@ -47,14 +27,13 @@ mod ecdsa_secp256k1 { use arrayref::array_ref; use digest::{consts::U32, Digest}; use ed25519_dalek::{Signer, Verifier}; - use elliptic_curve::sec1::FromEncodedPoint; use iroha_primitives::const_vec::ConstVec; use k256; use rand::{rngs::OsRng, RngCore, SeedableRng}; use rand_chacha::ChaChaRng; use zeroize::Zeroize; - use super::{ALGORITHM, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, PUBLIC_UNCOMPRESSED_KEY_SIZE}; + use super::{ALGORITHM, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE}; use crate::{Error, KeyGenOption, PrivateKey, PublicKey}; pub struct EcdsaSecp256k1Impl; @@ -67,31 +46,6 @@ mod ecdsa_secp256k1 { compressed.to_vec() } - pub fn public_key_uncompressed(&self, pk: &PublicKey) -> Vec { - assert_eq!(pk.digest_function, ALGORITHM); - let mut uncompressed = [0u8; PUBLIC_UNCOMPRESSED_KEY_SIZE]; - ecp::ECP::frombytes(&pk.payload[..]).tobytes(&mut uncompressed, false); - uncompressed.to_vec() - } - - pub fn parse(&self, data: &[u8]) -> Result { - match data.len() { - PUBLIC_KEY_SIZE => Ok(PublicKey { - digest_function: ALGORITHM, - payload: ConstVec::new(data.to_vec()), - }), - PUBLIC_UNCOMPRESSED_KEY_SIZE => { - let mut compressed = [0u8; PUBLIC_KEY_SIZE]; - ecp::ECP::frombytes(data).tobytes(&mut compressed, true); - Ok(PublicKey { - digest_function: ALGORITHM, - payload: ConstVec::new(compressed.to_vec()), - }) - } - _ => Err(Error::Parse("Invalid key length".to_string())), - } - } - pub fn new() -> Self { Self {} } @@ -165,237 +119,195 @@ mod ecdsa_secp256k1 { Ok(verifying_key.verify(message, &signature).is_ok()) } - - pub fn normalize_s(&self, signature: &mut [u8]) -> Result<(), Error> { - let mut sig = k256::ecdsa::Signature::from_slice(signature) - .map_err(|e| Error::Signing(format!("{:?}", e)))?; - sig.normalize_s(); - signature.copy_from_slice(&sig.to_bytes()); - Ok(()) - } } } impl From for Error { fn from(error: elliptic_curve::Error) -> Error { - // rust crypto doesn't expose any kind of error information.. + // RustCrypto doesn't expose any kind of error information =( Error::Other(format!("{}", error)) } } -// #[cfg(test)] -// mod test { -// use openssl::{ -// bn::{BigNum, BigNumContext}, -// ec::{EcGroup, EcKey, EcPoint}, -// ecdsa::EcdsaSig, -// nid::Nid, -// }; -// use secp256k1; -// use sha2::Digest; -// -// use super::{EcdsaPublicKeyHandler, *}; -// -// const MESSAGE_1: &[u8] = b"This is a dummy message for use with tests"; -// const SIGNATURE_1: &str = "ae46d3fec8e2eb95ebeaf95f7f096ec4bf517f5ef898e4379651f8af8e209ed75f3c47156445d6687a5f817fb3e188e2a76df653b330df859ec47579c8c409be"; -// const PRIVATE_KEY: &str = "e4f21b38e005d4f895a29e84948d7cc83eac79041aeb644ee4fab8d9da42f713"; -// const PUBLIC_KEY: &str = "0242c1e1f775237a26da4fd51b8d75ee2709711f6e90303e511169a324ef0789c0"; -// -// #[test] -// #[ignore] -// fn create_new_keys() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// let (s, p) = scheme.keypair(None).unwrap(); -// -// println!("{:?}", s); -// println!("{:?}", p); -// } -// -// #[test] -// fn secp256k1_load_keys() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let sres = scheme.keypair(Some(KeyGenOption::FromSecretKey(secret))); -// assert!(sres.is_ok()); -// let pres = scheme.parse(hex::decode(PUBLIC_KEY).unwrap().as_slice()); -// assert!(pres.is_ok()); -// let (p1, _) = sres.unwrap(); -// assert_eq!(p1, pres.unwrap()); -// } -// -// #[test] -// fn secp256k1_compatibility() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let (p, s) = scheme -// .keypair(Some(KeyGenOption::FromSecretKey(secret))) -// .unwrap(); -// -// let p_u = scheme.parse(&scheme.public_key_uncompressed(&p)); -// assert!(p_u.is_ok()); -// let p_u = p_u.unwrap(); -// assert_eq!(p_u, p); -// -// let sk = secp256k1::key::SecretKey::from_slice(&s[..]); -// assert!(sk.is_ok()); -// let pk = secp256k1::key::PublicKey::from_slice(&p[..]); -// assert!(pk.is_ok()); -// let pk = secp256k1::key::PublicKey::from_slice(&scheme.public_key_uncompressed(&p)[..]); -// assert!(pk.is_ok()); -// -// let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); -// let mut ctx = BigNumContext::new().unwrap(); -// let openssl_point = EcPoint::from_bytes( -// &openssl_group, -// &scheme.public_key_uncompressed(&p)[..], -// &mut ctx, -// ); -// assert!(openssl_point.is_ok()); -// } -// -// #[test] -// fn secp256k1_verify() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// let p = PublicKey(hex::decode(PUBLIC_KEY).unwrap()); -// -// let result = scheme.verify(&MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// let context = secp256k1::Secp256k1::new(); -// let pk = secp256k1::key::PublicKey::from_slice( -// hex::decode(PUBLIC_KEY).unwrap().as_slice(), -// ) -// .unwrap(); -// -// let h = sha2::Sha256::digest(&MESSAGE_1); -// let msg = secp256k1::Message::from_slice(h.as_slice()).unwrap(); -// -// //Check if signatures produced here can be verified by secp256k1 -// let mut signature = -// secp256k1::Signature::from_compact(&hex::decode(SIGNATURE_1).unwrap()[..]) -// .unwrap(); -// signature.normalize_s(); -// let result = context.verify(&msg, &signature, &pk); -// assert!(result.is_ok()); -// -// let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); -// let mut ctx = BigNumContext::new().unwrap(); -// let openssl_point = -// EcPoint::from_bytes(&openssl_group, &pk.serialize_uncompressed(), &mut ctx).unwrap(); -// let openssl_pkey = EcKey::from_public_key(&openssl_group, &openssl_point).unwrap(); -// -// //Check if the signatures produced here can be verified by openssl -// let (r, s) = SIGNATURE_1.split_at(SIGNATURE_1.len() / 2); -// let openssl_r = BigNum::from_hex_str(r).unwrap(); -// let openssl_s = BigNum::from_hex_str(s).unwrap(); -// let openssl_sig = EcdsaSig::from_private_components(openssl_r, openssl_s).unwrap(); -// let openssl_result = openssl_sig.verify(h.as_slice(), &openssl_pkey); -// assert!(openssl_result.is_ok()); -// assert!(openssl_result.unwrap()); -// } -// -// #[test] -// fn secp256k1_sign() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// let secret = PrivateKey(hex::decode(PRIVATE_KEY).unwrap()); -// let (p, s) = scheme -// .keypair(Some(KeyGenOption::FromSecretKey(secret))) -// .unwrap(); -// -// match scheme.sign(MESSAGE_1, &s) { -// Ok(sig) => { -// let result = scheme.verify(&MESSAGE_1, &sig, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// assert_eq!(sig.len(), SIGNATURE_SIZE); -// -// //Check if secp256k1 signs the message and this module still can verify it -// //And that private keys can sign with other libraries -// let context = secp256k1::Secp256k1::new(); -// let sk = secp256k1::key::SecretKey::from_slice( -// hex::decode(PRIVATE_KEY).unwrap().as_slice(), -// ) -// .unwrap(); -// -// let h = sha2::Sha256::digest(&MESSAGE_1); -// -// let msg = secp256k1::Message::from_slice(h.as_slice()).unwrap(); -// let sig_1 = context.sign(&msg, &sk).serialize_compact(); -// -// let result = scheme.verify(&MESSAGE_1, &sig_1, &p); -// -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); -// let mut ctx = BigNumContext::new().unwrap(); -// let openssl_point = EcPoint::from_bytes( -// &openssl_group, -// &scheme.public_key_uncompressed(&p)[..], -// &mut ctx, -// ) -// .unwrap(); -// let openssl_pkey = EcKey::from_public_key(&openssl_group, &openssl_point).unwrap(); -// let openssl_skey = EcKey::from_private_components( -// &openssl_group, -// &BigNum::from_hex_str(PRIVATE_KEY).unwrap(), -// &openssl_point, -// ) -// .unwrap(); -// -// let openssl_sig = EcdsaSig::sign(h.as_slice(), &openssl_skey).unwrap(); -// let openssl_result = openssl_sig.verify(h.as_slice(), &openssl_pkey); -// assert!(openssl_result.is_ok()); -// assert!(openssl_result.unwrap()); -// let mut temp_sig = Vec::new(); -// temp_sig.extend(openssl_sig.r().to_vec()); -// temp_sig.extend(openssl_sig.s().to_vec()); -// -// //secp256k1 expects normalized "s"'s. -// scheme.normalize_s(temp_sig.as_mut_slice()).unwrap(); -// let result = scheme.verify(&MESSAGE_1, temp_sig.as_slice(), &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// -// let (p, s) = scheme.keypair(None).unwrap(); -// match scheme.sign(&MESSAGE_1, &s) { -// Ok(signed) => { -// let result = scheme.verify(&MESSAGE_1, &signed, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// } -// Err(er) => assert!(false, "{}", er), -// } -// -// let signer = Signer::new(&scheme, &s); -// match signer.sign(&MESSAGE_1) { -// Ok(signed) => { -// let result = scheme.verify(&MESSAGE_1, &signed, &p); -// assert!(result.is_ok()); -// assert!(result.unwrap()); -// } -// Err(er) => assert!(false, "{}", er), -// } -// } -// Err(e) => assert!(false, "{}", e), -// } -// } -// -// #[test] -// fn secp256k1_publickey_compression() { -// let scheme = EcdsaSecp256k1Sha256::new(); -// -// let pk = PublicKey(hex::decode(PUBLIC_KEY).unwrap()); -// -// let res = scheme.public_key_compressed(&pk); -// assert_eq!(res[..], pk[..]); -// -// let res = scheme.public_key_uncompressed(&pk); -// let pk = PublicKey(res); -// -// let res = scheme.public_key_uncompressed(&pk); -// assert_eq!(res[..], pk[..]); -// } -// } +#[cfg(test)] +mod test { + use amcl::secp256k1::ecp; + use openssl::{ + bn::{BigNum, BigNumContext}, + ec::{EcGroup, EcKey, EcPoint}, + ecdsa::EcdsaSig, + nid::Nid, + }; + use secp256k1; + use sha2::Digest; + + use super::*; + + const MESSAGE_1: &[u8] = b"This is a dummy message for use with tests"; + const SIGNATURE_1: &str = "ae46d3fec8e2eb95ebeaf95f7f096ec4bf517f5ef898e4379651f8af8e209ed75f3c47156445d6687a5f817fb3e188e2a76df653b330df859ec47579c8c409be"; + const PRIVATE_KEY: &str = "e4f21b38e005d4f895a29e84948d7cc83eac79041aeb644ee4fab8d9da42f713"; + const PUBLIC_KEY: &str = "0242c1e1f775237a26da4fd51b8d75ee2709711f6e90303e511169a324ef0789c0"; + + fn public_key_uncompressed(pk: &PublicKey) -> Vec { + const PUBLIC_UNCOMPRESSED_KEY_SIZE: usize = 65; + + assert_eq!(pk.digest_function, ALGORITHM); + let mut uncompressed = [0u8; PUBLIC_UNCOMPRESSED_KEY_SIZE]; + ecp::ECP::frombytes(&pk.payload[..]).tobytes(&mut uncompressed, false); + uncompressed.to_vec() + } + + #[test] + #[ignore] + fn create_new_keys() { + let scheme = EcdsaSecp256k1Sha256::new(); + let (s, p) = scheme.keypair(None).unwrap(); + + println!("{:?}", s); + println!("{:?}", p); + } + + #[test] + fn secp256k1_load_keys() { + let scheme = EcdsaSecp256k1Sha256::new(); + let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); + let sres = scheme.keypair(Some(KeyGenOption::FromPrivateKey(secret))); + assert!(sres.is_ok()); + } + + #[test] + fn secp256k1_compatibility() { + let scheme = EcdsaSecp256k1Sha256::new(); + let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); + let (p, s) = scheme + .keypair(Some(KeyGenOption::FromPrivateKey(secret))) + .unwrap(); + + let sk = secp256k1::SecretKey::from_slice(s.payload()); + assert!(sk.is_ok()); + let pk = secp256k1::PublicKey::from_slice(p.payload()); + assert!(pk.is_ok()); + + let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let openssl_point = + EcPoint::from_bytes(&openssl_group, &public_key_uncompressed(&p)[..], &mut ctx); + assert!(openssl_point.is_ok()); + } + + #[test] + fn secp256k1_verify() { + let scheme = EcdsaSecp256k1Sha256::new(); + let p = PublicKey::from_hex(ALGORITHM, PUBLIC_KEY).unwrap(); + + let result = scheme.verify(&MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + + let context = secp256k1::Secp256k1::new(); + let pk = + secp256k1::PublicKey::from_slice(hex::decode(PUBLIC_KEY).unwrap().as_slice()).unwrap(); + + let h = sha2::Sha256::digest(&MESSAGE_1); + let msg = secp256k1::Message::from_digest_slice(h.as_slice()).unwrap(); + + //Check if signatures produced here can be verified by secp256k1 + let mut signature = + secp256k1::ecdsa::Signature::from_compact(&hex::decode(SIGNATURE_1).unwrap()[..]) + .unwrap(); + signature.normalize_s(); + let result = context.verify_ecdsa(&msg, &signature, &pk); + assert!(result.is_ok()); + + let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let openssl_point = + EcPoint::from_bytes(&openssl_group, &pk.serialize_uncompressed(), &mut ctx).unwrap(); + let openssl_pkey = EcKey::from_public_key(&openssl_group, &openssl_point).unwrap(); + + // Check if the signatures produced here can be verified by openssl + let (r, s) = SIGNATURE_1.split_at(SIGNATURE_1.len() / 2); + let openssl_r = BigNum::from_hex_str(r).unwrap(); + let openssl_s = BigNum::from_hex_str(s).unwrap(); + let openssl_sig = EcdsaSig::from_private_components(openssl_r, openssl_s).unwrap(); + let openssl_result = openssl_sig.verify(h.as_slice(), &openssl_pkey); + assert!(openssl_result.is_ok()); + assert!(openssl_result.unwrap()); + } + + #[test] + fn secp256k1_sign() { + let scheme = EcdsaSecp256k1Sha256::new(); + let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); + let (p, s) = scheme + .keypair(Some(KeyGenOption::FromPrivateKey(secret))) + .unwrap(); + + match scheme.sign(MESSAGE_1, &s) { + Ok(sig) => { + let result = scheme.verify(&MESSAGE_1, &sig, &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + + const SIGNATURE_SIZE: usize = 64; + assert_eq!(sig.len(), SIGNATURE_SIZE); + + // Check if secp256k1 signs the message and this module still can verify it + // And that private keys can sign with other libraries + let context = secp256k1::Secp256k1::new(); + let sk = + secp256k1::SecretKey::from_slice(hex::decode(PRIVATE_KEY).unwrap().as_slice()) + .unwrap(); + + let h = sha2::Sha256::digest(&MESSAGE_1); + + let msg = secp256k1::Message::from_digest_slice(h.as_slice()).unwrap(); + let sig_1 = context.sign_ecdsa(&msg, &sk).serialize_compact(); + + let result = scheme.verify(&MESSAGE_1, &sig_1, &p); + + assert!(result.is_ok()); + assert!(result.unwrap()); + + let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let openssl_point = + EcPoint::from_bytes(&openssl_group, &public_key_uncompressed(&p)[..], &mut ctx) + .unwrap(); + let openssl_pkey = EcKey::from_public_key(&openssl_group, &openssl_point).unwrap(); + let openssl_skey = EcKey::from_private_components( + &openssl_group, + &BigNum::from_hex_str(PRIVATE_KEY).unwrap(), + &openssl_point, + ) + .unwrap(); + + let openssl_sig = EcdsaSig::sign(h.as_slice(), &openssl_skey).unwrap(); + let openssl_result = openssl_sig.verify(h.as_slice(), &openssl_pkey); + assert!(openssl_result.is_ok()); + assert!(openssl_result.unwrap()); + let mut temp_sig = Vec::new(); + temp_sig.extend(openssl_sig.r().to_vec()); + temp_sig.extend(openssl_sig.s().to_vec()); + + // secp256k1 expects normalized "s"'s. + // scheme.normalize_s(temp_sig.as_mut_slice()).unwrap(); + // k256 seems to be normalizing always now + let result = scheme.verify(&MESSAGE_1, temp_sig.as_slice(), &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + + let (p, s) = scheme.keypair(None).unwrap(); + match scheme.sign(&MESSAGE_1, &s) { + Ok(signed) => { + let result = scheme.verify(&MESSAGE_1, &signed, &p); + assert!(result.is_ok()); + assert!(result.unwrap()); + } + Err(er) => assert!(false, "{}", er), + } + } + Err(e) => assert!(false, "{}", e), + } + } +}