Skip to content

Commit

Permalink
Fix todos
Browse files Browse the repository at this point in the history
  • Loading branch information
deuszx committed Feb 25, 2025
1 parent 4cf8f77 commit 26fcca2
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 23 deletions.
6 changes: 5 additions & 1 deletion linera-base/src/crypto/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@ impl TryFrom<&[u8]> for Ed25519PublicKey {

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
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);
Expand Down
10 changes: 6 additions & 4 deletions linera-base/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}")]
Expand Down
71 changes: 54 additions & 17 deletions linera-base/src/crypto/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<Self, CryptoError> {
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),

Check failure on line 104 in linera-base/src/crypto/secp256k1.rs

View workflow job for this annotation

GitHub Actions / test

no function or associated item named `from_source` found for struct `ed25519_dalek::ed25519::Error` in the current scope

Check failure on line 104 in linera-base/src/crypto/secp256k1.rs

View workflow job for this annotation

GitHub Actions / execution-wasmtime-test

no function or associated item named `from_source` found for struct `ed25519_dalek::ed25519::Error` in the current scope
)),
}
}
}

Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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)?)
}
}
}
Expand All @@ -151,9 +188,7 @@ impl TryFrom<&[u8]> for Secp256k1PublicKey {
type Error = CryptoError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
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)
}
}

Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -326,19 +361,21 @@ 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.
// Since we want a fixed size representation, we wrap it in this helper struct and use serde_as.
#[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)]
Expand Down Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion linera-rpc/src/grpc/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ impl TryFrom<api::ValidatorPublicKey> for ValidatorPublicKey {
type Error = GrpcProtoConversionError;

fn try_from(public_key: api::ValidatorPublicKey) -> Result<Self, Self::Error> {
Ok(ValidatorPublicKey::from_bytes(public_key.bytes.as_slice()))
Ok(ValidatorPublicKey::from_bytes(public_key.bytes.as_slice())?)
}
}

Expand Down
43 changes: 43 additions & 0 deletions prepush.sh
Original file line number Diff line number Diff line change
@@ -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 "<!-- cargo-rdme start -->" "${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)

0 comments on commit 26fcca2

Please sign in to comment.