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

Compiler assembly support for ecr1 and ed19 #5133

Merged
merged 11 commits into from
Oct 16, 2023
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
Loading