From 26fcca271246836e5b94baa0fca0dffef8733222 Mon Sep 17 00:00:00 2001 From: deuszx Date: Tue, 25 Feb 2025 13:44:31 +0000 Subject: [PATCH] Fix todos --- linera-base/src/crypto/ed25519.rs | 6 ++- linera-base/src/crypto/mod.rs | 10 ++-- linera-base/src/crypto/secp256k1.rs | 71 ++++++++++++++++++++++------- linera-rpc/src/grpc/conversions.rs | 2 +- prepush.sh | 43 +++++++++++++++++ 5 files changed, 109 insertions(+), 23 deletions(-) create mode 100755 prepush.sh diff --git a/linera-base/src/crypto/ed25519.rs b/linera-base/src/crypto/ed25519.rs index 4009fcbc14bc..bd054480540a 100644 --- a/linera-base/src/crypto/ed25519.rs +++ b/linera-base/src/crypto/ed25519.rs @@ -141,7 +141,11 @@ impl TryFrom<&[u8]> for Ed25519PublicKey { fn try_from(value: &[u8]) -> Result { if value.len() != dalek::PUBLIC_KEY_LENGTH { - return Err(CryptoError::IncorrectPublicKeySize(value.len())); + return Err(CryptoError::IncorrectPublicKeySize { + scheme: "Ed25519".to_string(), + len: value.len(), + expected: dalek::PUBLIC_KEY_LENGTH, + }); } let mut pubkey = [0u8; dalek::PUBLIC_KEY_LENGTH]; pubkey.copy_from_slice(value); diff --git a/linera-base/src/crypto/mod.rs b/linera-base/src/crypto/mod.rs index d687ca24c5e1..5df2274c29d2 100644 --- a/linera-base/src/crypto/mod.rs +++ b/linera-base/src/crypto/mod.rs @@ -11,7 +11,6 @@ mod secp256k1; use std::{io, num::ParseIntError}; use alloy_primitives::FixedBytes; -use ed25519_dalek::{self as dalek}; pub use hash::*; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -50,10 +49,13 @@ pub enum CryptoError { )] IncorrectHashSize(usize), #[error( - "Byte slice has length {0} but a `PublicKey` requires exactly {expected} bytes", - expected = dalek::PUBLIC_KEY_LENGTH, + "Byte slice has length {len} but a {scheme} `PublicKey` requires exactly {expected} bytes" )] - IncorrectPublicKeySize(usize), + IncorrectPublicKeySize { + scheme: String, + len: usize, + expected: usize, + }, #[error("Could not parse integer: {0}")] ParseIntError(#[from] ParseIntError), #[error("secp256k1 error: {0}")] diff --git a/linera-base/src/crypto/secp256k1.rs b/linera-base/src/crypto/secp256k1.rs index 357e63763914..5636269864cf 100644 --- a/linera-base/src/crypto/secp256k1.rs +++ b/linera-base/src/crypto/secp256k1.rs @@ -20,6 +20,12 @@ use serde::{Deserialize, Serialize}; use super::{BcsSignable, CryptoError, CryptoHash, HasTypeName}; use crate::doc_scalar; +/// Length of secp256k1 compressed public key. +const SECP256K1_PUBLIC_KEY_SIZE: usize = 33; + +/// Length of secp256k1 signature. +const SECP256K1_SIGNATURE_SIZE: usize = 64; + /// A secp256k1 secret key. #[derive(Eq, PartialEq)] pub struct Secp256k1SecretKey(pub SigningKey); @@ -66,12 +72,38 @@ impl Secp256k1PublicKey { /// Expects the bytes to be of compressed representation. /// /// Panics if the encoding can't be done in a constant time. - pub fn from_bytes(bytes: &[u8]) -> Self { - Self( - k256::PublicKey::from_encoded_point(&EncodedPoint::from_bytes(bytes).unwrap()) - .expect("Decode in constant time.") - .into(), - ) + pub fn from_bytes(bytes: &[u8]) -> Result { + let encoded_point = + EncodedPoint::from_bytes(bytes).map_err(|_| CryptoError::IncorrectPublicKeySize { + scheme: "secp256k1".to_string(), + len: bytes.len(), + expected: SECP256K1_PUBLIC_KEY_SIZE, + })?; + + // Intermediate struct to handle the case when the point is at infinity. + // There's no error variant available in `k256` crate itself for that. + // We don't want to panic here since that would crash the whole server/client + // and, in theory, someone could send us a dummy certificate with invalid signature. + #[derive(Debug)] + struct InvalidPublicKeyInput; + + impl fmt::Display for InvalidPublicKeyInput { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Invalid bytes for Secp256k1PublicKey. Point at infinity." + ) + } + } + + impl std::error::Error for InvalidPublicKeyInput {} + + match k256::PublicKey::from_encoded_point(&encoded_point).into_option() { + Some(public_key) => Ok(Self(public_key.into())), + None => Err(CryptoError::Secp256k1Error( + k256::ecdsa::Error::from_source(InvalidPublicKeyInput), + )), + } } } @@ -114,7 +146,12 @@ impl Serialize for Secp256k1PublicKey { if serializer.is_human_readable() { serializer.serialize_str(&hex::encode(self.as_bytes())) } else { - let compact_pk = serde_utils::CompactPublicKey(self.as_bytes().try_into().unwrap()); + let compact_pk = + serde_utils::CompressedPublicKey(self.as_bytes().try_into().map_err(|_| { + serde::ser::Error::custom(format!( + "Invalid bytes for public key. Expected {SECP256K1_PUBLIC_KEY_SIZE} bytes." + )) + })?); serializer.serialize_newtype_struct("Secp256k1PublicKey", &compact_pk) } } @@ -128,13 +165,13 @@ impl<'de> Deserialize<'de> for Secp256k1PublicKey { if deserializer.is_human_readable() { let s = String::deserialize(deserializer)?; let value = hex::decode(s).map_err(serde::de::Error::custom)?; - Ok(Secp256k1PublicKey::from_bytes(&value)) + Ok(Secp256k1PublicKey::from_bytes(&value).map_err(serde::de::Error::custom)?) } else { #[derive(Deserialize)] #[serde(rename = "Secp256k1PublicKey")] - struct PublicKey(serde_utils::CompactPublicKey); + struct PublicKey(serde_utils::CompressedPublicKey); let compact = PublicKey::deserialize(deserializer)?; - Ok(Secp256k1PublicKey::from_bytes(&compact.0 .0)) + Ok(Secp256k1PublicKey::from_bytes(&compact.0 .0).map_err(serde::de::Error::custom)?) } } } @@ -151,9 +188,7 @@ impl TryFrom<&[u8]> for Secp256k1PublicKey { type Error = CryptoError; fn try_from(value: &[u8]) -> Result { - let pk = k256::PublicKey::from_encoded_point(&EncodedPoint::from_bytes(value).unwrap()) - .expect("Decode in constant time."); - Ok(Secp256k1PublicKey(pk.into())) + Self::from_bytes(value) } } @@ -240,7 +275,7 @@ impl Secp256k1Signature { } /// Returns the byte representation of the signature. - pub fn as_bytes(&self) -> [u8; 64] { + pub fn as_bytes(&self) -> [u8; SECP256K1_SIGNATURE_SIZE] { self.0.to_bytes().into() } @@ -326,6 +361,8 @@ mod serde_utils { use serde::{Deserialize, Serialize}; use serde_with::serde_as; + use super::{SECP256K1_PUBLIC_KEY_SIZE, SECP256K1_SIGNATURE_SIZE}; + /// Wrapper around compact signature serialization /// so that we can implement custom serializer for it that uses fixed length. // Serde treats arrays larger than 32 as variable length arrays, and adds the length as a prefix. @@ -333,12 +370,12 @@ mod serde_utils { #[serde_as] #[derive(Serialize, Deserialize)] #[serde(transparent)] - pub struct CompactSignature(#[serde_as(as = "[_; 64]")] pub [u8; 64]); + pub struct CompactSignature(#[serde_as(as = "[_; 64]")] pub [u8; SECP256K1_SIGNATURE_SIZE]); #[serde_as] #[derive(Serialize, Deserialize)] #[serde(transparent)] - pub struct CompactPublicKey(#[serde_as(as = "[_; 33]")] pub [u8; 33]); + pub struct CompressedPublicKey(#[serde_as(as = "[_; 33]")] pub [u8; SECP256K1_PUBLIC_KEY_SIZE]); } #[cfg(with_testing)] @@ -430,7 +467,7 @@ mod tests { bytes.len() == 33, "::to_bytes() should return compressed representation" ); - let key_out = Secp256k1PublicKey::from_bytes(&bytes); + let key_out = Secp256k1PublicKey::from_bytes(&bytes).unwrap(); assert_eq!(key_in, key_out); } diff --git a/linera-rpc/src/grpc/conversions.rs b/linera-rpc/src/grpc/conversions.rs index 99aee28a088b..13c32797ed25 100644 --- a/linera-rpc/src/grpc/conversions.rs +++ b/linera-rpc/src/grpc/conversions.rs @@ -637,7 +637,7 @@ impl TryFrom for ValidatorPublicKey { type Error = GrpcProtoConversionError; fn try_from(public_key: api::ValidatorPublicKey) -> Result { - Ok(ValidatorPublicKey::from_bytes(public_key.bytes.as_slice())) + Ok(ValidatorPublicKey::from_bytes(public_key.bytes.as_slice())?) } } diff --git a/prepush.sh b/prepush.sh new file mode 100755 index 000000000000..766490e44c37 --- /dev/null +++ b/prepush.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e +set -x + +export RUSTFLAGS="-D warnings" + +# cargo sort -w +# (set -e; cd examples && cargo sort -w) + +(set -e; for I in linera-*; do if [ -d "$I" ]; then echo $I; cargo rdme -w $I; fi; done) +(set -e; cd examples; for dir in */; do + if [ -f "${dir}README.md" ] && grep -q "" "${dir}README.md"; then + dir_name="${dir%/}" + echo "${dir_name}" + cargo rdme -fw "${dir_name}" + fi +done) + +# cargo machete +# cargo +nightly fmt +# (set -e; cd examples && cargo +nightly fmt) + +# cargo run --locked --bin linera help-markdown > CLI.md +# cargo run --locked --bin linera-schema-export > linera-service-graphql-client/gql/service_schema.graphql +# cargo run --locked --bin linera-indexer schema > linera-indexer/graphql-client/gql/indexer_schema.graphql +# cargo run --locked --bin linera-indexer schema operations > linera-indexer/graphql-client/gql/operations_schema.graphql +# rm -r indexer.db + +# cargo build -p linera-sdk +# cargo build -p linera-witty-test-modules --target wasm32-unknown-unknown + +# cargo clippy --locked --all-targets --all-features +# cargo clippy --locked --no-default-features +# # (set -e; cd examples; cargo clippy --locked --all-targets --all-features) +# # (set -e; cd examples; cargo clippy --locked --no-default-features) + +# cargo run --locked --bin wit-generator + +# (cd scripts/check_copyright_header && cargo build --release --locked) +# find linera-* examples -name '*.rs' -a -not -wholename '*/target/*' -print0 | xargs -0 scripts/target/release/check_copyright_header + +cargo test --locked -p linera-rpc test_format || (cargo insta accept && cargo test --locked -p linera-rpc test_format) \ No newline at end of file