Skip to content

Commit

Permalink
[refactor]: stronger types in KeyGenOption, renames and fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Dmitry Balashov <43530070+0x009922@users.noreply.github.com>
  • Loading branch information
0x009922 committed Mar 6, 2024
1 parent 125093e commit e79f617
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 239 deletions.
10 changes: 5 additions & 5 deletions crypto/src/kex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use alloc::vec::Vec;

pub use x25519::X25519Sha256;

use crate::{error::ParseError, KeyGenOption, SessionKey};
use crate::{error::ParseError, KeyPairGenOption, SessionKey};

/// A Generic trait for key exchange schemes. Each scheme provides a way to generate keys and
/// do a diffie-hellman computation
Expand All @@ -24,14 +24,14 @@ pub trait KeyExchangeScheme {
fn new() -> Self;

/// Create new keypairs. If
/// - `options` is [`Random`](KeyGenOption::Random), the keys are generated ephemerally from the [`OsRng`](rand::rngs::OsRng)
/// - `options` is [`UseSeed`](KeyGenOption::UseSeed), the keys are generated ephemerally from the sha256 hash of the seed which is
/// - `options` is [`Random`](KeyPairGenOption::Random), the keys are generated ephemerally from the [`OsRng`](rand::rngs::OsRng)
/// - `options` is [`UseSeed`](KeyPairGenOption::UseSeed), the keys are generated ephemerally from the sha256 hash of the seed which is
/// 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
/// - `options` is [`FromPrivateKey`](KeyPairGenOption::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<Self::PrivateKey>,
options: KeyPairGenOption<Self::PrivateKey>,
) -> (Self::PublicKey, Self::PrivateKey);

/// Compute the diffie-hellman shared secret.
Expand Down
17 changes: 9 additions & 8 deletions crypto/src/kex/x25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use x25519_dalek::{PublicKey, StaticSecret};
use zeroize::Zeroize;

use super::KeyExchangeScheme;
use crate::{error::ParseError, KeyGenOption, SessionKey};
use crate::{error::ParseError, KeyPairGenOption, SessionKey};

/// Implements the [`KeyExchangeScheme`] using X25519 key exchange and SHA256 hash function.
#[derive(Copy, Clone)]
Expand All @@ -28,25 +28,25 @@ impl KeyExchangeScheme for X25519Sha256 {

fn keypair(
&self,
mut option: KeyGenOption<Self::PrivateKey>,
mut option: KeyPairGenOption<Self::PrivateKey>,
) -> (Self::PublicKey, Self::PrivateKey) {
match option {
#[cfg(feature = "rand")]
KeyGenOption::Random => {
KeyPairGenOption::Random => {
let rng = OsRng;
let sk = StaticSecret::random_from_rng(rng);
let pk = PublicKey::from(&sk);
(pk, sk)
}
KeyGenOption::UseSeed(ref mut s) => {
KeyPairGenOption::UseSeed(ref mut s) => {
let hash = sha2::Sha256::digest(s.as_slice());
s.zeroize();
let rng = ChaChaRng::from_seed(*array_ref!(hash.as_slice(), 0, 32));
let sk = StaticSecret::random_from_rng(rng);
let pk = PublicKey::from(&sk);
(pk, sk)
}
KeyGenOption::FromPrivateKey(ref sk) => {
KeyPairGenOption::FromPrivateKey(ref sk) => {
let pk = PublicKey::from(sk);
(pk, sk.clone())
}
Expand Down Expand Up @@ -91,14 +91,15 @@ mod tests {
#[test]
fn key_exchange() {
let scheme = X25519Sha256::new();
let (public_key1, secret_key1) = scheme.keypair(KeyGenOption::Random);
let (public_key1, secret_key1) = scheme.keypair(KeyPairGenOption::Random);

let (public_key2, secret_key2) = scheme.keypair(KeyGenOption::Random);
let (public_key2, secret_key2) = scheme.keypair(KeyPairGenOption::Random);
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(secret_key1));
let (public_key2, _secret_key1) =
scheme.keypair(KeyPairGenOption::FromPrivateKey(secret_key1));
assert_eq!(public_key2, public_key1);
}
}
206 changes: 99 additions & 107 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,24 @@ impl FromStr for Algorithm {
}
}

/// Options for key generation
#[cfg(not(feature = "ffi_import"))]
#[derive(Debug, Clone)]
pub enum KeyGenOption<K = PrivateKey> {
/// Needed since it couldn't be represented in FFI
#[derive(Clone)]
#[cfg_attr(not(feature = "ffi_import"), derive(Debug))]
enum KeyPairGenConfigInner {
/// Use random number generator
#[cfg(feature = "rand")]
Random { algorithm: Algorithm },
/// Use seed
UseSeed { seed: Vec<u8>, algorithm: Algorithm },
/// Derive from a private key
FromPrivateKey(PrivateKey),
}

/// While [`KeyPairGenConfig`] is for external users, this structure is for internal use
/// to pass it to a specific algorithm.
/// Parameter `K` is intended to be set as a `PrivateKey` type of that specific algorithm.
#[derive(Debug)]
pub enum KeyPairGenOption<K> {
/// Use random number generator
#[cfg(feature = "rand")]
Random,
Expand All @@ -124,85 +138,46 @@ pub enum KeyGenOption<K = PrivateKey> {
FromPrivateKey(K),
}

mod config_algorithm_state {
use crate::Algorithm;

#[derive(Copy, Clone, Debug)]
pub struct Default;

impl From<Default> for Algorithm {
fn from(_: Default) -> Self {
use std::default::Default;
Self::default()
}
}

#[derive(Copy, Clone, Debug)]
pub struct Set(pub Algorithm);

impl From<Set> for Algorithm {
fn from(Set(value): Set) -> Self {
value
}
}
}

ffi::ffi_item! {
/// Configuration of key generation
/// Configuration for key generation
#[derive(Clone)]
#[cfg_attr(not(feature="ffi_import"), derive(Debug))]
pub struct KeyGenConfiguration<A> {
/// Options
key_gen_option: KeyGenOption,
/// Algorithm
algorithm: A,
#[cfg_attr(not(feature = "ffi_import"), derive(Debug))]
pub struct KeyPairGenConfig{
inner: KeyPairGenConfigInner
}
}

#[ffi_impl_opaque]
impl KeyGenConfiguration<config_algorithm_state::Default> {
/// Construct using random number generation with [`Ed25519`](Algorithm::Ed25519) algorithm
impl KeyPairGenConfig {
/// Generate using random number generation with a specified algorithm
#[cfg(feature = "rand")]
#[must_use]
pub fn from_random() -> Self {
pub fn from_random(algorithm: Algorithm) -> Self {
Self {
key_gen_option: KeyGenOption::Random,
algorithm: config_algorithm_state::Default,
inner: KeyPairGenConfigInner::Random { algorithm },
}
}

/// Construct using seed with [`Ed25519`](Algorithm::Ed25519) algorithm
/// Construct using a seed with a specified algorithm
#[must_use]
pub fn from_seed(seed: Vec<u8>) -> Self {
pub fn from_seed(seed: Vec<u8>, algorithm: Algorithm) -> Self {
Self {
key_gen_option: KeyGenOption::UseSeed(seed),
algorithm: config_algorithm_state::Default,
inner: KeyPairGenConfigInner::UseSeed { seed, algorithm },
}
}

/// With algorithm
/// Construct the pair from a private key. Infers the algorithm from the key's one.
#[must_use]
pub fn with_algorithm(
self,
algorithm: Algorithm,
) -> KeyGenConfiguration<config_algorithm_state::Set> {
KeyGenConfiguration {
key_gen_option: self.key_gen_option,
algorithm: config_algorithm_state::Set(algorithm),
pub fn from_private_key(key: PrivateKey) -> Self {
Self {
inner: KeyPairGenConfigInner::FromPrivateKey(key),
}
}
}

impl KeyGenConfiguration<config_algorithm_state::Set> {
/// Construct using private key with [`Ed25519`](Algorithm::Ed25519) algorithm
/// Generates a key pair
#[must_use]
pub fn from_private_key(private_key: impl Into<PrivateKey>) -> Self {
let key = private_key.into();
let algorithm = key.algorithm();
Self {
key_gen_option: KeyGenOption::FromPrivateKey(key),
algorithm: config_algorithm_state::Set(algorithm),
}
pub fn generate(self) -> KeyPair {
KeyPair::generate_with_config(self)
}
}

Expand All @@ -220,12 +195,10 @@ ffi::ffi_item! {
}

impl KeyPair {
/// Generates a pair of Public and Private key with [`Algorithm::default()`] selected as generation algorithm.
/// Generates a random key pair using [`Algorithm::default()`]
#[cfg(feature = "rand")]
pub fn generate() -> Self {
Self::generate_with_configuration(
KeyGenConfiguration::from_random().with_algorithm(Algorithm::Ed25519),
)
Self::generate_with_config(KeyPairGenConfig::from_random(Algorithm::default()))
}
}

Expand All @@ -239,7 +212,7 @@ impl KeyPair {
/// Construct a [`KeyPair`]
///
/// # Errors
/// If public and private key don't match, i.e. if they don't make a pair
/// If public and private keys don't match, i.e. if they don't make a pair
pub fn new(public_key: PublicKey, private_key: PrivateKey) -> Result<Self, Error> {
let algorithm = private_key.algorithm();

Expand All @@ -257,22 +230,42 @@ impl KeyPair {
})
}

/// Generates a pair of Public and Private key with the corresponding [`KeyGenConfiguration`].
pub fn generate_with_configuration<A: Into<Algorithm>>(
KeyGenConfiguration {
key_gen_option,
algorithm,
}: KeyGenConfiguration<A>,
) -> Self {
let algorithm: Algorithm = algorithm.into();
match algorithm {
Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512::keypair(key_gen_option).into(),
Algorithm::Secp256k1 => {
signature::secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option).into()
}
Algorithm::BlsNormal => signature::bls::BlsNormal::keypair(key_gen_option).into(),
Algorithm::BlsSmall => signature::bls::BlsSmall::keypair(key_gen_option).into(),
/// Generates a pair of Public and Private key with the corresponding [`KeyPairGenOption`].
pub fn generate_with_config(config: KeyPairGenConfig) -> Self {
use KeyPairGenConfigInner;
use KeyPairGenOption;

macro_rules! with_algorithm_variations {
($(($alg:ident, $alg_mod:path)),+) => {
match config.inner {
#[cfg(feature = "rand")]
KeyPairGenConfigInner::Random { algorithm } => {
match algorithm {
$(Algorithm::$alg => <$alg_mod>::keypair(KeyPairGenOption::Random).into()),*
}
},
KeyPairGenConfigInner::UseSeed { seed, algorithm } => {
match algorithm {
$(Algorithm::$alg => <$alg_mod>::keypair(KeyPairGenOption::UseSeed(seed)).into()),*
}
}
KeyPairGenConfigInner::FromPrivateKey(key) => {
match *key.0 {
$(
PrivateKeyInner::$alg(secret) => <$alg_mod>::keypair(KeyPairGenOption::FromPrivateKey(secret)).into(),
)*
}
}
}
};
}

with_algorithm_variations!(
(Ed25519, ed25519::Ed25519Sha512),
(Secp256k1, secp256k1::EcdsaSecp256k1Sha256),
(BlsNormal, bls::BlsNormal),
(BlsSmall, bls::BlsSmall)
)
}

/// Get [`PublicKey`] and [`PrivateKey`] contained in the [`KeyPair`].
Expand Down Expand Up @@ -597,24 +590,27 @@ impl FromStr for PublicKey {
#[cfg(not(feature = "ffi_import"))]
impl From<PrivateKey> for PublicKey {
fn from(private_key: PrivateKey) -> Self {
let algorithm = private_key.algorithm();
let key_gen_option = KeyGenOption::FromPrivateKey(private_key);

let inner = match algorithm {
Algorithm::Ed25519 => {
PublicKeyInner::Ed25519(ed25519::Ed25519Sha512::keypair(key_gen_option).0)
}
Algorithm::Secp256k1 => PublicKeyInner::Secp256k1(
secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option).0,
),
Algorithm::BlsNormal => {
PublicKeyInner::BlsNormal(bls::BlsNormal::keypair(key_gen_option).0)
}
Algorithm::BlsSmall => {
PublicKeyInner::BlsSmall(bls::BlsSmall::keypair(key_gen_option).0)
macro_rules! with_algorithm_variations {
($private_inner:expr, $(($alg:ident, $alg_mod:path)),+) => {
match $private_inner {
$(
PrivateKeyInner::$alg(secret) => {
PublicKeyInner::$alg(<$alg_mod>::keypair(KeyPairGenOption::FromPrivateKey(secret)).0)
}
)*
}
}
};
PublicKey(Box::new(inner))
}

let inner = with_algorithm_variations!(
*private_key.0,
(Ed25519, ed25519::Ed25519Sha512),
(Secp256k1, secp256k1::EcdsaSecp256k1Sha256),
(BlsNormal, bls::BlsNormal),
(BlsSmall, bls::BlsSmall)
);

Self(Box::new(inner))
}
}

Expand Down Expand Up @@ -869,7 +865,7 @@ mod ffi {

#[cfg(any(feature = "ffi_export", feature = "ffi_import"))]
iroha_ffi::handles! {
KeyGenConfiguration,
KeyPairGenConfig,
PublicKey,
PrivateKey,
KeyPair,
Expand All @@ -880,8 +876,8 @@ mod ffi {
iroha_ffi::decl_ffi_fns! { link_prefix="iroha_crypto" Drop, Clone, Eq, Ord, Default }
#[cfg(all(feature = "ffi_export", not(feature = "ffi_import")))]
iroha_ffi::def_ffi_fns! { link_prefix="iroha_crypto"
Drop: { KeyGenConfiguration, PublicKey, PrivateKey, KeyPair, Signature },
Clone: { KeyGenConfiguration, PublicKey, PrivateKey, KeyPair, Signature },
Drop: { KeyPairGenConfig, PublicKey, PrivateKey, KeyPair, Signature },
Clone: { KeyPairGenConfig, PublicKey, PrivateKey, KeyPair, Signature },
Eq: { PublicKey, PrivateKey, KeyPair, Signature },
Ord: { PublicKey, Signature },
}
Expand Down Expand Up @@ -941,9 +937,7 @@ mod tests {
Algorithm::BlsNormal,
Algorithm::BlsSmall,
] {
let key_pair = KeyPair::generate_with_configuration(
KeyGenConfiguration::from_random().with_algorithm(algorithm),
);
let key_pair = KeyPairGenConfig::from_random(algorithm).generate();

assert_eq!(
key_pair,
Expand Down Expand Up @@ -1002,9 +996,7 @@ mod tests {
Algorithm::BlsNormal,
Algorithm::BlsSmall,
] {
let key_pair = KeyPair::generate_with_configuration(
KeyGenConfiguration::from_random().with_algorithm(algorithm),
);
let key_pair = KeyPairGenConfig::from_random(algorithm).generate();
let (public_key, _) = key_pair.into();

let encoded_public_key = public_key.encode();
Expand Down
Loading

0 comments on commit e79f617

Please sign in to comment.