From 2a98b27d3038a20d5f68e27a1c0a0a51c10c83b7 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Tue, 7 Apr 2026 10:33:59 +0800 Subject: [PATCH 1/8] refactor: :art: reconstruct the structure of the repo --- Cargo.toml | 3 +- bbs/Cargo.toml | 20 +- bbs/src/lib.rs | 17 +- bbs_plus/Cargo.lock | 575 ++++++++++++++++++++ bbs_plus/Cargo.toml | 24 + {bbs => bbs_plus}/benchmark/sign_pairing.rs | 0 {bbs => bbs_plus}/src/bbs_bn254/blind.rs | 44 +- {bbs => bbs_plus}/src/bbs_bn254/keygen.rs | 0 {bbs => bbs_plus}/src/bbs_bn254/mod.rs | 0 {bbs => bbs_plus}/src/bbs_bn254/signer.rs | 0 {bbs => bbs_plus}/src/bbs_bn254/structs.rs | 0 {bbs => bbs_plus}/src/bbs_bn254/utils.rs | 60 +- {bbs => bbs_plus}/src/bbs_bn254/verify.rs | 0 bbs_plus/src/lib.rs | 3 + {bbs => bbs_plus}/tests/test_no_blind.rs | 0 {bbs => bbs_plus}/tests/test_utils.rs | 0 {bbs => bbs_plus}/tests/test_with_blind.rs | 0 {bbs_wasm => bbs_plus_wasm}/.gitignore | 0 {bbs_wasm => bbs_plus_wasm}/Cargo.lock | 0 {bbs_wasm => bbs_plus_wasm}/Cargo.toml | 2 +- {bbs_wasm => bbs_plus_wasm}/html/test.html | 0 {bbs_wasm => bbs_plus_wasm}/src/lib.rs | 0 {bbs_wasm => bbs_plus_wasm}/src/utils.rs | 0 23 files changed, 710 insertions(+), 38 deletions(-) create mode 100644 bbs_plus/Cargo.lock create mode 100644 bbs_plus/Cargo.toml rename {bbs => bbs_plus}/benchmark/sign_pairing.rs (100%) rename {bbs => bbs_plus}/src/bbs_bn254/blind.rs (82%) rename {bbs => bbs_plus}/src/bbs_bn254/keygen.rs (100%) rename {bbs => bbs_plus}/src/bbs_bn254/mod.rs (100%) rename {bbs => bbs_plus}/src/bbs_bn254/signer.rs (100%) rename {bbs => bbs_plus}/src/bbs_bn254/structs.rs (100%) rename {bbs => bbs_plus}/src/bbs_bn254/utils.rs (85%) rename {bbs => bbs_plus}/src/bbs_bn254/verify.rs (100%) create mode 100644 bbs_plus/src/lib.rs rename {bbs => bbs_plus}/tests/test_no_blind.rs (100%) rename {bbs => bbs_plus}/tests/test_utils.rs (100%) rename {bbs => bbs_plus}/tests/test_with_blind.rs (100%) rename {bbs_wasm => bbs_plus_wasm}/.gitignore (100%) rename {bbs_wasm => bbs_plus_wasm}/Cargo.lock (100%) rename {bbs_wasm => bbs_plus_wasm}/Cargo.toml (92%) rename {bbs_wasm => bbs_plus_wasm}/html/test.html (100%) rename {bbs_wasm => bbs_plus_wasm}/src/lib.rs (100%) rename {bbs_wasm => bbs_plus_wasm}/src/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 7c1f27c..f757aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [workspace] members = [ "bbs", - "bbs_wasm", + "bbs_plus", + "bbs_plus_wasm", ] resolver = "3" \ No newline at end of file diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index da0c5b5..86b8b84 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -1,24 +1,6 @@ [package] name = "bbs" -version = "26.3.29" +version = "0.1.0" edition = "2024" [dependencies] -ark-bn254 = "0.5.0" -ark-ec = "0.5.0" -ark-ff = "0.5.0" -ark-poly = "0.5.0" -ark-serialize = "0.5.0" -ark-std = "0.5.0" -substrate-bn = "0.6.0" -sha2 = "0.10" -serde_json = "1.0.149" -serde = { version = "1.0.228", features = ["derive"] } - -[dev-dependencies] -criterion = { version = "0.5", features = ["html_reports"] } - -[[bench]] -name = "sign_verify" -path = "benchmark/sign_pairing.rs" -harness = false diff --git a/bbs/src/lib.rs b/bbs/src/lib.rs index 72b3f7b..b93cf3f 100644 --- a/bbs/src/lib.rs +++ b/bbs/src/lib.rs @@ -1,3 +1,14 @@ -//! BBS signature scheme implementation in Rust. -//! Using bn254 curve. -pub mod bbs_bn254; +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/bbs_plus/Cargo.lock b/bbs_plus/Cargo.lock new file mode 100644 index 0000000..7213a14 --- /dev/null +++ b/bbs_plus/Cargo.lock @@ -0,0 +1,575 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest", + "educe", + "itertools", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bbs" +version = "26.3.19" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "serde", + "serde_json", + "sha2", + "substrate-bn", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/bbs_plus/Cargo.toml b/bbs_plus/Cargo.toml new file mode 100644 index 0000000..da0c5b5 --- /dev/null +++ b/bbs_plus/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "bbs" +version = "26.3.29" +edition = "2024" + +[dependencies] +ark-bn254 = "0.5.0" +ark-ec = "0.5.0" +ark-ff = "0.5.0" +ark-poly = "0.5.0" +ark-serialize = "0.5.0" +ark-std = "0.5.0" +substrate-bn = "0.6.0" +sha2 = "0.10" +serde_json = "1.0.149" +serde = { version = "1.0.228", features = ["derive"] } + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } + +[[bench]] +name = "sign_verify" +path = "benchmark/sign_pairing.rs" +harness = false diff --git a/bbs/benchmark/sign_pairing.rs b/bbs_plus/benchmark/sign_pairing.rs similarity index 100% rename from bbs/benchmark/sign_pairing.rs rename to bbs_plus/benchmark/sign_pairing.rs diff --git a/bbs/src/bbs_bn254/blind.rs b/bbs_plus/src/bbs_bn254/blind.rs similarity index 82% rename from bbs/src/bbs_bn254/blind.rs rename to bbs_plus/src/bbs_bn254/blind.rs index 3ad676c..26737e2 100644 --- a/bbs/src/bbs_bn254/blind.rs +++ b/bbs_plus/src/bbs_bn254/blind.rs @@ -81,10 +81,11 @@ pub fn unblind( pub fn commitment_pok_prove( params: &Parameters, commitment: &BlindedCommitment, + blind_index: &usize, messages: &[Scalar], ) -> Result { let mut rng = ark_std::test_rng(); - commitment_pok_prove_with_rng(params, commitment, messages, &mut rng) + commitment_pok_prove_with_rng(params, commitment, messages, blind_index, &mut rng) } /// Generate a proof of knowledge for the blinded commitment with a caller RNG. @@ -92,32 +93,42 @@ pub fn commitment_pok_prove_with_rng( params: &Parameters, commitment: &BlindedCommitment, messages: &[Scalar], + blind_index: &usize, rng: &mut R, ) -> Result { if params.H.len() < messages.len() + 1 { return Err("parameters do not include enough message base points"); } + // sample r_s let r_s = Scalar::rand(rng); - let mut r_ms = Vec::with_capacity(messages.len()); + // sample r_m for each hidden message + let mut r_m_vec = Vec::with_capacity(messages.len()); for _ in 0..messages.len() { - r_ms.push(Scalar::rand(rng)); + r_m_vec.push(Scalar::rand(rng)); } - + // build temp commitment T = r_s*H_0 + sum(r_m*H_{m+1}) let mut t = params.H[0] * r_s; - for (i, r_m) in r_ms.iter().enumerate() { - t += params.H[i + 1] * r_m; + let mut index = *blind_index; + for (j, r_m) in r_m_vec.iter().enumerate() { + t += params.H[index + 1] * r_m; + index += 1; } let t_affine = t.into_affine(); + // calc challenge = Hash(C | T | hidden_message_len) let challenge = commitment_challenge(&commitment.commitment, &t_affine, messages.len())?; + // calc responses + // s_hat = r_s + challenge * commitment.blinding_factor let s_hat = r_s + challenge * commitment.blinding_factor; + // m_hat_j = m_j + c*m_j let mut m_hats = Vec::with_capacity(messages.len()); - for (m, r_m) in messages.iter().zip(r_ms.iter()) { - m_hats.push(*r_m + challenge * m); + for (j, r_m) in r_m_vec.iter().enumerate() { + m_hats.push(*r_m + challenge * messages[j]) } + // pi = (T, c, s_hat, {m_hat_j}) Ok(CommitmentProof { t: t_affine, challenge, @@ -131,20 +142,25 @@ pub fn commitment_pok_verify( params: &Parameters, commitment: &BlindedCommitment, proof: &CommitmentProof, + blind_index: &usize ) -> Result { if params.H.len() < proof.m_hats.len() + 1 { return Err("parameters do not include enough message base points"); } + // first step: check c' == c let expected_challenge = commitment_challenge(&commitment.commitment, &proof.t, proof.m_hats.len())?; if expected_challenge != proof.challenge { return Ok(false); } + // second step: assert(s_hat * H_0 + sum(m_hat_j*H_j) == T + challenge * commitement) let mut lhs = params.H[0] * proof.s_hat; + let mut index = *blind_index; for (i, m_hat) in proof.m_hats.iter().enumerate() { - lhs += params.H[i + 1] * m_hat; + lhs += params.H[index + 1] * m_hat; + index += 1; } let rhs = G1Projective::from(proof.t) + (commitment.commitment * proof.challenge); @@ -172,18 +188,20 @@ fn commitment_challenge( mod tests { use super::*; use crate::bbs_bn254::keygen::keygen; - - // #[test] + + #[test] fn blind_and_pok_roundtrip() { let (params, _pk, _sk) = keygen(3); let messages = vec![Scalar::from(1u64), Scalar::from(2u64), Scalar::from(3u64)]; let mut rng = ark_std::test_rng(); + let blind_index = 1; let commitment = blind_with_rng(¶ms, &messages, &1, &mut rng).unwrap(); + let hidden_part = vec![Scalar::from(2u64), Scalar::from(3u64)]; let proof = - commitment_pok_prove_with_rng(¶ms, &commitment, &messages, &mut rng).unwrap(); + commitment_pok_prove_with_rng(¶ms, &commitment, &hidden_part, &blind_index, &mut rng).unwrap(); - let ok = commitment_pok_verify(¶ms, &commitment, &proof).unwrap(); + let ok = commitment_pok_verify(¶ms, &commitment, &proof, &blind_index).unwrap(); assert!(ok); } } diff --git a/bbs/src/bbs_bn254/keygen.rs b/bbs_plus/src/bbs_bn254/keygen.rs similarity index 100% rename from bbs/src/bbs_bn254/keygen.rs rename to bbs_plus/src/bbs_bn254/keygen.rs diff --git a/bbs/src/bbs_bn254/mod.rs b/bbs_plus/src/bbs_bn254/mod.rs similarity index 100% rename from bbs/src/bbs_bn254/mod.rs rename to bbs_plus/src/bbs_bn254/mod.rs diff --git a/bbs/src/bbs_bn254/signer.rs b/bbs_plus/src/bbs_bn254/signer.rs similarity index 100% rename from bbs/src/bbs_bn254/signer.rs rename to bbs_plus/src/bbs_bn254/signer.rs diff --git a/bbs/src/bbs_bn254/structs.rs b/bbs_plus/src/bbs_bn254/structs.rs similarity index 100% rename from bbs/src/bbs_bn254/structs.rs rename to bbs_plus/src/bbs_bn254/structs.rs diff --git a/bbs/src/bbs_bn254/utils.rs b/bbs_plus/src/bbs_bn254/utils.rs similarity index 85% rename from bbs/src/bbs_bn254/utils.rs rename to bbs_plus/src/bbs_bn254/utils.rs index 7cdeb17..e64fa80 100644 --- a/bbs/src/bbs_bn254/utils.rs +++ b/bbs_plus/src/bbs_bn254/utils.rs @@ -4,7 +4,7 @@ use crate::bbs_bn254::{ PrivateKey, PublicKey, - structs::{BlindedCommitment, Parameters, Signature}, + structs::{BlindedCommitment, CommitmentProof, Parameters, Signature}, }; use ark_bn254::{Fq, Fq2, Fr as Scalar, G1Affine as G1, G2Affine as G2}; use serde::{Deserialize, Serialize}; @@ -66,6 +66,20 @@ struct SignatureJson { s: String, } +#[derive(Serialize, Deserialize)] +struct BlindedCommitmentJson { + commitment: G1Json, + blinding_factor: String, +} + +#[derive(Serialize, Deserialize)] +struct CommitmentProofJson { + t: G1Json, + challenge: String, + s_hat: String, + m_hats: Vec, +} + fn g1_to_json(p: &G1) -> G1Json { G1Json { x: p.x.to_string(), @@ -438,3 +452,47 @@ pub fn load_g1_from_json(s: &str) -> Result { from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; g1_from_json(&decoded, "g1") } + + +impl CommitmentProof { + pub fn export_to_json(&self) -> String { + json!({ + "t": { + "x": self.t.x.to_string(), + "y": self.t.y.to_string(), + }, + "challenge": self.challenge.to_string(), + "s": self.s_hat.to_string(), + "m_hats": self.m_hats.iter().map(|m_hat| m_hat.to_string()).collect::>(), + }) + .to_string() + } + + pub fn export_to_obj(&self) -> serde_json::Value { + json!({ + "t": { + "x": self.t.x.to_string(), + "y": self.t.y.to_string(), + }, + "challenge": self.challenge.to_string(), + "s": self.s_hat.to_string(), + "m_hats": self.m_hats.iter().map(|m_hat| m_hat.to_string()).collect::>(), + }) + } + + pub fn load_from_json(s: &str) -> Result { + let decoded: CommitmentProofJson = from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; + let mut m_hats_vec: Vec = Vec::with_capacity(decoded.m_hats.len()); + for (i, m_hat_str) in decoded.m_hats.iter().enumerate() { + let m_hat = m_hat_str.parse::().map_err(|_| ParamsJsonError::InvalidJson(format!("m_hats[{}]", i)))?; + m_hats_vec.push(m_hat); + } + + Ok(CommitmentProof { + t: g1_from_json(&decoded.t, "t")?, + challenge: decoded.challenge.parse::().map_err(|_| ParamsJsonError::InvalidField("challenge"))?, + s_hat: decoded.s_hat.parse::().map_err(|_| ParamsJsonError::InvalidField("s_hat"))?, + m_hats: m_hats_vec + }) + } +} diff --git a/bbs/src/bbs_bn254/verify.rs b/bbs_plus/src/bbs_bn254/verify.rs similarity index 100% rename from bbs/src/bbs_bn254/verify.rs rename to bbs_plus/src/bbs_bn254/verify.rs diff --git a/bbs_plus/src/lib.rs b/bbs_plus/src/lib.rs new file mode 100644 index 0000000..72b3f7b --- /dev/null +++ b/bbs_plus/src/lib.rs @@ -0,0 +1,3 @@ +//! BBS signature scheme implementation in Rust. +//! Using bn254 curve. +pub mod bbs_bn254; diff --git a/bbs/tests/test_no_blind.rs b/bbs_plus/tests/test_no_blind.rs similarity index 100% rename from bbs/tests/test_no_blind.rs rename to bbs_plus/tests/test_no_blind.rs diff --git a/bbs/tests/test_utils.rs b/bbs_plus/tests/test_utils.rs similarity index 100% rename from bbs/tests/test_utils.rs rename to bbs_plus/tests/test_utils.rs diff --git a/bbs/tests/test_with_blind.rs b/bbs_plus/tests/test_with_blind.rs similarity index 100% rename from bbs/tests/test_with_blind.rs rename to bbs_plus/tests/test_with_blind.rs diff --git a/bbs_wasm/.gitignore b/bbs_plus_wasm/.gitignore similarity index 100% rename from bbs_wasm/.gitignore rename to bbs_plus_wasm/.gitignore diff --git a/bbs_wasm/Cargo.lock b/bbs_plus_wasm/Cargo.lock similarity index 100% rename from bbs_wasm/Cargo.lock rename to bbs_plus_wasm/Cargo.lock diff --git a/bbs_wasm/Cargo.toml b/bbs_plus_wasm/Cargo.toml similarity index 92% rename from bbs_wasm/Cargo.toml rename to bbs_plus_wasm/Cargo.toml index 12736f2..16e4a59 100644 --- a/bbs_wasm/Cargo.toml +++ b/bbs_plus_wasm/Cargo.toml @@ -15,6 +15,6 @@ console_error_panic_hook = "0.1" getrandom = { version = "0.2", features = ["core", "js"] } rand = "0.8" -bbs = { path = "../bbs" } +bbs = { path = "../bbs_plus" } ark-bn254 = "0.5.0" serde_json = "1.0.149" diff --git a/bbs_wasm/html/test.html b/bbs_plus_wasm/html/test.html similarity index 100% rename from bbs_wasm/html/test.html rename to bbs_plus_wasm/html/test.html diff --git a/bbs_wasm/src/lib.rs b/bbs_plus_wasm/src/lib.rs similarity index 100% rename from bbs_wasm/src/lib.rs rename to bbs_plus_wasm/src/lib.rs diff --git a/bbs_wasm/src/utils.rs b/bbs_plus_wasm/src/utils.rs similarity index 100% rename from bbs_wasm/src/utils.rs rename to bbs_plus_wasm/src/utils.rs From 50a024705a16534528d3c567d21990302ec3cd61 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Wed, 8 Apr 2026 13:21:30 +0800 Subject: [PATCH 2/8] feat: :sparkles: add bbs_pok & bbs_wasm pack --- Cargo.lock | 62 +++- Cargo.toml | 5 +- bbs/Cargo.toml | 13 + bbs/src/bbs.rs | 78 +++++ bbs/src/lib.rs | 6 + bbs/src/modified_serde.rs | 110 +++++++ bbs/src/pok.rs | 454 +++++++++++++++++++++++++++++ bbs/src/pub_use.rs | 7 + bbs/src/structs.rs | 59 ++++ bbs/tests/bbs_base_test.rs | 33 +++ bbs/tests/pok_test.rs | 111 +++++++ bbs_plus/Cargo.toml | 2 +- bbs_plus/benchmark/sign_pairing.rs | 2 +- bbs_plus/tests/test_no_blind.rs | 2 +- bbs_plus/tests/test_utils.rs | 2 +- bbs_plus/tests/test_with_blind.rs | 2 +- bbs_plus_wasm/Cargo.toml | 4 +- bbs_plus_wasm/src/lib.rs | 6 +- bbs_wasm/.gitignore | 1 + bbs_wasm/Cargo.toml | 22 ++ bbs_wasm/html/test.html | 89 ++++++ bbs_wasm/src/lib.rs | 212 ++++++++++++++ bbs_wasm/src/utils.rs | 225 ++++++++++++++ 23 files changed, 1494 insertions(+), 13 deletions(-) create mode 100644 bbs/src/bbs.rs create mode 100644 bbs/src/modified_serde.rs create mode 100644 bbs/src/pok.rs create mode 100644 bbs/src/pub_use.rs create mode 100644 bbs/src/structs.rs create mode 100644 bbs/tests/bbs_base_test.rs create mode 100644 bbs/tests/pok_test.rs create mode 100644 bbs_wasm/.gitignore create mode 100644 bbs_wasm/Cargo.toml create mode 100644 bbs_wasm/html/test.html create mode 100644 bbs_wasm/src/lib.rs create mode 100644 bbs_wasm/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index f937143..ca1aaa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,6 +190,23 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bbs" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "criterion", + "serde", + "serde_json", + "sha3", + "substrate-bn", +] + +[[package]] +name = "bbs_plus" version = "26.3.29" dependencies = [ "ark-bn254", @@ -205,20 +222,38 @@ dependencies = [ "substrate-bn", ] +[[package]] +name = "bbs_plus_wasm" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "bbs_plus", + "console_error_panic_hook", + "getrandom", + "rand", + "serde", + "serde-wasm-bindgen", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-test", +] + [[package]] name = "bbs_wasm" version = "0.1.0" dependencies = [ "ark-bn254", + "ark-serialize", + "ark-std", "bbs", "console_error_panic_hook", "getrandom", + "hex", "rand", "serde", "serde-wasm-bindgen", "serde_json", "wasm-bindgen", - "wasm-bindgen-test", ] [[package]] @@ -546,6 +581,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "is-terminal" version = "0.4.17" @@ -593,6 +634,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -918,6 +968,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index f757aec..f42e878 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ members = [ "bbs", "bbs_plus", - "bbs_plus_wasm", + "bbs_plus_wasm", + "bbs_wasm", ] -resolver = "3" \ No newline at end of file +resolver = "3" diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index 86b8b84..b780375 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -4,3 +4,16 @@ version = "0.1.0" edition = "2024" [dependencies] +ark-bn254 = "0.5.0" +ark-ec = "0.5.0" +ark-ff = "0.5.0" +ark-poly = "0.5.0" +ark-serialize = "0.5.0" +ark-std = "0.5.0" +substrate-bn = "0.6.0" +sha3 = "0.10" +serde_json = "1.0.149" +serde = { version = "1.0.228", features = ["derive"] } + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } \ No newline at end of file diff --git a/bbs/src/bbs.rs b/bbs/src/bbs.rs new file mode 100644 index 0000000..6c159d6 --- /dev/null +++ b/bbs/src/bbs.rs @@ -0,0 +1,78 @@ +//! Key gen related functions here. +#![allow(non_snake_case)] + +use ark_bn254::Bn254; +use ark_std::{rand::RngCore, UniformRand}; +use ark_ec::pairing::Pairing; + +use crate::structs::*; +use crate::pub_use::*; + +/// Key gen function. +/// `L`: the length of messages. +pub fn setup(L: usize, rng: &mut R) -> Params { + // Sample vector H of length L + let mut H = Vec::with_capacity(L); + for _ in 0..L { + H.push(G1::rand(rng)); + } + + Params { + G1: G1::generator(), + G2: G2::generator(), + L: L, + H: H + } +} + +/// Keygen function here +/// `rng`: the rng engine +pub fn keygen(rng: &mut R) -> (PublicKey, PrivateKey) { + let x = Scalar::rand(rng); + let X = (G2::generator() * x).into_affine(); + (PublicKey { X }, PrivateKey { x }) +} + +/// Sign function here +/// + `messages`: the messages to be signed, each message is mapped to a Scalar +/// + `params`: the public parameters +/// + `sk`: the private key +/// + `rng`: the rng engine +pub fn sign(messages: &Messages, params: &Params, sk: &PrivateKey, rng: &mut R) -> Result { + if messages.0.len() > params.L { + return Err("message length exceeds parameters"); + } + + // 1. calc commitment C = G_1 + h_0*s' + h_1*m_1 + ... + h_L*m_L + let mut C = params.G1.into_group(); + for (i, m_i) in messages.0.iter().enumerate() { + C += params.H[i] * m_i; + } + // 2. sample random scalars e + let e =Scalar::rand(rng); + + // 3. calc A = C * (1/(x+e)) + let mut tmp = sk.x + e; + tmp = tmp.inverse().ok_or("failed to compute inverse")?; + let A = (C * tmp).into_affine(); + + Ok(Signature { A, e }) +} + +pub fn verify(messages: &Messages, signature: &Signature, params: &Params, pk: &PublicKey) -> bool { + if messages.0.len() > params.L { + return false; + } + + // 1. calc C' = G_1 + h_0*s' + h_1*m_1 + ... + h_L*m_L + let mut C_prime = params.G1.into_group(); + for (i, m_i) in messages.0.iter().enumerate() { + C_prime += params.H[i] * m_i; + } + // 2. calc e(A, X + e*G2) + let left = Bn254::pairing(signature.A, pk.X + params.G2 * signature.e); + // 3. calc e(C', G2) + let right = Bn254::pairing(C_prime.into_affine(), params.G2); + + left == right +} \ No newline at end of file diff --git a/bbs/src/lib.rs b/bbs/src/lib.rs index b93cf3f..0391188 100644 --- a/bbs/src/lib.rs +++ b/bbs/src/lib.rs @@ -1,3 +1,9 @@ +mod pub_use; +mod modified_serde; +pub mod structs; +pub mod bbs; +pub mod pok; + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/bbs/src/modified_serde.rs b/bbs/src/modified_serde.rs new file mode 100644 index 0000000..9298025 --- /dev/null +++ b/bbs/src/modified_serde.rs @@ -0,0 +1,110 @@ +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; +use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; + +pub fn serialize_scalar(scalar: &Scalar, serializer: S) -> Result +where + S: serde::Serializer, +{ + let mut bytes = Vec::new(); + scalar + .serialize_compressed(&mut bytes) + .map_err(serde::ser::Error::custom)?; + serializer.serialize_bytes(&bytes) +} + +pub fn deserialize_scalar<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let bytes: Vec = serde::Deserialize::deserialize(deserializer)?; + Scalar::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom) +} + +pub fn serialize_g1(g1: &G1, serializer: S) -> Result +where + S: serde::Serializer, +{ + let mut bytes = Vec::new(); + g1.serialize_compressed(&mut bytes) + .map_err(serde::ser::Error::custom)?; + serializer.serialize_bytes(&bytes) +} + +pub fn serialize_vec_g1(g1_vec: &Vec, serializer: S) -> Result +where + S: serde::Serializer, +{ + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(g1_vec.len()))?; + for g1 in g1_vec { + let mut bytes = Vec::new(); + g1.serialize_compressed(&mut bytes) + .map_err(serde::ser::Error::custom)?; + seq.serialize_element(&bytes)?; + } + seq.end() +} + +pub fn serialize_g2(g2: &G2, serializer: S) -> Result +where + S: serde::Serializer, +{ + let mut bytes = Vec::new(); + g2.serialize_compressed(&mut bytes) + .map_err(serde::ser::Error::custom)?; + serializer.serialize_bytes(&bytes) +} + +pub fn deserialize_g1<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let bytes: Vec = serde::Deserialize::deserialize(deserializer)?; + G1::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom) +} + +pub fn deserialize_g2<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let bytes: Vec = serde::Deserialize::deserialize(deserializer)?; + G2::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom) +} + +pub fn deserialize_vec_g1<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let bytes_vec: Vec> = serde::Deserialize::deserialize(deserializer)?; + bytes_vec + .into_iter() + .map(|bytes| G1::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom)) + .collect() +} + +pub fn serialize_vec_scalar(scalar_vec: &Vec, serializer: S) -> Result +where + S: serde::Serializer, +{ + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(scalar_vec.len()))?; + for scalar in scalar_vec { + let mut bytes = Vec::new(); + scalar + .serialize_compressed(&mut bytes) + .map_err(serde::ser::Error::custom)?; + seq.serialize_element(&bytes)?; + } + seq.end() +} + +pub fn deserialize_vec_scalar<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let bytes_vec: Vec> = serde::Deserialize::deserialize(deserializer)?; + bytes_vec + .into_iter() + .map(|bytes| Scalar::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom)) + .collect() +} \ No newline at end of file diff --git a/bbs/src/pok.rs b/bbs/src/pok.rs new file mode 100644 index 0000000..57d4419 --- /dev/null +++ b/bbs/src/pok.rs @@ -0,0 +1,454 @@ +//! Proof of Knowledge of BBS Signature with selective disclosure + +#![allow(non_snake_case)] +use ark_bn254::{Bn254, Fr as Scalar, G1Affine as G1}; +use ark_ec::pairing::Pairing; +use ark_ec::CurveGroup; +use ark_ff::PrimeField; +use ark_std::{rand::RngCore, UniformRand, Zero}; +use std::collections::{HashMap, HashSet}; + +use crate::pub_use::*; +use crate::structs::{Messages, Params, PublicKey, Signature}; +use sha3::{Digest, Keccak256}; + +/// Represents the public messages provided by the verifier +pub type DisclosedMessages = HashMap; + +/// Prover's state for the interactive protocol +pub struct InteractiveProverState { + pub r: Scalar, + pub alpha: Scalar, + pub beta: Scalar, + pub deltas: HashMap, + pub hidden_indices: HashSet, + pub e: Scalar, + pub hidden_messages: HashMap, +} + +/// Commitment sent from Prover to Verifier in step 1 of interactive protocol +pub struct PoKCommitment { + pub A_bar: G1, + pub B_bar: G1, + pub U: G1, +} + +/// Challenge from the Verifier +pub type Challenge = Scalar; + +/// Response from Prover to Verifier in step 2 of interactive protocol +pub struct PoKResponse { + pub s: Scalar, + pub t: Scalar, + pub u_i: HashMap, +} + +/// Non-interactive Proof (fs) +pub struct NonInteractiveProof { + pub A_bar: G1, + pub B_bar: G1, + pub U: G1, + pub s: Scalar, + pub t: Scalar, + pub u_i: HashMap, +} + +/// Context for FS heuristic +const POK_CTX: &[u8] = b"BBS_PoK_1"; + +/// Compute the public commitment $C_J = G_1 + \sum_{j \in J} m_j \cdot H_j$ +pub fn compute_c_j(params: &Params, disclosed: &DisclosedMessages) -> G1 { + let mut c_j = params.G1.into_group(); + for (&j, m_j) in disclosed.iter() { + if j < params.L { + c_j += params.H[j] * m_j; + } + } + c_j.into_affine() +} + +/// Interactive Protocol: Step 1 (Prover Commit) +pub fn pok_commit( + params: &Params, + messages: &Messages, + signature: &Signature, + disclosed_indices: &HashSet, + rng: &mut R, +) -> Result<(PoKCommitment, InteractiveProverState), &'static str> { + if messages.0.len() > params.L { + return Err("message length exceeds parameters"); + } + + let l = messages.0.len(); + let hidden_indices: HashSet = (0..l).filter(|i| !disclosed_indices.contains(i)).collect(); + + // 1. r <- Z_p* + let mut r = Scalar::rand(rng); + while r.is_zero() { + r = Scalar::rand(rng); + } + + // 2. A_bar <- r * A + let a_bar = (signature.A * r).into_affine(); + + // 3. Compute C_J + let mut disclosed_msgs = HashMap::new(); + for &j in disclosed_indices.iter() { + if j < l { + disclosed_msgs.insert(j, messages.0[j]); + } + } + let c_j = compute_c_j(params, &disclosed_msgs); + + // 4. Compute C and B_bar + let mut c = params.G1.into_group(); + for (i, m_i) in messages.0.iter().enumerate() { + c += params.H[i] * m_i; + } + let b_bar = (c * r - signature.A * (r * signature.e)).into_affine(); + + // 5. Random scalars + let alpha = Scalar::rand(rng); + let beta = Scalar::rand(rng); + let mut deltas = HashMap::new(); + let mut u_term = params.G1.into_group() * Scalar::zero(); + for &i in &hidden_indices { + let delta_i = Scalar::rand(rng); + deltas.insert(i, delta_i); + u_term += params.H[i] * delta_i; + } + + // 6. U <- alpha * C_J + beta * A_bar + sum(delta_i * H_i) + let u = (c_j * alpha + a_bar * beta + u_term).into_affine(); + + let commitment = PoKCommitment { + A_bar: a_bar, + B_bar: b_bar, + U: u, + }; + + let mut hidden_messages = HashMap::new(); + for &i in &hidden_indices { + hidden_messages.insert(i, messages.0[i]); + } + + let state = InteractiveProverState { + r, + alpha, + beta, + deltas, + hidden_indices, + e: signature.e, + hidden_messages, + }; + + Ok((commitment, state)) +} + +/// Interactive Protocol: Step 2 (Prover Response) +pub fn pok_prove(state: &InteractiveProverState, challenge: &Challenge) -> PoKResponse { + let c = *challenge; + let s = state.alpha + state.r * c; + let t = state.beta - state.e * c; + + let mut u_i = HashMap::new(); + for &i in &state.hidden_indices { + let delta_i = state.deltas.get(&i).unwrap(); + let m_i = state.hidden_messages.get(&i).unwrap(); + let u = *delta_i + state.r * m_i * c; + u_i.insert(i, u); + } + + PoKResponse { s, t, u_i } +} + +/// Interactive Protocol: Step 3 (Verifier Check) +pub fn pok_verify( + params: &Params, + pk: &PublicKey, + disclosed: &DisclosedMessages, + commitment: &PoKCommitment, + challenge: &Challenge, + response: &PoKResponse, +) -> bool { + let c_j = compute_c_j(params, disclosed); + + // 1. Pairing check: e(A_bar, X) == e(B_bar, G2) + let left_pairing = Bn254::pairing(commitment.A_bar, pk.X); + let right_pairing = Bn254::pairing(commitment.B_bar, params.G2); + if left_pairing != right_pairing { + return false; + } + + // 2. Homomorphic check: U + c * B_bar == s * C_J + t * A_bar + sum(u_i * H_i) + let lhs = (commitment.U.into_group() + commitment.B_bar * challenge).into_affine(); + + let mut sum_ui_hi = params.G1.into_group() * Scalar::zero(); + for (&i, u_i) in response.u_i.iter() { + if i < params.L { + sum_ui_hi += params.H[i] * u_i; + } + } + + let rhs = (c_j * response.s + commitment.A_bar * response.t + sum_ui_hi).into_affine(); + + lhs == rhs +} + +/* ================== Non-Interactive Version (Fiat-Shamir) ================== */ + +fn compute_challenge( + pk: &PublicKey, + disclosed: &DisclosedMessages, + a_bar: &G1, + b_bar: &G1, + u: &G1, +) -> Scalar { + let mut hasher = Keccak256::new(); + hasher.update(POK_CTX); + + // Convert components to bytes appropriately (this is simplified, ideal uses CanonicalSerialize) + use ark_serialize::CanonicalSerialize; + + let mut pk_bytes = Vec::new(); + pk.X.serialize_compressed(&mut pk_bytes).unwrap_or_default(); + hasher.update(&pk_bytes); + + // Sort disclosed messages by index to ensure deterministic hashing + let mut indices: Vec<_> = disclosed.keys().cloned().collect(); + indices.sort_unstable(); + for i in indices { + hasher.update(&(i as u64).to_be_bytes()); + let mut m_bytes = Vec::new(); + disclosed[&i].serialize_compressed(&mut m_bytes).unwrap_or_default(); + hasher.update(&m_bytes); + } + + let mut a_bytes = Vec::new(); + a_bar.serialize_compressed(&mut a_bytes).unwrap_or_default(); + hasher.update(&a_bytes); + + let mut b_bytes = Vec::new(); + b_bar.serialize_compressed(&mut b_bytes).unwrap_or_default(); + hasher.update(&b_bytes); + + let mut u_bytes = Vec::new(); + u.serialize_compressed(&mut u_bytes).unwrap_or_default(); + hasher.update(&u_bytes); + + let hash_result = hasher.finalize(); + // Convert hash to scalar + Scalar::from_be_bytes_mod_order(&hash_result) +} + +/// NIZK Prove (Fiat-Shamir) +pub fn nizk_prove( + params: &Params, + pk: &PublicKey, + messages: &Messages, + signature: &Signature, + disclosed_indices: &HashSet, + rng: &mut R, +) -> Result { + let (commitment, state) = pok_commit(params, messages, signature, disclosed_indices, rng)?; + + let l = messages.0.len(); + let mut disclosed_msgs = HashMap::new(); + for &j in disclosed_indices.iter() { + if j < l { + disclosed_msgs.insert(j, messages.0[j]); + } + } + + let challenge = compute_challenge(pk, &disclosed_msgs, &commitment.A_bar, &commitment.B_bar, &commitment.U); + let response = pok_prove(&state, &challenge); + + Ok(NonInteractiveProof { + A_bar: commitment.A_bar, + B_bar: commitment.B_bar, + U: commitment.U, + s: response.s, + t: response.t, + u_i: response.u_i, + }) +} + +/// NIZK Verify (Fiat-Shamir) +pub fn nizk_verify( + params: &Params, + pk: &PublicKey, + disclosed: &DisclosedMessages, + proof: &NonInteractiveProof, +) -> bool { + let challenge = compute_challenge(pk, disclosed, &proof.A_bar, &proof.B_bar, &proof.U); + + let commitment = PoKCommitment { + A_bar: proof.A_bar, + B_bar: proof.B_bar, + U: proof.U, + }; + let response = PoKResponse { + s: proof.s, + t: proof.t, + u_i: proof.u_i.clone(), + }; + + pok_verify(params, pk, disclosed, &commitment, &challenge, &response) +} + +/* ================== Optimized Non-Interactive Version (Prefix / On-Chain Friendly) ================== */ + +/// Non-interactive Proof assuming disclosed messages are a continuous prefix: 0..disclosed_count-1 +pub struct NonInteractiveProofPrefix { + pub A_bar: G1, + pub B_bar: G1, + pub U: G1, + pub s: Scalar, + pub t: Scalar, + pub u_i: Vec, +} + +fn compute_challenge_prefix( + pk: &PublicKey, + disclosed: &[Scalar], + a_bar: &G1, + b_bar: &G1, + u: &G1, +) -> Scalar { + let mut hasher = Keccak256::new(); + hasher.update(POK_CTX); + + use ark_serialize::CanonicalSerialize; + + let mut pk_bytes = Vec::new(); + pk.X.serialize_compressed(&mut pk_bytes).unwrap_or_default(); + hasher.update(&pk_bytes); + + for (i, m) in disclosed.iter().enumerate() { + hasher.update(&(i as u64).to_be_bytes()); + let mut m_bytes = Vec::new(); + m.serialize_compressed(&mut m_bytes).unwrap_or_default(); + hasher.update(&m_bytes); + } + + let mut a_bytes = Vec::new(); + a_bar.serialize_compressed(&mut a_bytes).unwrap_or_default(); + hasher.update(&a_bytes); + + let mut b_bytes = Vec::new(); + b_bar.serialize_compressed(&mut b_bytes).unwrap_or_default(); + hasher.update(&b_bytes); + + let mut u_bytes = Vec::new(); + u.serialize_compressed(&mut u_bytes).unwrap_or_default(); + hasher.update(&u_bytes); + + let hash_result = hasher.finalize(); + Scalar::from_be_bytes_mod_order(&hash_result) +} + +/// NIZK Prove (Prefix Disclosure: 0..disclosed_count-1 are public) +pub fn nizk_prove_prefix( + params: &Params, + pk: &PublicKey, + messages: &Messages, + signature: &Signature, + disclosed_count: usize, + rng: &mut R, +) -> Result { + if disclosed_count > messages.0.len() || messages.0.len() > params.L { + return Err("invalid disclosed count or message length"); + } + + let l = messages.0.len(); + + let mut r = Scalar::rand(rng); + while r.is_zero() { r = Scalar::rand(rng); } + + let a_bar = (signature.A * r).into_affine(); + + let mut c_j = params.G1.into_group(); + for j in 0..disclosed_count { + c_j += params.H[j] * messages.0[j]; + } + + let mut c = c_j; + for i in disclosed_count..l { + c += params.H[i] * messages.0[i]; + } + let b_bar = (c * r - signature.A * (r * signature.e)).into_affine(); + + let alpha = Scalar::rand(rng); + let beta = Scalar::rand(rng); + let mut deltas = Vec::with_capacity(l - disclosed_count); + let mut u_term = params.G1.into_group() * Scalar::zero(); + + for i in disclosed_count..l { + let delta_i = Scalar::rand(rng); + deltas.push(delta_i); + u_term += params.H[i] * delta_i; + } + + let u = (c_j * alpha + a_bar * beta + u_term).into_affine(); + + let disclosed_msgs = &messages.0[0..disclosed_count]; + let challenge = compute_challenge_prefix(pk, disclosed_msgs, &a_bar, &b_bar, &u); + + let s = alpha + r * challenge; + let t = beta - signature.e * challenge; + + let mut u_i = Vec::with_capacity(l - disclosed_count); + for i in disclosed_count..l { + let idx = i - disclosed_count; + u_i.push(deltas[idx] + r * messages.0[i] * challenge); + } + + Ok(NonInteractiveProofPrefix { + A_bar: a_bar, + B_bar: b_bar, + U: u, + s, + t, + u_i, + }) +} + +/// NIZK Verify (Prefix Disclosure) +pub fn nizk_verify_prefix( + params: &Params, + pk: &PublicKey, + disclosed_msgs: &[Scalar], + proof: &NonInteractiveProofPrefix, +) -> bool { + let disclosed_count = disclosed_msgs.len(); + if disclosed_count > params.L { + return false; + } + + let challenge = compute_challenge_prefix(pk, disclosed_msgs, &proof.A_bar, &proof.B_bar, &proof.U); + + let mut c_j = params.G1.into_group(); + for (j, &m_j) in disclosed_msgs.iter().enumerate() { + c_j += params.H[j] * m_j; + } + + let left_pairing = Bn254::pairing(proof.A_bar, pk.X); + let right_pairing = Bn254::pairing(proof.B_bar, params.G2); + if left_pairing != right_pairing { + return false; + } + + let lhs = (proof.U.into_group() + proof.B_bar * challenge).into_affine(); + + let mut sum_ui_hi = params.G1.into_group() * Scalar::zero(); + for (idx, &u_i_val) in proof.u_i.iter().enumerate() { + let i = disclosed_count + idx; + if i < params.L { + sum_ui_hi += params.H[i] * u_i_val; + } + } + + let rhs = (c_j * proof.s + proof.A_bar * proof.t + sum_ui_hi).into_affine(); + + lhs == rhs +} diff --git a/bbs/src/pub_use.rs b/bbs/src/pub_use.rs new file mode 100644 index 0000000..1230d5a --- /dev/null +++ b/bbs/src/pub_use.rs @@ -0,0 +1,7 @@ +//! Ark dependencies lazy import hare + +#![allow(dead_code, unused_imports)] +pub use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; +pub use ark_ec::{AffineRepr, CurveGroup}; +pub use ark_ff::Field; +pub use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; diff --git a/bbs/src/structs.rs b/bbs/src/structs.rs new file mode 100644 index 0000000..0ae821a --- /dev/null +++ b/bbs/src/structs.rs @@ -0,0 +1,59 @@ +//! Define the core data structures here. + +#![allow(non_snake_case, dead_code, unused_imports)] +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2, g2}; +use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; +use ark_ff::Field; +use serde::{Deserialize, Serialize, de}; + +use crate::modified_serde::*; + +/// BBS signature params +/// + `G1` generator +/// + `G2` generator +/// + `L` the length of messages +/// + `H` message base point +#[derive(Serialize,Deserialize, Debug)] +pub struct Params { + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub G1: G1, + #[serde(serialize_with = "serialize_g2",deserialize_with = "deserialize_g2")] + pub G2: G2, + pub L: usize, + #[serde(serialize_with = "serialize_vec_g1",deserialize_with="deserialize_vec_g1")] + pub H: Vec, +} + +/// Private key +/// `x`: the secret, Scalar +#[derive(Serialize, Deserialize, Debug)] +pub struct PrivateKey { + #[serde(serialize_with = "serialize_scalar", deserialize_with = "deserialize_scalar")] + pub x: Scalar, +} + + +/// Publc key +/// `X`: the public key point in G2, X = x*G2 +#[derive(Serialize, Deserialize, Debug)] +pub struct PublicKey { + #[serde(serialize_with = "serialize_g2",deserialize_with = "deserialize_g2")] + pub X: G2 +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Messages ( + #[serde(serialize_with = "serialize_vec_scalar",deserialize_with="deserialize_vec_scalar")] + pub Vec +); + +/// Signature +/// + `A`: the signature point in G1 +/// + `e`: random scalar in Scalar +#[derive(Serialize, Deserialize, Debug)] +pub struct Signature { + #[serde(serialize_with = "serialize_g1",deserialize_with = "deserialize_g1")] + pub A: G1, + #[serde(serialize_with = "serialize_scalar", deserialize_with = "deserialize_scalar")] + pub e: Scalar, +} \ No newline at end of file diff --git a/bbs/tests/bbs_base_test.rs b/bbs/tests/bbs_base_test.rs new file mode 100644 index 0000000..203da45 --- /dev/null +++ b/bbs/tests/bbs_base_test.rs @@ -0,0 +1,33 @@ +mod tests { + use bbs::bbs::*; + use bbs::structs::*; + use ark_std::test_rng; + use ark_std::UniformRand; + use ark_bn254::Fr as Scalar; + + #[test] + fn bbs_base_test() { + let mut rng = test_rng(); + let l = 5; + + // 1. Setup + let params = setup(l, &mut rng); + + // 2. KeyGen + let (pk, sk) = keygen(&mut rng); + + // 3. Create Messages + let mut msgs = Vec::new(); + for _ in 0..l { + msgs.push(Scalar::rand(&mut rng)); + } + let messages = Messages(msgs); + + // 4. Sign + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); + + // 5. Verify + let is_valid = verify(&messages, &signature, ¶ms, &pk); + assert!(is_valid, "BBS signature verification failed!"); + } +} \ No newline at end of file diff --git a/bbs/tests/pok_test.rs b/bbs/tests/pok_test.rs new file mode 100644 index 0000000..1886bb6 --- /dev/null +++ b/bbs/tests/pok_test.rs @@ -0,0 +1,111 @@ +mod tests { + use bbs::bbs::*; + use bbs::structs::*; + use bbs::pok::*; + use ark_std::test_rng; + use ark_std::UniformRand; + use ark_bn254::Fr as Scalar; + use std::collections::{HashSet, HashMap}; + + #[test] + fn test_pok_interactive() { + let mut rng = test_rng(); + let l = 5; + + let params = setup(l, &mut rng); + let (pk, sk) = keygen(&mut rng); + + let mut msgs = Vec::new(); + for _ in 0..l { + msgs.push(Scalar::rand(&mut rng)); + } + let messages = Messages(msgs.clone()); + + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); + + // Let's disclose indices 0 and 2 + let mut disclosed_indices = HashSet::new(); + disclosed_indices.insert(0); + disclosed_indices.insert(2); + + let mut disclosed_msgs = HashMap::new(); + disclosed_msgs.insert(0, msgs[0]); + disclosed_msgs.insert(2, msgs[2]); + + // Prover Step 1 + let (commitment, state) = pok_commit(¶ms, &messages, &signature, &disclosed_indices, &mut rng).unwrap(); + + // Verifier Step 1 + let challenge = Scalar::rand(&mut rng); + + // Prover Step 2 + let response = pok_prove(&state, &challenge); + + // Verifier Step 2 + let is_valid = pok_verify(¶ms, &pk, &disclosed_msgs, &commitment, &challenge, &response); + assert!(is_valid, "Interactive PoK verification failed!"); + } + + #[test] + fn test_pok_non_interactive() { + let mut rng = test_rng(); + let l = 5; + + let params = setup(l, &mut rng); + println!("{:?}", params); + let (pk, sk) = keygen(&mut rng); + println!("Public Key: {:?}, Private Key: {:?}", pk, sk); + + let mut msgs = Vec::new(); + for _ in 0..l { + msgs.push(Scalar::rand(&mut rng)); + } + let messages = Messages(msgs.clone()); + + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); + + // Let's disclose indices 1 and 4 + let mut disclosed_indices = HashSet::new(); + disclosed_indices.insert(1); + disclosed_indices.insert(4); + + let mut disclosed_msgs = HashMap::new(); + disclosed_msgs.insert(1, msgs[1]); + disclosed_msgs.insert(4, msgs[4]); + + // NIZK Prove + let proof = nizk_prove(¶ms, &pk, &messages, &signature, &disclosed_indices, &mut rng).unwrap(); + + // NIZK Verify + let is_valid = nizk_verify(¶ms, &pk, &disclosed_msgs, &proof); + assert!(is_valid, "Non-Interactive PoK verification failed!"); + } + + #[test] + fn test_pok_non_interactive_prefix() { + let mut rng = test_rng(); + let l = 5; + + let params = setup(l, &mut rng); + let (pk, sk) = keygen(&mut rng); + + let mut msgs = Vec::new(); + for _ in 0..l { + msgs.push(Scalar::rand(&mut rng)); + } + let messages = Messages(msgs.clone()); + + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); + + // Disclose indices 0..2 (count = 3) + let disclosed_count = 3; + let disclosed_msgs = &msgs[0..disclosed_count]; + + // NIZK Prove + let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng).unwrap(); + + // NIZK Verify + let is_valid = nizk_verify_prefix(¶ms, &pk, disclosed_msgs, &proof); + assert!(is_valid, "Prefix Non-Interactive PoK verification failed!"); + } +} diff --git a/bbs_plus/Cargo.toml b/bbs_plus/Cargo.toml index da0c5b5..c0f22a2 100644 --- a/bbs_plus/Cargo.toml +++ b/bbs_plus/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "bbs" +name = "bbs_plus" version = "26.3.29" edition = "2024" diff --git a/bbs_plus/benchmark/sign_pairing.rs b/bbs_plus/benchmark/sign_pairing.rs index 1749e93..f15fd46 100644 --- a/bbs_plus/benchmark/sign_pairing.rs +++ b/bbs_plus/benchmark/sign_pairing.rs @@ -1,5 +1,5 @@ use ark_bn254::Fr as Scalar; -use bbs::bbs_bn254::{keygen, sign_no_blind, verify_no_blind}; +use bbs_plus::bbs_bn254::{keygen, sign_no_blind, verify_no_blind}; use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; fn sample_messages(count: usize) -> Vec { diff --git a/bbs_plus/tests/test_no_blind.rs b/bbs_plus/tests/test_no_blind.rs index 961aa59..e644e9c 100644 --- a/bbs_plus/tests/test_no_blind.rs +++ b/bbs_plus/tests/test_no_blind.rs @@ -1,6 +1,6 @@ mod tests { use ark_bn254::Fr as Scalar; - use bbs::bbs_bn254::{verify::verify_no_blind, *}; + use bbs_plus::bbs_bn254::{verify::verify_no_blind, *}; #[test] fn test_no_blind() { let (params, pk, sk) = keygen(5); diff --git a/bbs_plus/tests/test_utils.rs b/bbs_plus/tests/test_utils.rs index 6175831..c422ac3 100644 --- a/bbs_plus/tests/test_utils.rs +++ b/bbs_plus/tests/test_utils.rs @@ -1,6 +1,6 @@ mod tests { use ark_bn254::Fr as Scalar; - use bbs::bbs_bn254::{Parameters, Signature, keygen, sign_no_blind}; + use bbs_plus::bbs_bn254::{Parameters, Signature, keygen, sign_no_blind}; #[test] fn test_to_json() { diff --git a/bbs_plus/tests/test_with_blind.rs b/bbs_plus/tests/test_with_blind.rs index a618c49..08ae4c9 100644 --- a/bbs_plus/tests/test_with_blind.rs +++ b/bbs_plus/tests/test_with_blind.rs @@ -1,6 +1,6 @@ mod tests { use ark_bn254::Fr as Scalar; - use bbs::bbs_bn254::{blind::unblind, verify::verify_no_blind, *}; + use bbs_plus::bbs_bn254::{blind::unblind, verify::verify_no_blind, *}; #[test] fn test_no_blind() { let (params, pk, sk) = keygen(5); diff --git a/bbs_plus_wasm/Cargo.toml b/bbs_plus_wasm/Cargo.toml index 16e4a59..09f4087 100644 --- a/bbs_plus_wasm/Cargo.toml +++ b/bbs_plus_wasm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "bbs_wasm" +name = "bbs_plus_wasm" version = "0.1.0" edition = "2024" @@ -15,6 +15,6 @@ console_error_panic_hook = "0.1" getrandom = { version = "0.2", features = ["core", "js"] } rand = "0.8" -bbs = { path = "../bbs_plus" } +bbs_plus = { path = "../bbs_plus" } ark-bn254 = "0.5.0" serde_json = "1.0.149" diff --git a/bbs_plus_wasm/src/lib.rs b/bbs_plus_wasm/src/lib.rs index 6a80b3d..3dd1724 100644 --- a/bbs_plus_wasm/src/lib.rs +++ b/bbs_plus_wasm/src/lib.rs @@ -1,6 +1,6 @@ mod utils; -use bbs::bbs_bn254::{ +use bbs_plus::bbs_bn254::{ utils::load_g1_from_json, BlindedCommitment, Parameters, PrivateKey, PublicKey, Signature, blind_with_rng, keygen, keygen_with_rng, sign_no_blind_with_rng, verify_no_blind }; @@ -110,7 +110,7 @@ pub fn unblind( let commitment = load_from_js(commitment, "commitment", BlindedCommitment::load_from_json)?; let unblinded_signature = - bbs::bbs_bn254::unblind(¶ms, &signature, &commitment).map_err(|e| { + bbs_plus::bbs_bn254::unblind(¶ms, &signature, &commitment).map_err(|e| { JsValue::from_str(&format!("unblinding failed: {e}")) })?; @@ -135,7 +135,7 @@ pub fn sign_with_blind( let commitment = load_from_js(commitment, "commitment", load_g1_from_json)?; let bind_index_usize = bind_index as usize; - let signature = bbs::bbs_bn254::sign_with_blind_with_rng(¶ms, &sk, &bind_index_usize, &commitment, &visual_messages, &mut rng) + let signature = bbs_plus::bbs_bn254::sign_with_blind_with_rng(¶ms, &sk, &bind_index_usize, &commitment, &visual_messages, &mut rng) .map_err(|e| JsValue::from_str(&format!("signing failed: {e}")))?; let signature_json: serde_json::Value = signature.export_to_obj(); diff --git a/bbs_wasm/.gitignore b/bbs_wasm/.gitignore new file mode 100644 index 0000000..1d16bd2 --- /dev/null +++ b/bbs_wasm/.gitignore @@ -0,0 +1 @@ +/pkg \ No newline at end of file diff --git a/bbs_wasm/Cargo.toml b/bbs_wasm/Cargo.toml new file mode 100644 index 0000000..75f2bac --- /dev/null +++ b/bbs_wasm/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bbs_wasm" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +wasm-bindgen = "0.2" +serde = { version = "1", features = ["derive"] } +serde_json = "1.0.149" +console_error_panic_hook = "0.1" +getrandom = { version = "0.2", features = ["js"] } +rand = "0.8" +ark-serialize = "0.5.0" + +bbs = { path = "../bbs" } +ark-bn254 = "0.5.0" +ark-std = "0.5.0" +hex = "0.4.3" +serde-wasm-bindgen = "0.6.5" diff --git a/bbs_wasm/html/test.html b/bbs_wasm/html/test.html new file mode 100644 index 0000000..59496c9 --- /dev/null +++ b/bbs_wasm/html/test.html @@ -0,0 +1,89 @@ + + + + + BBS Wasm Test + + +

BBS Wasm Test

+

Open the browser's developer tools (Console) to view the output results.

+ + + + diff --git a/bbs_wasm/src/lib.rs b/bbs_wasm/src/lib.rs new file mode 100644 index 0000000..ed8b4ac --- /dev/null +++ b/bbs_wasm/src/lib.rs @@ -0,0 +1,212 @@ +mod utils; + +use wasm_bindgen::prelude::*; +use ark_std::test_rng; +use rand::thread_rng; +use bbs::bbs::{keygen as bbs_keygen, setup as bbs_setup, sign as bbs_sign, verify as bbs_verify}; +use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix}; +use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; +use std::convert::TryInto; +use serde_wasm_bindgen::{from_value, to_value}; +use serde::Serialize; + +#[wasm_bindgen] +pub fn init_panic_hook() { + utils::set_panic_hook(); +} + +#[derive(Serialize)] +struct KeysJson { + pk: utils::PublicKeyJson, + sk: utils::PrivateKeyJson, +} + +#[wasm_bindgen] +pub fn setup(l: usize) -> Result { + let mut rng = thread_rng(); + let params = bbs_setup(l, &mut rng); + + let params_dto: utils::ParamsJson = (¶ms).into(); + to_value(¶ms_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn keygen() -> Result { + let mut rng = thread_rng(); + let (pk, sk) = bbs_keygen(&mut rng); + + let pk_dto: utils::PublicKeyJson = (&pk).into(); + let sk_dto: utils::PrivateKeyJson = (&sk).into(); + + let keys = KeysJson { + pk: pk_dto, + sk: sk_dto + }; + + to_value(&keys).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn sign(messages_js: JsValue, params_js: JsValue, sk_js: JsValue) -> Result { + let mut rng = thread_rng(); + + let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sk_dto: utils::PrivateKeyJson = from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sig = bbs_sign(&messages, ¶ms, &sk, &mut rng) + .map_err(|e| JsValue::from_str(&format!("Signing failed: {}", e)))?; + + let sig_dto: utils::SignatureJson = (&sig).into(); + to_value(&sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn verify(messages_js: JsValue, signature_js: JsValue, params_js: JsValue, pk_js: JsValue) -> Result { + let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + Ok(bbs_verify(&messages, &signature, ¶ms, &pk)) +} + + + +#[wasm_bindgen] +pub fn pok_nizk_prove( + params_js: JsValue, + pk_js: JsValue, + messages_js: JsValue, + signature_js: JsValue, + disclosed_count: usize, +) -> Result { + let mut rng = thread_rng(); + + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng) + .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; + + let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); + to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn pok_nizk_verify( + params_js: JsValue, + pk_js: JsValue, + disclosed_msgs_js: JsValue, + proof_js: JsValue, +) -> Result { + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = from_value(disclosed_msgs_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let disclosed_msgs: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let proof_dto: utils::NonInteractiveProofPrefixJson = from_value(proof_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let proof: utils::NonInteractiveProofPrefix = (&proof_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + Ok(nizk_verify_prefix(¶ms, &pk, &disclosed_msgs.0, &proof)) +} + +#[wasm_bindgen] +pub fn setup_debug(l: usize) -> Result { + let mut rng = test_rng(); + let params = bbs_setup(l, &mut rng); + + let params_dto: utils::ParamsJson = (¶ms).into(); + to_value(¶ms_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn keygen_debug() -> Result { + let mut rng = test_rng(); + let (pk, sk) = bbs_keygen(&mut rng); + + let pk_dto: utils::PublicKeyJson = (&pk).into(); + let sk_dto: utils::PrivateKeyJson = (&sk).into(); + + let keys = KeysJson { + pk: pk_dto, + sk: sk_dto + }; + + to_value(&keys).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn sign_debug(messages_js: JsValue, params_js: JsValue, sk_js: JsValue) -> Result { + let mut rng = test_rng(); + + let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sk_dto: utils::PrivateKeyJson = from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sig = bbs_sign(&messages, ¶ms, &sk, &mut rng) + .map_err(|e| JsValue::from_str(&format!("Signing failed: {}", e)))?; + + let sig_dto: utils::SignatureJson = (&sig).into(); + to_value(&sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn pok_nizk_prove_debug( + params_js: JsValue, + pk_js: JsValue, + messages_js: JsValue, + signature_js: JsValue, + disclosed_count: usize, +) -> Result { + let mut rng = test_rng(); + + let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng) + .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; + + let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); + to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + diff --git a/bbs_wasm/src/utils.rs b/bbs_wasm/src/utils.rs new file mode 100644 index 0000000..ef5a6d8 --- /dev/null +++ b/bbs_wasm/src/utils.rs @@ -0,0 +1,225 @@ +#![allow(non_snake_case, dead_code)] + +use ark_bn254::{Fq, Fq2, Fr as Scalar, G1Affine as G1, G2Affine as G2}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; + +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + console_error_panic_hook::set_once(); +} + +#[derive(Serialize, Deserialize)] +pub struct G1Json { + pub x: String, + pub y: String, +} + +#[derive(Serialize, Deserialize)] +pub struct Fq2Json { + pub c0: String, + pub c1: String, +} + +#[derive(Serialize, Deserialize)] +pub struct G2Json { + pub x: Fq2Json, + pub y: Fq2Json, +} + +pub fn g1_to_json(p: &G1) -> G1Json { + G1Json { + x: p.x.to_string(), + y: p.y.to_string(), + } +} + +pub fn g1_from_json(p: &G1Json) -> Result { + let x = Fq::from_str(&p.x).map_err(|_| format!("Invalid x: {}", p.x))?; + let y = Fq::from_str(&p.y).map_err(|_| format!("Invalid y: {}", p.y))?; + Ok(G1::new_unchecked(x, y)) +} + +pub fn fq2_to_json(p: &Fq2) -> Fq2Json { + Fq2Json { + c0: p.c0.to_string(), + c1: p.c1.to_string(), + } +} + +pub fn fq2_from_json(p: &Fq2Json) -> Result { + let c0 = Fq::from_str(&p.c0).map_err(|_| "Invalid c0")?; + let c1 = Fq::from_str(&p.c1).map_err(|_| "Invalid c1")?; + Ok(Fq2::new(c0, c1)) +} + +pub fn g2_to_json(p: &G2) -> G2Json { + G2Json { + x: fq2_to_json(&p.x), + y: fq2_to_json(&p.y), + } +} + +pub fn g2_from_json(p: &G2Json) -> Result { + let x_c0 = Fq::from_str(&p.x.c0).map_err(|_| "Invalid x.c0")?; + let x_c1 = Fq::from_str(&p.x.c1).map_err(|_| "Invalid x.c1")?; + let y_c0 = Fq::from_str(&p.y.c0).map_err(|_| "Invalid y.c0")?; + let y_c1 = Fq::from_str(&p.y.c1).map_err(|_| "Invalid y.c1")?; + Ok(G2::new_unchecked( + Fq2::new(x_c0, x_c1), + Fq2::new(y_c0, y_c1), + )) +} + +#[derive(Serialize, Deserialize)] +pub struct ParamsJson { + pub G1: G1Json, + pub G2: G2Json, + pub L: usize, + pub H: Vec, +} + +impl From<&Params> for ParamsJson { + fn from(params: &Params) -> Self { + Self { + G1: g1_to_json(¶ms.G1), + G2: g2_to_json(¶ms.G2), + L: params.L, + H: params.H.iter().map(g1_to_json).collect(), + } + } +} +impl TryFrom<&ParamsJson> for Params { + type Error = String; + fn try_from(dto: &ParamsJson) -> Result { + Ok(Params { + G1: g1_from_json(&dto.G1)?, + G2: g2_from_json(&dto.G2)?, + L: dto.L, + H: dto.H.iter().map(g1_from_json).collect::, _>>()?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct PrivateKeyJson { + pub x: String, +} + +impl From<&PrivateKey> for PrivateKeyJson { + fn from(sk: &PrivateKey) -> Self { + Self { + x: sk.x.to_string(), + } + } +} +impl TryFrom<&PrivateKeyJson> for PrivateKey { + type Error = String; + fn try_from(dto: &PrivateKeyJson) -> Result { + Ok(PrivateKey { + x: Scalar::from_str(&dto.x).map_err(|_| "Invalid x")?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct PublicKeyJson { + pub X: G2Json, +} + +impl From<&PublicKey> for PublicKeyJson { + fn from(pk: &PublicKey) -> Self { + Self { + X: g2_to_json(&pk.X), + } + } +} +impl TryFrom<&PublicKeyJson> for PublicKey { + type Error = String; + fn try_from(dto: &PublicKeyJson) -> Result { + Ok(PublicKey { + X: g2_from_json(&dto.X)?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct SignatureJson { + pub A: G1Json, + pub e: String, +} + +impl From<&Signature> for SignatureJson { + fn from(sig: &Signature) -> Self { + Self { + A: g1_to_json(&sig.A), + e: sig.e.to_string(), + } + } +} +impl TryFrom<&SignatureJson> for Signature { + type Error = String; + fn try_from(dto: &SignatureJson) -> Result { + Ok(Signature { + A: g1_from_json(&dto.A)?, + e: Scalar::from_str(&dto.e).map_err(|_| "Invalid e")?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct MessagesJson(pub Vec); + +impl From<&Messages> for MessagesJson { + fn from(msgs: &Messages) -> Self { + Self(msgs.0.iter().map(|s| s.to_string()).collect()) + } +} +impl TryFrom<&MessagesJson> for Messages { + type Error = String; + fn try_from(dto: &MessagesJson) -> Result { + let scalars = dto.0.iter().map(|s| Scalar::from_str(s).map_err(|_| format!("Invalid scalar {}", s))).collect::, _>>()?; + Ok(Messages(scalars)) + } +} + +pub use bbs::pok::NonInteractiveProofPrefix; + +#[derive(Serialize, Deserialize)] +pub struct NonInteractiveProofPrefixJson { + pub A_bar: G1Json, + pub B_bar: G1Json, + pub U: G1Json, + pub s: String, + pub t: String, + pub u_i: Vec, +} + +impl From<&NonInteractiveProofPrefix> for NonInteractiveProofPrefixJson { + fn from(proof: &NonInteractiveProofPrefix) -> Self { + Self { + A_bar: g1_to_json(&proof.A_bar), + B_bar: g1_to_json(&proof.B_bar), + U: g1_to_json(&proof.U), + s: proof.s.to_string(), + t: proof.t.to_string(), + u_i: proof.u_i.iter().map(|s| s.to_string()).collect(), + } + } +} +impl TryFrom<&NonInteractiveProofPrefixJson> for NonInteractiveProofPrefix { + type Error = String; + fn try_from(dto: &NonInteractiveProofPrefixJson) -> Result { + Ok(NonInteractiveProofPrefix { + A_bar: g1_from_json(&dto.A_bar)?, + B_bar: g1_from_json(&dto.B_bar)?, + U: g1_from_json(&dto.U)?, + s: Scalar::from_str(&dto.s).map_err(|_| "Invalid s")?, + t: Scalar::from_str(&dto.t).map_err(|_| "Invalid t")?, + u_i: dto.u_i.iter().map(|s| Scalar::from_str(s).map_err(|_| "Invalid u_i")).collect::, _>>()?, + }) + } +} \ No newline at end of file From 8355b24f615e8fe2eb056c7ee5e652c71b60096e Mon Sep 17 00:00:00 2001 From: klizz111 Date: Wed, 8 Apr 2026 13:23:22 +0800 Subject: [PATCH 3/8] feat: :art: run cargo fmt --- bbs/src/bbs.rs | 19 +- bbs/src/lib.rs | 6 +- bbs/src/modified_serde.rs | 4 +- bbs/src/pok.rs | 112 ++++++------ bbs/src/pub_use.rs | 2 +- bbs/src/structs.rs | 43 +++-- bbs/tests/bbs_base_test.rs | 18 +- bbs/tests/pok_test.rs | 66 ++++--- bbs_plus/benchmark/sign_pairing.rs | 47 +++-- bbs_plus/src/bbs_bn254/blind.rs | 14 +- bbs_plus/src/bbs_bn254/mod.rs | 8 +- bbs_plus/src/bbs_bn254/utils.rs | 50 +++--- bbs_plus_wasm/src/lib.rs | 23 ++- bbs_wasm/src/lib.rs | 272 +++++++++++++++++++---------- bbs_wasm/src/utils.rs | 22 ++- 15 files changed, 442 insertions(+), 264 deletions(-) diff --git a/bbs/src/bbs.rs b/bbs/src/bbs.rs index 6c159d6..b99c5e8 100644 --- a/bbs/src/bbs.rs +++ b/bbs/src/bbs.rs @@ -2,11 +2,11 @@ #![allow(non_snake_case)] use ark_bn254::Bn254; -use ark_std::{rand::RngCore, UniformRand}; use ark_ec::pairing::Pairing; +use ark_std::{UniformRand, rand::RngCore}; -use crate::structs::*; use crate::pub_use::*; +use crate::structs::*; /// Key gen function. /// `L`: the length of messages. @@ -21,7 +21,7 @@ pub fn setup(L: usize, rng: &mut R) -> Params { G1: G1::generator(), G2: G2::generator(), L: L, - H: H + H: H, } } @@ -38,7 +38,12 @@ pub fn keygen(rng: &mut R) -> (PublicKey, PrivateKey) { /// + `params`: the public parameters /// + `sk`: the private key /// + `rng`: the rng engine -pub fn sign(messages: &Messages, params: &Params, sk: &PrivateKey, rng: &mut R) -> Result { +pub fn sign( + messages: &Messages, + params: &Params, + sk: &PrivateKey, + rng: &mut R, +) -> Result { if messages.0.len() > params.L { return Err("message length exceeds parameters"); } @@ -49,7 +54,7 @@ pub fn sign(messages: &Messages, params: &Params, sk: &PrivateKey, r C += params.H[i] * m_i; } // 2. sample random scalars e - let e =Scalar::rand(rng); + let e = Scalar::rand(rng); // 3. calc A = C * (1/(x+e)) let mut tmp = sk.x + e; @@ -73,6 +78,6 @@ pub fn verify(messages: &Messages, signature: &Signature, params: &Params, pk: & let left = Bn254::pairing(signature.A, pk.X + params.G2 * signature.e); // 3. calc e(C', G2) let right = Bn254::pairing(C_prime.into_affine(), params.G2); - + left == right -} \ No newline at end of file +} diff --git a/bbs/src/lib.rs b/bbs/src/lib.rs index 0391188..74c0145 100644 --- a/bbs/src/lib.rs +++ b/bbs/src/lib.rs @@ -1,8 +1,8 @@ -mod pub_use; -mod modified_serde; -pub mod structs; pub mod bbs; +mod modified_serde; pub mod pok; +mod pub_use; +pub mod structs; pub fn add(left: u64, right: u64) -> u64 { left + right diff --git a/bbs/src/modified_serde.rs b/bbs/src/modified_serde.rs index 9298025..3d11a80 100644 --- a/bbs/src/modified_serde.rs +++ b/bbs/src/modified_serde.rs @@ -1,5 +1,5 @@ use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; -use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; pub fn serialize_scalar(scalar: &Scalar, serializer: S) -> Result where @@ -107,4 +107,4 @@ where .into_iter() .map(|bytes| Scalar::deserialize_compressed(&bytes[..]).map_err(serde::de::Error::custom)) .collect() -} \ No newline at end of file +} diff --git a/bbs/src/pok.rs b/bbs/src/pok.rs index 57d4419..006b43c 100644 --- a/bbs/src/pok.rs +++ b/bbs/src/pok.rs @@ -2,10 +2,10 @@ #![allow(non_snake_case)] use ark_bn254::{Bn254, Fr as Scalar, G1Affine as G1}; -use ark_ec::pairing::Pairing; use ark_ec::CurveGroup; +use ark_ec::pairing::Pairing; use ark_ff::PrimeField; -use ark_std::{rand::RngCore, UniformRand, Zero}; +use ark_std::{UniformRand, Zero, rand::RngCore}; use std::collections::{HashMap, HashSet}; use crate::pub_use::*; @@ -80,7 +80,8 @@ pub fn pok_commit( } let l = messages.0.len(); - let hidden_indices: HashSet = (0..l).filter(|i| !disclosed_indices.contains(i)).collect(); + let hidden_indices: HashSet = + (0..l).filter(|i| !disclosed_indices.contains(i)).collect(); // 1. r <- Z_p* let mut r = Scalar::rand(rng); @@ -150,7 +151,7 @@ pub fn pok_prove(state: &InteractiveProverState, challenge: &Challenge) -> PoKRe let c = *challenge; let s = state.alpha + state.r * c; let t = state.beta - state.e * c; - + let mut u_i = HashMap::new(); for &i in &state.hidden_indices { let delta_i = state.deltas.get(&i).unwrap(); @@ -172,7 +173,7 @@ pub fn pok_verify( response: &PoKResponse, ) -> bool { let c_j = compute_c_j(params, disclosed); - + // 1. Pairing check: e(A_bar, X) == e(B_bar, G2) let left_pairing = Bn254::pairing(commitment.A_bar, pk.X); let right_pairing = Bn254::pairing(commitment.B_bar, params.G2); @@ -182,16 +183,16 @@ pub fn pok_verify( // 2. Homomorphic check: U + c * B_bar == s * C_J + t * A_bar + sum(u_i * H_i) let lhs = (commitment.U.into_group() + commitment.B_bar * challenge).into_affine(); - + let mut sum_ui_hi = params.G1.into_group() * Scalar::zero(); for (&i, u_i) in response.u_i.iter() { if i < params.L { sum_ui_hi += params.H[i] * u_i; } } - + let rhs = (c_j * response.s + commitment.A_bar * response.t + sum_ui_hi).into_affine(); - + lhs == rhs } @@ -206,36 +207,38 @@ fn compute_challenge( ) -> Scalar { let mut hasher = Keccak256::new(); hasher.update(POK_CTX); - + // Convert components to bytes appropriately (this is simplified, ideal uses CanonicalSerialize) use ark_serialize::CanonicalSerialize; - + let mut pk_bytes = Vec::new(); pk.X.serialize_compressed(&mut pk_bytes).unwrap_or_default(); hasher.update(&pk_bytes); - + // Sort disclosed messages by index to ensure deterministic hashing let mut indices: Vec<_> = disclosed.keys().cloned().collect(); indices.sort_unstable(); for i in indices { hasher.update(&(i as u64).to_be_bytes()); let mut m_bytes = Vec::new(); - disclosed[&i].serialize_compressed(&mut m_bytes).unwrap_or_default(); + disclosed[&i] + .serialize_compressed(&mut m_bytes) + .unwrap_or_default(); hasher.update(&m_bytes); } - + let mut a_bytes = Vec::new(); a_bar.serialize_compressed(&mut a_bytes).unwrap_or_default(); hasher.update(&a_bytes); - + let mut b_bytes = Vec::new(); b_bar.serialize_compressed(&mut b_bytes).unwrap_or_default(); hasher.update(&b_bytes); - + let mut u_bytes = Vec::new(); u.serialize_compressed(&mut u_bytes).unwrap_or_default(); hasher.update(&u_bytes); - + let hash_result = hasher.finalize(); // Convert hash to scalar Scalar::from_be_bytes_mod_order(&hash_result) @@ -251,7 +254,7 @@ pub fn nizk_prove( rng: &mut R, ) -> Result { let (commitment, state) = pok_commit(params, messages, signature, disclosed_indices, rng)?; - + let l = messages.0.len(); let mut disclosed_msgs = HashMap::new(); for &j in disclosed_indices.iter() { @@ -259,10 +262,16 @@ pub fn nizk_prove( disclosed_msgs.insert(j, messages.0[j]); } } - - let challenge = compute_challenge(pk, &disclosed_msgs, &commitment.A_bar, &commitment.B_bar, &commitment.U); + + let challenge = compute_challenge( + pk, + &disclosed_msgs, + &commitment.A_bar, + &commitment.B_bar, + &commitment.U, + ); let response = pok_prove(&state, &challenge); - + Ok(NonInteractiveProof { A_bar: commitment.A_bar, B_bar: commitment.B_bar, @@ -281,7 +290,7 @@ pub fn nizk_verify( proof: &NonInteractiveProof, ) -> bool { let challenge = compute_challenge(pk, disclosed, &proof.A_bar, &proof.B_bar, &proof.U); - + let commitment = PoKCommitment { A_bar: proof.A_bar, B_bar: proof.B_bar, @@ -292,7 +301,7 @@ pub fn nizk_verify( t: proof.t, u_i: proof.u_i.clone(), }; - + pok_verify(params, pk, disclosed, &commitment, &challenge, &response) } @@ -317,32 +326,32 @@ fn compute_challenge_prefix( ) -> Scalar { let mut hasher = Keccak256::new(); hasher.update(POK_CTX); - + use ark_serialize::CanonicalSerialize; - + let mut pk_bytes = Vec::new(); pk.X.serialize_compressed(&mut pk_bytes).unwrap_or_default(); hasher.update(&pk_bytes); - + for (i, m) in disclosed.iter().enumerate() { hasher.update(&(i as u64).to_be_bytes()); let mut m_bytes = Vec::new(); m.serialize_compressed(&mut m_bytes).unwrap_or_default(); hasher.update(&m_bytes); } - + let mut a_bytes = Vec::new(); a_bar.serialize_compressed(&mut a_bytes).unwrap_or_default(); hasher.update(&a_bytes); - + let mut b_bytes = Vec::new(); b_bar.serialize_compressed(&mut b_bytes).unwrap_or_default(); hasher.update(&b_bytes); - + let mut u_bytes = Vec::new(); u.serialize_compressed(&mut u_bytes).unwrap_or_default(); hasher.update(&u_bytes); - + let hash_result = hasher.finalize(); Scalar::from_be_bytes_mod_order(&hash_result) } @@ -359,50 +368,52 @@ pub fn nizk_prove_prefix( if disclosed_count > messages.0.len() || messages.0.len() > params.L { return Err("invalid disclosed count or message length"); } - + let l = messages.0.len(); - + let mut r = Scalar::rand(rng); - while r.is_zero() { r = Scalar::rand(rng); } - + while r.is_zero() { + r = Scalar::rand(rng); + } + let a_bar = (signature.A * r).into_affine(); - + let mut c_j = params.G1.into_group(); for j in 0..disclosed_count { c_j += params.H[j] * messages.0[j]; } - + let mut c = c_j; for i in disclosed_count..l { c += params.H[i] * messages.0[i]; } let b_bar = (c * r - signature.A * (r * signature.e)).into_affine(); - + let alpha = Scalar::rand(rng); let beta = Scalar::rand(rng); let mut deltas = Vec::with_capacity(l - disclosed_count); let mut u_term = params.G1.into_group() * Scalar::zero(); - + for i in disclosed_count..l { let delta_i = Scalar::rand(rng); deltas.push(delta_i); u_term += params.H[i] * delta_i; } - + let u = (c_j * alpha + a_bar * beta + u_term).into_affine(); - + let disclosed_msgs = &messages.0[0..disclosed_count]; let challenge = compute_challenge_prefix(pk, disclosed_msgs, &a_bar, &b_bar, &u); - + let s = alpha + r * challenge; let t = beta - signature.e * challenge; - + let mut u_i = Vec::with_capacity(l - disclosed_count); for i in disclosed_count..l { let idx = i - disclosed_count; u_i.push(deltas[idx] + r * messages.0[i] * challenge); } - + Ok(NonInteractiveProofPrefix { A_bar: a_bar, B_bar: b_bar, @@ -424,22 +435,23 @@ pub fn nizk_verify_prefix( if disclosed_count > params.L { return false; } - - let challenge = compute_challenge_prefix(pk, disclosed_msgs, &proof.A_bar, &proof.B_bar, &proof.U); - + + let challenge = + compute_challenge_prefix(pk, disclosed_msgs, &proof.A_bar, &proof.B_bar, &proof.U); + let mut c_j = params.G1.into_group(); for (j, &m_j) in disclosed_msgs.iter().enumerate() { c_j += params.H[j] * m_j; } - + let left_pairing = Bn254::pairing(proof.A_bar, pk.X); let right_pairing = Bn254::pairing(proof.B_bar, params.G2); if left_pairing != right_pairing { return false; } - + let lhs = (proof.U.into_group() + proof.B_bar * challenge).into_affine(); - + let mut sum_ui_hi = params.G1.into_group() * Scalar::zero(); for (idx, &u_i_val) in proof.u_i.iter().enumerate() { let i = disclosed_count + idx; @@ -447,8 +459,8 @@ pub fn nizk_verify_prefix( sum_ui_hi += params.H[i] * u_i_val; } } - + let rhs = (c_j * proof.s + proof.A_bar * proof.t + sum_ui_hi).into_affine(); - + lhs == rhs } diff --git a/bbs/src/pub_use.rs b/bbs/src/pub_use.rs index 1230d5a..c3721df 100644 --- a/bbs/src/pub_use.rs +++ b/bbs/src/pub_use.rs @@ -4,4 +4,4 @@ pub use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; pub use ark_ec::{AffineRepr, CurveGroup}; pub use ark_ff::Field; -pub use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; +pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; diff --git a/bbs/src/structs.rs b/bbs/src/structs.rs index 0ae821a..903626d 100644 --- a/bbs/src/structs.rs +++ b/bbs/src/structs.rs @@ -2,25 +2,28 @@ #![allow(non_snake_case, dead_code, unused_imports)] use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2, g2}; -use ark_serialize::{CanonicalSerialize, CanonicalDeserialize}; use ark_ff::Field; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use serde::{Deserialize, Serialize, de}; use crate::modified_serde::*; /// BBS signature params -/// + `G1` generator +/// + `G1` generator /// + `G2` generator /// + `L` the length of messages /// + `H` message base point -#[derive(Serialize,Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug)] pub struct Params { #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] pub G1: G1, - #[serde(serialize_with = "serialize_g2",deserialize_with = "deserialize_g2")] + #[serde(serialize_with = "serialize_g2", deserialize_with = "deserialize_g2")] pub G2: G2, pub L: usize, - #[serde(serialize_with = "serialize_vec_g1",deserialize_with="deserialize_vec_g1")] + #[serde( + serialize_with = "serialize_vec_g1", + deserialize_with = "deserialize_vec_g1" + )] pub H: Vec, } @@ -28,32 +31,40 @@ pub struct Params { /// `x`: the secret, Scalar #[derive(Serialize, Deserialize, Debug)] pub struct PrivateKey { - #[serde(serialize_with = "serialize_scalar", deserialize_with = "deserialize_scalar")] + #[serde( + serialize_with = "serialize_scalar", + deserialize_with = "deserialize_scalar" + )] pub x: Scalar, } - /// Publc key /// `X`: the public key point in G2, X = x*G2 #[derive(Serialize, Deserialize, Debug)] pub struct PublicKey { - #[serde(serialize_with = "serialize_g2",deserialize_with = "deserialize_g2")] - pub X: G2 + #[serde(serialize_with = "serialize_g2", deserialize_with = "deserialize_g2")] + pub X: G2, } #[derive(Serialize, Deserialize, Debug)] -pub struct Messages ( - #[serde(serialize_with = "serialize_vec_scalar",deserialize_with="deserialize_vec_scalar")] - pub Vec +pub struct Messages( + #[serde( + serialize_with = "serialize_vec_scalar", + deserialize_with = "deserialize_vec_scalar" + )] + pub Vec, ); -/// Signature +/// Signature /// + `A`: the signature point in G1 /// + `e`: random scalar in Scalar #[derive(Serialize, Deserialize, Debug)] pub struct Signature { - #[serde(serialize_with = "serialize_g1",deserialize_with = "deserialize_g1")] + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] pub A: G1, - #[serde(serialize_with = "serialize_scalar", deserialize_with = "deserialize_scalar")] + #[serde( + serialize_with = "serialize_scalar", + deserialize_with = "deserialize_scalar" + )] pub e: Scalar, -} \ No newline at end of file +} diff --git a/bbs/tests/bbs_base_test.rs b/bbs/tests/bbs_base_test.rs index 203da45..0f99eb3 100644 --- a/bbs/tests/bbs_base_test.rs +++ b/bbs/tests/bbs_base_test.rs @@ -1,33 +1,33 @@ mod tests { + use ark_bn254::Fr as Scalar; + use ark_std::UniformRand; + use ark_std::test_rng; use bbs::bbs::*; use bbs::structs::*; - use ark_std::test_rng; - use ark_std::UniformRand; - use ark_bn254::Fr as Scalar; #[test] fn bbs_base_test() { let mut rng = test_rng(); let l = 5; - + // 1. Setup let params = setup(l, &mut rng); - + // 2. KeyGen let (pk, sk) = keygen(&mut rng); - + // 3. Create Messages let mut msgs = Vec::new(); for _ in 0..l { msgs.push(Scalar::rand(&mut rng)); } let messages = Messages(msgs); - + // 4. Sign let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); - + // 5. Verify let is_valid = verify(&messages, &signature, ¶ms, &pk); assert!(is_valid, "BBS signature verification failed!"); } -} \ No newline at end of file +} diff --git a/bbs/tests/pok_test.rs b/bbs/tests/pok_test.rs index 1886bb6..78ee33e 100644 --- a/bbs/tests/pok_test.rs +++ b/bbs/tests/pok_test.rs @@ -1,28 +1,28 @@ mod tests { + use ark_bn254::Fr as Scalar; + use ark_std::UniformRand; + use ark_std::test_rng; use bbs::bbs::*; - use bbs::structs::*; use bbs::pok::*; - use ark_std::test_rng; - use ark_std::UniformRand; - use ark_bn254::Fr as Scalar; - use std::collections::{HashSet, HashMap}; + use bbs::structs::*; + use std::collections::{HashMap, HashSet}; #[test] fn test_pok_interactive() { let mut rng = test_rng(); let l = 5; - + let params = setup(l, &mut rng); let (pk, sk) = keygen(&mut rng); - + let mut msgs = Vec::new(); for _ in 0..l { msgs.push(Scalar::rand(&mut rng)); } let messages = Messages(msgs.clone()); - + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); - + // Let's disclose indices 0 and 2 let mut disclosed_indices = HashSet::new(); disclosed_indices.insert(0); @@ -33,7 +33,8 @@ mod tests { disclosed_msgs.insert(2, msgs[2]); // Prover Step 1 - let (commitment, state) = pok_commit(¶ms, &messages, &signature, &disclosed_indices, &mut rng).unwrap(); + let (commitment, state) = + pok_commit(¶ms, &messages, &signature, &disclosed_indices, &mut rng).unwrap(); // Verifier Step 1 let challenge = Scalar::rand(&mut rng); @@ -42,7 +43,14 @@ mod tests { let response = pok_prove(&state, &challenge); // Verifier Step 2 - let is_valid = pok_verify(¶ms, &pk, &disclosed_msgs, &commitment, &challenge, &response); + let is_valid = pok_verify( + ¶ms, + &pk, + &disclosed_msgs, + &commitment, + &challenge, + &response, + ); assert!(is_valid, "Interactive PoK verification failed!"); } @@ -50,20 +58,20 @@ mod tests { fn test_pok_non_interactive() { let mut rng = test_rng(); let l = 5; - + let params = setup(l, &mut rng); println!("{:?}", params); let (pk, sk) = keygen(&mut rng); println!("Public Key: {:?}, Private Key: {:?}", pk, sk); - + let mut msgs = Vec::new(); for _ in 0..l { msgs.push(Scalar::rand(&mut rng)); } let messages = Messages(msgs.clone()); - + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); - + // Let's disclose indices 1 and 4 let mut disclosed_indices = HashSet::new(); disclosed_indices.insert(1); @@ -74,7 +82,15 @@ mod tests { disclosed_msgs.insert(4, msgs[4]); // NIZK Prove - let proof = nizk_prove(¶ms, &pk, &messages, &signature, &disclosed_indices, &mut rng).unwrap(); + let proof = nizk_prove( + ¶ms, + &pk, + &messages, + &signature, + &disclosed_indices, + &mut rng, + ) + .unwrap(); // NIZK Verify let is_valid = nizk_verify(¶ms, &pk, &disclosed_msgs, &proof); @@ -85,24 +101,32 @@ mod tests { fn test_pok_non_interactive_prefix() { let mut rng = test_rng(); let l = 5; - + let params = setup(l, &mut rng); let (pk, sk) = keygen(&mut rng); - + let mut msgs = Vec::new(); for _ in 0..l { msgs.push(Scalar::rand(&mut rng)); } let messages = Messages(msgs.clone()); - + let signature = sign(&messages, ¶ms, &sk, &mut rng).expect("failed to sign"); - + // Disclose indices 0..2 (count = 3) let disclosed_count = 3; let disclosed_msgs = &msgs[0..disclosed_count]; // NIZK Prove - let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng).unwrap(); + let proof = nizk_prove_prefix( + ¶ms, + &pk, + &messages, + &signature, + disclosed_count, + &mut rng, + ) + .unwrap(); // NIZK Verify let is_valid = nizk_verify_prefix(¶ms, &pk, disclosed_msgs, &proof); diff --git a/bbs_plus/benchmark/sign_pairing.rs b/bbs_plus/benchmark/sign_pairing.rs index f15fd46..1d8bbbe 100644 --- a/bbs_plus/benchmark/sign_pairing.rs +++ b/bbs_plus/benchmark/sign_pairing.rs @@ -15,13 +15,18 @@ fn bench_sign(c: &mut Criterion) { let (params, _pk, sk) = keygen(msg_count); let messages = sample_messages(msg_count); - group.bench_with_input(BenchmarkId::from_parameter(msg_count), &msg_count, |b, _| { - b.iter(|| { - let signature = sign_no_blind(black_box(¶ms), black_box(&sk), black_box(&messages)) - .expect("sign_no_blind should succeed for valid inputs"); - black_box(signature); - }); - }); + group.bench_with_input( + BenchmarkId::from_parameter(msg_count), + &msg_count, + |b, _| { + b.iter(|| { + let signature = + sign_no_blind(black_box(¶ms), black_box(&sk), black_box(&messages)) + .expect("sign_no_blind should succeed for valid inputs"); + black_box(signature); + }); + }, + ); } group.finish(); @@ -36,18 +41,22 @@ fn bench_verify(c: &mut Criterion) { let signature = sign_no_blind(¶ms, &sk, &messages) .expect("sign_no_blind should succeed for valid benchmark input"); - group.bench_with_input(BenchmarkId::from_parameter(msg_count), &msg_count, |b, _| { - b.iter(|| { - let ok = verify_no_blind( - black_box(¶ms), - black_box(&pk), - black_box(&messages), - black_box(&signature), - ) - .expect("verify_no_blind should not fail for valid benchmark input"); - black_box(ok); - }); - }); + group.bench_with_input( + BenchmarkId::from_parameter(msg_count), + &msg_count, + |b, _| { + b.iter(|| { + let ok = verify_no_blind( + black_box(¶ms), + black_box(&pk), + black_box(&messages), + black_box(&signature), + ) + .expect("verify_no_blind should not fail for valid benchmark input"); + black_box(ok); + }); + }, + ); } group.finish(); diff --git a/bbs_plus/src/bbs_bn254/blind.rs b/bbs_plus/src/bbs_bn254/blind.rs index 26737e2..841b206 100644 --- a/bbs_plus/src/bbs_bn254/blind.rs +++ b/bbs_plus/src/bbs_bn254/blind.rs @@ -142,7 +142,7 @@ pub fn commitment_pok_verify( params: &Parameters, commitment: &BlindedCommitment, proof: &CommitmentProof, - blind_index: &usize + blind_index: &usize, ) -> Result { if params.H.len() < proof.m_hats.len() + 1 { return Err("parameters do not include enough message base points"); @@ -188,7 +188,7 @@ fn commitment_challenge( mod tests { use super::*; use crate::bbs_bn254::keygen::keygen; - + #[test] fn blind_and_pok_roundtrip() { let (params, _pk, _sk) = keygen(3); @@ -198,8 +198,14 @@ mod tests { let blind_index = 1; let commitment = blind_with_rng(¶ms, &messages, &1, &mut rng).unwrap(); let hidden_part = vec![Scalar::from(2u64), Scalar::from(3u64)]; - let proof = - commitment_pok_prove_with_rng(¶ms, &commitment, &hidden_part, &blind_index, &mut rng).unwrap(); + let proof = commitment_pok_prove_with_rng( + ¶ms, + &commitment, + &hidden_part, + &blind_index, + &mut rng, + ) + .unwrap(); let ok = commitment_pok_verify(¶ms, &commitment, &proof, &blind_index).unwrap(); assert!(ok); diff --git a/bbs_plus/src/bbs_bn254/mod.rs b/bbs_plus/src/bbs_bn254/mod.rs index 4e340e1..b7d4cf4 100644 --- a/bbs_plus/src/bbs_bn254/mod.rs +++ b/bbs_plus/src/bbs_bn254/mod.rs @@ -7,9 +7,11 @@ pub mod verify; pub use blind::{ blind, blind_with_rng, commitment_pok_prove, commitment_pok_prove_with_rng, - commitment_pok_verify,unblind + commitment_pok_verify, unblind, }; pub use keygen::{keygen, keygen_with_rng}; -pub use signer::{sign_no_blind, sign_with_blind, sign_no_blind_with_rng, sign_with_blind_with_rng}; -pub use structs::{Parameters, PrivateKey, PublicKey, Signature, BlindedCommitment}; +pub use signer::{ + sign_no_blind, sign_no_blind_with_rng, sign_with_blind, sign_with_blind_with_rng, +}; +pub use structs::{BlindedCommitment, Parameters, PrivateKey, PublicKey, Signature}; pub use verify::verify_no_blind; diff --git a/bbs_plus/src/bbs_bn254/utils.rs b/bbs_plus/src/bbs_bn254/utils.rs index e64fa80..5d83fcf 100644 --- a/bbs_plus/src/bbs_bn254/utils.rs +++ b/bbs_plus/src/bbs_bn254/utils.rs @@ -232,7 +232,6 @@ impl Signature { pub fn export(&self) -> String { format!("(({},{}), {}, {})", self.A.x, self.A.y, self.e, self.s) - } pub fn load_from_json(s: &str) -> Result { @@ -400,8 +399,8 @@ impl BlindedCommitment { .get("blinding_factor") .and_then(|v| v.as_str()) .ok_or(ParamsJsonError::InvalidField("blinding_factor"))?; - let blinding_factor = - Scalar::from_str(blinding_factor_str).map_err(|_| ParamsJsonError::InvalidField("blinding_factor"))?; + let blinding_factor = Scalar::from_str(blinding_factor_str) + .map_err(|_| ParamsJsonError::InvalidField("blinding_factor"))?; Ok(BlindedCommitment { commitment, @@ -442,18 +441,16 @@ impl G1Ext for G1 { fn load_from_json(s: &str) -> Result { let decoded: G1Json = - from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; + from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; g1_from_json(&decoded, "g1") } } pub fn load_g1_from_json(s: &str) -> Result { - let decoded: G1Json = - from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; + let decoded: G1Json = from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; g1_from_json(&decoded, "g1") } - impl CommitmentProof { pub fn export_to_json(&self) -> String { json!({ @@ -479,20 +476,29 @@ impl CommitmentProof { "m_hats": self.m_hats.iter().map(|m_hat| m_hat.to_string()).collect::>(), }) } - - pub fn load_from_json(s: &str) -> Result { - let decoded: CommitmentProofJson = from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; - let mut m_hats_vec: Vec = Vec::with_capacity(decoded.m_hats.len()); - for (i, m_hat_str) in decoded.m_hats.iter().enumerate() { - let m_hat = m_hat_str.parse::().map_err(|_| ParamsJsonError::InvalidJson(format!("m_hats[{}]", i)))?; - m_hats_vec.push(m_hat); - } - - Ok(CommitmentProof { - t: g1_from_json(&decoded.t, "t")?, - challenge: decoded.challenge.parse::().map_err(|_| ParamsJsonError::InvalidField("challenge"))?, - s_hat: decoded.s_hat.parse::().map_err(|_| ParamsJsonError::InvalidField("s_hat"))?, - m_hats: m_hats_vec - }) + + pub fn load_from_json(s: &str) -> Result { + let decoded: CommitmentProofJson = + from_str(s).map_err(|e| ParamsJsonError::InvalidJson(e.to_string()))?; + let mut m_hats_vec: Vec = Vec::with_capacity(decoded.m_hats.len()); + for (i, m_hat_str) in decoded.m_hats.iter().enumerate() { + let m_hat = m_hat_str + .parse::() + .map_err(|_| ParamsJsonError::InvalidJson(format!("m_hats[{}]", i)))?; + m_hats_vec.push(m_hat); } + + Ok(CommitmentProof { + t: g1_from_json(&decoded.t, "t")?, + challenge: decoded + .challenge + .parse::() + .map_err(|_| ParamsJsonError::InvalidField("challenge"))?, + s_hat: decoded + .s_hat + .parse::() + .map_err(|_| ParamsJsonError::InvalidField("s_hat"))?, + m_hats: m_hats_vec, + }) + } } diff --git a/bbs_plus_wasm/src/lib.rs b/bbs_plus_wasm/src/lib.rs index 3dd1724..c5140db 100644 --- a/bbs_plus_wasm/src/lib.rs +++ b/bbs_plus_wasm/src/lib.rs @@ -1,8 +1,8 @@ mod utils; use bbs_plus::bbs_bn254::{ - utils::load_g1_from_json, - BlindedCommitment, Parameters, PrivateKey, PublicKey, Signature, blind_with_rng, keygen, keygen_with_rng, sign_no_blind_with_rng, verify_no_blind + BlindedCommitment, Parameters, PrivateKey, PublicKey, Signature, blind_with_rng, keygen, + keygen_with_rng, sign_no_blind_with_rng, utils::load_g1_from_json, verify_no_blind, }; use rand::rngs::OsRng; use serde_json::json; @@ -109,10 +109,8 @@ pub fn unblind( let signature = load_from_js(signature, "signature", Signature::load_from_json)?; let commitment = load_from_js(commitment, "commitment", BlindedCommitment::load_from_json)?; - let unblinded_signature = - bbs_plus::bbs_bn254::unblind(¶ms, &signature, &commitment).map_err(|e| { - JsValue::from_str(&format!("unblinding failed: {e}")) - })?; + let unblinded_signature = bbs_plus::bbs_bn254::unblind(¶ms, &signature, &commitment) + .map_err(|e| JsValue::from_str(&format!("unblinding failed: {e}")))?; let unblinded_signature_json = unblinded_signature.export_to_obj(); @@ -135,10 +133,17 @@ pub fn sign_with_blind( let commitment = load_from_js(commitment, "commitment", load_g1_from_json)?; let bind_index_usize = bind_index as usize; - let signature = bbs_plus::bbs_bn254::sign_with_blind_with_rng(¶ms, &sk, &bind_index_usize, &commitment, &visual_messages, &mut rng) - .map_err(|e| JsValue::from_str(&format!("signing failed: {e}")))?; + let signature = bbs_plus::bbs_bn254::sign_with_blind_with_rng( + ¶ms, + &sk, + &bind_index_usize, + &commitment, + &visual_messages, + &mut rng, + ) + .map_err(|e| JsValue::from_str(&format!("signing failed: {e}")))?; let signature_json: serde_json::Value = signature.export_to_obj(); to_js_json_compatible(&signature_json) -} \ No newline at end of file +} diff --git a/bbs_wasm/src/lib.rs b/bbs_wasm/src/lib.rs index ed8b4ac..20e085b 100644 --- a/bbs_wasm/src/lib.rs +++ b/bbs_wasm/src/lib.rs @@ -1,14 +1,14 @@ mod utils; -use wasm_bindgen::prelude::*; use ark_std::test_rng; -use rand::thread_rng; use bbs::bbs::{keygen as bbs_keygen, setup as bbs_setup, sign as bbs_sign, verify as bbs_verify}; use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix}; use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; -use std::convert::TryInto; -use serde_wasm_bindgen::{from_value, to_value}; +use rand::thread_rng; use serde::Serialize; +use serde_wasm_bindgen::{from_value, to_value}; +use std::convert::TryInto; +use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn init_panic_hook() { @@ -25,7 +25,7 @@ struct KeysJson { pub fn setup(l: usize) -> Result { let mut rng = thread_rng(); let params = bbs_setup(l, &mut rng); - + let params_dto: utils::ParamsJson = (¶ms).into(); to_value(¶ms_dto).map_err(|e| JsValue::from_str(&e.to_string())) } @@ -34,56 +34,80 @@ pub fn setup(l: usize) -> Result { pub fn keygen() -> Result { let mut rng = thread_rng(); let (pk, sk) = bbs_keygen(&mut rng); - + let pk_dto: utils::PublicKeyJson = (&pk).into(); let sk_dto: utils::PrivateKeyJson = (&sk).into(); - + let keys = KeysJson { pk: pk_dto, - sk: sk_dto + sk: sk_dto, }; - + to_value(&keys).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] pub fn sign(messages_js: JsValue, params_js: JsValue, sk_js: JsValue) -> Result { let mut rng = thread_rng(); - - let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let sk_dto: utils::PrivateKeyJson = from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let sk: PrivateKey = (&sk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let sk_dto: utils::PrivateKeyJson = + from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; let sig = bbs_sign(&messages, ¶ms, &sk, &mut rng) .map_err(|e| JsValue::from_str(&format!("Signing failed: {}", e)))?; - + let sig_dto: utils::SignatureJson = (&sig).into(); to_value(&sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] -pub fn verify(messages_js: JsValue, signature_js: JsValue, params_js: JsValue, pk_js: JsValue) -> Result { - let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; +pub fn verify( + messages_js: JsValue, + signature_js: JsValue, + params_js: JsValue, + pk_js: JsValue, +) -> Result { + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; - Ok(bbs_verify(&messages, &signature, ¶ms, &pk)) -} + let sig_dto: utils::SignatureJson = + from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + let pk_dto: utils::PublicKeyJson = + from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + Ok(bbs_verify(&messages, &signature, ¶ms, &pk)) +} #[wasm_bindgen] pub fn pok_nizk_prove( @@ -94,22 +118,41 @@ pub fn pok_nizk_prove( disclosed_count: usize, ) -> Result { let mut rng = thread_rng(); - - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng) - .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; - + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = + from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let sig_dto: utils::SignatureJson = + from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let proof = nizk_prove_prefix( + ¶ms, + &pk, + &messages, + &signature, + disclosed_count, + &mut rng, + ) + .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; + let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) } @@ -121,17 +164,29 @@ pub fn pok_nizk_verify( disclosed_msgs_js: JsValue, proof_js: JsValue, ) -> Result { - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let msgs_dto: utils::MessagesJson = from_value(disclosed_msgs_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let disclosed_msgs: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let proof_dto: utils::NonInteractiveProofPrefixJson = from_value(proof_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let proof: utils::NonInteractiveProofPrefix = (&proof_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = + from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(disclosed_msgs_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let disclosed_msgs: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let proof_dto: utils::NonInteractiveProofPrefixJson = + from_value(proof_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let proof: utils::NonInteractiveProofPrefix = (&proof_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; Ok(nizk_verify_prefix(¶ms, &pk, &disclosed_msgs.0, &proof)) } @@ -140,7 +195,7 @@ pub fn pok_nizk_verify( pub fn setup_debug(l: usize) -> Result { let mut rng = test_rng(); let params = bbs_setup(l, &mut rng); - + let params_dto: utils::ParamsJson = (¶ms).into(); to_value(¶ms_dto).map_err(|e| JsValue::from_str(&e.to_string())) } @@ -149,34 +204,47 @@ pub fn setup_debug(l: usize) -> Result { pub fn keygen_debug() -> Result { let mut rng = test_rng(); let (pk, sk) = bbs_keygen(&mut rng); - + let pk_dto: utils::PublicKeyJson = (&pk).into(); let sk_dto: utils::PrivateKeyJson = (&sk).into(); - + let keys = KeysJson { pk: pk_dto, - sk: sk_dto + sk: sk_dto, }; - + to_value(&keys).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] -pub fn sign_debug(messages_js: JsValue, params_js: JsValue, sk_js: JsValue) -> Result { +pub fn sign_debug( + messages_js: JsValue, + params_js: JsValue, + sk_js: JsValue, +) -> Result { let mut rng = test_rng(); - - let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let sk_dto: utils::PrivateKeyJson = from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let sk: PrivateKey = (&sk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let sk_dto: utils::PrivateKeyJson = + from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; let sig = bbs_sign(&messages, ¶ms, &sk, &mut rng) .map_err(|e| JsValue::from_str(&format!("Signing failed: {}", e)))?; - + let sig_dto: utils::SignatureJson = (&sig).into(); to_value(&sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) } @@ -190,23 +258,41 @@ pub fn pok_nizk_prove_debug( disclosed_count: usize, ) -> Result { let mut rng = test_rng(); - - let params_dto: utils::ParamsJson = from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let params: Params = (¶ms_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let pk_dto: utils::PublicKeyJson = from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let pk: PublicKey = (&pk_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let msgs_dto: utils::MessagesJson = from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let messages: Messages = (&msgs_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let sig_dto: utils::SignatureJson = from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; - let signature: Signature = (&sig_dto).try_into().map_err(|e: String| JsValue::from_str(&e))?; - - let proof = nizk_prove_prefix(¶ms, &pk, &messages, &signature, disclosed_count, &mut rng) - .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; - + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let pk_dto: utils::PublicKeyJson = + from_value(pk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let pk: PublicKey = (&pk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let sig_dto: utils::SignatureJson = + from_value(signature_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let signature: Signature = (&sig_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let proof = nizk_prove_prefix( + ¶ms, + &pk, + &messages, + &signature, + disclosed_count, + &mut rng, + ) + .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; + let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) } - diff --git a/bbs_wasm/src/utils.rs b/bbs_wasm/src/utils.rs index ef5a6d8..da713a3 100644 --- a/bbs_wasm/src/utils.rs +++ b/bbs_wasm/src/utils.rs @@ -1,9 +1,9 @@ #![allow(non_snake_case, dead_code)] use ark_bn254::{Fq, Fq2, Fr as Scalar, G1Affine as G1, G2Affine as G2}; +use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; use serde::{Deserialize, Serialize}; use std::str::FromStr; -use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; pub fn set_panic_hook() { // When the `console_error_panic_hook` feature is enabled, we can call the @@ -99,7 +99,11 @@ impl TryFrom<&ParamsJson> for Params { G1: g1_from_json(&dto.G1)?, G2: g2_from_json(&dto.G2)?, L: dto.L, - H: dto.H.iter().map(g1_from_json).collect::, _>>()?, + H: dto + .H + .iter() + .map(g1_from_json) + .collect::, _>>()?, }) } } @@ -181,7 +185,11 @@ impl From<&Messages> for MessagesJson { impl TryFrom<&MessagesJson> for Messages { type Error = String; fn try_from(dto: &MessagesJson) -> Result { - let scalars = dto.0.iter().map(|s| Scalar::from_str(s).map_err(|_| format!("Invalid scalar {}", s))).collect::, _>>()?; + let scalars = dto + .0 + .iter() + .map(|s| Scalar::from_str(s).map_err(|_| format!("Invalid scalar {}", s))) + .collect::, _>>()?; Ok(Messages(scalars)) } } @@ -219,7 +227,11 @@ impl TryFrom<&NonInteractiveProofPrefixJson> for NonInteractiveProofPrefix { U: g1_from_json(&dto.U)?, s: Scalar::from_str(&dto.s).map_err(|_| "Invalid s")?, t: Scalar::from_str(&dto.t).map_err(|_| "Invalid t")?, - u_i: dto.u_i.iter().map(|s| Scalar::from_str(s).map_err(|_| "Invalid u_i")).collect::, _>>()?, + u_i: dto + .u_i + .iter() + .map(|s| Scalar::from_str(s).map_err(|_| "Invalid u_i")) + .collect::, _>>()?, }) } -} \ No newline at end of file +} From cd583a310c7b05404abdc2cd9595c690143d95f8 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Mon, 13 Apr 2026 15:51:41 +0800 Subject: [PATCH 4/8] add files for out extend scheme --- .github/workflows/build-wasm.yml | 6 +- .github/workflows/rust-test.yml | 10 ++- Cargo.lock | 1 + README.md | 4 ++ bbs/Cargo.toml | 1 + bbs/src/bbs.rs | 2 +- bbs/src/extend.rs | 101 ++++++++++++++++++++++++++++ bbs/src/extend_structs.rs | 58 ++++++++++++++++ bbs/src/lib.rs | 4 +- bbs/tests/bbs_extended_test.rs | 112 +++++++++++++++++++++++++++++++ bbs/tests/hash_test.rs | 66 ++++++++++++++++++ 11 files changed, 357 insertions(+), 8 deletions(-) create mode 100644 bbs/src/extend.rs create mode 100644 bbs/src/extend_structs.rs create mode 100644 bbs/tests/bbs_extended_test.rs create mode 100644 bbs/tests/hash_test.rs diff --git a/.github/workflows/build-wasm.yml b/.github/workflows/build-wasm.yml index 8b7eac2..7d2a7cc 100644 --- a/.github/workflows/build-wasm.yml +++ b/.github/workflows/build-wasm.yml @@ -1,9 +1,9 @@ name: Build Wasm "on": - push: - branches: - - master + # push: + # branches: + # - master pull_request: branches: - master diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index cb2e299..65627bc 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -1,14 +1,18 @@ name: Rust CI "on": - push: - branches: - - master + # push: + # branches: + # - master pull_request: branches: - master workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + jobs: build-and-test: name: Build and test signer diff --git a/Cargo.lock b/Cargo.lock index ca1aaa5..447784e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,6 +199,7 @@ dependencies = [ "ark-serialize", "ark-std", "criterion", + "hex", "serde", "serde_json", "sha3", diff --git a/README.md b/README.md index a9114f4..58a25bc 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ > run `cargo bench -p bbs` to run the benchmarks. +## Reference + ++ [Revisiting BBS Signatures](https://eprint.iacr.org/2023/275) + ## Thanks + [arkworks::algebra](https://github.com/arkworks-rs/algebra) \ No newline at end of file diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index b780375..9c8b3e3 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -14,6 +14,7 @@ substrate-bn = "0.6.0" sha3 = "0.10" serde_json = "1.0.149" serde = { version = "1.0.228", features = ["derive"] } +hex = "0.4.3" [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } \ No newline at end of file diff --git a/bbs/src/bbs.rs b/bbs/src/bbs.rs index b99c5e8..166e163 100644 --- a/bbs/src/bbs.rs +++ b/bbs/src/bbs.rs @@ -1,4 +1,4 @@ -//! Key gen related functions here. +//! Basic BBS scheme functions here. #![allow(non_snake_case)] use ark_bn254::Bn254; diff --git a/bbs/src/extend.rs b/bbs/src/extend.rs new file mode 100644 index 0000000..c4415a1 --- /dev/null +++ b/bbs/src/extend.rs @@ -0,0 +1,101 @@ +//! Extend funcs and methods for supporting our scheme. +#![allow(non_snake_case)] + +use crate::pub_use::*; +use ark_std::{UniformRand, rand::RngCore}; + +use crate::extend_structs::{PartialSignature, UserCommitment}; +use crate::structs::{Signature}; + +pub struct BBSPlusExtendedScheme; + +use ark_ff::{BigInteger, PrimeField}; +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; + +/// Converts a Field element to BIG ENDIAN bytes (32 bytes for Fr/Fq) +pub fn field_to_bytes_be(f: &F) -> Vec { + f.into_bigint().to_bytes_be() +} + +/// Serializes G1 into 64 bytes (X || Y) for EVM compatibility +pub fn push_g1_to_bytes(p: &G1, buf: &mut Vec) { + buf.extend_from_slice(&field_to_bytes_be(&p.x)); + buf.extend_from_slice(&field_to_bytes_be(&p.y)); +} + +/// Serializes G2 into 128 bytes (X.c1 || X.c0 || Y.c1 || Y.c0) for EVM Bn254 compatibility +pub fn push_g2_to_bytes(p: &G2, buf: &mut Vec) { + buf.extend_from_slice(&field_to_bytes_be(&p.x.c1)); + buf.extend_from_slice(&field_to_bytes_be(&p.x.c0)); + buf.extend_from_slice(&field_to_bytes_be(&p.y.c1)); + buf.extend_from_slice(&field_to_bytes_be(&p.y.c0)); +} + +impl BBSPlusExtendedScheme { + /// Step 2: User computes blinded commitment C2 + pub fn user_commit( + m_null: &Scalar, + m_gamma: &Scalar, + lambda: &Scalar, + h_bases: &[G1], + ) -> UserCommitment { + let l = h_bases.len(); + let h_null = &h_bases[l - 2]; + let h_gamma = &h_bases[l - 1]; + + let mut c2_proj = *h_null * *m_null; + c2_proj += *h_gamma * *m_gamma; + c2_proj = c2_proj * *lambda; + UserCommitment { + C2: c2_proj.into_affine(), + } + } + + /// Step 3: Signer generates partial signature + pub fn signer_sign( + rng: &mut R, + sk: &Scalar, + messages: &[Scalar], // public messages m_0 to m_{l-3} + h_bases: &[G1], // H0 to H_{l-1} + g1: &G1, + c2: &G1, + ) -> PartialSignature { + let l = h_bases.len(); + assert_eq!(messages.len(), l - 2, "Public messages count must be l-2"); + + // C1 = G1 + \sum m_i H_i + let mut c1_proj = g1.into_group(); + for (m, h) in messages.iter().zip(h_bases.iter()) { + c1_proj += *h * *m; + } + + let e = Scalar::rand(rng); + let mut denominator = *sk; + denominator += e; + let inv_denominator = denominator.inverse().unwrap(); + + let a1 = (c1_proj * inv_denominator).into_affine(); + let a2_prime = (*c2 * inv_denominator).into_affine(); + + PartialSignature { + A1: a1, + A2_prime: a2_prime, + e, + } + } + + /// Step 4.1: User unblinds the partial signature to get the full signature (A, e) + pub fn user_unblind( + partial_sig: &PartialSignature, + lambda: &Scalar, + ) -> Signature { + let inv_lambda = lambda.inverse().unwrap(); + let a2 = (partial_sig.A2_prime * inv_lambda).into_affine(); + let a = (partial_sig.A1.into_group() + a2).into_affine(); + + Signature { + A: a, + e: partial_sig.e, + } + } +} \ No newline at end of file diff --git a/bbs/src/extend_structs.rs b/bbs/src/extend_structs.rs new file mode 100644 index 0000000..8930d07 --- /dev/null +++ b/bbs/src/extend_structs.rs @@ -0,0 +1,58 @@ +//! Extend structs for our scheme +#![allow(non_snake_case)] + +use ark_bn254::{Fr as Scalar, G1Affine as G1}; +use serde::{Deserialize, Serialize}; + +use crate::modified_serde::*; + +/// User's blinded commitment for partial signature +/// C2 = lambda * (m_null * H_null + m_gamma * H_gamma) +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UserCommitment { + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub C2: G1, +} + +/// Partial signature generated by the Signer +/// Contains two A points and the random scalar e +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PartialSignature { + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub A1: G1, + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub A2_prime: G1, + #[serde( + serialize_with = "serialize_scalar", + deserialize_with = "deserialize_scalar" + )] + pub e: Scalar, +} + +/// NIZK Proof for BBS+ partial disclosure +/// pi = (A_bar, B_bar, U, s, t, hidden_responses) +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BBSPlusNIZKProof { + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub A_bar: G1, + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub B_bar: G1, + #[serde(serialize_with = "serialize_g1", deserialize_with = "deserialize_g1")] + pub U: G1, + #[serde( + serialize_with = "serialize_scalar", + deserialize_with = "deserialize_scalar" + )] + pub s: Scalar, + #[serde( + serialize_with = "serialize_scalar", + deserialize_with = "deserialize_scalar" + )] + pub t: Scalar, + /// Store the responses for hidden values (e.g., u_gamma) + #[serde( + serialize_with = "serialize_vec_scalar", + deserialize_with = "deserialize_vec_scalar" + )] + pub hidden_responses: Vec, +} \ No newline at end of file diff --git a/bbs/src/lib.rs b/bbs/src/lib.rs index 74c0145..5190241 100644 --- a/bbs/src/lib.rs +++ b/bbs/src/lib.rs @@ -1,8 +1,10 @@ pub mod bbs; mod modified_serde; pub mod pok; -mod pub_use; +pub mod pub_use; pub mod structs; +pub mod extend; +pub mod extend_structs; pub fn add(left: u64, right: u64) -> u64 { left + right diff --git a/bbs/tests/bbs_extended_test.rs b/bbs/tests/bbs_extended_test.rs new file mode 100644 index 0000000..ffa08c7 --- /dev/null +++ b/bbs/tests/bbs_extended_test.rs @@ -0,0 +1,112 @@ +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; +use ark_std::UniformRand; +use ark_std::test_rng; +use bbs::extend::BBSPlusExtendedScheme; +use bbs::structs::{PublicKey, Messages, Params}; +use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix}; +use ark_ec::{CurveGroup, AffineRepr}; + +#[test] +fn test_bbs_extended_scheme() { + let mut rng = test_rng(); + + // --------------------------------------------------------- + // 0. Setup parameters (Assume Trusted Setup / Issuer) + // --------------------------------------------------------- + let g1 = G1::generator(); + let g2 = G2::generator(); + + // We need 5 public messages (m0 to m4), + 2 extra messages (m_null, m_gamma) + let num_public_messages = 5; + let total_messages = num_public_messages + 2; + + let mut h_bases = Vec::with_capacity(total_messages); + for _ in 0..total_messages { + h_bases.push(G1::rand(&mut rng)); + } + + let params = Params { + G1: g1, + G2: g2, + L: total_messages, + H: h_bases.clone(), + }; + + // Signer keys + let sk = Scalar::rand(&mut rng); + let pk_point = (g2 * sk).into_affine(); + let pk = PublicKey { X: pk_point }; + + // --------------------------------------------------------- + // 1. User Application (Init & Blinding) + // --------------------------------------------------------- + // User attributes + let mut messages_vec = Vec::with_capacity(total_messages); + for _ in 0..num_public_messages { + messages_vec.push(Scalar::rand(&mut rng)); // e.g., age=18, nationality="CN", etc. + } + let m_null = Scalar::rand(&mut rng); + let m_gamma = Scalar::rand(&mut rng); + let lambda = Scalar::rand(&mut rng); + + // Push m_null and m_gamma as the last two messages + messages_vec.push(m_null); + messages_vec.push(m_gamma); + let messages = Messages(messages_vec.clone()); + + // User commits to m_null and m_gamma + let user_commitment = BBSPlusExtendedScheme::user_commit( + &m_null, + &m_gamma, + &lambda, + &h_bases + ); + + // --------------------------------------------------------- + // 2. Signer Generates Partial Signature + // --------------------------------------------------------- + // Signer checks public constraints, and issues partial sig + let public_messages = &messages_vec[..num_public_messages]; + let partial_sig = BBSPlusExtendedScheme::signer_sign( + &mut rng, + &sk, + public_messages, + &h_bases, + &g1, + &user_commitment.C2, + ); + + // --------------------------------------------------------- + // 3. User Unblinds the Partial Signature + // --------------------------------------------------------- + let full_signature = BBSPlusExtendedScheme::user_unblind(&partial_sig, &lambda); + + // --------------------------------------------------------- + // 4. User Generates NIZK Proof (Prove ownership of valid sig) + // --------------------------------------------------------- + // Disclosed count: num_public_messages + 1 (m_null is disclosed to verifier) + // The only hidden message is m_gamma at the end + let disclosed_count = num_public_messages + 1; + + let proof = nizk_prove_prefix( + ¶ms, + &pk, + &messages, + &full_signature, + disclosed_count, + &mut rng + ).expect("nizk_prove_prefix failed"); + + // --------------------------------------------------------- + // 5. Verifier Validates the NIZK Proof + // --------------------------------------------------------- + let disclosed_msgs = &messages_vec[..disclosed_count]; + let is_valid = nizk_verify_prefix( + ¶ms, + &pk, + disclosed_msgs, + &proof + ); + + assert!(is_valid, "The extended BBS scheme NIZK proof failed to verify!"); +} diff --git a/bbs/tests/hash_test.rs b/bbs/tests/hash_test.rs new file mode 100644 index 0000000..67bedac --- /dev/null +++ b/bbs/tests/hash_test.rs @@ -0,0 +1,66 @@ +use ark_bn254::{Fr as Scalar, G1Affine as G1, G2Affine as G2}; +use ark_ec::AffineRepr; +use bbs::structs::PublicKey; +use sha3::{Digest, Keccak256}; +use ark_serialize::CanonicalSerialize; +use hex; + +#[test] +fn main() { + let pk = PublicKey { X: G2::generator() }; + let disclosed = vec![Scalar::from(42u64)]; + let a_bar = G1::generator(); + let b_bar = G1::generator(); + let u = G1::generator(); + + let mut hasher = Keccak256::new(); + let mut all_bytes = Vec::new(); + + let ctx = b"BBS_PoK_1"; + all_bytes.extend_from_slice(ctx); + + let mut pk_bytes = Vec::new(); + pk.X.serialize_compressed(&mut pk_bytes).unwrap(); + all_bytes.extend_from_slice(&pk_bytes); + + for (i, m) in disclosed.iter().enumerate() { + all_bytes.extend_from_slice(&(i as u64).to_be_bytes()); + let mut m_bytes = Vec::new(); + m.serialize_compressed(&mut m_bytes).unwrap(); + all_bytes.extend_from_slice(&m_bytes); + } + + let mut a_bytes = Vec::new(); + a_bar.serialize_compressed(&mut a_bytes).unwrap(); + all_bytes.extend_from_slice(&a_bytes); + + let mut b_bytes = Vec::new(); + b_bar.serialize_compressed(&mut b_bytes).unwrap(); + all_bytes.extend_from_slice(&b_bytes); + + let mut u_bytes = Vec::new(); + u.serialize_compressed(&mut u_bytes).unwrap(); + all_bytes.extend_from_slice(&u_bytes); + + hasher.update(&all_bytes); + let hash_result = hasher.finalize(); + + println!("All bytes: 0x{}", hex::encode(&all_bytes)); + println!("Hash result: 0x{}", hex::encode(&hash_result)); + + let hex_str = format!("0x{}", hex::encode(&all_bytes)); + println!("\nExecuting command: cast keccak {}", hex_str); + + // Call shell command directly using std::process::Command + let output = std::process::Command::new("cast") + .arg("keccak") + .arg(&hex_str) + .output() + .expect("Failed to execute cast command. Please Make sure foundry/cast is installed in your PATH."); + + if output.status.success() { + println!("cast output: {}", String::from_utf8_lossy(&output.stdout).trim()); + } else { + eprintln!("cast failed: {}", String::from_utf8_lossy(&output.stderr).trim()); + } +} From efd3533ad35b0e90237358f45e6342d0e21d8fe2 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Tue, 14 Apr 2026 19:47:34 +0800 Subject: [PATCH 5/8] wasm wrapper for bbs updated --- bbs/Cargo.toml | 6 ++- bbs/bin/tools.rs | 21 ++++++++ bbs_wasm/READMD.md | 1 + bbs_wasm/html/test.html | 102 +++++++++++++++++++++++--------------- bbs_wasm/src/lib.rs | 105 +++++++++++++++++++++++++++++++++++++++- bbs_wasm/src/utils.rs | 52 ++++++++++++++++++++ 6 files changed, 245 insertions(+), 42 deletions(-) create mode 100644 bbs/bin/tools.rs create mode 100644 bbs_wasm/READMD.md diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index 9c8b3e3..7aa2c07 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -17,4 +17,8 @@ serde = { version = "1.0.228", features = ["derive"] } hex = "0.4.3" [dev-dependencies] -criterion = { version = "0.5", features = ["html_reports"] } \ No newline at end of file +criterion = { version = "0.5", features = ["html_reports"] } + +[[bin]] +name = "tools" +path = "bin/tools.rs" \ No newline at end of file diff --git a/bbs/bin/tools.rs b/bbs/bin/tools.rs new file mode 100644 index 0000000..f00f9a2 --- /dev/null +++ b/bbs/bin/tools.rs @@ -0,0 +1,21 @@ +use sha3::{Digest, Keccak256}; + +pub fn main() { + let mut hasher = Keccak256::new(); + let m1 = String::from("Age=18"); + let m2 = String::from("Nationality=US"); + let m3 = String::from("Verified=true"); + let m4 = String::from("Empty"); + + hasher.update(m1.as_bytes()); + println!("Hash of Age=18: 0x{}", hex::encode(hasher.finalize_reset())); + + hasher.update(m2.as_bytes()); + println!("Hash of Nationality=US: 0x{}", hex::encode(hasher.finalize_reset())); + + hasher.update(m3.as_bytes()); + println!("Hash of Verified=true: 0x{}", hex::encode(hasher.finalize_reset())); + + hasher.update(m4.as_bytes()); + println!("Hash of Empty: 0x{}", hex::encode(hasher.finalize_reset())); +} \ No newline at end of file diff --git a/bbs_wasm/READMD.md b/bbs_wasm/READMD.md new file mode 100644 index 0000000..5ab0277 --- /dev/null +++ b/bbs_wasm/READMD.md @@ -0,0 +1 @@ +> wasm-pack builde --target web \ No newline at end of file diff --git a/bbs_wasm/html/test.html b/bbs_wasm/html/test.html index 59496c9..27c867e 100644 --- a/bbs_wasm/html/test.html +++ b/bbs_wasm/html/test.html @@ -16,7 +16,10 @@

BBS Wasm Test

sign_debug, verify, pok_nizk_prove_debug, - pok_nizk_verify + pok_nizk_verify, + user_commit, + signer_sign, + user_unblind } from '../pkg/bbs_wasm.js'; async function runTest() { @@ -28,55 +31,74 @@

BBS Wasm Test

console.log("Panic hook initialized successfully"); try { - // 1. Setup - const numMessages = 3; - console.log(`\n=== 1. Setup (L=${numMessages}) ===`); - const params = setup_debug(numMessages); - console.log("Params:", params); + console.log("============================================="); + console.log(" BBS V2 Extended Scheme Pipeline "); + console.log("============================================="); - // 2. KeyGen - console.log("\n=== 2. Key Generation ==="); + // We need 4 public constraints + 2 hidden/semi-hidden (m_null, m_gamma) + const numPublicMessages = 4; + const totalMessages = numPublicMessages + 2; + + // 1. Setup Parameters & Keys + console.log(`\n=== 1. Init (L=${totalMessages}) ===`); + const params = setup_debug(totalMessages); const keys = keygen_debug(); - console.log("Public Key:", keys.pk); - console.log("Private Key:", keys.sk); + console.log("G1", params.G1); + console.log("G2", params.G2); + console.log("Params (H Length):", params.H); + console.log("Signer Public Key:", keys.pk); + console.log("Signer Secret Key:", keys.sk); + + // 2. User Apply (Commit) + console.log("\n=== 2. User Apply (Commit) ==="); + // Using scalar string representations for testing purposes + const m_null = "192837465"; + const m_gamma = "987654321"; + const lambda = "1122334455"; + const publicMessages = ["48305238028827234457057068308976830949311451167141786071899284143701496669988", + "30362564611297414121344052595118041020684015714278959822893592029797294529055", + "89017634352366059964332753369766654038457760534865200995547152036320389318152", + "89017634352366059964332753369766654038457760534865200995547152036320389318152"]; // Constrains + + console.log("User's m_null:", m_null, "| m_gamma:", m_gamma, "| lambda:", lambda); + const userCommitment = user_commit(m_null, m_gamma, lambda, params); + console.log("User Commitment (C2):", userCommitment); - // 3. Create Messages - console.log("\n=== 3. Create Messages ==="); - const messagesObj = [ - "1", - "2", - "3" - ]; - console.log("Messages:", messagesObj); + // 3. Signer Generates Partial Signature + console.log("\n=== 3. Signer Generates Partial Signature ==="); + console.log("Signer checking constraints:", publicMessages); + const partialSig = signer_sign(keys.sk, publicMessages, params, userCommitment); + console.log("Partial Signature:", partialSig); - // 4. Sign - console.log("\n=== 4. Sign ==="); - const sig = sign_debug(messagesObj, params, keys.sk); - console.log("Signature:", sig); + // 4. User Unblinds to Get Full Signature + console.log("\n=== 4. User Unblind ==="); + const fullSig = user_unblind(partialSig, lambda); + console.log("Full Signature:", fullSig); - // 5. Verify - console.log("\n=== 5. Verify ==="); - const isValid = verify(messagesObj, sig, params, keys.pk); - console.log("Verify result:", isValid); + // Optional: Verify the standard full signature + const allMessages = [...publicMessages, m_null, m_gamma]; + const isSigValid = verify(allMessages, fullSig, params, keys.pk); + console.log("Standard Validity Check (Is sig valid?):", isSigValid); - // 6. PoK NIZK Prove (disclose the 1st message, hide the rest) - const disclosedCount = 1; - console.log(`\n=== 6. PoK Prove (Disclosed: ${disclosedCount}) ===`); - const proof = pok_nizk_prove_debug(params, keys.pk, messagesObj, sig, disclosedCount); - console.log("PoK Proof:", proof); + // 5. User Generates NIZK Proof (Prove ownership whilst hiding m_gamma) + console.log("\n=== 5. User Generate NIZK Proof ==="); + // Disclose constraints + m_null + const disclosedCount = numPublicMessages + 1; + const proof = pok_nizk_prove_debug(params, keys.pk, allMessages, fullSig, disclosedCount); + console.log(`Proof generated. Disclosed ${disclosedCount} messages (m_gamma is hidden).`); + console.log("Proof Object:", proof); - // 7. PoK NIZK Verify - console.log("\n=== 7. PoK Verify ==="); - const disclosedMsgsObj = messagesObj.slice(0, disclosedCount); - console.log("Disclosed Messages:", disclosedMsgsObj); - - const isProofValid = pok_nizk_verify(params, keys.pk, disclosedMsgsObj, proof); + // 6. Verifier Verify NIZK Proof + console.log("\n=== 6. Verifier Validate Proof ==="); + const disclosedMsgs = allMessages.slice(0, disclosedCount); + console.log("Verifier checking disclosed msgs:", disclosedMsgs); + const isProofValid = pok_nizk_verify(params, keys.pk, disclosedMsgs, proof); console.log("PoK Verify result:", isProofValid); - if (isValid && isProofValid) { - console.log("\n✅ All tests passed!"); + if (isSigValid && isProofValid) { + console.log("\n✅ V2 Scheme all tests passed!"); } else { - console.log("\n❌ Tests failed!"); + console.log("\n❌ V2 Scheme tests failed!"); } } catch (err) { console.error("\n❌ An error occurred:", err); diff --git a/bbs_wasm/src/lib.rs b/bbs_wasm/src/lib.rs index 20e085b..df9c790 100644 --- a/bbs_wasm/src/lib.rs +++ b/bbs_wasm/src/lib.rs @@ -1,9 +1,13 @@ mod utils; -use ark_std::test_rng; +use ark_std::{test_rng, UniformRand}; use bbs::bbs::{keygen as bbs_keygen, setup as bbs_setup, sign as bbs_sign, verify as bbs_verify}; +use bbs::extend::BBSPlusExtendedScheme; +use bbs::extend_structs::{PartialSignature, UserCommitment}; use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix}; use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; +use ark_bn254::Fr as Scalar; +use std::str::FromStr; use rand::thread_rng; use serde::Serialize; use serde_wasm_bindgen::{from_value, to_value}; @@ -191,6 +195,95 @@ pub fn pok_nizk_verify( Ok(nizk_verify_prefix(¶ms, &pk, &disclosed_msgs.0, &proof)) } +#[wasm_bindgen] +pub fn user_commit( + m_null_str: String, + m_gamma_str: String, + lambda_str: String, + params_js: JsValue, +) -> Result { + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let m_null = Scalar::from_str(&m_null_str).map_err(|_| JsValue::from_str("Invalid m_null"))?; + let m_gamma = Scalar::from_str(&m_gamma_str).map_err(|_| JsValue::from_str("Invalid m_gamma"))?; + let lambda = Scalar::from_str(&lambda_str).map_err(|_| JsValue::from_str("Invalid lambda"))?; + + let commitment = BBSPlusExtendedScheme::user_commit(&m_null, &m_gamma, &lambda, ¶ms.H); + + let commit_dto: utils::UserCommitmentJson = (&commitment).into(); + to_value(&commit_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn signer_sign( + sk_js: JsValue, + messages_js: JsValue, + params_js: JsValue, + user_commit_js: JsValue, +) -> Result { + let mut rng = thread_rng(); + + let sk_dto: utils::PrivateKeyJson = + from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let commit_dto: utils::UserCommitmentJson = + from_value(user_commit_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let commit: UserCommitment = (&commit_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let partial_sig = BBSPlusExtendedScheme::signer_sign( + &mut rng, + &sk.x, + &messages.0, + ¶ms.H, + ¶ms.G1, + &commit.C2, + ); + + let partial_sig_dto: utils::PartialSignatureJson = (&partial_sig).into(); + to_value(&partial_sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +#[wasm_bindgen] +pub fn user_unblind( + partial_sig_js: JsValue, + lambda_str: String, +) -> Result { + let partial_sig_dto: utils::PartialSignatureJson = + from_value(partial_sig_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let partial_sig: PartialSignature = (&partial_sig_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let lambda = Scalar::from_str(&lambda_str).map_err(|_| JsValue::from_str("Invalid lambda"))?; + + let full_sig = BBSPlusExtendedScheme::user_unblind(&partial_sig, &lambda); + + let sig_dto: utils::SignatureJson = (&full_sig).into(); + to_value(&sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + +/// debug only, gen fixed vec #[wasm_bindgen] pub fn setup_debug(l: usize) -> Result { let mut rng = test_rng(); @@ -200,6 +293,7 @@ pub fn setup_debug(l: usize) -> Result { to_value(¶ms_dto).map_err(|e| JsValue::from_str(&e.to_string())) } +/// debug only, gen fixed pk/sk #[wasm_bindgen] pub fn keygen_debug() -> Result { let mut rng = test_rng(); @@ -216,6 +310,7 @@ pub fn keygen_debug() -> Result { to_value(&keys).map_err(|e| JsValue::from_str(&e.to_string())) } +/// debug only, gen sig with test_rng #[wasm_bindgen] pub fn sign_debug( messages_js: JsValue, @@ -296,3 +391,11 @@ pub fn pok_nizk_prove_debug( let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) } + +/// Sample random Scalar +#[wasm_bindgen] +pub fn sample_random_scalar() -> String { + let mut rng = thread_rng(); + let scalar = Scalar::rand(&mut rng); + scalar.to_string() +} diff --git a/bbs_wasm/src/utils.rs b/bbs_wasm/src/utils.rs index da713a3..f485b51 100644 --- a/bbs_wasm/src/utils.rs +++ b/bbs_wasm/src/utils.rs @@ -235,3 +235,55 @@ impl TryFrom<&NonInteractiveProofPrefixJson> for NonInteractiveProofPrefix { }) } } + +use bbs::extend_structs::{UserCommitment, PartialSignature}; + +#[derive(Serialize, Deserialize)] +pub struct UserCommitmentJson { + pub C2: G1Json, +} + +impl From<&UserCommitment> for UserCommitmentJson { + fn from(commit: &UserCommitment) -> Self { + Self { + C2: g1_to_json(&commit.C2), + } + } +} + +impl TryFrom<&UserCommitmentJson> for UserCommitment { + type Error = String; + fn try_from(dto: &UserCommitmentJson) -> Result { + Ok(UserCommitment { + C2: g1_from_json(&dto.C2)?, + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct PartialSignatureJson { + pub A1: G1Json, + pub A2_prime: G1Json, + pub e: String, +} + +impl From<&PartialSignature> for PartialSignatureJson { + fn from(sig: &PartialSignature) -> Self { + Self { + A1: g1_to_json(&sig.A1), + A2_prime: g1_to_json(&sig.A2_prime), + e: sig.e.to_string(), + } + } +} + +impl TryFrom<&PartialSignatureJson> for PartialSignature { + type Error = String; + fn try_from(dto: &PartialSignatureJson) -> Result { + Ok(PartialSignature { + A1: g1_from_json(&dto.A1)?, + A2_prime: g1_from_json(&dto.A2_prime)?, + e: Scalar::from_str(&dto.e).map_err(|_| "Invalid e")?, + }) + } +} From 26950916b0c5e93d2dc578a6ab15c3184c304727 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Fri, 17 Apr 2026 01:24:29 +0800 Subject: [PATCH 6/8] challenge computation overhaul --- .github/workflows/publish-wasm.yml | 40 +++++++++++++++ Cargo.lock | 3 +- bbs/Cargo.toml | 6 ++- bbs/script/hash_test.rs | 38 ++++++++++++++ bbs/{bin => script}/tools.rs | 0 bbs/src/pok.rs | 82 +++++++++++++++++++----------- bbs/tests/bbs_extended_test.rs | 5 +- bbs/tests/pok_test.rs | 9 +++- bbs_wasm/Cargo.toml | 4 +- bbs_wasm/html/test.html | 20 ++++---- bbs_wasm/src/lib.rs | 66 +++++++++++++++++++++++- 11 files changed, 223 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/publish-wasm.yml create mode 100644 bbs/script/hash_test.rs rename bbs/{bin => script}/tools.rs (100%) diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml new file mode 100644 index 0000000..6514f9b --- /dev/null +++ b/.github/workflows/publish-wasm.yml @@ -0,0 +1,40 @@ +name: Publish wasm package + +on: + push: + tags: + - 'v*' # Capture pushes to tags that start with 'v', e.g., v0.1.0 + +jobs: + build-and-publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Build wasm package + run: | + cd bbs_wasm + wasm-pack build --target web + + - name: Publish to npm + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + cd bbs_wasm + wasm-pack publish --access public diff --git a/Cargo.lock b/Cargo.lock index 447784e..55e0e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,7 @@ dependencies = [ [[package]] name = "bbs_wasm" -version = "0.1.0" +version = "0.1.1" dependencies = [ "ark-bn254", "ark-serialize", @@ -255,6 +255,7 @@ dependencies = [ "serde-wasm-bindgen", "serde_json", "wasm-bindgen", + "web-sys", ] [[package]] diff --git a/bbs/Cargo.toml b/bbs/Cargo.toml index 7aa2c07..f3e3238 100644 --- a/bbs/Cargo.toml +++ b/bbs/Cargo.toml @@ -21,4 +21,8 @@ criterion = { version = "0.5", features = ["html_reports"] } [[bin]] name = "tools" -path = "bin/tools.rs" \ No newline at end of file +path = "script/tools.rs" + +[[bin]] +name = "hash_test" +path = "script/hash_test.rs" \ No newline at end of file diff --git a/bbs/script/hash_test.rs b/bbs/script/hash_test.rs new file mode 100644 index 0000000..7cc6693 --- /dev/null +++ b/bbs/script/hash_test.rs @@ -0,0 +1,38 @@ +use std::str::FromStr; +use ark_bn254::Fr as Scalar; +use ark_ff::BigInteger; +use ark_ff::PrimeField; +use sha3::{Digest, Keccak256}; + +fn main() { + let ctx = "LetsVerify".as_bytes(); + let m0 = Scalar::from_str("48305238028827234457057068308976830949311451167141786071899284143701496669988").unwrap(); + let m1 = Scalar::from_str("30362564611297414121344052595118041020684015714278959822893592029797294529055").unwrap(); + let m2 = Scalar::from_str("89017634352366059964332753369766654038457760534865200995547152036320389318152").unwrap(); + let m3 = Scalar::from_str("89017634352366059964332753369766654038457760534865200995547152036320389318152").unwrap(); + + let mut hasher = Keccak256::new(); + let mut payload = Vec::new(); + + let mut ctx_bytes = [0u8; 32]; + let len = ctx.len().min(32); + ctx_bytes[..len].copy_from_slice(&ctx[..len]); + payload.extend_from_slice(&ctx_bytes); + + for m in &[m0, m1, m2, m3] { + let m_bytes = m.into_bigint().to_bytes_be(); + let mut padded = [0u8; 32]; + let offset = 32 - m_bytes.len().min(32); + padded[offset..].copy_from_slice(&m_bytes); + payload.extend_from_slice(&padded); + } + + hasher.update(&payload); + let hash_result = hasher.finalize(); + println!("Payload Hex: {}", hex::encode(&payload)); + println!("Hash: {}", hex::encode(&hash_result)); + + let c = Scalar::from_be_bytes_mod_order(&hash_result); + // Print decimal representation + println!("C: {}", c); +} \ No newline at end of file diff --git a/bbs/bin/tools.rs b/bbs/script/tools.rs similarity index 100% rename from bbs/bin/tools.rs rename to bbs/script/tools.rs diff --git a/bbs/src/pok.rs b/bbs/src/pok.rs index 006b43c..1ed7418 100644 --- a/bbs/src/pok.rs +++ b/bbs/src/pok.rs @@ -53,9 +53,6 @@ pub struct NonInteractiveProof { pub u_i: HashMap, } -/// Context for FS heuristic -const POK_CTX: &[u8] = b"BBS_PoK_1"; - /// Compute the public commitment $C_J = G_1 + \sum_{j \in J} m_j \cdot H_j$ pub fn compute_c_j(params: &Params, disclosed: &DisclosedMessages) -> G1 { let mut c_j = params.G1.into_group(); @@ -199,6 +196,7 @@ pub fn pok_verify( /* ================== Non-Interactive Version (Fiat-Shamir) ================== */ fn compute_challenge( + pok_ctx: &[u8], pk: &PublicKey, disclosed: &DisclosedMessages, a_bar: &G1, @@ -206,7 +204,7 @@ fn compute_challenge( u: &G1, ) -> Scalar { let mut hasher = Keccak256::new(); - hasher.update(POK_CTX); + hasher.update(pok_ctx); // Convert components to bytes appropriately (this is simplified, ideal uses CanonicalSerialize) use ark_serialize::CanonicalSerialize; @@ -246,6 +244,7 @@ fn compute_challenge( /// NIZK Prove (Fiat-Shamir) pub fn nizk_prove( + pok_ctx: &[u8], params: &Params, pk: &PublicKey, messages: &Messages, @@ -264,6 +263,7 @@ pub fn nizk_prove( } let challenge = compute_challenge( + pok_ctx, pk, &disclosed_msgs, &commitment.A_bar, @@ -284,12 +284,13 @@ pub fn nizk_prove( /// NIZK Verify (Fiat-Shamir) pub fn nizk_verify( + pok_ctx: &[u8], params: &Params, pk: &PublicKey, disclosed: &DisclosedMessages, proof: &NonInteractiveProof, ) -> bool { - let challenge = compute_challenge(pk, disclosed, &proof.A_bar, &proof.B_bar, &proof.U); + let challenge = compute_challenge(pok_ctx, pk, disclosed, &proof.A_bar, &proof.B_bar, &proof.U); let commitment = PoKCommitment { A_bar: proof.A_bar, @@ -317,47 +318,65 @@ pub struct NonInteractiveProofPrefix { pub u_i: Vec, } -fn compute_challenge_prefix( - pk: &PublicKey, +use ark_ff::{BigInteger}; + +pub fn compute_challenge_prefix( + pok_ctx: &[u8], + _pk: &PublicKey, disclosed: &[Scalar], a_bar: &G1, b_bar: &G1, u: &G1, ) -> Scalar { let mut hasher = Keccak256::new(); - hasher.update(POK_CTX); - - use ark_serialize::CanonicalSerialize; - - let mut pk_bytes = Vec::new(); - pk.X.serialize_compressed(&mut pk_bytes).unwrap_or_default(); - hasher.update(&pk_bytes); - - for (i, m) in disclosed.iter().enumerate() { - hasher.update(&(i as u64).to_be_bytes()); - let mut m_bytes = Vec::new(); - m.serialize_compressed(&mut m_bytes).unwrap_or_default(); - hasher.update(&m_bytes); + let mut payload = Vec::new(); + + // 1. Hash ctx (Assuming pok_ctx is already 32 bytes externally or padded/hashed to 32 bytes) + // For compatibility with Solidity's `bytes32 ctx`, we expect `pok_ctx` to be exactly 32 bytes. + let mut ctx_bytes = [0u8; 32]; + if pok_ctx.len() == 32 { + ctx_bytes.copy_from_slice(pok_ctx); + } else { + // Pad with trailing zeros to match Solidity's bytes32("str") + let len = pok_ctx.len().min(32); + ctx_bytes[..len].copy_from_slice(&pok_ctx[..len]); + } + payload.extend_from_slice(&ctx_bytes); + + // 2. Hash disclosed messages. (Solidity `abi.encodePacked` of `uint256[]` is just flat consecutive 32-byte words) + for m in disclosed.iter() { + let m_bytes = m.into_bigint().to_bytes_be(); + // Since arkworks `to_bytes_be` might not pad to 32 bytes exactly depending on the field, + // we ensure it is 32 bytes for Solidity uint256: + let mut padded = [0u8; 32]; + let offset = 32 - m_bytes.len().min(32); + padded[offset..].copy_from_slice(&m_bytes); + payload.extend_from_slice(&padded); } - let mut a_bytes = Vec::new(); - a_bar.serialize_compressed(&mut a_bytes).unwrap_or_default(); - hasher.update(&a_bytes); + // 3. Hash A_bar.x, A_bar.y + let push_g1 = |payload: &mut Vec, p: &G1| { + let x_bytes = p.x.into_bigint().to_bytes_be(); + let mut px = [0u8; 32]; px[32 - x_bytes.len().min(32)..].copy_from_slice(&x_bytes); + payload.extend_from_slice(&px); - let mut b_bytes = Vec::new(); - b_bar.serialize_compressed(&mut b_bytes).unwrap_or_default(); - hasher.update(&b_bytes); + let y_bytes = p.y.into_bigint().to_bytes_be(); + let mut py = [0u8; 32]; py[32 - y_bytes.len().min(32)..].copy_from_slice(&y_bytes); + payload.extend_from_slice(&py); + }; - let mut u_bytes = Vec::new(); - u.serialize_compressed(&mut u_bytes).unwrap_or_default(); - hasher.update(&u_bytes); + push_g1(&mut payload, a_bar); + push_g1(&mut payload, b_bar); + push_g1(&mut payload, u); + hasher.update(&payload); let hash_result = hasher.finalize(); Scalar::from_be_bytes_mod_order(&hash_result) } /// NIZK Prove (Prefix Disclosure: 0..disclosed_count-1 are public) pub fn nizk_prove_prefix( + pok_ctx: &[u8], params: &Params, pk: &PublicKey, messages: &Messages, @@ -403,7 +422,7 @@ pub fn nizk_prove_prefix( let u = (c_j * alpha + a_bar * beta + u_term).into_affine(); let disclosed_msgs = &messages.0[0..disclosed_count]; - let challenge = compute_challenge_prefix(pk, disclosed_msgs, &a_bar, &b_bar, &u); + let challenge = compute_challenge_prefix(pok_ctx, pk, disclosed_msgs, &a_bar, &b_bar, &u); let s = alpha + r * challenge; let t = beta - signature.e * challenge; @@ -426,6 +445,7 @@ pub fn nizk_prove_prefix( /// NIZK Verify (Prefix Disclosure) pub fn nizk_verify_prefix( + pok_ctx: &[u8], params: &Params, pk: &PublicKey, disclosed_msgs: &[Scalar], @@ -437,7 +457,7 @@ pub fn nizk_verify_prefix( } let challenge = - compute_challenge_prefix(pk, disclosed_msgs, &proof.A_bar, &proof.B_bar, &proof.U); + compute_challenge_prefix(pok_ctx, pk, disclosed_msgs, &proof.A_bar, &proof.B_bar, &proof.U); let mut c_j = params.G1.into_group(); for (j, &m_j) in disclosed_msgs.iter().enumerate() { diff --git a/bbs/tests/bbs_extended_test.rs b/bbs/tests/bbs_extended_test.rs index ffa08c7..6d12c40 100644 --- a/bbs/tests/bbs_extended_test.rs +++ b/bbs/tests/bbs_extended_test.rs @@ -87,8 +87,10 @@ fn test_bbs_extended_scheme() { // Disclosed count: num_public_messages + 1 (m_null is disclosed to verifier) // The only hidden message is m_gamma at the end let disclosed_count = num_public_messages + 1; - + let ctx = "LetsVerify".as_bytes(); + let proof = nizk_prove_prefix( + &ctx, ¶ms, &pk, &messages, @@ -102,6 +104,7 @@ fn test_bbs_extended_scheme() { // --------------------------------------------------------- let disclosed_msgs = &messages_vec[..disclosed_count]; let is_valid = nizk_verify_prefix( + &ctx, ¶ms, &pk, disclosed_msgs, diff --git a/bbs/tests/pok_test.rs b/bbs/tests/pok_test.rs index 78ee33e..3eef2d5 100644 --- a/bbs/tests/pok_test.rs +++ b/bbs/tests/pok_test.rs @@ -81,8 +81,11 @@ mod tests { disclosed_msgs.insert(1, msgs[1]); disclosed_msgs.insert(4, msgs[4]); + let ctx = "LestsVerify".as_bytes(); + // NIZK Prove let proof = nizk_prove( + ctx, ¶ms, &pk, &messages, @@ -93,7 +96,7 @@ mod tests { .unwrap(); // NIZK Verify - let is_valid = nizk_verify(¶ms, &pk, &disclosed_msgs, &proof); + let is_valid = nizk_verify(ctx, ¶ms, &pk, &disclosed_msgs, &proof); assert!(is_valid, "Non-Interactive PoK verification failed!"); } @@ -117,8 +120,10 @@ mod tests { let disclosed_count = 3; let disclosed_msgs = &msgs[0..disclosed_count]; + let ctx = "LestsVerify".as_bytes(); // NIZK Prove let proof = nizk_prove_prefix( + ctx, ¶ms, &pk, &messages, @@ -129,7 +134,7 @@ mod tests { .unwrap(); // NIZK Verify - let is_valid = nizk_verify_prefix(¶ms, &pk, disclosed_msgs, &proof); + let is_valid = nizk_verify_prefix(ctx, ¶ms, &pk, disclosed_msgs, &proof); assert!(is_valid, "Prefix Non-Interactive PoK verification failed!"); } } diff --git a/bbs_wasm/Cargo.toml b/bbs_wasm/Cargo.toml index 75f2bac..19d4c29 100644 --- a/bbs_wasm/Cargo.toml +++ b/bbs_wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bbs_wasm" -version = "0.1.0" +version = "0.1.1" edition = "2024" [lib] @@ -14,7 +14,7 @@ console_error_panic_hook = "0.1" getrandom = { version = "0.2", features = ["js"] } rand = "0.8" ark-serialize = "0.5.0" - +web-sys = { version = "0.3", features = ["console"] } bbs = { path = "../bbs" } ark-bn254 = "0.5.0" ark-std = "0.5.0" diff --git a/bbs_wasm/html/test.html b/bbs_wasm/html/test.html index 27c867e..8aa9087 100644 --- a/bbs_wasm/html/test.html +++ b/bbs_wasm/html/test.html @@ -13,12 +13,11 @@

BBS Wasm Test

init_panic_hook, setup_debug, keygen_debug, - sign_debug, + signer_sign_debug, verify, pok_nizk_prove_debug, pok_nizk_verify, user_commit, - signer_sign, user_unblind } from '../pkg/bbs_wasm.js'; @@ -45,7 +44,7 @@

BBS Wasm Test

const keys = keygen_debug(); console.log("G1", params.G1); console.log("G2", params.G2); - console.log("Params (H Length):", params.H); + console.log("Params Vec H:", params.H); console.log("Signer Public Key:", keys.pk); console.log("Signer Secret Key:", keys.sk); @@ -55,10 +54,10 @@

BBS Wasm Test

const m_null = "192837465"; const m_gamma = "987654321"; const lambda = "1122334455"; - const publicMessages = ["48305238028827234457057068308976830949311451167141786071899284143701496669988", - "30362564611297414121344052595118041020684015714278959822893592029797294529055", - "89017634352366059964332753369766654038457760534865200995547152036320389318152", - "89017634352366059964332753369766654038457760534865200995547152036320389318152"]; // Constrains + const publicMessages = ["4528752285148684012564256818462280771918828852546138746521208354411044252822", + "8474321739458138899097646849860765932135651313862925479195387843221486033438", + "1464662865008959075347130388737553683672515905673906344791000457739484483820", + "1464662865008959075347130388737553683672515905673906344791000457739484483820"]; // Constrains console.log("User's m_null:", m_null, "| m_gamma:", m_gamma, "| lambda:", lambda); const userCommitment = user_commit(m_null, m_gamma, lambda, params); @@ -67,7 +66,7 @@

BBS Wasm Test

// 3. Signer Generates Partial Signature console.log("\n=== 3. Signer Generates Partial Signature ==="); console.log("Signer checking constraints:", publicMessages); - const partialSig = signer_sign(keys.sk, publicMessages, params, userCommitment); + const partialSig = signer_sign_debug(keys.sk, publicMessages, params, userCommitment); console.log("Partial Signature:", partialSig); // 4. User Unblinds to Get Full Signature @@ -84,7 +83,8 @@

BBS Wasm Test

console.log("\n=== 5. User Generate NIZK Proof ==="); // Disclose constraints + m_null const disclosedCount = numPublicMessages + 1; - const proof = pok_nizk_prove_debug(params, keys.pk, allMessages, fullSig, disclosedCount); + const ctx = "LetsVerify"; + const proof = pok_nizk_prove_debug(ctx, params, keys.pk, allMessages, fullSig, disclosedCount); console.log(`Proof generated. Disclosed ${disclosedCount} messages (m_gamma is hidden).`); console.log("Proof Object:", proof); @@ -92,7 +92,7 @@

BBS Wasm Test

console.log("\n=== 6. Verifier Validate Proof ==="); const disclosedMsgs = allMessages.slice(0, disclosedCount); console.log("Verifier checking disclosed msgs:", disclosedMsgs); - const isProofValid = pok_nizk_verify(params, keys.pk, disclosedMsgs, proof); + const isProofValid = pok_nizk_verify(ctx, params, keys.pk, disclosedMsgs, proof); console.log("PoK Verify result:", isProofValid); if (isSigValid && isProofValid) { diff --git a/bbs_wasm/src/lib.rs b/bbs_wasm/src/lib.rs index df9c790..1f96f03 100644 --- a/bbs_wasm/src/lib.rs +++ b/bbs_wasm/src/lib.rs @@ -4,7 +4,7 @@ use ark_std::{test_rng, UniformRand}; use bbs::bbs::{keygen as bbs_keygen, setup as bbs_setup, sign as bbs_sign, verify as bbs_verify}; use bbs::extend::BBSPlusExtendedScheme; use bbs::extend_structs::{PartialSignature, UserCommitment}; -use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix}; +use bbs::pok::{nizk_prove_prefix, nizk_verify_prefix, compute_challenge_prefix}; use bbs::structs::{Messages, Params, PrivateKey, PublicKey, Signature}; use ark_bn254::Fr as Scalar; use std::str::FromStr; @@ -13,6 +13,7 @@ use serde::Serialize; use serde_wasm_bindgen::{from_value, to_value}; use std::convert::TryInto; use wasm_bindgen::prelude::*; +use web_sys::console; #[wasm_bindgen] pub fn init_panic_hook() { @@ -115,6 +116,7 @@ pub fn verify( #[wasm_bindgen] pub fn pok_nizk_prove( + ctx: String, params_js: JsValue, pk_js: JsValue, messages_js: JsValue, @@ -148,6 +150,7 @@ pub fn pok_nizk_prove( .map_err(|e: String| JsValue::from_str(&e))?; let proof = nizk_prove_prefix( + ctx.as_bytes(), ¶ms, &pk, &messages, @@ -163,6 +166,7 @@ pub fn pok_nizk_prove( #[wasm_bindgen] pub fn pok_nizk_verify( + ctx: String, params_js: JsValue, pk_js: JsValue, disclosed_msgs_js: JsValue, @@ -192,7 +196,7 @@ pub fn pok_nizk_verify( .try_into() .map_err(|e: String| JsValue::from_str(&e))?; - Ok(nizk_verify_prefix(¶ms, &pk, &disclosed_msgs.0, &proof)) + Ok(nizk_verify_prefix(ctx.as_bytes(), ¶ms, &pk, &disclosed_msgs.0, &proof)) } #[wasm_bindgen] @@ -346,6 +350,7 @@ pub fn sign_debug( #[wasm_bindgen] pub fn pok_nizk_prove_debug( + ctx: String, params_js: JsValue, pk_js: JsValue, messages_js: JsValue, @@ -379,6 +384,7 @@ pub fn pok_nizk_prove_debug( .map_err(|e: String| JsValue::from_str(&e))?; let proof = nizk_prove_prefix( + ctx.as_bytes(), ¶ms, &pk, &messages, @@ -388,10 +394,66 @@ pub fn pok_nizk_prove_debug( ) .map_err(|e| JsValue::from_str(&format!("NIZK Prove failed: {}", e)))?; + let challenge = compute_challenge_prefix( + ctx.as_bytes(), + &pk, + &messages.0[0..disclosed_count], + &proof.A_bar, + &proof.B_bar, + &proof.U, + ); + console::log_1(&format!("Challenge: {}", challenge).into()); + let proof_dto: utils::NonInteractiveProofPrefixJson = (&proof).into(); to_value(&proof_dto).map_err(|e| JsValue::from_str(&e.to_string())) } +#[wasm_bindgen] +pub fn signer_sign_debug( + sk_js: JsValue, + messages_js: JsValue, + params_js: JsValue, + user_commit_js: JsValue, +) -> Result { + let mut rng = test_rng(); + + let sk_dto: utils::PrivateKeyJson = + from_value(sk_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let sk: PrivateKey = (&sk_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let msgs_dto: utils::MessagesJson = + from_value(messages_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let messages: Messages = (&msgs_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let params_dto: utils::ParamsJson = + from_value(params_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let params: Params = (¶ms_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let commit_dto: utils::UserCommitmentJson = + from_value(user_commit_js).map_err(|e| JsValue::from_str(&e.to_string()))?; + let commit: UserCommitment = (&commit_dto) + .try_into() + .map_err(|e: String| JsValue::from_str(&e))?; + + let partial_sig = BBSPlusExtendedScheme::signer_sign( + &mut rng, + &sk.x, + &messages.0, + ¶ms.H, + ¶ms.G1, + &commit.C2, + ); + + let partial_sig_dto: utils::PartialSignatureJson = (&partial_sig).into(); + to_value(&partial_sig_dto).map_err(|e| JsValue::from_str(&e.to_string())) +} + /// Sample random Scalar #[wasm_bindgen] pub fn sample_random_scalar() -> String { From aa05afee3d74c716515fc0291c03dd0f8ee42b16 Mon Sep 17 00:00:00 2001 From: klizz111 Date: Sat, 18 Apr 2026 16:40:20 +0800 Subject: [PATCH 7/8] update .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ef52176..ad491e9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ temp .vscode # rust -**/target \ No newline at end of file +**/target + +# mac +**/.DS_Store \ No newline at end of file From 97c3dea3e39334bdb6c29f2466ad4962c7312a8c Mon Sep 17 00:00:00 2001 From: klizz111 Date: Sat, 25 Apr 2026 16:27:41 +0800 Subject: [PATCH 8/8] chore: :technologist: Small modify - Modified README.md, add npm path - Update bbs_wasm version --- Cargo.lock | 2 +- README.md | 4 ++++ bbs_wasm/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55e0e81..db3f80d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,7 @@ dependencies = [ [[package]] name = "bbs_wasm" -version = "0.1.1" +version = "0.1.2" dependencies = [ "ark-bn254", "ark-serialize", diff --git a/README.md b/README.md index 58a25bc..0eb0402 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ > run `cargo bench -p bbs` to run the benchmarks. +## Npm Package + +> npm i bbs_wasm + ## Reference + [Revisiting BBS Signatures](https://eprint.iacr.org/2023/275) diff --git a/bbs_wasm/Cargo.toml b/bbs_wasm/Cargo.toml index 19d4c29..556c0f8 100644 --- a/bbs_wasm/Cargo.toml +++ b/bbs_wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bbs_wasm" -version = "0.1.1" +version = "0.1.2" edition = "2024" [lib]