From 2375b22555efad033291d28302b32f1373d50540 Mon Sep 17 00:00:00 2001 From: Georges Palauqui Date: Tue, 29 Oct 2024 11:18:22 +0100 Subject: [PATCH] noise_sv2: is a optional `no_std` crate, `std` by default - std::ptr -> core::ptr - std::boxed::Box -> alloc::boxed::Box - std::vec::Vec -> alloc::vec::Vec - std::string::{String, ToString} -> alloc::string::{String, ToString} - std::convert::TryInto -> core::convert::TryInto - std::fmt::{Debug, Formatter, Result} -> core::fmt::{Debug, Formatter, Result} - std::time::Duration -> core::time::Duration To have a `no-std` version, the `--no-default-features` must be used public API changed to be able to be `no_std` (delegate the choice of the Ramdom Number Generator and the current System Time to the caller, instead of assuming using the ones from `std`) : - `Initiator::new()`, `Initiator::from_raw_k()`, `Initiator::without_pk()`, `Responder::new()`, `Responder::from_authority_kp()` and `Responder::generate_key()` take an additional argument implementing rand::Rng + ?Sized - `Responder::step_1()` and `SignatureNoiseMessage::sign()` take an additional argument implementing rand::Rng + rand::CryptoRng - `Initiator::step_2()`, `Responder::step_1()`, `Responder::step_2()` and `SignatureNoiseMessage::verify()` take an additional argument for the current system time epoch --- protocols/v2/noise-sv2/Cargo.toml | 11 +++-- protocols/v2/noise-sv2/examples/handshake.rs | 33 +++++++++---- protocols/v2/noise-sv2/src/cipher_state.rs | 2 +- protocols/v2/noise-sv2/src/error.rs | 2 + protocols/v2/noise-sv2/src/handshake.rs | 15 +++--- protocols/v2/noise-sv2/src/initiator.rs | 28 +++++++---- protocols/v2/noise-sv2/src/lib.rs | 9 +++- protocols/v2/noise-sv2/src/responder.rs | 46 +++++++++++-------- .../v2/noise-sv2/src/signature_message.rs | 23 ++++++---- protocols/v2/noise-sv2/src/test.rs | 20 ++++++-- utils/key-utils/src/main.rs | 8 ++-- 11 files changed, 132 insertions(+), 65 deletions(-) diff --git a/protocols/v2/noise-sv2/Cargo.toml b/protocols/v2/noise-sv2/Cargo.toml index 2bcb8ba6d0..8eef4a1034 100644 --- a/protocols/v2/noise-sv2/Cargo.toml +++ b/protocols/v2/noise-sv2/Cargo.toml @@ -12,16 +12,21 @@ homepage = "https://stratumprotocol.org" keywords = ["stratum", "mining", "bitcoin", "protocol"] [dependencies] -secp256k1 = { version = "0.28.2", default-features = false, features =["hashes", "alloc","rand","rand-std"] } -rand = {version = "0.8.5", default-features = false, features = ["std","std_rng"] } +secp256k1 = { version = "0.28.2", default-features = false, features = ["hashes", "alloc", "rand"] } +rand = {version = "0.8.5", default-features = false } aes-gcm = "0.10.2" chacha20poly1305 = "0.10.1" -rand_chacha = "0.3.1" +rand_chacha = { version = "0.3.1", default-features = false } const_sv2 = { version = "^2.0.0", path = "../../../protocols/v2/const-sv2"} +[features] +default = ["std"] +std = ["rand/std", "rand/std_rng", "rand_chacha/std", "secp256k1/rand-std"] + [dev-dependencies] quickcheck = "1.0.3" quickcheck_macros = "1" +rand = {version = "0.8.5", default-features = false, features = ["std", "std_rng"] } [package.metadata.docs.rs] all-features = true \ No newline at end of file diff --git a/protocols/v2/noise-sv2/examples/handshake.rs b/protocols/v2/noise-sv2/examples/handshake.rs index d3d6a22645..bf3a9bc4ff 100644 --- a/protocols/v2/noise-sv2/examples/handshake.rs +++ b/protocols/v2/noise-sv2/examples/handshake.rs @@ -25,35 +25,52 @@ const PARITY: Parity = Parity::Even; const RESPONDER_CERT_VALIDITY: u32 = 3600; // Generates a secp256k1 public/private key pair for the responder. -fn generate_key() -> Keypair { +fn generate_key(rng: &mut R) -> Keypair { let secp = Secp256k1::new(); - let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret_key, _) = secp.generate_keypair(rng); let kp = Keypair::from_secret_key(&secp, &secret_key); if kp.x_only_public_key().1 == PARITY { kp } else { - generate_key() + generate_key(rng) } } fn main() { let mut secret_message = "Ciao, Mondo!".as_bytes().to_vec(); - let responder_key_pair = generate_key(); + let responder_key_pair = generate_key(&mut rand::thread_rng()); - let mut initiator = Initiator::new(Some(responder_key_pair.public_key().into())); - let mut responder = Responder::new(responder_key_pair, RESPONDER_CERT_VALIDITY); + let mut initiator = Initiator::new( + Some(responder_key_pair.public_key().into()), + &mut rand::thread_rng(), + ); + let mut responder = Responder::new( + responder_key_pair, + RESPONDER_CERT_VALIDITY, + &mut rand::thread_rng(), + ); let first_message = initiator .step_0() .expect("Initiator failed first step of handshake"); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + let (second_message, mut responder_state) = responder - .step_1(first_message) + .step_1(first_message, now, &mut rand::thread_rng()) .expect("Responder failed second step of handshake"); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + let mut initiator_state = initiator - .step_2(second_message) + .step_2(second_message, now) .expect("Initiator failed third step of handshake"); initiator_state diff --git a/protocols/v2/noise-sv2/src/cipher_state.rs b/protocols/v2/noise-sv2/src/cipher_state.rs index dc324328ff..559a681dd6 100644 --- a/protocols/v2/noise-sv2/src/cipher_state.rs +++ b/protocols/v2/noise-sv2/src/cipher_state.rs @@ -33,7 +33,7 @@ // within the Noise protocol, ensuring secure data handling, key management, and nonce tracking // throughout the communication session. -use std::ptr; +use core::ptr; use crate::aed_cipher::AeadCipher; use aes_gcm::Aes256Gcm; diff --git a/protocols/v2/noise-sv2/src/error.rs b/protocols/v2/noise-sv2/src/error.rs index 5e5be3fff0..128d476338 100644 --- a/protocols/v2/noise-sv2/src/error.rs +++ b/protocols/v2/noise-sv2/src/error.rs @@ -2,6 +2,8 @@ // // Defines error types and utilities for handling errors in the `noise_sv2` module. +use alloc::vec::Vec; + use aes_gcm::Error as AesGcm; /// Noise protocol error handling. diff --git a/protocols/v2/noise-sv2/src/handshake.rs b/protocols/v2/noise-sv2/src/handshake.rs index 0d71fde445..ef16692523 100644 --- a/protocols/v2/noise-sv2/src/handshake.rs +++ b/protocols/v2/noise-sv2/src/handshake.rs @@ -30,6 +30,8 @@ // mandatory for communication across external networks (e.g., between a local mining proxy and a // remote pool). +use alloc::{string::String, vec::Vec}; + use crate::{aed_cipher::AeadCipher, cipher_state::CipherState, NOISE_HASHED_PROTOCOL_NAME_CHACHA}; use chacha20poly1305::ChaCha20Poly1305; use secp256k1::{ @@ -99,14 +101,14 @@ pub trait HandshakeOp: CipherState { // Generates a fresh key pair, consisting of a secret key and a corresponding public key, // using the [`Secp256k1`] elliptic curve. If the generated public key does not match the // expected parity, a new key pair is generated to ensure consistency. - fn generate_key() -> Keypair { + fn generate_key(rng: &mut R) -> Keypair { let secp = Secp256k1::new(); - let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret_key, _) = secp.generate_keypair(rng); let kp = Keypair::from_secret_key(&secp, &secret_key); if kp.x_only_public_key().1 == crate::PARITY { kp } else { - Self::generate_key() + Self::generate_key(rng) } } @@ -274,10 +276,11 @@ pub trait HandshakeOp: CipherState { #[cfg(test)] mod test { use super::*; + use alloc::string::ToString; + use core::convert::TryInto; use quickcheck::{Arbitrary, TestResult}; use quickcheck_macros; use secp256k1::{ecdh::SharedSecret, SecretKey, XOnlyPublicKey}; - use std::convert::TryInto; struct TestHandShake { k: Option<[u8; 32]>, @@ -466,8 +469,8 @@ mod test { #[test] fn test_ecdh() { - let key_pair_1 = TestHandShake::generate_key(); - let key_pair_2 = TestHandShake::generate_key(); + let key_pair_1 = TestHandShake::generate_key(&mut rand::thread_rng()); + let key_pair_2 = TestHandShake::generate_key(&mut rand::thread_rng()); let secret_1 = key_pair_1.secret_bytes(); let secret_2 = key_pair_2.secret_bytes(); diff --git a/protocols/v2/noise-sv2/src/initiator.rs b/protocols/v2/noise-sv2/src/initiator.rs index 24a3423f35..8b99d43159 100644 --- a/protocols/v2/noise-sv2/src/initiator.rs +++ b/protocols/v2/noise-sv2/src/initiator.rs @@ -34,7 +34,11 @@ // The [`Drop`] trait is implemented to automatically trigger secure erasure when the [`Initiator`] // instance goes out of scope, preventing potential misuse or leakage of cryptographic material. -use std::{convert::TryInto, ptr}; +use alloc::{ + boxed::Box, + string::{String, ToString}, +}; +use core::{convert::TryInto, ptr}; use crate::{ cipher_state::{Cipher, CipherState, GenericCipher}, @@ -93,8 +97,8 @@ pub struct Initiator { c2: Option, } -impl std::fmt::Debug for Initiator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Debug for Initiator { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Initiator").finish() } } @@ -164,14 +168,14 @@ impl Initiator { /// If the responder public key is provided, the initiator uses this key to authenticate the /// responder during the handshake. The initial initiator state is instantiated with the /// ephemeral key pair and handshake hash. - pub fn new(pk: Option) -> Box { + pub fn new(pk: Option, rng: &mut R) -> Box { let mut self_ = Self { handshake_cipher: None, k: None, n: 0, ck: [0; 32], h: [0; 32], - e: Self::generate_key(), + e: Self::generate_key(rng), responder_authority_pk: pk, c1: None, c2: None, @@ -187,10 +191,13 @@ impl Initiator { /// valid [`XOnlyPublicKey`], an [`Error::InvalidRawPublicKey`] error is returned. /// /// Typically used when the initiator is aware of the responder's public key in advance. - pub fn from_raw_k(key: [u8; 32]) -> Result, Error> { + pub fn from_raw_k( + key: [u8; 32], + rng: &mut R, + ) -> Result, Error> { let pk = secp256k1::XOnlyPublicKey::from_slice(&key).map_err(|_| Error::InvalidRawPublicKey)?; - Ok(Self::new(Some(pk))) + Ok(Self::new(Some(pk), rng)) } /// Creates a new [`Initiator`] without requiring the responder's authority public key. @@ -198,8 +205,8 @@ impl Initiator { /// for use when both the initiator and responder are within the same network. In this case, /// the initiator does not validate the responder's static key from a certificate. However, /// the connection remains encrypted. - pub fn without_pk() -> Result, Error> { - Ok(Self::new(None)) + pub fn without_pk(rng: &mut R) -> Result, Error> { + Ok(Self::new(None, rng)) } /// Executes the initial step of the Noise NX protocol handshake. @@ -244,6 +251,7 @@ impl Initiator { pub fn step_2( &mut self, message: [u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE], + now: u32, ) -> Result { // 2. interprets first 64 bytes as ElligatorSwift encoding of x-coordinate of public key // from this is derived the 32-bytes remote ephemeral public key `re.public_key` @@ -308,7 +316,7 @@ impl Initiator { .0 .serialize(); let rs_pk_xonly = XOnlyPublicKey::from_slice(&rs_pub_key).unwrap(); - if signature_message.verify(&rs_pk_xonly, &self.responder_authority_pk) { + if signature_message.verify(&rs_pk_xonly, &self.responder_authority_pk, now) { let (temp_k1, temp_k2) = Self::hkdf_2(self.get_ck(), &[]); let c1 = ChaCha20Poly1305::new(&temp_k1.into()); let c2 = ChaCha20Poly1305::new(&temp_k2.into()); diff --git a/protocols/v2/noise-sv2/src/lib.rs b/protocols/v2/noise-sv2/src/lib.rs index 46316b2edf..9bbd766912 100644 --- a/protocols/v2/noise-sv2/src/lib.rs +++ b/protocols/v2/noise-sv2/src/lib.rs @@ -32,6 +32,11 @@ //! used to authenticate messages and validate the identities of the Sv2 roles, ensuring that //! critical messages like job templates and share submissions originate from legitimate sources. +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] + +#[macro_use] +extern crate alloc; + use aes_gcm::aead::Buffer; pub use aes_gcm::aead::Error as AeadError; use cipher_state::GenericCipher; @@ -66,8 +71,8 @@ pub struct NoiseCodec { decryptor: GenericCipher, } -impl std::fmt::Debug for NoiseCodec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Debug for NoiseCodec { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("NoiseCodec").finish() } } diff --git a/protocols/v2/noise-sv2/src/responder.rs b/protocols/v2/noise-sv2/src/responder.rs index 7fd6dca722..dc86528e2b 100644 --- a/protocols/v2/noise-sv2/src/responder.rs +++ b/protocols/v2/noise-sv2/src/responder.rs @@ -34,7 +34,7 @@ // The [`Drop`] trait is implemented to automatically trigger secure erasure when the [`Responder`] // instance goes out of scope, preventing potential misuse or leakage of cryptographic material. -use std::{ptr, time::Duration}; +use core::{ptr, time::Duration}; use crate::{ cipher_state::{Cipher, CipherState, GenericCipher}, @@ -44,6 +44,11 @@ use crate::{ NoiseCodec, }; use aes_gcm::KeyInit; +use alloc::{ + boxed::Box, + string::{String, ToString}, + vec::Vec, +}; use chacha20poly1305::ChaCha20Poly1305; use const_sv2::{ ELLSWIFT_ENCODING_SIZE, ENCRYPTED_ELLSWIFT_ENCODING_SIZE, @@ -97,8 +102,8 @@ pub struct Responder { cert_validity: u32, } -impl std::fmt::Debug for Responder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Debug for Responder { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Responder").finish() } } @@ -170,15 +175,15 @@ impl Responder { /// Constructs a new [`Responder`] with the necessary cryptographic state for the Noise NX protocol /// handshake. It generates ephemeral and static key pairs for the responder and prepares the /// handshake state. The authority keypair and certificate validity period are also configured. - pub fn new(a: Keypair, cert_validity: u32) -> Box { + pub fn new(a: Keypair, cert_validity: u32, rng: &mut R) -> Box { let mut self_ = Self { handshake_cipher: None, k: None, n: 0, ck: [0; 32], h: [0; 32], - e: Self::generate_key(), - s: Self::generate_key(), + e: Self::generate_key(rng), + s: Self::generate_key(rng), a, c1: None, c2: None, @@ -194,17 +199,18 @@ impl Responder { /// the responder's authority credentials. It verifies that the provided public key matches the /// corresponding private key, ensuring the authenticity of the authority key pair. The /// certificate validity duration is also set here. Fails if the key pair is mismatched. - pub fn from_authority_kp( + pub fn from_authority_kp( public: &[u8; 32], private: &[u8; 32], cert_validity: Duration, + rng: &mut R, ) -> Result, Error> { let secp = Secp256k1::new(); let secret = SecretKey::from_slice(private).map_err(|_| Error::InvalidRawPrivateKey)?; let kp = Keypair::from_secret_key(&secp, &secret); let pub_ = kp.x_only_public_key().0.serialize(); if public == &pub_[..] { - Ok(Self::new(kp, cert_validity.as_secs() as u32)) + Ok(Self::new(kp, cert_validity.as_secs() as u32, rng)) } else { Err(Error::InvalidRawPublicKey) } @@ -225,9 +231,11 @@ impl Responder { /// /// On failure, the method returns an error if there is an issue during encryption, decryption, /// or any other step of the handshake process. - pub fn step_1( + pub fn step_1( &mut self, elligatorswift_theirs_ephemeral_serialized: [u8; ELLSWIFT_ENCODING_SIZE], + now: u32, + rng: &mut R, ) -> Result<([u8; INITIATOR_EXPECTED_HANDSHAKE_MESSAGE_SIZE], NoiseCodec), aes_gcm::Error> { // 4.5.1.2 Responder Self::mix_hash(self, &elligatorswift_theirs_ephemeral_serialized[..]); @@ -286,13 +294,9 @@ impl Responder { Self::mix_key(self, &ecdh_static[..]); // 7. appends `EncryptAndHash(SIGNATURE_NOISE_MESSAGE)` to the buffer - let valid_from = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(); - let not_valid_after = valid_from as u32 + self.cert_validity; - let signature_noise_message = - self.get_signature(VERSION, valid_from as u32, not_valid_after); + let valid_from = now; + let not_valid_after = now + self.cert_validity; + let signature_noise_message = self.get_signature(VERSION, valid_from, not_valid_after, rng); let mut signature_part = Vec::with_capacity(ENCRYPTED_SIGNATURE_NOISE_MESSAGE_SIZE); signature_part.extend_from_slice(&signature_noise_message[..]); Self::encrypt_and_hash(self, &mut signature_part)?; @@ -328,7 +332,13 @@ impl Responder { // certificate validity period, and a cryptographic signature. The signature is created using // the responder's static public key and authority keypair, ensuring that the responder's // identity and certificate validity are cryptographically verifiable. - fn get_signature(&self, version: u16, valid_from: u32, not_valid_after: u32) -> [u8; 74] { + fn get_signature( + &self, + version: u16, + valid_from: u32, + not_valid_after: u32, + rng: &mut R, + ) -> [u8; 74] { let mut ret = [0; 74]; let version = version.to_le_bytes(); let valid_from = valid_from.to_le_bytes(); @@ -343,7 +353,7 @@ impl Responder { ret[7] = not_valid_after[1]; ret[8] = not_valid_after[2]; ret[9] = not_valid_after[3]; - SignatureNoiseMessage::sign(&mut ret, &self.s.x_only_public_key().0, &self.a); + SignatureNoiseMessage::sign(&mut ret, &self.s.x_only_public_key().0, &self.a, rng); ret } diff --git a/protocols/v2/noise-sv2/src/signature_message.rs b/protocols/v2/noise-sv2/src/signature_message.rs index df997f7ad7..412d1de989 100644 --- a/protocols/v2/noise-sv2/src/signature_message.rs +++ b/protocols/v2/noise-sv2/src/signature_message.rs @@ -21,8 +21,9 @@ // public key and optional authority key, while ensuring the message falls within the specified // validity period. +use core::convert::TryInto; + use secp256k1::{hashes::sha256, schnorr::Signature, Keypair, Message, Secp256k1, XOnlyPublicKey}; -use std::{convert::TryInto, time::SystemTime}; /// `SignatureNoiseMessage` represents a signed message used in the Noise NX protocol /// for authentication during the handshake process. It encapsulates the necessary @@ -70,12 +71,13 @@ impl SignatureNoiseMessage { // // If an authority public key is not provided, the function assumes that the signature // is already valid without further verification. - pub fn verify(self, pk: &XOnlyPublicKey, authority_pk: &Option) -> bool { + pub fn verify( + self, + pk: &XOnlyPublicKey, + authority_pk: &Option, + now: u32, + ) -> bool { if let Some(authority_pk) = authority_pk { - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs() as u32; if self.valid_from <= now && self.not_valid_after >= now { let secp = Secp256k1::verification_only(); let (m, s) = self.split(); @@ -100,11 +102,16 @@ impl SignatureNoiseMessage { // Creates a Schnorr signature for the message, combining the version, validity period, and // the static public key of the server (`static_pk`). The resulting signature is then written // into the provided message buffer (`msg`). - pub fn sign(msg: &mut [u8; 74], static_pk: &XOnlyPublicKey, kp: &Keypair) { + pub fn sign( + msg: &mut [u8; 74], + static_pk: &XOnlyPublicKey, + kp: &Keypair, + rng: &mut R, + ) { let secp = Secp256k1::signing_only(); let m = [&msg[0..10], &static_pk.serialize()].concat(); let m = Message::from_hashed_data::(&m); - let signature = secp.sign_schnorr(&m, kp); + let signature = secp.sign_schnorr_with_rng(&m, kp, rng); for (i, b) in signature.as_ref().iter().enumerate() { msg[10 + i] = *b; } diff --git a/protocols/v2/noise-sv2/src/test.rs b/protocols/v2/noise-sv2/src/test.rs index 3c287fc6a0..cf22d39134 100644 --- a/protocols/v2/noise-sv2/src/test.rs +++ b/protocols/v2/noise-sv2/src/test.rs @@ -2,13 +2,23 @@ use crate::{handshake::HandshakeOp, initiator::Initiator, responder::Responder}; #[test] fn test_1() { - let key_pair = Responder::generate_key(); + let key_pair = Responder::generate_key(&mut rand::thread_rng()); - let mut initiator = Initiator::new(Some(key_pair.public_key().into())); - let mut responder = Responder::new(key_pair, 31449600); + let mut initiator = Initiator::new(Some(key_pair.public_key().into()), &mut rand::thread_rng()); + let mut responder = Responder::new(key_pair, 31449600, &mut rand::thread_rng()); let first_message = initiator.step_0().unwrap(); - let (second_message, mut codec_responder) = responder.step_1(first_message).unwrap(); - let mut codec_initiator = initiator.step_2(second_message).unwrap(); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + let (second_message, mut codec_responder) = responder + .step_1(first_message, now, &mut rand::thread_rng()) + .unwrap(); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + let mut codec_initiator = initiator.step_2(second_message, now).unwrap(); let mut message = "ciao".as_bytes().to_vec(); codec_initiator.encrypt(&mut message).unwrap(); assert!(message != "ciao".as_bytes().to_vec()); diff --git a/utils/key-utils/src/main.rs b/utils/key-utils/src/main.rs index ffdc121c76..09dcd8514a 100644 --- a/utils/key-utils/src/main.rs +++ b/utils/key-utils/src/main.rs @@ -1,9 +1,9 @@ use ::key_utils::{Secp256k1PublicKey, Secp256k1SecretKey}; use secp256k1::{rand, Keypair, Secp256k1}; -fn generate_key() -> (Secp256k1SecretKey, Secp256k1PublicKey) { +fn generate_key(rng: &mut R) -> (Secp256k1SecretKey, Secp256k1PublicKey) { let secp = Secp256k1::new(); - let (secret_key, _) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret_key, _) = secp.generate_keypair(rng); let kp = Keypair::from_secret_key(&secp, &secret_key); if kp.x_only_public_key().1 == secp256k1::Parity::Even { ( @@ -11,12 +11,12 @@ fn generate_key() -> (Secp256k1SecretKey, Secp256k1PublicKey) { Secp256k1PublicKey(kp.x_only_public_key().0), ) } else { - generate_key() + generate_key(rng) } } fn main() { - let (secret, public) = generate_key(); + let (secret, public) = generate_key(&mut rand::thread_rng()); let secret: String = secret.into(); let public: String = public.into(); println!("Secret Key: {}", secret);