diff --git a/Cargo.lock b/Cargo.lock index 78903cf5..b85466d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" @@ -103,15 +115,18 @@ name = "clvmr" version = "0.2.5" dependencies = [ "bls12_381", + "getrandom", "group", "hex", + "k256", "lazy_static", "num-bigint", "num-integer", "num-traits", "openssl", + "p256", "rstest", - "sha2", + "sha2 0.9.9", ] [[package]] @@ -124,6 +139,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + [[package]] name = "cpufeatures" version = "0.2.7" @@ -133,6 +154,18 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -143,6 +176,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "digest" version = "0.9.0" @@ -159,7 +203,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -173,6 +219,40 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ecdsa" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +dependencies = [ + "der", + "digest 0.10.6", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "ff" version = "0.13.0" @@ -308,6 +388,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -317,8 +398,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -344,6 +427,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -353,6 +445,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.6", + "signature", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -480,6 +586,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.6", +] + [[package]] name = "pairing" version = "0.23.0" @@ -489,6 +607,15 @@ dependencies = [ "group", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -501,6 +628,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.26" @@ -513,6 +650,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8d3875361e28f7753baefef104386e7aa47642c93023356d97fdef4003bfb5" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.56" @@ -567,6 +713,16 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rstest" version = "0.17.0" @@ -608,6 +764,20 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.17" @@ -638,6 +808,27 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + [[package]] name = "slab" version = "0.4.8" @@ -647,11 +838,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -819,3 +1020,9 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index b7e17954..644add96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ bls12_381 = { version = "=0.8.0", features = ["experimental"] } group = "=0.13.0" sha2 = "=0.9.9" openssl = { version = "=0.10.52", features = ["vendored"], optional = true } +getrandom = { version = "=0.2.9", features = ["js" ]} +# for secp sigs +k256 = { version = "0.13.1", features = ["ecdsa"] } +p256 = { version = "0.13.2", features = ["ecdsa"] } [dev-dependencies] rstest = "=0.17.0" diff --git a/fuzz/fuzz_targets/operators.rs b/fuzz/fuzz_targets/operators.rs index 6ed0fddc..9aaecdb3 100644 --- a/fuzz/fuzz_targets/operators.rs +++ b/fuzz/fuzz_targets/operators.rs @@ -15,11 +15,12 @@ use clvmr::more_ops::{ op_pubkey_for_exp, op_sha256, op_strlen, op_substr, op_subtract, }; use clvmr::reduction::{EvalErr, Response}; +use clvmr::secp_ops::{op_secp256k1_verify, op_secp256r1_verify}; use clvmr::serde::node_from_bytes; type Opf = fn(&mut Allocator, NodePtr, Cost) -> Response; -const FUNS: [Opf; 41] = [ +const FUNS: [Opf; 43] = [ op_if as Opf, op_cons as Opf, op_first as Opf, @@ -62,6 +63,9 @@ const FUNS: [Opf; 41] = [ op_bls_map_to_g2 as Opf, op_bls_pairing_identity as Opf, op_bls_verify as Opf, + // Secp operators + op_secp256k1_verify as Opf, + op_secp256r1_verify as Opf, ]; fuzz_target!(|data: &[u8]| { diff --git a/fuzz/fuzz_targets/run_program.rs b/fuzz/fuzz_targets/run_program.rs index 3c732e43..47b71426 100644 --- a/fuzz/fuzz_targets/run_program.rs +++ b/fuzz/fuzz_targets/run_program.rs @@ -3,7 +3,7 @@ use libfuzzer_sys::fuzz_target; use clvmr::allocator::Allocator; use clvmr::chia_dialect::{ - ChiaDialect, ENABLE_BLS_OPS, ENABLE_BLS_OPS_OUTSIDE_GUARD, LIMIT_HEAP, MEMPOOL_MODE, + ChiaDialect, ENABLE_BLS_OPS, ENABLE_BLS_OPS_OUTSIDE_GUARD, ENABLE_SECP_OPS, MEMPOOL_MODE, NO_UNKNOWN_OPS, }; use clvmr::cost::Cost; @@ -25,10 +25,8 @@ fuzz_target!(|data: &[u8]| { for flags in [ 0, - NO_UNKNOWN_OPS, - LIMIT_HEAP, - ENABLE_BLS_OPS, - ENABLE_BLS_OPS_OUTSIDE_GUARD, + ENABLE_BLS_OPS | ENABLE_BLS_OPS_OUTSIDE_GUARD | ENABLE_SECP_OPS, + ENABLE_BLS_OPS | ENABLE_BLS_OPS_OUTSIDE_GUARD | ENABLE_SECP_OPS | NO_UNKNOWN_OPS, MEMPOOL_MODE, ] { let dialect = ChiaDialect::new(flags); diff --git a/op-tests/test-secp-verify.txt b/op-tests/test-secp-verify.txt new file mode 100644 index 00000000..4a267486 --- /dev/null +++ b/op-tests/test-secp-verify.txt @@ -0,0 +1,66 @@ +; verify k1 +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => 0 | 850000 +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c019 => FAIL + +; arguments too long +secp256k1_verify 0x0102888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x1074c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x101acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL + +; arguments too short + +secp256k1_verify 0x888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0xc2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0xcb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL + +; missing arguments + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 => FAIL +secp256k1_verify => FAIL + +; extra argument + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 0 => FAIL + +; invalid arguments + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 (0 1 2) => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 (0 1 2) 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify (0 1 3) 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify (1 2) => FAIL + + +; verify r1 + +secp256r1_verify 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc => 0 | 1850000 +secp256r1_verify 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffd => FAIL + +; arguments too long + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b43900 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf012254866800 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c01800 => FAIL + +; arguments too short + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b4 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf01225486 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c0 => FAIL + +; missing arguments + +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668 => FAIL +secp256k1_verify 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439 => FAIL +secp256k1_verify => FAIL + +; extra argument + +secp256r1_verify 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc 0 => FAIL + +; invalid arguments + +secp256r1_verify (1 2 3) 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc => FAIL +secp256r1_verify 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9 (1 2 3) 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc => FAIL +secp256r1_verify 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 (1 2 3) => FAIL +secp256r1_verify (1 2 3) => FAIL diff --git a/src/chia_dialect.rs b/src/chia_dialect.rs index d1868666..e8a5d4c2 100644 --- a/src/chia_dialect.rs +++ b/src/chia_dialect.rs @@ -14,6 +14,7 @@ use crate::more_ops::{ op_pubkey_for_exp, op_sha256, op_strlen, op_substr, op_subtract, op_unknown, }; use crate::reduction::Response; +use crate::secp_ops::{op_secp256k1_verify, op_secp256r1_verify}; // unknown operators are disallowed // (otherwise they are no-ops with well defined cost) @@ -31,6 +32,9 @@ pub const ENABLE_BLS_OPS: u32 = 0x0010; // hard-fork and should only be enabled when it activates pub const ENABLE_BLS_OPS_OUTSIDE_GUARD: u32 = 0x0020; +// enables the secp operators. This is a soft-fork +pub const ENABLE_SECP_OPS: u32 = 0x0040; + // The default mode when running grnerators in mempool-mode (i.e. the stricter // mode) pub const MEMPOOL_MODE: u32 = NO_UNKNOWN_OPS | LIMIT_HEAP; @@ -68,7 +72,36 @@ impl Dialect for ChiaDialect { max_cost: Cost, extension: OperatorSet, ) -> Response { - let b = &allocator.atom(o); + let b = allocator.atom(o); + if b.len() == 4 { + // these are unkown operators with assigned cost + // the formula is: + // +---+---+---+------------+ + // | multiplier|XX | XXXXXX | + // +---+---+---+---+--------+ + // ^ ^ ^ + // | | + 6 bits ignored when computing cost + // cost | + // (3 bytes) + 2 bits + // cost_function + + let opcode = u32::from_be_bytes(b.try_into().unwrap()); + + if (self.flags & ENABLE_SECP_OPS) != 0 { + // the secp operators have a fixed cost of 1850000 and 850000, + // which makes the multiplier 0x1c3a8f and 0x0cf84f (there is an + // implied +1) and cost function 0 + let f = match opcode { + 0x0cf84f00 => op_secp256k1_verify, + 0x1c3a8f00 => op_secp256r1_verify, + _ => { + return unknown_operator(allocator, o, argument_list, self.flags, max_cost); + } + }; + return f(allocator, argument_list, max_cost); + } + return unknown_operator(allocator, o, argument_list, self.flags, max_cost); + } if b.len() != 1 { return unknown_operator(allocator, o, argument_list, self.flags, max_cost); } diff --git a/src/f_table.rs b/src/f_table.rs index b87fc70f..f8d4d17e 100644 --- a/src/f_table.rs +++ b/src/f_table.rs @@ -14,13 +14,14 @@ use crate::more_ops::{ op_sha256, op_strlen, op_substr, op_subtract, }; use crate::reduction::Response; +use crate::secp_ops::{op_secp256k1_verify, op_secp256r1_verify}; type OpFn = fn(&mut Allocator, NodePtr, Cost) -> Response; pub type FLookup = [Option; 256]; pub fn opcode_by_name(name: &str) -> Option { - let opcode_lookup: [(OpFn, &str); 40] = [ + let opcode_lookup: [(OpFn, &str); 42] = [ (op_if, "op_if"), (op_cons, "op_cons"), (op_first, "op_first"), @@ -61,6 +62,8 @@ pub fn opcode_by_name(name: &str) -> Option { (op_bls_map_to_g2, "op_g2_map"), (op_bls_pairing_identity, "op_bls_pairing_identity"), (op_bls_verify, "op_bls_verify"), + (op_secp256k1_verify, "op_secp256k1_verify"), + (op_secp256r1_verify, "op_secp256r1_verify"), ]; let name: &[u8] = name.as_ref(); for (f, op) in opcode_lookup.iter() { diff --git a/src/lib.rs b/src/lib.rs index e6720378..e33f1c3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub mod op_utils; pub mod reduction; pub mod run_program; pub mod runtime_dialect; +pub mod secp_ops; pub mod serde; pub mod sha2; pub mod traverse_path; @@ -22,7 +23,8 @@ pub use chia_dialect::ChiaDialect; pub use run_program::run_program; pub use chia_dialect::{ - ENABLE_BLS_OPS, ENABLE_BLS_OPS_OUTSIDE_GUARD, LIMIT_HEAP, MEMPOOL_MODE, NO_UNKNOWN_OPS, + ENABLE_BLS_OPS, ENABLE_BLS_OPS_OUTSIDE_GUARD, ENABLE_SECP_OPS, LIMIT_HEAP, MEMPOOL_MODE, + NO_UNKNOWN_OPS, }; #[cfg(feature = "counters")] diff --git a/src/more_ops.rs b/src/more_ops.rs index adcc7591..41ad6bb9 100644 --- a/src/more_ops.rs +++ b/src/more_ops.rs @@ -166,7 +166,7 @@ pub fn op_unknown( // ^ ^ ^ // | | + 6 bits ignored when computing cost // cost_multiplier | - // + 2 bits + // (up to 4 bytes) + 2 bits // cost_function // 1 is always added to the multiplier before using it to multiply the cost, this diff --git a/src/run_program.rs b/src/run_program.rs index 32b44b83..7de10160 100644 --- a/src/run_program.rs +++ b/src/run_program.rs @@ -569,6 +569,8 @@ use crate::chia_dialect::ENABLE_BLS_OPS; #[cfg(test)] use crate::chia_dialect::ENABLE_BLS_OPS_OUTSIDE_GUARD; #[cfg(test)] +use crate::chia_dialect::ENABLE_SECP_OPS; +#[cfg(test)] use crate::chia_dialect::NO_UNKNOWN_OPS; #[cfg(test)] @@ -1203,6 +1205,79 @@ const TEST_CASES: &[RunProgramTest] = &[ cost: 861, err: "unimplemented operator", }, + + // secp261k1 + + // secp ops are unknown if flag not set + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 861, + err: "unimplemented operator", + }, + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c018))", + args: "()", + flags: ENABLE_SECP_OPS, + result: Some("0"), + cost: 850061, + err: "", + }, + // invalid signature + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c019))", + args: "()", + flags: ENABLE_SECP_OPS, + result: None, + cost: 0, + err: "secp256k1_verify failed", + }, + RunProgramTest { + prg: "(secp256k1_verify (q . 0x02888b0c110ef0b4962e3fc6929cbba7a8bb25b4b2c885f55c76365018c909b439) (q . 0x74c2941eb2ebe5aa4f2287a4c5e506a6290c045004058de97a7edf0122548668) (q . 0x1acb7a6e062e78ccd4237b12c22f02b5a8d9b33cb3ba13c35e88e036baa1cbca75253bb9a96ffc48b43196c69c2972d8f965b1baa4e52348d8081cde65e6c019))", + args: "()", + flags: 0, + result: Some("0"), + cost: 850061, + err: "", + }, + + // secp261r1 + + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc))", + args: "()", + flags: NO_UNKNOWN_OPS, + result: None, + cost: 861, + err: "unimplemented operator", + }, + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffc))", + args: "()", + flags: ENABLE_SECP_OPS, + result: Some("0"), + cost: 1850061, + err: "", + }, + // invalid signature + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffd))", + args: "()", + flags: ENABLE_SECP_OPS, + result: None, + cost: 0, + err: "secp256r1_verify failed", + }, + RunProgramTest { + prg: "(secp256r1_verify (q . 0x0437a1674f3883b7171a11a20140eee014947b433723cf9f181a18fee4fcf96056103b3ff2318f00cca605e6f361d18ff0d2d6b817b1fa587e414f8bb1ab60d2b9) (q . 0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) (q . 0xe8de121f4cceca12d97527cc957cca64a4bcfc685cffdee051b38ee81cb22d7e2c187fec82c731018ed2d56f08a4a5cbc40c5bfe9ae18c02295bb65e7f605ffd))", + args: "()", + flags: 0, + result: Some("0"), + cost: 1850061, + err: "", + }, ]; #[cfg(test)] diff --git a/src/secp_ops.rs b/src/secp_ops.rs new file mode 100644 index 00000000..d425cae2 --- /dev/null +++ b/src/secp_ops.rs @@ -0,0 +1,83 @@ +use crate::allocator::{Allocator, NodePtr}; +use crate::cost::{check_cost, Cost}; +use crate::node::Node; +use crate::op_utils::{atom, check_arg_count}; +use crate::reduction::{Reduction, Response}; +use k256::ecdsa::{Signature as K1Signature, VerifyingKey as K1VerifyingKey}; +use p256::ecdsa::signature::hazmat::PrehashVerifier; +use p256::ecdsa::{Signature as P1Signature, VerifyingKey as P1VerifyingKey}; + +const SECP256R1_VERIFY_COST: Cost = 1850000; +const SECP256K1_VERIFY_COST: Cost = 850000; + +// expects: pubkey msg sig +pub fn op_secp256r1_verify(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { + let args = Node::new(a, input); + check_arg_count(&args, 3, "secp256r1_verify")?; + + let cost = SECP256R1_VERIFY_COST; + check_cost(a, cost, max_cost)?; + + // first argument is sec1 encoded pubkey + let pubkey = atom(args.first()?, "secp256r1_verify pubkey")?; + let verifier = P1VerifyingKey::from_sec1_bytes(pubkey) + .or_else(|_| args.err("secp256r1_verify pubkey is not valid"))?; + + // second arg is sha256 hash of message + let args = args.rest()?; + let msg = atom(args.first()?, "secp256r1_verify msg")?; + if msg.len() != 32 { + return args.err("secp256r1_verify message digest is not 32 bytes"); + } + + // third arg is a fixed-size signature + let args = args.rest()?; + let sig = atom(args.first()?, "secp256r1_verify sig")?; + let sig = + P1Signature::from_slice(sig).or_else(|_| args.err("secp256r1_verify sig is not valid"))?; + + // verify signature + let result = verifier.verify_prehash(msg, &sig); + + if result.is_err() { + Node::new(a, input).err("secp256r1_verify failed") + } else { + Ok(Reduction(cost, a.null())) + } +} + +// expects: pubkey msg sig +pub fn op_secp256k1_verify(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { + let args = Node::new(a, input); + check_arg_count(&args, 3, "secp256k1_verify")?; + + let cost = SECP256K1_VERIFY_COST; + check_cost(a, cost, max_cost)?; + + // first argument is sec1 encoded pubkey + let pubkey = atom(args.first()?, "secp256k1_verify pubkey")?; + let verifier = K1VerifyingKey::from_sec1_bytes(pubkey) + .or_else(|_| args.err("secp256k1_verify pubkey is not valid"))?; + + // second arg is message + let args = args.rest()?; + let msg = atom(args.first()?, "secp256k1_verify msg")?; + if msg.len() != 32 { + return args.err("secp256k1_verify message digest is not 32 bytes"); + } + + // third arg is a fixed-size signature + let args = args.rest()?; + let sig = atom(args.first()?, "secp256k1_verify sig")?; + let sig = + K1Signature::from_slice(sig).or_else(|_| args.err("secp256k1_verify sig is not valid"))?; + + // verify signature + let result = verifier.verify_prehash(msg, &sig); + + if result.is_err() { + Node::new(a, input).err("secp256k1_verify failed") + } else { + Ok(Reduction(cost, a.null())) + } +} diff --git a/src/test_ops.rs b/src/test_ops.rs index 0921c698..f7a05b06 100644 --- a/src/test_ops.rs +++ b/src/test_ops.rs @@ -13,6 +13,7 @@ use crate::more_ops::{ }; use crate::number::Number; use crate::reduction::{EvalErr, Reduction, Response}; +use crate::secp_ops::{op_secp256k1_verify, op_secp256r1_verify}; use hex::FromHex; use num_traits::Num; @@ -98,6 +99,8 @@ fn parse_atom(a: &mut Allocator, v: &str) -> NodePtr { "g2_map" => a.new_atom(&[57]).unwrap(), "bls_pairing_identity" => a.new_atom(&[58]).unwrap(), "bls_verify" => a.new_atom(&[59]).unwrap(), + "secp256k1_verify" => a.new_atom(&[0x0c, 0xf8, 0x4f, 0x00]).unwrap(), + "secp256r1_verify" => a.new_atom(&[0x1c, 0x3a, 0x8f, 0x00]).unwrap(), _ => { panic!("atom not supported \"{}\"", v); } @@ -227,6 +230,7 @@ use rstest::rstest; #[case("test-blspy-hash")] #[case("test-blspy-pairing")] #[case("test-blspy-verify")] +#[case("test-secp-verify")] fn test_ops(#[case] filename: &str) { use std::fs::read_to_string; @@ -276,6 +280,8 @@ fn test_ops(#[case] filename: &str) { ("g2_map", op_bls_map_to_g2 as Opf), ("bls_pairing_identity", op_bls_pairing_identity as Opf), ("bls_verify", op_bls_verify as Opf), + ("secp256k1_verify", op_secp256k1_verify as Opf), + ("secp256r1_verify", op_secp256r1_verify as Opf), ]); println!("Test cases from: {filename}");