Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: align Signature with octez crypto impl #60

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ 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.
- Altered hashes to implement `AsRef<[u8]>` instead of `AsRef<Vec<u8>>`.
- Renamed `hash::Signature` to `hash::UnknownSignature`.

### Deprecated

Expand Down
4 changes: 2 additions & 2 deletions crypto/src/blake2b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ where
// this implementation will calculate the same hash [5, 5] two times.
pub fn merkle_tree<Leaf>(list: &[Leaf]) -> Result<Vec<u8>, Blake2bError>
where
Leaf: AsRef<Vec<u8>>,
Leaf: AsRef<[u8]>,
{
use std::ops::{Index, RangeFrom, RangeTo};

Expand Down Expand Up @@ -156,7 +156,7 @@ where
degree: u32,
) -> Result<Vec<u8>, Blake2bError>
where
Leaf: AsRef<Vec<u8>>,
Leaf: AsRef<[u8]>,
{
match degree {
0 => digest_256(list[0].as_ref()),
Expand Down
52 changes: 33 additions & 19 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 @@ -51,7 +52,7 @@ mod prefix_bytes {

pub type Hash = Vec<u8>;

pub trait HashTrait: Into<Hash> + AsRef<Hash> {
pub trait HashTrait: Into<Hash> + AsRef<[u8]> {
/// Returns this hash type.
fn hash_type() -> HashType;

Expand All @@ -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 @@ -171,8 +172,8 @@ macro_rules! define_hash {
}
}

impl std::convert::AsRef<Hash> for $name {
fn as_ref(&self) -> &Hash {
impl std::convert::AsRef<[u8]> for $name {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
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
130 changes: 130 additions & 0 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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 {
emturner marked this conversation as resolved.
Show resolved Hide resolved
Ed25519(Ed25519Signature),
Secp256k1(Secp256k1Signature),
P256(P256Signature),
Bls(BlsSignature),
Unknown(UnknownSignature),
}

impl Signature {
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 {
emturner marked this conversation as resolved.
Show resolved Hide resolved
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 From<Signature> for Vec<u8> {
fn from(s: Signature) -> Self {
match s {
Signature::Ed25519(s) => s.0,
Signature::Secp256k1(s) => s.0,
Signature::P256(s) => s.0,
Signature::Bls(s) => s.0,
Signature::Unknown(s) => s.0,
}
}
}

impl TryFrom<Vec<u8>> for Signature {
type Error = FromBytesError;

fn try_from(hash: Vec<u8>) -> Result<Self, Self::Error> {
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)
}
}
}

impl TryFrom<&[u8]> for Signature {
type Error = FromBytesError;

fn try_from(hash: &[u8]) -> Result<Self, Self::Error> {
if hash.len() == HashType::BlsSignature.size() {
Ok(Signature::Bls(BlsSignature(hash.to_vec())))
} else if hash.len() == HashType::UnknownSignature.size() {
Ok(Signature::Unknown(UnknownSignature(hash.to_vec())))
} else {
Err(FromBytesError::InvalidSize)
}
}
}

impl TryFrom<&str> for Signature {
type Error = FromBase58CheckError;

fn try_from(s: &str) -> Result<Self, Self::Error> {
Signature::from_base58_check(s)
}
}

impl ::core::str::FromStr for Signature {
type Err = FromBase58CheckError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Signature::from_base58_check(s)
}
}

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
Loading