Skip to content

Commit

Permalink
crypto: align Signature with octez crypto impl
Browse files Browse the repository at this point in the history
  • Loading branch information
emturner committed Dec 19, 2023
1 parent 2347939 commit 3aa4bea
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- Add `FromBase58CheckError::IncorrectBase58Prefix` variant.
- Add `NomReader`, `BinWriter` support for `Ed25519Signature`.
- Add `signature::Signature` enum representing possible types of signature used in Tezos.

### Changed

- `tezos_data_encoding`: The `NomReader` trait is now explicitly
parameterized by the lifetime of the input byte slice.
- Renamed `hash::Signature` to `hash::UnknownSignature`.

### Deprecated

Expand Down
46 changes: 30 additions & 16 deletions crypto/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::convert::{TryFrom, TryInto};
use crate::{
base58::{FromBase58Check, FromBase58CheckError, ToBase58Check},
blake2b::{self, Blake2bError},
signature::Signature,
CryptoError, PublicKeySignatureVerifier, PublicKeyWithHash,
};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -75,7 +76,7 @@ pub enum FromBytesError {
/// Invalid data size
#[error("invalid hash size")]
InvalidSize,
/// Ed25519 decrompression
/// Ed25519 decompression
#[error("From bytes ed25519: {0:?}")]
Ed25519(ed25519_dalek::SignatureError),
}
Expand Down Expand Up @@ -308,7 +309,7 @@ define_hash!(PublicKeyBls);
define_hash!(SeedEd25519);
define_hash!(SecretKeyEd25519);
define_hash!(SecretKeyBls);
define_hash!(Signature);
define_hash!(UnknownSignature);
define_hash!(Ed25519Signature);
define_hash!(Secp256k1Signature);
define_hash!(P256Signature);
Expand Down Expand Up @@ -367,7 +368,7 @@ pub enum HashType {
// "\003\150\192\040" (* BLsk(54) *)
SecretKeyBls,
// "\004\130\043" (* sig(96) *)
Signature,
UnknownSignature,
// "\009\245\205\134\018" (* edsig(99) *)
Ed25519Signature,
// "\013\115\101\019\063" (* spsig1(99) *)
Expand Down Expand Up @@ -412,7 +413,7 @@ impl HashType {
HashType::SeedEd25519 => &SEED_ED25519,
HashType::SecretKeyEd25519 => &SECRET_KEY_ED25519,
HashType::SecretKeyBls => &SECRET_KEY_BLS,
HashType::Signature => &GENERIC_SIGNATURE_HASH,
HashType::UnknownSignature => &GENERIC_SIGNATURE_HASH,
HashType::Ed25519Signature => &ED22519_SIGNATURE_HASH,
HashType::Secp256k1Signature => &SECP256K1_SIGNATURE_HASH,
HashType::P256Signature => &P256_SIGNATURE_HASH,
Expand Down Expand Up @@ -453,7 +454,7 @@ impl HashType {
| HashType::Ed25519Signature
| HashType::Secp256k1Signature
| HashType::P256Signature
| HashType::Signature => 64,
| HashType::UnknownSignature => 64,
HashType::BlsSignature => 96,
}
}
Expand All @@ -464,7 +465,8 @@ impl HashType {
Err(FromBytesError::InvalidSize)
} else {
let mut hash = Vec::with_capacity(self.base58check_prefix().len() + data.len());
if matches!(self, Self::Signature) && data == [0; Self::Ed25519Signature.size()] {
if matches!(self, Self::UnknownSignature) && data == [0; Self::Ed25519Signature.size()]
{
hash.extend(Self::Ed25519Signature.base58check_prefix());
} else {
hash.extend(self.base58check_prefix());
Expand All @@ -482,7 +484,7 @@ impl HashType {
/// Convert string representation of the hash to bytes form.
pub fn b58check_to_hash(&self, data: &str) -> Result<Hash, FromBase58CheckError> {
let mut hash = data.from_base58check()?;
if let HashType::Signature = self {
if let HashType::UnknownSignature = self {
// zero signature is represented as Ed25519 signature
if hash.len()
== HashType::Ed25519Signature.size()
Expand Down Expand Up @@ -665,7 +667,9 @@ impl SecretKeyEd25519 {
let payload = crate::blake2b::digest_256(data.as_ref())
.map_err(|e| CryptoError::AlgorithmError(e.to_string()))?;
let signature = sk.sign(&payload);
Ok(Signature(signature.to_bytes().to_vec()))
Ok(Signature::Ed25519(Ed25519Signature(
signature.to_bytes().to_vec(),
)))
}
}

Expand All @@ -676,8 +680,7 @@ impl PublicKeySignatureVerifier for PublicKeyEd25519 {
/// Verifies the correctness of `bytes` signed by Ed25519 as the `signature`.
fn verify_signature(&self, signature: &Signature, bytes: &[u8]) -> Result<bool, Self::Error> {
let signature = signature
.0
.as_slice()
.as_ref()
.try_into()
.map(ed25519_dalek::Signature::from_bytes)
.map_err(|_| CryptoError::InvalidSignature)?;
Expand Down Expand Up @@ -706,7 +709,7 @@ impl PublicKeySignatureVerifier for PublicKeySecp256k1 {
Some(libsecp256k1::PublicKeyFormat::Compressed),
)
.map_err(|_| CryptoError::InvalidPublicKey)?;
let sig = libsecp256k1::Signature::parse_standard_slice(&signature.0)
let sig = libsecp256k1::Signature::parse_standard_slice(signature.as_ref())
.map_err(|_| CryptoError::InvalidSignature)?;
let msg =
libsecp256k1::Message::parse_slice(bytes).map_err(|_| CryptoError::InvalidMessage)?;
Expand Down Expand Up @@ -767,10 +770,10 @@ impl PublicKeySignatureVerifier for PublicKeyP256 {

let pk = p256::ecdsa::VerifyingKey::from_sec1_bytes(&self.0)
.map_err(|_| CryptoError::InvalidPublicKey)?;
let r: [u8; 32] = signature.0[..32]
let r: [u8; 32] = signature.as_ref()[..32]
.try_into()
.map_err(|_| CryptoError::InvalidSignature)?;
let s: [u8; 32] = signature.0[32..]
let s: [u8; 32] = signature.as_ref()[32..]
.try_into()
.map_err(|_| CryptoError::InvalidSignature)?;
let sig = p256::ecdsa::Signature::from_scalars(r, s)
Expand Down Expand Up @@ -1097,13 +1100,13 @@ mod tests {
#[test]
fn test_b85_to_signature_hash() -> Result<(), anyhow::Error> {
let encoded = "sigbQ5ZNvkjvGssJgoAnUAfY4Wvvg3QZqawBYB1j1VDBNTMBAALnCzRHWzer34bnfmzgHg3EvwdzQKdxgSghB897cono6gbQ";
let decoded = hex::encode(HashType::Signature.b58check_to_hash(encoded)?);
let decoded = hex::encode(HashType::UnknownSignature.b58check_to_hash(encoded)?);
let expected = "66804fe735e06e97e26da8236b6341b91c625d5e82b3524ec0a88cc982365e70f8a5b9bc65df2ea6d21ee244cc3a96fb33031c394c78b1179ff1b8a44237740c";
assert_eq!(expected, decoded);

assert_eq!(
encoded,
HashType::Signature.hash_to_b58check(&hex::decode(decoded)?)?
HashType::UnknownSignature.hash_to_b58check(&hex::decode(decoded)?)?
);
Ok(())
}
Expand Down Expand Up @@ -1299,7 +1302,18 @@ mod tests {
["p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"]
);

test!(generic_sig, Signature, ["sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1"]);
test!(generic_sig, UnknownSignature, ["sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1"]);

use crate::signature::Signature;
test!(
composite_sig,
Signature,
[
"BLsigAmLKnuw12tethjMmotFPaQ6u4XCKrVk6c15dkRXKkjDDjHywbhS3nd4rBT31yrCvvQrS2HntWhDRu7sX8Vvek53zBUwQHqfcHRiVKVj1ehq8CBYs1Z7XW2rkL2XkVNHua4cnvxY7F",
"edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q",
"spsig1PPUFZucuAQybs5wsqsNQ68QNgFaBnVKMFaoZZfi1BtNnuCAWnmL9wVy5HfHkR6AeodjVGxpBVVSYcJKyMURn6K1yknYLm",
"p2sigRmXDp38VNVaEQH28LYukfLPn8QB5hPEberhvQrrUpRscDZJrrApbRh2u46PTVTwKXjxTLKNN9dyLhPQU6U6jWPGxe4d9v"
]);

test!(
smart_rollup_hash,
Expand Down
1 change: 1 addition & 0 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod base58;
pub mod bls;
#[macro_use]
pub mod hash;
pub mod signature;

#[derive(Debug, Error)]
pub enum CryptoError {
Expand Down
85 changes: 85 additions & 0 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: 2023 TriliTech <[email protected]>
// Copyright (c) SimpleStaking, Viable Systems and Tezedge Contributors
//
// Ported from octez: lib_crypto/signature_v1.ml
//
// Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <[email protected]>
// Copyright (c) 2020 Metastate AG <[email protected]>
// Copyright (c) 2022 Nomadic Labs <[email protected]>
//
// SPDX-License-Identifier: MIT

use crate::base58::FromBase58CheckError;
use crate::hash::{
BlsSignature, Ed25519Signature, FromBytesError, HashTrait, HashType, P256Signature,
Secp256k1Signature, UnknownSignature,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Signature {
Ed25519(Ed25519Signature),
Secp256k1(Secp256k1Signature),
P256(P256Signature),
Bls(BlsSignature),
Unknown(UnknownSignature),
}

impl Signature {
#[doc(hidden)]
pub fn from_vec(hash: Vec<u8>) -> Result<Self, FromBytesError> {
if hash.len() == HashType::BlsSignature.size() {
Ok(Signature::Bls(BlsSignature(hash)))
} else if hash.len() == HashType::UnknownSignature.size() {
Ok(Signature::Unknown(UnknownSignature(hash)))
} else {
Err(FromBytesError::InvalidSize)
}
}

pub fn from_base58_check(data: &str) -> Result<Self, FromBase58CheckError> {
if data.starts_with("edsig") {
Ok(Signature::Ed25519(Ed25519Signature::from_b58check(data)?))
} else if data.starts_with("spsig1") {
Ok(Signature::Secp256k1(Secp256k1Signature::from_b58check(
data,
)?))
} else if data.starts_with("p2sig") {
Ok(Signature::P256(P256Signature::from_b58check(data)?))
} else if data.starts_with("BLsig") {
Ok(Signature::Bls(BlsSignature::from_b58check(data)?))
} else {
Ok(Signature::Unknown(UnknownSignature::from_b58check(data)?))
}
}

pub fn to_base58_check(&self) -> String {
match self {
Self::Ed25519(s) => s.to_b58check(),
Self::Secp256k1(s) => s.to_b58check(),
Self::P256(s) => s.to_b58check(),
Self::Bls(s) => s.to_b58check(),
Self::Unknown(s) => s.to_b58check(),
}
}
}

impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
match self {
Self::Ed25519(s) => s.0.as_ref(),
Self::Secp256k1(s) => s.0.as_ref(),
Self::P256(s) => s.0.as_ref(),
Self::Bls(s) => s.0.as_ref(),
Self::Unknown(s) => s.0.as_ref(),
}
}
}

impl ::std::fmt::Display for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// TODO - this could be done without the need to perform a heap allocation.
write!(f, "{}", self.to_base58_check())
}
}
8 changes: 7 additions & 1 deletion tezos-encoding/src/enc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,14 +351,20 @@ encode_hash!(crypto::hash::PublicKeyP256);
encode_hash!(crypto::hash::PublicKeyBls);
encode_hash!(crypto::hash::SecretKeyEd25519);
encode_hash!(crypto::hash::SecretKeyBls);
encode_hash!(crypto::hash::Signature);
encode_hash!(crypto::hash::UnknownSignature);
encode_hash!(crypto::hash::Ed25519Signature);
encode_hash!(crypto::hash::Secp256k1Signature);
encode_hash!(crypto::hash::P256Signature);
encode_hash!(crypto::hash::BlsSignature);
encode_hash!(crypto::hash::NonceHash);
encode_hash!(crypto::hash::SmartRollupHash);

impl BinWriter for crypto::signature::Signature {
fn bin_write(&self, out: &mut Vec<u8>) -> BinResult {
dynamic(bytes)(self, out)
}
}

impl BinWriter for Mutez {
fn bin_write(&self, out: &mut Vec<u8>) -> BinResult {
n_bignum(self.0.magnitude(), out)
Expand Down
11 changes: 10 additions & 1 deletion tezos-encoding/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,20 @@ hash_has_encoding!(PublicKeyP256, PUBLIC_KEY_P256);
hash_has_encoding!(PublicKeyBls, PUBLIC_KEY_BLS);
hash_has_encoding!(SecretKeyEd25519, SECRET_KEY_ED25519);
hash_has_encoding!(SecretKeyBls, SECRET_KEY_BLS);
hash_has_encoding!(Signature, SIGNATURE);
hash_has_encoding!(UnknownSignature, UNKNOWN_SIGNATURE);
hash_has_encoding!(Ed25519Signature, ED25519_SIGNATURE_HASH);
hash_has_encoding!(Secp256k1Signature, SECP256K1_SIGNATURE_HASH);
hash_has_encoding!(P256Signature, P256_SIGNATURE_HASH);
hash_has_encoding!(BlsSignature, BLS_SIGNATURE_HASH);
hash_has_encoding!(NonceHash, NONCE_HASH);
hash_has_encoding!(SmartRollupHash, SMART_ROLLUP_HASH);

impl HasEncoding for crypto::signature::Signature {
fn encoding() -> Encoding {
Encoding::Custom
}
}

/// Creates impl HasEncoding for given struct backed by lazy_static ref instance with encoding.
#[macro_export]
macro_rules! has_encoding {
Expand Down
16 changes: 15 additions & 1 deletion tezos-encoding/src/nom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,28 @@ hash_nom_reader!(PublicKeyP256);
hash_nom_reader!(PublicKeyBls);
hash_nom_reader!(SecretKeyEd25519);
hash_nom_reader!(SecretKeyBls);
hash_nom_reader!(Signature);
hash_nom_reader!(UnknownSignature);
hash_nom_reader!(Ed25519Signature);
hash_nom_reader!(Secp256k1Signature);
hash_nom_reader!(P256Signature);
hash_nom_reader!(BlsSignature);
hash_nom_reader!(NonceHash);
hash_nom_reader!(SmartRollupHash);

impl<'a> NomReader<'a> for crypto::signature::Signature {
fn nom_read(input: &'a [u8]) -> NomResult<'a, Self> {
let (rest, v) = dynamic(bytes)(input)?;
if let Ok(v) = Self::from_vec(v) {
Ok((rest, v))
} else {
Err(Err::Error(DecodeError::limit(
input,
BoundedEncodingKind::Bounded,
)))
}
}
}

impl<'a> NomReader<'a> for Zarith {
fn nom_read(bytes: &[u8]) -> NomResult<Self> {
map(z_bignum, |big_int| big_int.into())(bytes)
Expand Down

0 comments on commit 3aa4bea

Please sign in to comment.