diff --git a/sway-ast/src/expr/op_code.rs b/sway-ast/src/expr/op_code.rs index 2fe414e0eea..1eba62831a2 100644 --- a/sway-ast/src/expr/op_code.rs +++ b/sway-ast/src/expr/op_code.rs @@ -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 */ diff --git a/sway-core/src/asm_lang/allocated_ops.rs b/sway-core/src/asm_lang/allocated_ops.rs index 4ec5d63f7b3..74508cc50ea 100644 --- a/sway-core/src/asm_lang/allocated_ops.rs +++ b/sway-core/src/asm_lang/allocated_ops.rs @@ -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), @@ -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![], @@ -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}"), @@ -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(), diff --git a/sway-core/src/asm_lang/mod.rs b/sway-core/src/asm_lang/mod.rs index 88e821e8955..5065a414877 100644 --- a/sway-core/src/asm_lang/mod.rs +++ b/sway-core/src/asm_lang/mod.rs @@ -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) @@ -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}"), diff --git a/sway-core/src/asm_lang/virtual_ops.rs b/sway-core/src/asm_lang/virtual_ops.rs index 1d9b501c607..c1210df3949 100644 --- a/sway-core/src/asm_lang/virtual_ops.rs +++ b/sway-core/src/asm_lang/virtual_ops.rs @@ -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), @@ -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], @@ -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], @@ -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![], @@ -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), @@ -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), diff --git a/sway-lib-std/src/ecr.sw b/sway-lib-std/src/ecr.sw index da2169a8747..da9d2c52155 100644 --- a/sway-lib-std/src/ecr.sw +++ b/sway-lib-std/src/ecr.sw @@ -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. @@ -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; @@ -58,9 +62,110 @@ pub fn ec_recover(signature: B512, msg_hash: b256) -> Result] - 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 { + 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] - 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 { + 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. @@ -73,7 +178,7 @@ pub fn ec_recover(signature: B512, msg_hash: b256) -> Result Result] - 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 { + 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); +} diff --git a/sway-parse/src/expr/op_code.rs b/sway-parse/src/expr/op_code.rs index 69934ce7293..c2742da33e9 100644 --- a/sway-parse/src/expr/op_code.rs +++ b/sway-parse/src/expr/op_code.rs @@ -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 */