From eddb38aac4e1e90dfe09b7d200be4f4f25df5389 Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Tue, 13 Feb 2024 01:21:09 +0300 Subject: [PATCH] [refactor] #4174: Stop encoding X25519 keys as Ed25519 Signed-off-by: Daniil Polyakov --- configs/peer/executor.wasm | Bin 616142 -> 616086 bytes crypto/src/kex/mod.rs | 39 ++++++++--- crypto/src/kex/x25519.rs | 111 +++++++++++------------------- crypto/src/lib.rs | 8 +-- crypto/src/signature/ed25519.rs | 6 +- crypto/src/signature/secp256k1.rs | 8 +-- p2p/src/peer.rs | 64 ++++++++--------- 7 files changed, 112 insertions(+), 124 deletions(-) diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index e326dcecc29723c2a218ea214e89ab4466b26c91..e85a2591150ce7b3f5e5b1e7af301d80c20142d9 100644 GIT binary patch delta 56073 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

C%mU@_Gm=@9 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 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);