diff --git a/rs/crypto/internal/crypto_service_provider/src/key_id/mod.rs b/rs/crypto/internal/crypto_service_provider/src/key_id/mod.rs index f92c7a9d1bf7..465d14dbec6d 100644 --- a/rs/crypto/internal/crypto_service_provider/src/key_id/mod.rs +++ b/rs/crypto/internal/crypto_service_provider/src/key_id/mod.rs @@ -162,9 +162,9 @@ impl TryFrom<&CspPublicCoefficients> for KeyId { let mut hash = Sha256::new_with_context(&DomainSeparationContext::new( THRESHOLD_PUBLIC_COEFFICIENTS_KEY_ID_DOMAIN, )); - hash.write(&serde_cbor::to_vec(&coefficients).map_err(|err| { + serde_cbor::to_writer(&mut hash, &coefficients).map_err(|err| { Self::Error::InvalidArguments(format!("Failed to serialize public coefficients: {err}")) - })?); + })?; Ok(KeyId::from(hash.finish())) } } diff --git a/rs/protobuf/src/types.rs b/rs/protobuf/src/types.rs index 3ac37c071b00..0fd3c9070d50 100644 --- a/rs/protobuf/src/types.rs +++ b/rs/protobuf/src/types.rs @@ -29,15 +29,6 @@ pub mod v1 { } } - impl CatchUpContent { - pub fn as_protobuf_vec(&self) -> Vec { - let mut buf = Vec::::new(); - self.encode(&mut buf) - .expect("CatchUpContent should serialize"); - buf - } - } - impl From for RejectCode { fn from(value: RejectCodePublic) -> Self { match value { diff --git a/rs/tests/consensus/request_auth_malicious_replica_test.rs b/rs/tests/consensus/request_auth_malicious_replica_test.rs index 3536766baf7b..0eb607ecc8da 100644 --- a/rs/tests/consensus/request_auth_malicious_replica_test.rs +++ b/rs/tests/consensus/request_auth_malicious_replica_test.rs @@ -598,7 +598,7 @@ async fn test_request_with_delegation( fn sign_delegation(delegation: Delegation, identity: &impl Identity) -> SignedDelegation { let mut msg = b"\x1Aic-request-auth-delegation".to_vec(); - msg.extend(&delegation.as_signed_bytes_without_domain_separator()); + delegation.write_signed_bytes_without_domain_separator(&mut msg); let signature = identity.sign_arbitrary(&msg).unwrap(); SignedDelegation::new(delegation, signature.signature.unwrap()) diff --git a/rs/types/types/src/canister_http.rs b/rs/types/types/src/canister_http.rs index 2d37bcc71636..f861e124f86c 100644 --- a/rs/types/types/src/canister_http.rs +++ b/rs/types/types/src/canister_http.rs @@ -1039,8 +1039,8 @@ impl CountBytes for CanisterHttpResponseMetadata { } impl crate::crypto::SignedBytesWithoutDomainSeparator for CanisterHttpResponseMetadata { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } diff --git a/rs/types/types/src/consensus.rs b/rs/types/types/src/consensus.rs index bd208b6c68ea..1d53d5f31bd4 100644 --- a/rs/types/types/src/consensus.rs +++ b/rs/types/types/src/consensus.rs @@ -300,8 +300,8 @@ impl Block { } impl SignedBytesWithoutDomainSeparator for BlockMetadata { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -439,8 +439,8 @@ impl NotarizationContent { } impl SignedBytesWithoutDomainSeparator for NotarizationContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -539,8 +539,8 @@ impl FinalizationContent { } impl SignedBytesWithoutDomainSeparator for FinalizationContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -647,8 +647,8 @@ impl RandomBeaconContent { } impl SignedBytesWithoutDomainSeparator for RandomBeaconContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -734,8 +734,8 @@ pub struct RandomTapeContent { } impl SignedBytesWithoutDomainSeparator for RandomTapeContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } diff --git a/rs/types/types/src/consensus/catchup.rs b/rs/types/types/src/consensus/catchup.rs index 6d812bb54a54..ebd330f18f62 100644 --- a/rs/types/types/src/consensus/catchup.rs +++ b/rs/types/types/src/consensus/catchup.rs @@ -143,8 +143,10 @@ impl TryFrom for CatchUpContent { } impl SignedBytesWithoutDomainSeparator for CatchUpContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - pb::CatchUpContent::from(self.clone()).as_protobuf_vec() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + pb::CatchUpContent::from(self.clone()) + .encode(bytes) + .expect("CatchUpContent should serialize"); } } @@ -211,7 +213,7 @@ impl From for pb::CatchUpPackage { Self { signer: Some(pb::NiDkgId::from(cup.signature.signer)), signature: cup.signature.signature.get().0, - content: pb::CatchUpContent::from(cup.content).as_protobuf_vec(), + content: pb::CatchUpContent::from(cup.content).encode_to_vec(), } } } @@ -379,8 +381,8 @@ impl From<&pb::CatchUpPackage> for CatchUpContentProtobufBytes { } impl SignedBytesWithoutDomainSeparator for CatchUpContentProtobufBytes { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.0.clone() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.0); } } diff --git a/rs/types/types/src/consensus/certification.rs b/rs/types/types/src/consensus/certification.rs index b25076582b6a..97e426807812 100644 --- a/rs/types/types/src/consensus/certification.rs +++ b/rs/types/types/src/consensus/certification.rs @@ -178,8 +178,8 @@ impl CertificationContent { } impl SignedBytesWithoutDomainSeparator for CertificationContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.hash.get_ref().0.clone() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.hash.get_ref().0); } } diff --git a/rs/types/types/src/consensus/dkg.rs b/rs/types/types/src/consensus/dkg.rs index 1b50bb6b6229..3e1d2443cb12 100644 --- a/rs/types/types/src/consensus/dkg.rs +++ b/rs/types/types/src/consensus/dkg.rs @@ -97,8 +97,8 @@ impl DealingContent { } impl SignedBytesWithoutDomainSeparator for DealingContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } diff --git a/rs/types/types/src/consensus/idkg.rs b/rs/types/types/src/consensus/idkg.rs index 94bb8f997d2b..9bd8364196d6 100644 --- a/rs/types/types/src/consensus/idkg.rs +++ b/rs/types/types/src/consensus/idkg.rs @@ -1688,14 +1688,14 @@ impl Display for SignedIDkgComplaint { } impl SignedBytesWithoutDomainSeparator for IDkgComplaintContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } impl SignedBytesWithoutDomainSeparator for SignedIDkgComplaint { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -1765,14 +1765,14 @@ impl Display for SignedIDkgOpening { } impl SignedBytesWithoutDomainSeparator for IDkgOpeningContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } impl SignedBytesWithoutDomainSeparator for SignedIDkgOpening { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } diff --git a/rs/types/types/src/crypto.rs b/rs/types/types/src/crypto.rs index 3e6eac62e756..ce4372ad94ce 100644 --- a/rs/types/types/src/crypto.rs +++ b/rs/types/types/src/crypto.rs @@ -101,13 +101,18 @@ impl CountBytes for Signed { /// Signed bytes, not containing a domain separator. Also refer to the doc of /// `SignedBytesWithoutDomainSeparator:: -/// as_signed_bytes_without_domain_separator`. +/// write_signed_bytes_without_domain_separator`. pub trait SignedBytesWithoutDomainSeparator { - /// Returns a bytes-representation of the object for digital signatures. - /// The returned value together with a domain-separator (that can be empty, - /// depending on the type) are the bytes that are used for + /// Appends the bytes-representation of the object for digital signatures to + /// `bytes`. The appended value together with a domain-separator (that can + /// be empty, depending on the type) are the bytes that are used for /// signing/verification. - fn as_signed_bytes_without_domain_separator(&self) -> Vec; + /// + /// Writing directly into `bytes` allows callers that assemble the full + /// signed bytes (e.g. a domain separator followed by these bytes) to avoid + /// materializing an intermediate `Vec` and copying it into the output + /// buffer. + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec); } /// A purpose of a key. This is used for storing and retrieving keys from the diff --git a/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs b/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs index 723d49bdcc08..c47b023af8b6 100644 --- a/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs +++ b/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs @@ -1045,8 +1045,8 @@ impl Display for IDkgDealing { } impl SignedBytesWithoutDomainSeparator for IDkgDealing { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } @@ -1090,8 +1090,8 @@ impl Display for SignedIDkgDealing { } impl SignedBytesWithoutDomainSeparator for SignedIDkgDealing { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - serde_cbor::to_vec(&self).unwrap() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + serde_cbor::to_writer(bytes, &self).unwrap(); } } diff --git a/rs/types/types/src/crypto/sign.rs b/rs/types/types/src/crypto/sign.rs index 0580c51c2c14..d4d886e29d69 100644 --- a/rs/types/types/src/crypto/sign.rs +++ b/rs/types/types/src/crypto/sign.rs @@ -37,7 +37,7 @@ where { fn as_signed_bytes(&self) -> Vec { let mut bytes = self.domain(); - bytes.append(&mut self.as_signed_bytes_without_domain_separator()); + self.write_signed_bytes_without_domain_separator(&mut bytes); bytes } } @@ -260,7 +260,7 @@ impl SignatureDomain for SignableMock { } impl SignedBytesWithoutDomainSeparator for SignableMock { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.signed_bytes_without_domain.clone() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.signed_bytes_without_domain); } } diff --git a/rs/types/types/src/crypto/vetkd.rs b/rs/types/types/src/crypto/vetkd.rs index 21afd5be8a54..c8ec23815cab 100644 --- a/rs/types/types/src/crypto/vetkd.rs +++ b/rs/types/types/src/crypto/vetkd.rs @@ -47,8 +47,8 @@ impl std::fmt::Debug for VetKdEncryptedKeyShareContent { impl_display_using_debug!(VetKdEncryptedKeyShareContent); impl SignedBytesWithoutDomainSeparator for VetKdEncryptedKeyShareContent { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.0.clone() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.0); } } diff --git a/rs/types/types/src/messages/http.rs b/rs/types/types/src/messages/http.rs index 50605300aa0e..add56b40d4ff 100644 --- a/rs/types/types/src/messages/http.rs +++ b/rs/types/types/src/messages/http.rs @@ -342,8 +342,8 @@ pub struct SignedSenderInfo { pub struct SenderInfoContent<'a>(pub &'a [u8]); impl crate::crypto::SignedBytesWithoutDomainSeparator for SenderInfoContent<'_> { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.0.to_vec() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(self.0); } } @@ -593,7 +593,7 @@ impl Delegation { } impl SignedBytesWithoutDomainSeparator for Delegation { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { use RawHttpRequestVal::*; let mut map = btreemap! { @@ -607,7 +607,7 @@ impl SignedBytesWithoutDomainSeparator for Delegation { ); } - hash_of_map(&map, |key, value| hash_key_val(key, value)).to_vec() + bytes.extend_from_slice(&hash_of_map(&map, |key, value| hash_key_val(key, value))); } } @@ -726,8 +726,8 @@ impl QueryResponseHash { } impl SignedBytesWithoutDomainSeparator for QueryResponseHash { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.0.to_vec() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.0); } } diff --git a/rs/types/types/src/messages/message_id.rs b/rs/types/types/src/messages/message_id.rs index fe334a1af2a4..a6c7ae4be623 100644 --- a/rs/types/types/src/messages/message_id.rs +++ b/rs/types/types/src/messages/message_id.rs @@ -69,8 +69,8 @@ impl MessageId { } impl SignedBytesWithoutDomainSeparator for MessageId { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.0.to_vec() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.0); } } diff --git a/rs/types/types/src/messages/webauthn.rs b/rs/types/types/src/messages/webauthn.rs index 1d0f8123c0a9..8f4dc8f9af5f 100644 --- a/rs/types/types/src/messages/webauthn.rs +++ b/rs/types/types/src/messages/webauthn.rs @@ -76,8 +76,8 @@ pub struct WebAuthnEnvelope { } impl WebAuthnEnvelope { - pub fn challenge(&self) -> Vec { - self.challenge.clone() + pub fn challenge(&self) -> &[u8] { + &self.challenge } } @@ -97,7 +97,7 @@ impl TryFrom<&WebAuthnSignature> for WebAuthnEnvelope { }; let mut signed_bytes = signature.authenticator_data.0.clone(); - signed_bytes.append(&mut Sha256::hash(&signature.client_data_json.0.clone()[..]).to_vec()); + signed_bytes.extend_from_slice(&Sha256::hash(&signature.client_data_json.0)); Ok(WebAuthnEnvelope { client_data_json: signature.client_data_json.0.clone(), @@ -110,8 +110,8 @@ impl TryFrom<&WebAuthnSignature> for WebAuthnEnvelope { } impl SignedBytesWithoutDomainSeparator for WebAuthnEnvelope { - fn as_signed_bytes_without_domain_separator(&self) -> Vec { - self.signed_bytes.clone() + fn write_signed_bytes_without_domain_separator(&self, bytes: &mut Vec) { + bytes.extend_from_slice(&self.signed_bytes); } } @@ -143,6 +143,8 @@ mod tests { hex!("2f1b671a93f444b8ec77e0211f9624c9c2612182b864f0d4ac9d335f5b4fe5020100000053") .to_vec() ); - assert_eq!(result.as_signed_bytes_without_domain_separator().to_vec(), hex!("2f1b671a93f444b8ec77e0211f9624c9c2612182b864f0d4ac9d335f5b4fe50201000000537f91225ffff1e2912a0f8ca7a0ef61df01ae3d8898fca283036239259bab4f82").to_vec()); + let mut signed_bytes = Vec::new(); + result.write_signed_bytes_without_domain_separator(&mut signed_bytes); + assert_eq!(signed_bytes, hex!("2f1b671a93f444b8ec77e0211f9624c9c2612182b864f0d4ac9d335f5b4fe50201000000537f91225ffff1e2912a0f8ca7a0ef61df01ae3d8898fca283036239259bab4f82").to_vec()); } } diff --git a/rs/validator/src/webauthn.rs b/rs/validator/src/webauthn.rs index ed4e406beffb..710213b20809 100644 --- a/rs/validator/src/webauthn.rs +++ b/rs/validator/src/webauthn.rs @@ -36,7 +36,7 @@ pub(crate) fn validate_webauthn_sig( // The challenge in the webauthn envelope must match signed bytes. let signed_bytes = signable.as_signed_bytes(); - if envelope.challenge() != signed_bytes { + if envelope.challenge() != signed_bytes.as_slice() { Err(format!( "Challenge in webauthn is {:?} while it is expected to be {:?}", envelope.challenge(),