Skip to content

Commit

Permalink
Compiler assembly support for ecr1 and ed19 (#5133)
Browse files Browse the repository at this point in the history
## Description
Closes #5125 .

There are no tests added as I am not familiar with how these
instructions are actually used. @IGI-111 Please add assignment to
someone who can add the corresponding stdlib functions and tests for it.

---------

Co-authored-by: Cameron Carstens <[email protected]>
Co-authored-by: bitzoic <[email protected]>
Co-authored-by: IGI-111 <[email protected]>
Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
5 people authored Oct 16, 2023
1 parent 13517ea commit 7f1422f
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 4 deletions.
4 changes: 3 additions & 1 deletion sway-ast/src/expr/op_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@ define_op_codes!(
(addr: reg, output: reg, coins: reg, asset: reg)
),
/* Cryptographic Instructions */
(Eck1, EcrOpcode, "eck1", (addr: reg, sig: reg, hash: reg)),
(Eck1, Eck1Opcode, "eck1", (addr: reg, sig: reg, hash: reg)),
(Ecr1, Ecr1Opcode, "ecr1", (addr: reg, sig: reg, hash: reg)),
(Ed19, Ed19Opcode, "ed19", (addr: reg, sig: reg, hash: reg)),
(K256, K256Opcode, "k256", (addr: reg, data: reg, size: reg)),
(S256, S256Opcode, "s256", (addr: reg, data: reg, size: reg)),
/* Other Instructions */
Expand Down
8 changes: 8 additions & 0 deletions sway-core/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ pub(crate) enum AllocatedOpcode {

/* Cryptographic Instructions */
ECK1(AllocatedRegister, AllocatedRegister, AllocatedRegister),
ECR1(AllocatedRegister, AllocatedRegister, AllocatedRegister),
ED19(AllocatedRegister, AllocatedRegister, AllocatedRegister),
K256(AllocatedRegister, AllocatedRegister, AllocatedRegister),
S256(AllocatedRegister, AllocatedRegister, AllocatedRegister),

Expand Down Expand Up @@ -337,6 +339,8 @@ impl AllocatedOpcode {

/* Cryptographic Instructions */
ECK1(_r1, _r2, _r3) => vec![],
ECR1(_r1, _r2, _r3) => vec![],
ED19(_r1, _r2, _r3) => vec![],
K256(_r1, _r2, _r3) => vec![],
S256(_r1, _r2, _r3) => vec![],

Expand Down Expand Up @@ -457,6 +461,8 @@ impl fmt::Display for AllocatedOpcode {

/* Cryptographic Instructions */
ECK1(a, b, c) => write!(fmtr, "eck1 {a} {b} {c}"),
ECR1(a, b, c) => write!(fmtr, "ecr1 {a} {b} {c}"),
ED19(a, b, c) => write!(fmtr, "ed19 {a} {b} {c}"),
K256(a, b, c) => write!(fmtr, "k256 {a} {b} {c}"),
S256(a, b, c) => write!(fmtr, "s256 {a} {b} {c}"),

Expand Down Expand Up @@ -636,6 +642,8 @@ impl AllocatedOp {

/* Cryptographic Instructions */
ECK1(a, b, c) => op::ECK1::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
ECR1(a, b, c) => op::ECR1::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
ED19(a, b, c) => op::ED19::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
K256(a, b, c) => op::K256::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
S256(a, b, c) => op::S256::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),

Expand Down
10 changes: 10 additions & 0 deletions sway-core/src/asm_lang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,14 @@ impl Op {
let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?;
VirtualOp::ECK1(r1, r2, r3)
}
"ecr1" => {
let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?;
VirtualOp::ECR1(r1, r2, r3)
}
"ed19" => {
let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?;
VirtualOp::ED19(r1, r2, r3)
}
"k256" => {
let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?;
VirtualOp::K256(r1, r2, r3)
Expand Down Expand Up @@ -1104,6 +1112,8 @@ impl fmt::Display for VirtualOp {

/* Cryptographic Instructions */
ECK1(a, b, c) => write!(fmtr, "eck1 {a} {b} {c}"),
ECR1(a, b, c) => write!(fmtr, "ecr1 {a} {b} {c}"),
ED19(a, b, c) => write!(fmtr, "ed19 {a} {b} {c}"),
K256(a, b, c) => write!(fmtr, "k256 {a} {b} {c}"),
S256(a, b, c) => write!(fmtr, "s256 {a} {b} {c}"),

Expand Down
28 changes: 28 additions & 0 deletions sway-core/src/asm_lang/virtual_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ pub(crate) enum VirtualOp {

/* Cryptographic Instructions */
ECK1(VirtualRegister, VirtualRegister, VirtualRegister),
ECR1(VirtualRegister, VirtualRegister, VirtualRegister),
ED19(VirtualRegister, VirtualRegister, VirtualRegister),
K256(VirtualRegister, VirtualRegister, VirtualRegister),
S256(VirtualRegister, VirtualRegister, VirtualRegister),

Expand Down Expand Up @@ -297,6 +299,8 @@ impl VirtualOp {

/* Cryptographic Instructions */
ECK1(r1, r2, r3) => vec![r1, r2, r3],
ECR1(r1, r2, r3) => vec![r1, r2, r3],
ED19(r1, r2, r3) => vec![r1, r2, r3],
K256(r1, r2, r3) => vec![r1, r2, r3],
S256(r1, r2, r3) => vec![r1, r2, r3],

Expand Down Expand Up @@ -413,6 +417,8 @@ impl VirtualOp {

/* Cryptographic Instructions */
ECK1(r1, r2, r3) => vec![r1, r2, r3],
ECR1(r1, r2, r3) => vec![r1, r2, r3],
ED19(r1, r2, r3) => vec![r1, r2, r3],
K256(r1, r2, r3) => vec![r1, r2, r3],
S256(r1, r2, r3) => vec![r1, r2, r3],

Expand Down Expand Up @@ -529,6 +535,8 @@ impl VirtualOp {

/* Cryptographic Instructions */
ECK1(_r1, _r2, _r3) => vec![],
ECR1(_r1, _r2, _r3) => vec![],
ED19(_r1, _r2, _r3) => vec![],
K256(_r1, _r2, _r3) => vec![],
S256(_r1, _r2, _r3) => vec![],

Expand Down Expand Up @@ -935,6 +943,16 @@ impl VirtualOp {
update_reg(reg_to_reg_map, r2),
update_reg(reg_to_reg_map, r3),
),
ECR1(r1, r2, r3) => Self::ECR1(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
update_reg(reg_to_reg_map, r3),
),
ED19(r1, r2, r3) => Self::ED19(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
update_reg(reg_to_reg_map, r3),
),
K256(r1, r2, r3) => Self::K256(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
Expand Down Expand Up @@ -1382,6 +1400,16 @@ impl VirtualOp {
map_reg(&mapping, reg2),
map_reg(&mapping, reg3),
),
ECR1(reg1, reg2, reg3) => AllocatedOpcode::ECR1(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
map_reg(&mapping, reg3),
),
ED19(reg1, reg2, reg3) => AllocatedOpcode::ED19(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
map_reg(&mapping, reg3),
),
K256(reg1, reg2, reg3) => AllocatedOpcode::K256(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
Expand Down
202 changes: 200 additions & 2 deletions sway-lib-std/src/ecr.sw
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ pub enum EcRecoverError {
/// Recover the public key derived from the private key used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
///
/// # Additional Informaiton
///
/// Follows the Secp256k1 elliptical curve.
///
/// # Arguments
///
/// * `signature`: [B512] - The signature generated by signing a message hash.
Expand All @@ -28,7 +32,7 @@ pub enum EcRecoverError {
/// # Examples
///
/// ```sway
/// use std::{erc::ec_recover, b512::B512};
/// use std::{ecr::ec_recover, b512::B512};
///
/// fn foo() {
/// let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c;
Expand Down Expand Up @@ -58,9 +62,110 @@ pub fn ec_recover(signature: B512, msg_hash: b256) -> Result<B512, EcRecoverErro
}
}

/// Recover the public key derived from the private key used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
///
/// # Additional Informaiton
///
/// Follows the Secp256r1 elliptical curve.
///
/// # Arguments
///
/// * `signature`: [B512] - The signature generated by signing a message hash.
/// * `msg_hash`: [b256] - The signed data.
///
/// # Returns
///
/// * [Result<B512, EcRecoverError>] - The recovered public key or an error.
///
/// # Examples
///
/// ```sway
/// use std::{ecr::ec_recover_r1, b512::B512};
///
/// fn foo() {
/// let hi = 0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac;
/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d;
/// let msg_hash = 0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323;
/// let pub_hi = 0xD73A188181464CC84AE267E45041AEF6AB938F278E636AA1D02D3014C1BEF74E;
/// let pub_lo = 0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452;
/// let signature: B512 = B512::from((hi, lo));
/// // A recovered public key pair.
/// let public_key = ec_recover_r1(signature, msg_hash).unwrap();
///
/// assert(public_key.bytes[0] == pub_hi);
/// assert(public_key.bytes[1] == pub_lo);
/// }
/// ```
pub fn ec_recover_r1(signature: B512, msg_hash: b256) -> Result<B512, EcRecoverError> {
let public_key = B512::new();
let was_error = asm(buffer: public_key.bytes, sig: signature.bytes, hash: msg_hash) {
ecr1 buffer sig hash;
err
};
// check the $err register to see if the `ecr1` opcode succeeded
if was_error == 1 {
Err(EcRecoverError::UnrecoverablePublicKey)
} else {
Ok(public_key)
}
}

/// Verifies that a public key derived from the private key was used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
///
/// # Additional Informaiton
///
/// Follows the edDSA curve25519 verification.
///
/// # Arguments
///
/// * `public_key`: [b256] - The public key that signed the message.
/// * `signature`: [B512] - The signature generated by signing a message hash.
/// * `msg_hash`: [b256] - The hashed signed data.
///
/// # Returns
///
/// * [Result<bool, EcRecoverError>] - A verfied result or an error.
///
/// # Examples
///
/// ```sway
/// use std::{ecr::ed_verify, b512::B512, constants::ZERO_B256};
///
/// fn foo() {
/// let pub_key = 0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10;
/// let msg = ZERO_B256;
/// let msg_hash = sha256(msg);

/// let hi = 0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545;
/// let lo = 0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00;
/// let signature: B512 = B512::from((hi, lo));
/// // A verify public key with signature
/// let verfied = ed_verify(pub_key, signature, msg_hash).unwrap();
/// assert(verfied);
/// }
/// ```
pub fn ed_verify(public_key: b256, signature: B512, msg_hash: b256) -> Result<bool, EcRecoverError> {
let was_error = asm(buffer: public_key, sig: signature.bytes, hash: msg_hash) {
ed19 buffer sig hash;
err
};
// check the $err register to see if the `ed19` opcode succeeded
if was_error == 1 {
Err(EcRecoverError::UnrecoverablePublicKey)
} else {
Ok(true)
}
}

/// Recover the address derived from the private key used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
///
/// # Additional Informaiton
///
/// Follows the Secp256k1 elliptical curve.
///
/// # Arguments
///
/// * `signature`: [B512] - The signature generated by signing a message hash.
Expand All @@ -73,7 +178,7 @@ pub fn ec_recover(signature: B512, msg_hash: b256) -> Result<B512, EcRecoverErro
/// # Examples
///
/// ```sway
/// use std::{erc::ec_recover_address, b512::B512};
/// use std::{ecr::ec_recover_address, b512::B512};
///
/// fn foo() {
/// let hi = 0xbd0c9b8792876713afa8bff383eebf31c43437823ed761cc3600d0016de5110c;
Expand All @@ -98,3 +203,96 @@ pub fn ec_recover_address(signature: B512, msg_hash: b256) -> Result<Address, Ec
Ok(Address::from(address))
}
}

/// Recover the address derived from the private key used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
///
/// # Additional Informaiton
///
/// Follows the Secp256r1 elliptical curve.
///
/// # Arguments
///
/// * `signature`: [B512] - The signature generated by signing a message hash.
/// * `msg_hash`: [b256] - The signed data.
///
/// # Returns
///
/// * [Result<Address, EcRecoverError>] - The recovered Fuel address or an error.
///
/// # Examples
///
/// ```sway
/// use std::{ecr::ec_recover_address_r1, b512::B512};
///
/// fn foo() {
/// let hi = 0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c;
/// let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d;
/// let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323;
/// let address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
/// let signature: B512 = B512::from((hi, lo));
/// // A recovered Fuel address.
/// let result_address = ec_recover_address_r1(signature, msg_hash).unwrap();
/// assert(result_address == address);
/// }
/// ```
pub fn ec_recover_address_r1(signature: B512, msg_hash: b256) -> Result<Address, EcRecoverError> {
let pub_key_result = ec_recover_r1(signature, msg_hash);

if let Err(e) = pub_key_result {
// propagate the error if it exists
Err(e)
} else {
let pub_key = pub_key_result.unwrap();
let address = sha256(((pub_key.bytes)[0], (pub_key.bytes)[1]));
Ok(Address::from(address))
}
}

#[test]
fn test_ec_recover_r1() {
use ::assert::assert;

let hi = 0xbd0c9b8792876712afadbff382e1bf31c44437823ed761cc3600d0016de511ac;
let lo = 0x44ac566bd156b4fc71a4a4cb2655d3da360c695edb27dc3b64d621e122fea23d;
let msg_hash = 0x1e45523606c96c98ba970ff7cf9511fab8b25e1bcd52ced30b81df1e4a9c4323;
let pub_hi = 0xd6ea577a54ae42411fbc78d686d4abba2150ca83540528e4b868002e346004b2;
let pub_lo = 0x62660ecce5979493fe5684526e8e00875b948e507a89a47096bc84064a175452;
let signature: B512 = B512::from((hi, lo));
// A recovered public key pair.
let public_key = ec_recover_r1(signature, msg_hash).unwrap();

assert(public_key.bytes[0] == pub_hi);
assert(public_key.bytes[1] == pub_lo);
}

#[test]
fn test_ec_recover_address_r1() {
use ::assert::assert;

let hi = 0xbd0c9b8792876713afa8bf3383eebf31c43437823ed761cc3600d0016de5110c;
let lo = 0x44ac566bd156b4fc71a4a4cb2655d3dd360c695edb17dc3b64d611e122fea23d;
let msg_hash = 0xee45573606c96c98ba970ff7cf9511f1b8b25e6bcd52ced30b89df1e4a9c4323;
let address = Address::from(0xb4a5fabee8cc852084b71f17107e9c18d682033a58967027af0ab01edf2f9a6a);
let signature: B512 = B512::from((hi, lo));
// A recovered Fuel address.
let result_address = ec_recover_address_r1(signature, msg_hash).unwrap();
assert(result_address == address);
}

#[test]
fn test_ed_verify() {
use ::assert::assert;
use ::constants::ZERO_B256;

let pub_key = 0x314fa58689bbe1da2430517de2d772b384a1c1d2e9cb87e73c6afcf246045b10;
let msg = ZERO_B256;
let msg_hash = sha256(msg);

let hi = 0xf38cef9361894be6c6e0eddec28a663d099d7ddff17c8077a1447d7ecb4e6545;
let lo = 0xf5084560039486d3462dd65a40c80a74709b2f06d450ffc5dc00345c6b2cdd00;
let signature: B512 = B512::from((hi, lo));
// A verify public key with signature
let verfied = ed_verify(pub_key, signature, msg_hash).unwrap();
assert(verfied);
}
4 changes: 3 additions & 1 deletion sway-parse/src/expr/op_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ define_op_codes!(
(Tr, TrOpcode, "tr", (contract, coins, asset)),
(Tro, TroOpcode, "tro", (addr, output, coins, asset)),
/* Cryptographic Instructions */
(Eck1, EcrOpcode, "eck1", (addr, sig, hash)),
(Eck1, Eck1Opcode, "eck1", (addr, sig, hash)),
(Ecr1, Ecr1Opcode, "ecr1", (addr, sig, hash)),
(Ed19, Ed19Opcode, "ed19", (addr, sig, hash)),
(K256, K256Opcode, "k256", (addr, data, size)),
(S256, S256Opcode, "s256", (addr, data, size)),
/* Other Instructions */
Expand Down

0 comments on commit 7f1422f

Please sign in to comment.