From 52e5a046d300dda07d6f337f99b02db94a68820f Mon Sep 17 00:00:00 2001 From: Stuart Stock Date: Fri, 11 Oct 2024 18:54:50 -0400 Subject: [PATCH] Refactor signing and verification to use ed25519-dalek `perf` shows the server spends most of its time in signature generation. Benchmarking shows ed25519-dalek is about 40% faster than Ring generating signatures on x86_64. Replaced Verifier and Signer with ed25519-dalek for signature operations. Updated relevant imports and function calls. --- Cargo.toml | 1 + src/bin/roughenough-client.rs | 4 +-- src/key/longterm.rs | 10 +++--- src/key/online.rs | 8 ++--- src/responder.rs | 5 ++- src/sign.rs | 63 ++++++++++++++++++----------------- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1859aa0..f895bd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ yaml-rust = "0.4" zeroize = "1.8" data-encoding = "2.6" enum-iterator = "2.1" +ed25519-dalek = "2.1" # Used by 'awskms' and 'gcpkms' futures = { version = "^0.3", optional = true } diff --git a/src/bin/roughenough-client.rs b/src/bin/roughenough-client.rs index 4e0ab3d..0959a7d 100644 --- a/src/bin/roughenough-client.rs +++ b/src/bin/roughenough-client.rs @@ -33,7 +33,7 @@ use ring::rand; use ring::rand::SecureRandom; use roughenough::key::LongTermKey; use roughenough::merkle::MerkleTree; -use roughenough::sign::Verifier; +use roughenough::sign::MsgVerifier; use roughenough::version::Version; use roughenough::{ roughenough_version, Error, RtMessage, Tag, CERTIFICATE_CONTEXT, REQUEST_FRAMING_BYTES, @@ -314,7 +314,7 @@ impl ResponseHandler { } fn validate_sig(&self, public_key: &[u8], sig: &[u8], data: &[u8]) -> bool { - let mut verifier = Verifier::new(public_key); + let mut verifier = MsgVerifier::new(public_key); verifier.update(data); verifier.verify(sig) } diff --git a/src/key/longterm.rs b/src/key/longterm.rs index 0b09fb6..aaab211 100644 --- a/src/key/longterm.rs +++ b/src/key/longterm.rs @@ -18,7 +18,7 @@ use crate::key::OnlineKey; use crate::message::RtMessage; -use crate::sign::Signer; +use crate::sign::MsgSigner; use crate::tag::Tag; use crate::CERTIFICATE_CONTEXT; use ring::digest; @@ -30,7 +30,7 @@ use std::fmt::Formatter; /// Represents the server's long-term identity. /// pub struct LongTermKey { - signer: Signer, + signer: MsgSigner, srv_value: Vec, } @@ -43,8 +43,8 @@ impl LongTermKey { } pub fn new(seed: &[u8]) -> Self { - let signer = Signer::from_seed(seed); - let srv_value = LongTermKey::calc_srv_value(signer.public_key_bytes()); + let signer = MsgSigner::from_seed(seed); + let srv_value = LongTermKey::calc_srv_value(&signer.public_key_bytes()); LongTermKey { signer, @@ -70,7 +70,7 @@ impl LongTermKey { } /// Return the public key for the provided seed - pub fn public_key(&self) -> &[u8] { + pub fn public_key(&self) -> Vec { self.signer.public_key_bytes() } diff --git a/src/key/online.rs b/src/key/online.rs index f018dee..33836fd 100644 --- a/src/key/online.rs +++ b/src/key/online.rs @@ -19,7 +19,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use byteorder::{LittleEndian, WriteBytesExt}; use crate::message::RtMessage; -use crate::sign::Signer; +use crate::sign::MsgSigner; use crate::tag::Tag; use crate::version::Version; use crate::SIGNED_RESPONSE_CONTEXT; @@ -28,7 +28,7 @@ use crate::SIGNED_RESPONSE_CONTEXT; /// Represents the delegated Roughtime ephemeral online key. /// pub struct OnlineKey { - signer: Signer, + signer: MsgSigner, } impl Default for OnlineKey { @@ -40,7 +40,7 @@ impl Default for OnlineKey { impl OnlineKey { pub fn new() -> Self { OnlineKey { - signer: Signer::new(), + signer: MsgSigner::new(), } } @@ -51,7 +51,7 @@ impl OnlineKey { let pub_key_bytes = self.signer.public_key_bytes(); let mut dele_msg = RtMessage::with_capacity(3); - dele_msg.add_field(Tag::PUBK, pub_key_bytes).unwrap(); + dele_msg.add_field(Tag::PUBK, &pub_key_bytes).unwrap(); dele_msg.add_field(Tag::MINT, &zeros).unwrap(); dele_msg.add_field(Tag::MAXT, &max).unwrap(); diff --git a/src/responder.rs b/src/responder.rs index c3960d4..1cb50e5 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -25,13 +25,12 @@ use data_encoding::{Encoding, HEXLOWER_PERMISSIVE}; use mio::net::UdpSocket; use crate::config::ServerConfig; -use crate::error::Error::SendingResponseFailed; use crate::grease::Grease; use crate::key::{LongTermKey, OnlineKey}; use crate::merkle::MerkleTree; use crate::stats::ServerStats; use crate::version::Version; -use crate::{Error, RtMessage, Tag}; +use crate::{RtMessage, Tag}; const HEX: Encoding = HEXLOWER_PERMISSIVE; @@ -50,7 +49,7 @@ impl Responder { pub fn new(version: Version, config: &dyn ServerConfig, ltk: &mut LongTermKey) -> Responder { let online_key = OnlineKey::new(); let cert_bytes = ltk.make_cert(&online_key).encode().expect("make_cert"); - let long_term_public_key = HEX.encode(ltk.public_key()); + let long_term_public_key = HEX.encode(<k.public_key()); let requests = Vec::with_capacity(config.batch_size() as usize); let grease = Grease::new(config.fault_percentage()); let thread_id = thread::current().name().unwrap().to_string(); diff --git a/src/sign.rs b/src/sign.rs index 563db2d..8e45aea 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -20,8 +20,8 @@ use std::fmt; use std::fmt::Formatter; use data_encoding::{Encoding, HEXLOWER_PERMISSIVE}; +use ed25519_dalek::{SigningKey, Signature, Verifier, Signer, VerifyingKey, SecretKey}; use ring::rand::SecureRandom; -use ring::signature::{self, Ed25519KeyPair, KeyPair}; use ring::rand; const HEX: Encoding = HEXLOWER_PERMISSIVE; @@ -30,28 +30,27 @@ const INITIAL_BUF_SIZE: usize = 1024; /// A multi-step (init-update-finish) interface for verifying an Ed25519 signature #[derive(Debug)] -pub struct Verifier { - pubkey: Vec, +pub struct MsgVerifier { + pubkey: VerifyingKey, buf: Vec, } -impl Verifier { +impl MsgVerifier { pub fn new(pubkey: &[u8]) -> Self { - Verifier { - pubkey: Vec::from(pubkey), + let pk: &[u8; 32] = pubkey.try_into().expect("valid pubkey"); + MsgVerifier { + pubkey: VerifyingKey::from_bytes(pk).unwrap(), buf: Vec::with_capacity(INITIAL_BUF_SIZE), } } pub fn update(&mut self, data: &[u8]) { - self.buf.reserve(data.len()); self.buf.extend_from_slice(data); } - pub fn verify(&self, expected_sig: &[u8]) -> bool { - let pk = signature::UnparsedPublicKey::new(&signature::ED25519, &self.pubkey); - - match pk.verify(&self.buf, expected_sig) { + pub fn verify(&self, provided_sig: &[u8]) -> bool { + let sig = Signature::from_slice(provided_sig).expect("valid signature"); + match self.pubkey.verify(&self.buf, &sig) { Ok(_) => true, _ => false, } @@ -59,29 +58,30 @@ impl Verifier { } /// A multi-step (init-update-finish) interface for creating an Ed25519 signature -pub struct Signer { - key_pair: Ed25519KeyPair, +pub struct MsgSigner { + signing_key: SigningKey, buf: Vec, } -impl Default for Signer { +impl Default for MsgSigner { fn default() -> Self { Self::new() } } -impl Signer { +impl MsgSigner { pub fn new() -> Self { let rng = rand::SystemRandom::new(); let mut seed = [0u8; 32]; rng.fill(&mut seed).unwrap(); - Signer::from_seed(&seed) + MsgSigner::from_seed(&seed) } pub fn from_seed(seed: &[u8]) -> Self { - Signer { - key_pair: Ed25519KeyPair::from_seed_unchecked(seed).unwrap(), + let secret_key = SecretKey::try_from(seed).expect("invalid seed"); + MsgSigner { + signing_key: SigningKey::from(secret_key), buf: Vec::with_capacity(INITIAL_BUF_SIZE), } } @@ -92,29 +92,30 @@ impl Signer { } pub fn sign(&mut self) -> Vec { - let signature = self.key_pair.sign(&self.buf).as_ref().to_vec(); + let signature = self.signing_key.sign(&self.buf).to_vec(); self.buf.clear(); signature } - pub fn public_key_bytes(&self) -> &[u8] { - self.key_pair.public_key().as_ref() + pub fn public_key_bytes(&self) -> Vec { + let binding = self.signing_key.verifying_key(); + binding.as_bytes().to_vec() } } -impl fmt::Display for Signer { +impl fmt::Display for MsgSigner { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", HEX.encode(self.public_key_bytes())) + write!(f, "{}", HEX.encode(&self.public_key_bytes())) } } -impl fmt::Debug for Signer { +impl fmt::Debug for MsgSigner { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!( f, "Signer({}, {:?})", - HEX.encode(self.public_key_bytes()), + HEX.encode(&self.public_key_bytes()), self.buf ) } @@ -135,7 +136,7 @@ mod test { "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b".as_ref() ).unwrap(); - let v = Verifier::new(&pubkey); + let v = MsgVerifier::new(&pubkey); let result = v.verify(&signature); assert_eq!(result, true); } @@ -152,7 +153,7 @@ mod test { "124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07".as_ref() ).unwrap(); - let mut v = Verifier::new(&pubkey); + let mut v = MsgVerifier::new(&pubkey); v.update(&message); let result = v.verify(&signature); assert_eq!(result, true); @@ -167,7 +168,7 @@ mod test { "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b".as_ref() ).unwrap(); - let mut s = Signer::from_seed(&seed); + let mut s = MsgSigner::from_seed(&seed); let sig = s.sign(); assert_eq!(sig, expected_sig); } @@ -183,7 +184,7 @@ mod test { "d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c".as_ref() ).unwrap(); - let mut s = Signer::from_seed(&seed); + let mut s = MsgSigner::from_seed(&seed); s.update(&message); let sig = s.sign(); assert_eq!(sig, expected_sig); @@ -196,11 +197,11 @@ mod test { let message = "Hello world".as_bytes(); - let mut signer = Signer::from_seed(&seed); + let mut signer = MsgSigner::from_seed(&seed); signer.update(&message); let signature = signer.sign(); - let mut v = Verifier::new(signer.public_key_bytes()); + let mut v = MsgVerifier::new(&signer.public_key_bytes()); v.update(&message); let result = v.verify(&signature);