From 43abc2412e306d3f80ef289596c7fe5b526e7b3d Mon Sep 17 00:00:00 2001 From: David Millar-Durrant Date: Fri, 2 Aug 2024 17:25:11 +0200 Subject: [PATCH] Further hardened delta We use re-randomization to mitigate the issues described in Groth,Shoup 22 (even though our model deviates substantially from theirs). The properties of a good delta is it has to be unpredictable before the signature request is created and it has to only be used once per signing request. Since we current use H ( VRF, H(signature_request, ...) ) we guarantee the first point, but there are situations where we may try to sign again using the same delta. e.g. if our protocol crashes or someone manages to get us to ingress a duplicate message. By including the public R paramater of the presignature and since we guarantee that we don't reuse presignatures, this PR guarantees the second property required. To read more we have a deeper dive into the subject here https://docs.google.com/document/d/1-Ibv5R5mokSlcV1FhKSGmAK6jHqyRwmffe2moTzR_k4/edit. --- chain-signatures/node/src/indexer.rs | 3 -- chain-signatures/node/src/kdf.rs | 17 ++++++-- chain-signatures/node/src/protocol/message.rs | 6 +-- .../node/src/protocol/signature.rs | 42 ++++++++++++------- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/chain-signatures/node/src/indexer.rs b/chain-signatures/node/src/indexer.rs index f032eacfd..d16acbd0e 100644 --- a/chain-signatures/node/src/indexer.rs +++ b/chain-signatures/node/src/indexer.rs @@ -1,6 +1,5 @@ use crate::gcp::error::DatastoreStorageError; use crate::gcp::GcpService; -use crate::kdf; use crate::protocol::{SignQueue, SignRequest}; use crate::types::LatestBlockHeight; use crypto_shared::{derive_epsilon, ScalarExt}; @@ -213,7 +212,6 @@ async fn handle_block( continue; }; let epsilon = derive_epsilon(&action.predecessor_id(), &arguments.request.path); - let delta = kdf::derive_delta(receipt_id, entropy); tracing::info!( receipt_id = %receipt_id, caller_id = receipt.predecessor_id().to_string(), @@ -232,7 +230,6 @@ async fn handle_block( receipt_id, request, epsilon, - delta, entropy, // TODO: use indexer timestamp instead. time_added: Instant::now(), diff --git a/chain-signatures/node/src/kdf.rs b/chain-signatures/node/src/kdf.rs index 966447196..d38218ae2 100644 --- a/chain-signatures/node/src/kdf.rs +++ b/chain-signatures/node/src/kdf.rs @@ -1,18 +1,27 @@ use anyhow::Context; use crypto_shared::{kdf::recover, x_coordinate, ScalarExt, SignatureResponse}; use hkdf::Hkdf; -use k256::{ecdsa::RecoveryId, elliptic_curve::sec1::ToEncodedPoint, Scalar}; +use k256::{ecdsa::RecoveryId, elliptic_curve::sec1::ToEncodedPoint, AffinePoint, Scalar}; use near_primitives::hash::CryptoHash; use sha3::Sha3_256; // In case there are multiple requests in the same block (hence same entropy), we need to ensure // that we generate different random scalars as delta tweaks. // Receipt ID should be unique inside of a block, so it serves us as the request identifier. -pub fn derive_delta(receipt_id: CryptoHash, entropy: [u8; 32]) -> Scalar { +pub fn derive_delta( + receipt_id: CryptoHash, + entropy: [u8; 32], + presignature_big_r: AffinePoint, +) -> Scalar { let hk = Hkdf::::new(None, &entropy); let info = format!("{DELTA_DERIVATION_PREFIX}:{}", receipt_id); let mut okm = [0u8; 32]; hk.expand(info.as_bytes(), &mut okm).unwrap(); + hk.expand( + presignature_big_r.to_encoded_point(true).as_bytes(), + &mut okm, + ) + .unwrap(); Scalar::from_non_biased(okm) } @@ -31,7 +40,7 @@ pub fn into_eth_sig( let signature = k256::ecdsa::Signature::from_scalars(x_coordinate(big_r), s) .context("cannot create signature from cait_sith signature")?; let pk0 = recover( - &msg_hash.to_bytes(), + &msg_hash.to_bytes()[..], &signature, RecoveryId::try_from(0).context("cannot create recovery_id=0")?, ) @@ -42,7 +51,7 @@ pub fn into_eth_sig( } let pk1 = recover( - &msg_hash.to_bytes(), + &msg_hash.to_bytes()[..], &signature, RecoveryId::try_from(1).context("cannot create recovery_id=1")?, ) diff --git a/chain-signatures/node/src/protocol/message.rs b/chain-signatures/node/src/protocol/message.rs index f402dfbe8..eb47ea695 100644 --- a/chain-signatures/node/src/protocol/message.rs +++ b/chain-signatures/node/src/protocol/message.rs @@ -68,7 +68,7 @@ pub struct SignatureMessage { pub presignature_id: PresignatureId, pub request: ContractSignRequest, pub epsilon: Scalar, - pub delta: Scalar, + pub entropy: [u8; 32], pub epoch: u64, pub from: Participant, pub data: MessageData, @@ -387,7 +387,7 @@ impl MessageHandler for RunningState { presignature_id, request, epsilon, - delta, + entropy, .. } = queue.front().unwrap(); @@ -418,7 +418,7 @@ impl MessageHandler for RunningState { *presignature_id, request, *epsilon, - *delta, + *entropy, &mut presignature_manager, protocol_cfg, ) { diff --git a/chain-signatures/node/src/protocol/signature.rs b/chain-signatures/node/src/protocol/signature.rs index f9038a38a..eb46356c1 100644 --- a/chain-signatures/node/src/protocol/signature.rs +++ b/chain-signatures/node/src/protocol/signature.rs @@ -2,9 +2,10 @@ use super::contract::primitives::Participants; use super::message::SignatureMessage; use super::presignature::{GenerationError, Presignature, PresignatureId, PresignatureManager}; use crate::indexer::ContractSignRequest; -use crate::kdf::into_eth_sig; +use crate::kdf::{derive_delta, into_eth_sig}; use crate::types::SignatureProtocol; use crate::util::AffinePointExt; +use near_primitives::hash::CryptoHash; use cait_sith::protocol::{Action, InitializationError, Participant, ProtocolError}; use cait_sith::{FullSignature, PresignOutput}; @@ -30,7 +31,6 @@ pub struct SignRequest { pub receipt_id: ReceiptId, pub request: ContractSignRequest, pub epsilon: Scalar, - pub delta: Scalar, pub entropy: [u8; 32], pub time_added: Instant, } @@ -162,7 +162,8 @@ pub struct SignatureGenerator { pub presignature_id: PresignatureId, pub request: ContractSignRequest, pub epsilon: Scalar, - pub delta: Scalar, + pub receipt_id: CryptoHash, + pub entropy: [u8; 32], pub sign_request_timestamp: Instant, pub generator_timestamp: Instant, pub timeout: Duration, @@ -178,7 +179,8 @@ impl SignatureGenerator { presignature_id: PresignatureId, request: ContractSignRequest, epsilon: Scalar, - delta: Scalar, + receipt_id: CryptoHash, + entropy: [u8; 32], sign_request_timestamp: Instant, cfg: &ProtocolConfig, ) -> Self { @@ -189,7 +191,8 @@ impl SignatureGenerator { presignature_id, request, epsilon, - delta, + receipt_id, + entropy, sign_request_timestamp, generator_timestamp: Instant::now(), timeout: Duration::from_millis(cfg.signature.generation_timeout), @@ -221,7 +224,8 @@ pub struct GenerationRequest { pub proposer: Participant, pub request: ContractSignRequest, pub epsilon: Scalar, - pub delta: Scalar, + pub receipt_id: CryptoHash, + pub entropy: [u8; 32], pub sign_request_timestamp: Instant, } @@ -302,10 +306,12 @@ impl SignatureManager { proposer, request, epsilon, - delta, + receipt_id, + entropy, sign_request_timestamp, } = req; let PresignOutput { big_r, k, sigma } = presignature.output; + let delta = derive_delta(receipt_id, entropy, big_r); // TODO: Check whether it is okay to use invert_vartime instead let output: PresignOutput = PresignOutput { big_r: (big_r * delta).to_affine(), @@ -330,7 +336,8 @@ impl SignatureManager { presignature_id, request, epsilon, - delta, + receipt_id, + entropy, sign_request_timestamp, cfg, )) @@ -368,7 +375,7 @@ impl SignatureManager { presignature: Presignature, request: ContractSignRequest, epsilon: Scalar, - delta: Scalar, + entropy: [u8; 32], sign_request_timestamp: Instant, cfg: &ProtocolConfig, ) -> Result<(), (Presignature, InitializationError)> { @@ -388,7 +395,8 @@ impl SignatureManager { proposer: self.me, request, epsilon, - delta, + receipt_id, + entropy, sign_request_timestamp, }, cfg, @@ -412,7 +420,7 @@ impl SignatureManager { presignature_id: PresignatureId, request: &ContractSignRequest, epsilon: Scalar, - delta: Scalar, + entropy: [u8; 32], presignature_manager: &mut PresignatureManager, cfg: &ProtocolConfig, ) -> Result<&mut SignatureProtocol, GenerationError> { @@ -449,7 +457,8 @@ impl SignatureManager { proposer, request: request.clone(), epsilon, - delta, + entropy, + receipt_id, sign_request_timestamp: Instant::now(), }, cfg, @@ -490,7 +499,8 @@ impl SignatureManager { proposer: generator.proposer, request: generator.request.clone(), epsilon: generator.epsilon, - delta: generator.delta, + receipt_id: generator.receipt_id, + entropy: generator.entropy, sign_request_timestamp: generator.sign_request_timestamp }, )); @@ -518,7 +528,7 @@ impl SignatureManager { presignature_id: generator.presignature_id, request: generator.request.clone(), epsilon: generator.epsilon, - delta: generator.delta, + entropy: generator.entropy, epoch: self.epoch, from: self.me, data: data.clone(), @@ -535,7 +545,7 @@ impl SignatureManager { presignature_id: generator.presignature_id, request: generator.request.clone(), epsilon: generator.epsilon, - delta: generator.delta, + entropy: generator.entropy, epoch: self.epoch, from: self.me, data, @@ -641,7 +651,7 @@ impl SignatureManager { presignature, my_request.request, my_request.epsilon, - my_request.delta, + my_request.entropy, my_request.time_added, cfg, ) {