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

Secp256r1 signature recovery and Ed25519 verification #486

Merged
merged 19 commits into from
Jul 12, 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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

Description of the upcoming release here.

### Added

- [#486](https://github.com/FuelLabs/fuel-vm/pull/486/): Adds `ed25519` signature verification and `secp256r1` signature recovery to `fuel-crypto`, and corresponding opcodes `ED19` and `ECR1` to `fuel-vm`.

### Removed

#### Breaking

- [#486](https://github.com/FuelLabs/fuel-vm/pull/486/): Removes apparently unused `Keystore` and `Signer` traits from `fuel-crypto`. Also renames `ECR` opcode to `ECK1`.

### Fixed

#### Breaking
Expand Down
2 changes: 1 addition & 1 deletion fuel-asm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let program = vec![
op::addi(0x10, 0x07, 1), // set r[0x10] := `$hp + 1` (allocated heap)
op::move_(0x11, 0x04), // set r[0x11] := $ssp
op::add(0x12, 0x04, 0x20), // set r[0x12] := `$ssp + r[0x20]`
op::ecr(0x10, 0x11, 0x12), // recover public key in memory[r[0x10], 64]
op::eck1(0x10, 0x11, 0x12),// recover public key in memory[r[0x10], 64]
op::ret(0x01), // return `1`
];

Expand Down
22 changes: 13 additions & 9 deletions fuel-asm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,17 @@ impl_instructions! {
"Transfer coins to a variable output."
0x3D TRO tro [contract_id_addr: RegId output_index: RegId amount: RegId asset_id_addr: RegId]
"The 64-byte public key (x, y) recovered from 64-byte signature on 32-byte message."
0x3E ECR ecr [dst_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
0x3E ECK1 eck1 [dst_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
"The 64-byte Secp256r1 public key (x, y) recovered from 64-byte signature on 32-byte message."
0x3F ECR1 ecr1 [dst_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
"Verify ED25519 public key and signature match a 32-byte message."
0x40 ED19 ed19 [pub_key_addr: RegId sig_addr: RegId msg_hash_addr: RegId]
"The keccak-256 hash of a slice."
0x3F K256 k256 [dst_addr: RegId src_addr: RegId len: RegId]
0x41 K256 k256 [dst_addr: RegId src_addr: RegId len: RegId]
"The SHA-2-256 hash of a slice."
0x40 S256 s256 [dst_addr: RegId src_addr: RegId len: RegId]
0x42 S256 s256 [dst_addr: RegId src_addr: RegId len: RegId]
"Get timestamp of block at given height."
0x41 TIME time [dst: RegId heigth: RegId]
0x43 TIME time [dst: RegId heigth: RegId]

"Performs no operation."
0x47 NOOP noop []
Expand Down Expand Up @@ -489,11 +493,11 @@ impl Opcode {
ADD | AND | DIV | EQ | EXP | GT | LT | MLOG | MROO | MOD | MOVE | MUL
| NOT | OR | SLL | SRL | SUB | XOR | WDCM | WQCM | WDOP | WQOP | WDML
| WQML | WDDV | WQDV | WDMD | WQMD | WDAM | WQAM | WDMM | WQMM | RET
| ALOC | MCL | MCP | MEQ | ECR | K256 | S256 | NOOP | FLAG | ADDI | ANDI
| DIVI | EXPI | MODI | MULI | MLDV | ORI | SLLI | SRLI | SUBI | XORI
| JNEI | LB | LW | SB | SW | MCPI | MCLI | GM | MOVI | JNZI | JI | JMP
| JNE | JMPF | JMPB | JNZF | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS
| GTF => true,
| ALOC | MCL | MCP | MEQ | ECK1 | ECR1 | ED19 | K256 | S256 | NOOP | FLAG
| ADDI | ANDI | DIVI | EXPI | MODI | MULI | MLDV | ORI | SLLI | SRLI
| SUBI | XORI | JNEI | LB | LW | SB | SW | MCPI | MCLI | GM | MOVI | JNZI
| JI | JMP | JNE | JMPF | JMPB | JNZF | JNZB | JNEF | JNEB | CFEI | CFSI
| CFE | CFS | GTF => true,
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion fuel-asm/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
//! op::addi(0x10, 0x07, 1), // set r[0x10] := `$hp + 1` (allocated heap)
//! op::move_(0x11, 0x04), // set r[0x11] := $ssp
//! op::add(0x12, 0x04, 0x20), // set r[0x12] := `$ssp + r[0x20]`
//! op::ecr(0x10, 0x11, 0x12), // recover public key in memory[r[0x10], 64]
//! op::eck1(0x10, 0x11, 0x12),// recover public key in memory[r[0x10], 64]
//! op::ret(0x01), // return `1`
//! ];
//! ```
Expand Down
6 changes: 5 additions & 1 deletion fuel-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ description = "Fuel cryptographic primitives."
borrown = "0.1"
coins-bip32 = { version = "0.8", default-features = false, optional = true }
coins-bip39 = { version = "0.8", default-features = false, features = ["english"], optional = true }
ecdsa = { version = "0.16", default-features = false }
ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] }
fuel-types = { workspace = true, default-features = false }
lazy_static = { version = "1.4", optional = true }
p256 = { version = "0.13", default-features = false, features = ["digest", "ecdsa"] }
rand = { version = "0.8", default-features = false, optional = true }
secp256k1 = { version = "0.26", default-features = false, features = ["recovery"], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
Expand All @@ -25,7 +28,7 @@ zeroize = { version = "1.5", features = ["derive"] }
[dev-dependencies]
bincode = { workspace = true }
criterion = "0.4"
fuel-crypto = { path = ".", default-features = false, features = ["random"] }
fuel-crypto = { workspace = true, features = ["random", "test-helpers"] }
k256 = { version = "0.11", features = [ "ecdsa" ] }
sha2 = "0.10"

Expand All @@ -38,6 +41,7 @@ serde = ["dep:serde", "fuel-types/serde"]
# the deterministic arguments of the signature (key, nonce, message), as defined in the RFC-6979
std = ["alloc", "coins-bip32", "coins-bip39", "fuel-types/std", "lazy_static", "rand?/std_rng", "secp256k1/rand-std", "serde?/default"]
wasm = ["secp256k1/rand"]
test-helpers = []

[[bench]]
name = "signature"
Expand Down
33 changes: 33 additions & 0 deletions fuel-crypto/src/ed25519.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! ED25519 signature verification

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we rexport the dalek lib, (maybe even under test-helpers)? That way applications using ed25519 don't have to worry about importing the correct version of dalek or pontentially compiling it twice.

use ed25519_dalek::Signature;
use fuel_types::{
Bytes32,
Bytes64,
};

use crate::{
Error,
Message,
};

/// Verify a signature against a message digest and a public key.
pub fn verify(
pub_key: &Bytes32,
signature: &Bytes64,
message: &Message,
) -> Result<(), Error> {
let Ok(signature) = Signature::from_bytes(&**signature) else {
return Err(Error::InvalidSignature);
};

let Ok(pub_key) = ed25519_dalek::PublicKey::from_bytes(&**pub_key) else {
return Err(Error::InvalidPublicKey);
};

if pub_key.verify_strict(&**message, &signature).is_ok() {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}
3 changes: 3 additions & 0 deletions fuel-crypto/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub enum Error {
/// Invalid secp256k1 signature
InvalidSignature,

/// Coudln't sign the message
FailedToSign,

/// The provided key wasn't found
KeyNotFound,

Expand Down
43 changes: 0 additions & 43 deletions fuel-crypto/src/keystore.rs

This file was deleted.

31 changes: 21 additions & 10 deletions fuel-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#![deny(unsafe_code)]
#![deny(unused_crate_dependencies)]

#[cfg(test)]
// Satisfy unused_crate_dependencies lint for self-dependency enabling test features
use fuel_crypto as _;

/// Required export to implement [`Keystore`].
#[doc(no_inline)]
pub use borrown;
Expand All @@ -30,24 +34,31 @@ pub use rand;

mod error;
mod hasher;
mod keystore;
mod message;
mod mnemonic;
mod public;
mod secret;
mod signature;
mod signer;

pub mod ed25519;
pub mod secp256r1;

#[cfg(test)]
mod tests;

pub use error::Error;
pub use hasher::Hasher;
pub use keystore::Keystore;
pub use message::Message;

#[cfg(all(feature = "std", feature = "random"))]
pub use mnemonic::generate_mnemonic_phrase;
pub use public::PublicKey;
pub use secret::SecretKey;
pub use signature::Signature;
pub use signer::Signer;

mod secp256k1 {
mod public;
mod secret;
mod signature;

pub use public::PublicKey;
pub use secret::SecretKey;
pub use signature::Signature;
}

// The default cryptographic primitives
pub use self::secp256k1::*;
2 changes: 1 addition & 1 deletion fuel-crypto/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::{
};
pub use fuel_types::Bytes32;

/// Normalized signature message
/// Normalized (hashed) message authenticated by a signature
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use fuel_types::{
Bytes64,
};

/// Asymmetric public key
/// Asymmetric secp256k1 public key
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
Expand Down
File renamed without changes.
File renamed without changes.
Loading