diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index e326dcecc29..e85a2591150 100644 Binary files a/configs/peer/executor.wasm and b/configs/peer/executor.wasm differ diff --git a/crypto/src/kex/mod.rs b/crypto/src/kex/mod.rs index 37e9696c9e7..db8e45bfb74 100644 --- a/crypto/src/kex/mod.rs +++ b/crypto/src/kex/mod.rs @@ -5,14 +5,22 @@ mod x25519; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + pub use x25519::X25519Sha256; -use crate::{Error, KeyGenOption, PrivateKey, PublicKey, SessionKey}; +use crate::{error::ParseError, KeyGenOption, SessionKey}; /// A Generic trait for key exchange schemes. Each scheme provides a way to generate keys and /// do a diffie-hellman computation pub trait KeyExchangeScheme { - /// Generate a new instance of the scheme + /// Public key used by the scheme. + type PublicKey: Send; + /// Private key used by the scheme. + type PrivateKey: Send; + + /// Generate a new instance of the scheme. fn new() -> Self; /// Create new keypairs. If @@ -21,20 +29,33 @@ pub trait KeyExchangeScheme { /// then used to seed the [`ChaChaRng`](rand_chacha::ChaChaRng) /// - `options` is [`FromPrivateKey`](KeyGenOption::FromPrivateKey), the corresponding public key is returned. This should be used for /// static Diffie-Hellman and loading a long-term key. - fn keypair(&self, options: KeyGenOption) -> (PublicKey, PrivateKey); + fn keypair( + &self, + options: KeyGenOption, + ) -> (Self::PublicKey, Self::PrivateKey); /// Compute the diffie-hellman shared secret. /// `local_private_key` is the key generated from calling `keypair` while /// `remote_public_key` is the key received from a different call to `keypair` from another party. + fn compute_shared_secret( + &self, + local_private_key: &Self::PrivateKey, + remote_public_key: &Self::PublicKey, + ) -> SessionKey; + + /// Get byte representation of a public key. + // + // TODO: Return `[u8; Self::PUBLIC_KEY_SIZE]` after https://github.com/rust-lang/rust/issues/76560 + fn encode_public_key(pk: &Self::PublicKey) -> &[u8]; + + /// Decode public key from byte representation. /// /// # Errors /// - /// Returns an error if the computation fails, i.e. remote key is invalid. - fn compute_shared_secret( - &self, - local_private_key: &PrivateKey, - remote_public_key: &PublicKey, - ) -> Result; + /// Any error during key decoding, e.g. wrong `bytes` length. + // + // TODO: Accept `[u8; Self::PUBLIC_KEY_SIZE]` after https://github.com/rust-lang/rust/issues/76560 + fn decode_public_key(bytes: Vec) -> Result; /// Size of the shared secret in bytes. const SHARED_SECRET_SIZE: usize; diff --git a/crypto/src/kex/x25519.rs b/crypto/src/kex/x25519.rs index d6c9b6e97f5..16207186d45 100644 --- a/crypto/src/kex/x25519.rs +++ b/crypto/src/kex/x25519.rs @@ -1,6 +1,5 @@ #[cfg(not(feature = "std"))] -use alloc::{borrow::ToOwned as _, boxed::Box}; -use core::borrow::Borrow as _; +use alloc::{format, vec::Vec}; use arrayref::array_ref; use iroha_primitives::const_vec::ConstVec; @@ -9,34 +8,34 @@ use rand::rngs::OsRng; use rand::SeedableRng; use rand_chacha::ChaChaRng; use sha2::Digest; -use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret}; +use x25519_dalek::{PublicKey, StaticSecret}; use zeroize::Zeroize; use super::KeyExchangeScheme; -use crate::{Error, KeyGenOption, ParseError, PrivateKey, PublicKey, SessionKey}; +use crate::{error::ParseError, KeyGenOption, SessionKey}; /// Implements the [`KeyExchangeScheme`] using X25519 key exchange and SHA256 hash function. #[derive(Copy, Clone)] pub struct X25519Sha256; impl KeyExchangeScheme for X25519Sha256 { + type PublicKey = PublicKey; + type PrivateKey = StaticSecret; + fn new() -> Self { Self } - /// # Note about implementation - /// - /// We encode the `X25519` public key as an [`Ed25519`](PublicKey::Ed25519) public key which is - /// a not so good idea, because we have to do extra computations and extra error handling. - /// - /// See #4174 for more details. - fn keypair(&self, mut option: KeyGenOption) -> (PublicKey, PrivateKey) { - let (pk, sk) = match option { + fn keypair( + &self, + mut option: KeyGenOption, + ) -> (Self::PublicKey, Self::PrivateKey) { + match option { #[cfg(feature = "rand")] KeyGenOption::Random => { let rng = OsRng; let sk = StaticSecret::random_from_rng(rng); - let pk = X25519PublicKey::from(&sk); + let pk = PublicKey::from(&sk); (pk, sk) } KeyGenOption::UseSeed(ref mut s) => { @@ -44,67 +43,40 @@ impl KeyExchangeScheme for X25519Sha256 { s.zeroize(); let rng = ChaChaRng::from_seed(*array_ref!(hash.as_slice(), 0, 32)); let sk = StaticSecret::random_from_rng(rng); - let pk = X25519PublicKey::from(&sk); + let pk = PublicKey::from(&sk); (pk, sk) } - KeyGenOption::FromPrivateKey(ref s) => { - let crate::PrivateKeyInner::Ed25519(s) = s.0.borrow() else { - panic!("Wrong private key type, expected `Ed25519`, got {s:?}") - }; - let sk = StaticSecret::from(*array_ref!(s.as_bytes(), 0, 32)); - let pk = X25519PublicKey::from(&sk); - (pk, sk) + KeyGenOption::FromPrivateKey(ref sk) => { + let pk = PublicKey::from(sk); + (pk, sk.clone()) } - }; - - let montgomery = curve25519_dalek::MontgomeryPoint(pk.to_bytes()); - // 0 here means the positive sign, but it doesn't matter, because in - // `compute_shared_secret()` we convert it back to Montgomery form losing the sign. - let edwards = montgomery - .to_edwards(0) - .expect("Montgomery to Edwards conversion failed"); - let edwards_compressed = edwards.compress(); - - ( - PublicKey(Box::new(crate::PublicKeyInner::Ed25519( - crate::ed25519::PublicKey::from_bytes(edwards_compressed.as_bytes()).expect( - "Ed25519 public key should be possible to create from X25519 public key", - ), - ))), - PrivateKey(Box::new(crate::PrivateKeyInner::Ed25519( - crate::ed25519::PrivateKey::from_bytes(sk.as_bytes()), - ))), - ) + } } fn compute_shared_secret( &self, - local_private_key: &PrivateKey, - remote_public_key: &PublicKey, - ) -> Result { - let crate::PrivateKeyInner::Ed25519(local_private_key) = local_private_key.0.borrow() - else { - panic!("Wrong private key type, expected `Ed25519`, got {local_private_key:?}") - }; - let crate::PublicKeyInner::Ed25519(remote_public_key) = remote_public_key.0.borrow() else { - panic!("Wrong public key type, expected `Ed25519`, got {remote_public_key:?}") - }; - + local_private_key: &Self::PrivateKey, + remote_public_key: &Self::PublicKey, + ) -> SessionKey { let sk = StaticSecret::from(*local_private_key.as_bytes()); - let pk_slice: &[u8; 32] = remote_public_key.as_bytes(); - let edwards_compressed = - curve25519_dalek::edwards::CompressedEdwardsY::from_slice(pk_slice) - .expect("Ed25519 public key has 32 bytes"); - let edwards = edwards_compressed.decompress().ok_or_else(|| { - ParseError("Invalid public key: failed to decompress edwards point".to_owned()) - })?; - let montgomery = edwards.to_montgomery(); - let pk = X25519PublicKey::from(montgomery.to_bytes()); - - let shared_secret = sk.diffie_hellman(&pk); + let shared_secret = sk.diffie_hellman(remote_public_key); let hash = sha2::Sha256::digest(shared_secret.as_bytes()); - Ok(SessionKey(ConstVec::new(hash.as_slice().to_vec()))) + SessionKey(ConstVec::new(hash.as_slice().to_vec())) + } + + fn encode_public_key(pk: &Self::PublicKey) -> &[u8] { + pk.as_bytes() + } + + fn decode_public_key(bytes: Vec) -> Result { + let bytes = <[u8; Self::PUBLIC_KEY_SIZE]>::try_from(bytes).map_err(|_| { + ParseError(format!( + "X25519 public key should be {} size long", + Self::PUBLIC_KEY_SIZE + )) + })?; + Ok(PublicKey::from(bytes)) } const SHARED_SECRET_SIZE: usize = 32; @@ -122,16 +94,11 @@ mod tests { let (public_key1, secret_key1) = scheme.keypair(KeyGenOption::Random); let (public_key2, secret_key2) = scheme.keypair(KeyGenOption::Random); - let shared_secret1 = scheme - .compute_shared_secret(&secret_key2, &public_key1) - .unwrap(); - let shared_secret2 = scheme - .compute_shared_secret(&secret_key1, &public_key2) - .unwrap(); + let shared_secret1 = scheme.compute_shared_secret(&secret_key2, &public_key1); + let shared_secret2 = scheme.compute_shared_secret(&secret_key1, &public_key2); assert_eq!(shared_secret1.payload(), shared_secret2.payload()); - let (public_key2, _secret_key1) = - scheme.keypair(KeyGenOption::FromPrivateKey(Box::new(secret_key1))); + let (public_key2, _secret_key1) = scheme.keypair(KeyGenOption::FromPrivateKey(secret_key1)); assert_eq!(public_key2, public_key1); } } diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index 0db94fcbb37..457874d8118 100755 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -113,14 +113,14 @@ impl FromStr for Algorithm { /// Options for key generation #[cfg(not(feature = "ffi_import"))] #[derive(Debug, Clone)] -pub enum KeyGenOption { +pub enum KeyGenOption { /// Use random number generator #[cfg(feature = "rand")] Random, /// Use seed UseSeed(Vec), /// Derive from private key - FromPrivateKey(Box), + FromPrivateKey(K), } ffi::ffi_item! { @@ -158,7 +158,7 @@ impl KeyGenConfiguration { /// Construct using private key with [`Ed25519`](Algorithm::Ed25519) algorithm #[must_use] - pub fn from_private_key(private_key: impl Into>) -> Self { + pub fn from_private_key(private_key: impl Into) -> Self { Self { key_gen_option: KeyGenOption::FromPrivateKey(private_key.into()), algorithm: Algorithm::default(), @@ -546,7 +546,7 @@ impl FromStr for PublicKey { impl From for PublicKey { fn from(private_key: PrivateKey) -> Self { let algorithm = private_key.algorithm(); - let key_gen_option = KeyGenOption::FromPrivateKey(Box::new(private_key)); + let key_gen_option = KeyGenOption::FromPrivateKey(private_key); let inner = match algorithm { Algorithm::Ed25519 => { diff --git a/crypto/src/signature/ed25519.rs b/crypto/src/signature/ed25519.rs index d7b4715d5ce..959eaf3bf48 100644 --- a/crypto/src/signature/ed25519.rs +++ b/crypto/src/signature/ed25519.rs @@ -93,7 +93,7 @@ mod test { #[test] fn ed25519_load_keys() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let (p1, s1) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(Box::new(secret))); + let (p1, s1) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(secret)); assert_eq!( PrivateKey(Box::new(crate::PrivateKeyInner::Ed25519(s1))), @@ -108,7 +108,7 @@ mod test { #[test] fn ed25519_verify() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let (p, _) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(Box::new(secret))); + let (p, _) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(secret)); Ed25519Sha512::verify(MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p).unwrap(); @@ -129,7 +129,7 @@ mod test { #[test] fn ed25519_sign() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let (p, s) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(Box::new(secret))); + let (p, s) = Ed25519Sha512::keypair(KeyGenOption::FromPrivateKey(secret)); let sig = Ed25519Sha512::sign(MESSAGE_1, &s); Ed25519Sha512::verify(MESSAGE_1, &sig, &p).unwrap(); diff --git a/crypto/src/signature/secp256k1.rs b/crypto/src/signature/secp256k1.rs index b0b50d7f878..bdfae36a66f 100644 --- a/crypto/src/signature/secp256k1.rs +++ b/crypto/src/signature/secp256k1.rs @@ -154,9 +154,9 @@ mod test { #[test] fn secp256k1_compatibility() { let secret = private_key(); - let (p, s) = EcdsaSecp256k1Sha256::keypair(KeyGenOption::FromPrivateKey(Box::new( + let (p, s) = EcdsaSecp256k1Sha256::keypair(KeyGenOption::FromPrivateKey( crate::PrivateKey(Box::new(crate::PrivateKeyInner::Secp256k1(secret))), - ))); + )); let _sk = secp256k1::SecretKey::from_slice(&s.to_bytes()).unwrap(); let _pk = secp256k1::PublicKey::from_slice(&p.to_sec1_bytes()).unwrap(); @@ -206,9 +206,9 @@ mod test { #[test] fn secp256k1_sign() { let secret = private_key(); - let (pk, sk) = EcdsaSecp256k1Sha256::keypair(KeyGenOption::FromPrivateKey(Box::new( + let (pk, sk) = EcdsaSecp256k1Sha256::keypair(KeyGenOption::FromPrivateKey( crate::PrivateKey(Box::new(crate::PrivateKeyInner::Secp256k1(secret))), - ))); + )); let sig = EcdsaSecp256k1Sha256::sign(MESSAGE_1, &sk); EcdsaSecp256k1Sha256::verify(MESSAGE_1, &sig, &pk).unwrap(); diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs index b7720d5661a..fd8551cb969 100644 --- a/p2p/src/peer.rs +++ b/p2p/src/peer.rs @@ -370,7 +370,7 @@ mod run { mod state { //! Module for peer stages. - use iroha_crypto::{KeyGenOption, KeyPair, PublicKey, Signature}; + use iroha_crypto::{KeyGenOption, KeyPair, Signature}; use iroha_primitives::addr::SocketAddr; use super::{cryptographer::Cryptographer, *}; @@ -416,13 +416,14 @@ mod state { key_pair, mut connection, }: Self, - ) -> Result, crate::Error> { + ) -> Result, crate::Error> { let key_exchange = K::new(); let (kx_local_pk, kx_local_sk) = key_exchange.keypair(KeyGenOption::Random); - let (algorithm, kx_local_pk_raw) = kx_local_pk.to_raw(); let write_half = &mut connection.write; garbage::write(write_half).await?; - write_half.write_all(&kx_local_pk_raw).await?; + write_half + .write_all(K::encode_public_key(&kx_local_pk)) + .await?; // Read server hello with node's public key let read_half = &mut connection.read; let kx_remote_pk = { @@ -430,9 +431,9 @@ mod state { // Then we have servers public key let mut key = vec![0_u8; 32]; let _ = read_half.read_exact(&mut key).await?; - PublicKey::from_raw(algorithm, &key).map_err(iroha_crypto::error::Error::from)? + K::decode_public_key(key).map_err(iroha_crypto::error::Error::from)? }; - let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk)?; + let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk); let cryptographer = Cryptographer::new(&shared_key); Ok(SendKey { peer_addr, @@ -461,22 +462,22 @@ mod state { mut connection, .. }: Self, - ) -> Result, crate::Error> { + ) -> Result, crate::Error> { let key_exchange = K::new(); let (kx_local_pk, kx_local_sk) = key_exchange.keypair(KeyGenOption::Random); - let (algorithm, kx_local_pk_raw) = kx_local_pk.to_raw(); + let kx_local_pk_raw = K::encode_public_key(&kx_local_pk); let read_half = &mut connection.read; let kx_remote_pk = { garbage::read(read_half).await?; // And then we have clients public key let mut key = vec![0_u8; 32]; let _ = read_half.read_exact(&mut key).await?; - PublicKey::from_raw(algorithm, &key).map_err(iroha_crypto::error::Error::from)? + K::decode_public_key(key).map_err(iroha_crypto::error::Error::from)? }; let write_half = &mut connection.write; garbage::write(write_half).await?; - write_half.write_all(&kx_local_pk_raw).await?; - let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk)?; + write_half.write_all(kx_local_pk_raw).await?; + let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk); let cryptographer = Cryptographer::new(&shared_key); Ok(SendKey { peer_addr, @@ -490,16 +491,16 @@ mod state { } /// Peer that needs to send key. - pub(super) struct SendKey { + pub(super) struct SendKey { peer_addr: SocketAddr, key_pair: KeyPair, - kx_local_pk: PublicKey, - kx_remote_pk: PublicKey, + kx_local_pk: K::PublicKey, + kx_remote_pk: K::PublicKey, connection: Connection, cryptographer: Cryptographer, } - impl SendKey { + impl SendKey { pub(super) async fn send_our_public_key( Self { peer_addr, @@ -509,10 +510,10 @@ mod state { mut connection, cryptographer, }: Self, - ) -> Result, crate::Error> { + ) -> Result, crate::Error> { let write_half = &mut connection.write; - let payload = create_payload(&kx_local_pk, &kx_remote_pk); + let payload = create_payload::(&kx_local_pk, &kx_remote_pk); let signature = Signature::new(&key_pair, &payload); let data = signature.encode(); @@ -535,15 +536,15 @@ mod state { } /// Peer that needs to get key. - pub struct GetKey { + pub struct GetKey { peer_addr: SocketAddr, connection: Connection, - kx_local_pk: PublicKey, - kx_remote_pk: PublicKey, + kx_local_pk: K::PublicKey, + kx_remote_pk: K::PublicKey, cryptographer: Cryptographer, } - impl GetKey { + impl GetKey { /// Read the peer's public key pub(super) async fn read_their_public_key( Self { @@ -565,7 +566,7 @@ mod state { let signature: Signature = DecodeAll::decode_all(&mut data.as_slice())?; // Swap order of keys since we are verifying for other peer order remote/local keys is reversed - let payload = create_payload(&kx_remote_pk, &kx_local_pk); + let payload = create_payload::(&kx_remote_pk, &kx_local_pk); signature.verify(&payload)?; let (remote_pub_key, _) = signature.into(); @@ -588,10 +589,9 @@ mod state { pub cryptographer: Cryptographer, } - fn create_payload(kx_local_pk: &PublicKey, kx_remote_pk: &PublicKey) -> Vec { - let mut payload = Vec::with_capacity(kx_local_pk.size_hint() + kx_remote_pk.size_hint()); - kx_local_pk.encode_to(&mut payload); - kx_remote_pk.encode_to(&mut payload); + fn create_payload(kx_local_pk: &K::PublicKey, kx_remote_pk: &K::PublicKey) -> Vec { + let mut payload = Vec::from(K::encode_public_key(kx_local_pk)); + payload.extend_from_slice(K::encode_public_key(kx_remote_pk)); payload } } @@ -633,10 +633,10 @@ mod handshake { } stage!(connect_to: Connecting => ConnectedTo); - stage!(send_client_hello::: ConnectedTo => SendKey); - stage!(read_client_hello::: ConnectedFrom => SendKey); - stage!(send_our_public_key: SendKey => GetKey); - stage!(read_their_public_key: GetKey => Ready); + stage!(send_client_hello::: ConnectedTo => SendKey); + stage!(read_client_hello::: ConnectedFrom => SendKey); + stage!(send_our_public_key: SendKey => GetKey); + stage!(read_their_public_key: GetKey => Ready); #[async_trait] pub(super) trait Handshake { @@ -666,8 +666,8 @@ mod handshake { }; } - impl_handshake!(base_case GetKey); - impl_handshake!(SendKey); + impl_handshake!(base_case GetKey); + impl_handshake!(SendKey); impl_handshake!(ConnectedFrom); impl_handshake!(ConnectedTo); impl_handshake!(Connecting);