From d143418cf8ab19189003aaf0038823ba4a9be7c0 Mon Sep 17 00:00:00 2001 From: Andrei Vasilescu Date: Fri, 30 Aug 2024 15:37:34 +0300 Subject: [PATCH 1/4] bls & multi-bls signing verification + tests Co-authored-by: Mihai Blacioti --- Cargo.lock | 52 +++++++++++++++++++++++ vm/Cargo.toml | 1 + vm/src/crypto_functions.rs | 87 +++++++++++++++++++++++++++++++------- vm/tests/test_crypto.rs | 60 +++++++++++++++++++++++++- 4 files changed, 183 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac352c58c6..85dfed3299 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,6 +329,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bonding-curve-contract" version = "0.0.0" @@ -1337,6 +1349,12 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "globset" version = "0.4.14" @@ -1965,6 +1983,7 @@ name = "multiversx-chain-vm" version = "0.9.0" dependencies = [ "bitflags", + "blst", "colored", "ed25519-dalek", "hex", @@ -2307,6 +2326,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.3" @@ -3438,6 +3467,15 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -4120,6 +4158,20 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zip" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 7e5349688d..f53f70e264 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -33,6 +33,7 @@ colored = "2.1.0" rand = { version= "0.8.5", optional = true } rand_seeder = "0.3.0" ed25519-dalek = "2.1.0" +blst = "0.3.13" [dependencies.multiversx-chain-vm-executor] version = "0.2.0" diff --git a/vm/src/crypto_functions.rs b/vm/src/crypto_functions.rs index 3dae63aad7..9d3d363d6b 100644 --- a/vm/src/crypto_functions.rs +++ b/vm/src/crypto_functions.rs @@ -3,6 +3,7 @@ use sha3::{Digest, Keccak256}; pub const SHA256_RESULT_LEN: usize = 32; pub const KECCAK256_RESULT_LEN: usize = 32; +pub const BLS_DST_VALUE: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; pub fn sha256(data: &[u8]) -> [u8; SHA256_RESULT_LEN] { let mut hasher = Sha256::new(); @@ -16,29 +17,83 @@ pub fn keccak256(data: &[u8]) -> [u8; KECCAK256_RESULT_LEN] { hasher.finalize().into() } +#[cfg(feature = "wasm-incopatible")] pub fn verify_ed25519(key: &[u8], message: &[u8], signature: &[u8]) -> bool { - use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + use ed25519_dalek::*; - let key_32: [u8; 32] = if let Ok(key_32) = key.try_into() { - key_32 - } else { + let public = PublicKey::from_bytes(key); + if public.is_err() { return false; - }; - let signature_64: [u8; 64] = if let Ok(signature_64) = signature.try_into() { - signature_64 - } else { + } + + let sig = Signature::from_bytes(signature); + if sig.is_err() { return false; + } + + public.unwrap().verify(message, &sig.unwrap()).is_ok() +} + +#[cfg(not(feature = "wasm-incopatible"))] +pub fn verify_ed25519(_key: &[u8], _message: &[u8], _signature: &[u8]) -> bool { + panic!("verify_ed25519 not supported for wasm builds, feature `wasm-incopatible` needs to be enabled") +} + +pub fn verify_bls_signature(key: &[u8], message: &[u8], signature: &[u8]) -> bool { + use blst::{ + min_pk::{PublicKey, Signature}, + BLST_ERROR, }; - let verifying_key_result = VerifyingKey::from_bytes(&key_32); - let verifying_key = if let Ok(verifying_key) = verifying_key_result { - verifying_key - } else { - return false; + let public_key = PublicKey::from_bytes(key).unwrap(); + + let sig = Signature::from_bytes(signature).unwrap(); + + let verify_response = sig.verify(true, message, BLS_DST_VALUE, &[], &public_key, true); + + match verify_response { + BLST_ERROR::BLST_SUCCESS => true, + _ => false, + } +} + +pub fn verify_multi_bls_signature( + public_keys: Vec<&[u8]>, + message: &[u8], + aggregated_signature: &[u8], +) -> bool { + use blst::{ + min_pk::{AggregatePublicKey, AggregateSignature, PublicKey, Signature}, + BLST_ERROR, }; - let sig = Signature::from_bytes(&signature_64); + let mut aggregate_pk: AggregatePublicKey = + AggregatePublicKey::from_public_key(&PublicKey::default()); + + let public_keys_converted: Vec = public_keys + .iter() + .filter_map(|key| PublicKey::from_bytes(key).ok()) + .collect(); + + public_keys_converted + .iter() + .for_each(|pk| aggregate_pk.add_public_key(pk, true).unwrap()); + + let aggregate_sig: Signature = + AggregateSignature::from_signature(&Signature::from_bytes(aggregated_signature).unwrap()) + .to_signature(); + + let verify_response = aggregate_sig.verify( + true, + message, + BLS_DST_VALUE, + &[], + &aggregate_pk.to_public_key(), + true, + ); - let result = verifying_key.verify(message, &sig); - result.is_ok() + match verify_response { + BLST_ERROR::BLST_SUCCESS => true, + _ => false, + } } diff --git a/vm/tests/test_crypto.rs b/vm/tests/test_crypto.rs index d1e472bfd7..219fe447bd 100644 --- a/vm/tests/test_crypto.rs +++ b/vm/tests/test_crypto.rs @@ -1,5 +1,8 @@ +use blst::min_pk::{AggregateSignature, Signature}; use hex::FromHex; -use multiversx_chain_vm::crypto_functions; +use multiversx_chain_vm::crypto_functions::{ + self, verify_bls_signature, verify_multi_bls_signature, +}; #[test] fn test_verify_ed25519_basic() { @@ -42,3 +45,58 @@ fn test_verify_ed25519_invalid_args() { let success = crypto_functions::verify_ed25519(&pub_bytes, &msg_bytes, &sig_bytes); assert!(!success); } + +#[test] +fn test_verify_bls_signature() { + let public_key_hex = b"82eb2ddfa71f1673fbfbd17952838cbca3816d5e60bf5cdb220d8cad6cb800e2ed18bb747ef45b17c9b8cbc971c6b980"; + let message = b"hello"; + let signature_hex = b"979a97882bd59dd97d860c99f9c4295e7d63e3fede1823b942d31d71ea3707d8c179ab733d38f7497b53bfa1535fe5e202f2a1c6e4df1dbc97dbe315dccd51676dbef31af1fe60d4b11c304db61913dc1d39e929f80f2cd10b72cbc661235048"; + + let public_key_bytes: Vec = FromHex::from_hex(public_key_hex).unwrap(); + let signature_bytes: Vec = FromHex::from_hex(signature_hex).unwrap(); + + let success = verify_bls_signature(&public_key_bytes, message, &signature_bytes); + + assert_eq!(success, true, "BLS signature verification failed"); +} + +#[test] +fn test_verify_aggregated_signatures() { + let message = b"hello"; + + let pk1 = b"82eb2ddfa71f1673fbfbd17952838cbca3816d5e60bf5cdb220d8cad6cb800e2ed18bb747ef45b17c9b8cbc971c6b980"; + let pk2 = b"a81795a7afa09274717a170d6ba42ab06b65b25c7887eca7be46dfddae4e5b1a249f104b15551a7a445cccac9b403926"; + let pk3 = b"8bf9e68f8fc54d8cb808ba43f0ada562cafa3c07448ab038eff6f579f1e4c1d497a957f50f6eca2608f36c39d874cbea"; + + let pk1_bytes: Vec = FromHex::from_hex(pk1).unwrap(); + let pk2_bytes: Vec = FromHex::from_hex(pk2).unwrap(); + let pk3_bytes: Vec = FromHex::from_hex(pk3).unwrap(); + + let sig1 = b"979a97882bd59dd97d860c99f9c4295e7d63e3fede1823b942d31d71ea3707d8c179ab733d38f7497b53bfa1535fe5e202f2a1c6e4df1dbc97dbe315dccd51676dbef31af1fe60d4b11c304db61913dc1d39e929f80f2cd10b72cbc661235048"; + let sig2 = b"a5a465bb34264faa5d695ad844cfbacc5514ef8cb50abdc874bbbe065e74fc2dcd69f22e6e3a25260c8a9b6c55d7ca6d0d42227a58385f7af440b85d447675bee433c88726c582ef66fba5357b60e6d980ce43e7c6a6929ed605dbe51d47c453"; + let sig3 = b"9936262ea50315d00c2b082cae3a97315f8852a021de195cb256bf532d439f1965a65c9fa536693ecb7a238611cb9ac30926d4958a7852955ff56aba9f60d9bda7ba10a939bd8b370bc42b667ee2f4fe4bb671482abc93bcac11ec10c0230229"; + + let sig1_bytes: Vec = FromHex::from_hex(sig1).unwrap(); + let sig2_bytes: Vec = FromHex::from_hex(sig2).unwrap(); + let sig3_bytes: Vec = FromHex::from_hex(sig3).unwrap(); + + let sig1_converted = Signature::from_bytes(&sig1_bytes).unwrap(); + let sig2_converted = Signature::from_bytes(&sig2_bytes).unwrap(); + let sig3_converted = Signature::from_bytes(&sig3_bytes).unwrap(); + + let mut agg_sig = AggregateSignature::from_signature(&sig1_converted); + agg_sig.add_signature(&sig2_converted, true).unwrap(); + agg_sig.add_signature(&sig3_converted, true).unwrap(); + let final_agg_sig = agg_sig.to_signature(); + + let success = verify_multi_bls_signature( + vec![&pk1_bytes, &pk2_bytes, &pk3_bytes], + message, + &final_agg_sig.to_bytes(), + ); + + assert_eq!( + success, true, + "Aggregated BLS signature verification failed" + ); +} From eee44a9e2223f852a16a5a8dde92627abda9ab4e Mon Sep 17 00:00:00 2001 From: Andrei Vasilescu Date: Fri, 30 Aug 2024 15:41:52 +0300 Subject: [PATCH 2/4] Clippy warnings resolved Co-authored-by: Mihai Blacioti --- vm/src/crypto_functions.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/vm/src/crypto_functions.rs b/vm/src/crypto_functions.rs index 9d3d363d6b..118e791052 100644 --- a/vm/src/crypto_functions.rs +++ b/vm/src/crypto_functions.rs @@ -51,10 +51,7 @@ pub fn verify_bls_signature(key: &[u8], message: &[u8], signature: &[u8]) -> boo let verify_response = sig.verify(true, message, BLS_DST_VALUE, &[], &public_key, true); - match verify_response { - BLST_ERROR::BLST_SUCCESS => true, - _ => false, - } + matches!(verify_response, BLST_ERROR::BLST_SUCCESS) } pub fn verify_multi_bls_signature( @@ -92,8 +89,5 @@ pub fn verify_multi_bls_signature( true, ); - match verify_response { - BLST_ERROR::BLST_SUCCESS => true, - _ => false, - } + matches!(verify_response, BLST_ERROR::BLST_SUCCESS) } From 979ee59bd14c8c2e3127e50629ce773f0c29eacc Mon Sep 17 00:00:00 2001 From: Andrei Vasilescu Date: Fri, 30 Aug 2024 17:38:35 +0300 Subject: [PATCH 3/4] added bls feature --- Cargo.lock | 1 + framework/scenario/Cargo.toml | 3 +++ vm/Cargo.toml | 5 +++-- vm/src/crypto_functions.rs | 33 ++++++++++++++++++++------------- vm/tests/test_crypto.rs | 14 +++++++++----- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85dfed3299..1924b1cca8 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -2124,6 +2124,7 @@ version = "0.52.3" dependencies = [ "base64", "bech32", + "blst", "colored", "hex", "itertools", diff --git a/framework/scenario/Cargo.toml b/framework/scenario/Cargo.toml index 796ab19bf0..cd23a0ef77 100644 --- a/framework/scenario/Cargo.toml +++ b/framework/scenario/Cargo.toml @@ -30,11 +30,14 @@ pathdiff = "0.2.1" itertools = "0.13.0" colored = "2.0" unwrap-infallible = "0.1.5" +blst = { version= "0.3.13", optional = true } + [features] default = ["wasm-incompatible"] wasm-incompatible = ["multiversx-chain-vm/wasm-incompatible"] run-go-tests = [] +bls = ["blst"] [dependencies.multiversx-sc] version = "=0.52.3" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index f53f70e264..cc9f149f5f 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -19,6 +19,7 @@ categories = ["cryptography::cryptocurrencies", "development-tools::debugging"] [features] # not supported when compiling to wasm wasm-incompatible = ["rand"] +bls = ["blst"] [dependencies] num-bigint = "0.4" @@ -33,7 +34,7 @@ colored = "2.1.0" rand = { version= "0.8.5", optional = true } rand_seeder = "0.3.0" ed25519-dalek = "2.1.0" -blst = "0.3.13" +blst = { version= "0.3.13", optional = true } [dependencies.multiversx-chain-vm-executor] -version = "0.2.0" +version = "0.2.0" \ No newline at end of file diff --git a/vm/src/crypto_functions.rs b/vm/src/crypto_functions.rs index 118e791052..3e386b8d47 100644 --- a/vm/src/crypto_functions.rs +++ b/vm/src/crypto_functions.rs @@ -17,28 +17,34 @@ pub fn keccak256(data: &[u8]) -> [u8; KECCAK256_RESULT_LEN] { hasher.finalize().into() } -#[cfg(feature = "wasm-incopatible")] pub fn verify_ed25519(key: &[u8], message: &[u8], signature: &[u8]) -> bool { - use ed25519_dalek::*; + use ed25519_dalek::{Signature, Verifier, VerifyingKey}; - let public = PublicKey::from_bytes(key); - if public.is_err() { + let key_32: [u8; 32] = if let Ok(key_32) = key.try_into() { + key_32 + } else { return false; - } + }; + let signature_64: [u8; 64] = if let Ok(signature_64) = signature.try_into() { + signature_64 + } else { + return false; + }; - let sig = Signature::from_bytes(signature); - if sig.is_err() { + let verifying_key_result = VerifyingKey::from_bytes(&key_32); + let verifying_key = if let Ok(verifying_key) = verifying_key_result { + verifying_key + } else { return false; - } + }; - public.unwrap().verify(message, &sig.unwrap()).is_ok() -} + let sig = Signature::from_bytes(&signature_64); -#[cfg(not(feature = "wasm-incopatible"))] -pub fn verify_ed25519(_key: &[u8], _message: &[u8], _signature: &[u8]) -> bool { - panic!("verify_ed25519 not supported for wasm builds, feature `wasm-incopatible` needs to be enabled") + let result = verifying_key.verify(message, &sig); + result.is_ok() } +#[cfg(feature = "bls")] pub fn verify_bls_signature(key: &[u8], message: &[u8], signature: &[u8]) -> bool { use blst::{ min_pk::{PublicKey, Signature}, @@ -54,6 +60,7 @@ pub fn verify_bls_signature(key: &[u8], message: &[u8], signature: &[u8]) -> boo matches!(verify_response, BLST_ERROR::BLST_SUCCESS) } +#[cfg(feature = "bls")] pub fn verify_multi_bls_signature( public_keys: Vec<&[u8]>, message: &[u8], diff --git a/vm/tests/test_crypto.rs b/vm/tests/test_crypto.rs index 219fe447bd..15f308ca49 100644 --- a/vm/tests/test_crypto.rs +++ b/vm/tests/test_crypto.rs @@ -1,8 +1,9 @@ +#[cfg(feature = "bls")] use blst::min_pk::{AggregateSignature, Signature}; + use hex::FromHex; -use multiversx_chain_vm::crypto_functions::{ - self, verify_bls_signature, verify_multi_bls_signature, -}; + +use multiversx_chain_vm::crypto_functions; #[test] fn test_verify_ed25519_basic() { @@ -46,6 +47,7 @@ fn test_verify_ed25519_invalid_args() { assert!(!success); } +#[cfg(feature = "bls")] #[test] fn test_verify_bls_signature() { let public_key_hex = b"82eb2ddfa71f1673fbfbd17952838cbca3816d5e60bf5cdb220d8cad6cb800e2ed18bb747ef45b17c9b8cbc971c6b980"; @@ -55,11 +57,13 @@ fn test_verify_bls_signature() { let public_key_bytes: Vec = FromHex::from_hex(public_key_hex).unwrap(); let signature_bytes: Vec = FromHex::from_hex(signature_hex).unwrap(); - let success = verify_bls_signature(&public_key_bytes, message, &signature_bytes); + let success = + crypto_functions::verify_bls_signature(&public_key_bytes, message, &signature_bytes); assert_eq!(success, true, "BLS signature verification failed"); } +#[cfg(feature = "bls")] #[test] fn test_verify_aggregated_signatures() { let message = b"hello"; @@ -89,7 +93,7 @@ fn test_verify_aggregated_signatures() { agg_sig.add_signature(&sig3_converted, true).unwrap(); let final_agg_sig = agg_sig.to_signature(); - let success = verify_multi_bls_signature( + let success = crypto_functions::verify_multi_bls_signature( vec![&pk1_bytes, &pk2_bytes, &pk3_bytes], message, &final_agg_sig.to_bytes(), From a006e7034005bc6cbdcbf8840c0220cf9652fd91 Mon Sep 17 00:00:00 2001 From: Andrei Vasilescu Date: Fri, 30 Aug 2024 17:57:31 +0300 Subject: [PATCH 4/4] clippy --- vm/tests/test_crypto.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vm/tests/test_crypto.rs b/vm/tests/test_crypto.rs index 15f308ca49..a2d4aa8bb2 100644 --- a/vm/tests/test_crypto.rs +++ b/vm/tests/test_crypto.rs @@ -60,7 +60,7 @@ fn test_verify_bls_signature() { let success = crypto_functions::verify_bls_signature(&public_key_bytes, message, &signature_bytes); - assert_eq!(success, true, "BLS signature verification failed"); + assert!(success, "BLS signature verification failed"); } #[cfg(feature = "bls")] @@ -99,8 +99,5 @@ fn test_verify_aggregated_signatures() { &final_agg_sig.to_bytes(), ); - assert_eq!( - success, true, - "Aggregated BLS signature verification failed" - ); + assert!(success, "Aggregated BLS signature verification failed"); }