From 932a17e1e43fefbff48893a3ed3631ab57d37930 Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 26 Sep 2024 14:51:15 +0200 Subject: [PATCH] Implement BBS+ Signatures (#2) * Implement BBS+ Signatures * Refactor CryptoProvider Arguments * Add more tests * Bump versions --- Cargo.lock | 650 ++++++++++++++++++++++++++- next-gen-signatures/Cargo.toml | 27 +- next-gen-signatures/src/common.rs | 52 ++- next-gen-signatures/src/crypto.rs | 183 +++++++- next-gen-signatures/src/macros.rs | 64 ++- next-gen-signing-service/Cargo.toml | 5 +- next-gen-signing-service/src/main.rs | 151 ++++++- 7 files changed, 1080 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b48f272..27e2c99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,39 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -32,6 +65,128 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "rayon", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -51,18 +206,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -107,6 +262,29 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bbs_plus" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f40c9468765fad293468ddbcd2dc092bd42dfd76555def0b786aedbf4139d4" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "digest", + "dock_crypto_utils", + "itertools 0.12.1", + "oblivious_transfer_protocols", + "rayon", + "schnorr_pok", + "secret_sharing_and_dkg", + "serde", + "serde_with", + "sha3", + "zeroize", +] + [[package]] name = "binascii" version = "0.1.4" @@ -119,6 +297,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -161,6 +348,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "cookie" version = "0.18.1" @@ -181,6 +378,31 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -191,6 +413,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "deranged" version = "0.3.11" @@ -200,6 +457,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "devise" version = "0.4.2" @@ -230,7 +498,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -241,6 +509,46 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "dock_crypto_utils" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4966d4f0a09dd065ef2885aa125b99a733842861cff6d787e5aebb517771d3" +dependencies = [ + "aead", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "digest", + "dock_merlin", + "hkdf", + "itertools 0.12.1", + "num", + "rayon", + "serde", + "serde_with", + "sha2", + "zeroize", +] + +[[package]] +name = "dock_merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f19d398293c9cb71dda798a521c7ffc5182d6222e0d66f731041755efaaa7ced" +dependencies = [ + "ark-serialize", + "ark-std", + "byteorder", + "keccak", + "rand_core", + "serde", + "zeroize", ] [[package]] @@ -441,6 +749,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -459,6 +776,24 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.12" @@ -528,6 +863,12 @@ dependencies = [ "want", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "2.5.0" @@ -535,7 +876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", "serde", ] @@ -545,6 +886,15 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -556,6 +906,24 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -683,21 +1051,30 @@ dependencies = [ [[package]] name = "next-gen-signatures" -version = "0.0.1" +version = "0.0.3" dependencies = [ "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-serialize", "base64", + "bbs_plus", + "blake2", "fips204", + "num-bigint", "paste", + "rand", + "rocket", ] [[package]] name = "next-gen-signing-service" -version = "0.0.2" +version = "0.0.3" dependencies = [ "next-gen-signatures", + "paste", "rocket", - "serde", + "rocket-errors", ] [[package]] @@ -710,12 +1087,85 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[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-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "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 = "num_cpus" version = "1.16.0" @@ -735,6 +1185,31 @@ dependencies = [ "memchr", ] +[[package]] +name = "oblivious_transfer_protocols" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a05f454419f17296f011a136db6399d8900a6cf90a8191b0f208a66c00048" +dependencies = [ + "aes", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "byteorder", + "cc", + "cipher", + "digest", + "dock_crypto_utils", + "itertools 0.12.1", + "rayon", + "schnorr_pok", + "serde", + "serde_with", + "sha3", + "zeroize", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -796,7 +1271,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -849,7 +1324,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", "version_check", "yansi", ] @@ -893,6 +1368,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.4" @@ -919,7 +1414,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1004,6 +1499,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "rocket-errors" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5ac1222b4578d545cc19d71a01cad41b43b383dc00c52737c2629c9c1affc0f" +dependencies = [ + "anyhow", + "rocket", +] + [[package]] name = "rocket_codegen" version = "0.5.1" @@ -1016,7 +1521,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn", + "syn 2.0.77", "unicode-xid", "version_check", ] @@ -1054,6 +1559,15 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.37" @@ -1079,6 +1593,24 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schnorr_pok" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a166f138517fe7359112179cb075ec04fb845267dfefb1c7f71258c5a817af49" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "digest", + "dock_crypto_utils", + "rayon", + "serde", + "serde_with", + "zeroize", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1091,6 +1623,32 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "secret_sharing_and_dkg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40ba413c8cbba3aa7dd76ffaef86af19d7bc4e93f9bccf5fc929c89b18b021d" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "digest", + "dock_crypto_utils", + "rayon", + "schnorr_pok", + "serde", + "serde_with", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.210" @@ -1108,7 +1666,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1132,6 +1690,39 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha3" version = "0.10.8" @@ -1215,6 +1806,29 @@ dependencies = [ "loom", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -1305,7 +1919,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1391,7 +2005,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1709,7 +2323,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1729,5 +2343,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] diff --git a/next-gen-signatures/Cargo.toml b/next-gen-signatures/Cargo.toml index d3a52a0..80df602 100644 --- a/next-gen-signatures/Cargo.toml +++ b/next-gen-signatures/Cargo.toml @@ -1,14 +1,35 @@ [package] name = "next-gen-signatures" -version = "0.0.1" +version = "0.0.3" edition = "2021" [features] -all-algos = ["fips204"] +all-algos = ["fips204", "bbs"] fips204 = ["dep:fips204"] +bbs = [ + "dep:ark-bls12-381", + "dep:ark-ec", + "dep:ark-serialize", + "dep:bbs_plus", + "dep:blake2", + "dep:num-bigint", + "dep:rand", +] [dependencies] anyhow = "1.0.89" base64 = "0.22.1" -fips204 = { version = "0.2.2", optional = true } paste = "1.0.15" +rocket = "0.5.1" + +# FIPS204 +fips204 = { version = "0.2.2", optional = true } + +# BBS+ +ark-bls12-381 = { version = "0.4.0", optional = true } +ark-ec = { version = "0.4.2", optional = true } +ark-serialize = { version = "0.4.2", optional = true } +bbs_plus = { version = "0.22.0", optional = true } +blake2 = { version = "0.10.6", optional = true } +num-bigint = { version = "0.4.6", optional = true } +rand = { version = "0.8.5", optional = true } diff --git a/next-gen-signatures/src/common.rs b/next-gen-signatures/src/common.rs index c72d0c7..4a8e1c1 100644 --- a/next-gen-signatures/src/common.rs +++ b/next-gen-signatures/src/common.rs @@ -1,12 +1,19 @@ +use std::fmt::Display; + use anyhow::Result; +use rocket::form::{self, DataField, FromForm, ValueField}; pub type ByteArray = Vec; pub trait CryptoProvider { + type GenParams: for<'a> FromForm<'a> + TestDefault; + type SignParams: for<'a> FromForm<'a> + TestDefault; + type VerifyParams: for<'a> FromForm<'a> + TestDefault; + type PublicKey; type SecretKey; - fn gen_keypair() -> Result<(Self::PublicKey, Self::SecretKey)>; + fn gen_keypair(params: Self::GenParams) -> Result<(Self::PublicKey, Self::SecretKey)>; fn pk_into_bytes(pk: Self::PublicKey) -> Result; fn pk_from_bytes(bytes: ByteArray) -> Result; @@ -14,6 +21,45 @@ pub trait CryptoProvider { fn sk_into_bytes(sk: Self::SecretKey) -> Result; fn sk_from_bytes(bytes: ByteArray) -> Result; - fn sign(sk: &Self::SecretKey, msg: ByteArray) -> Result; - fn verify(pk: &Self::PublicKey, msg: ByteArray, sig: ByteArray) -> Result; + fn sign(sk: &Self::SecretKey, msg: ByteArray, params: Self::SignParams) -> Result; + fn verify( + pk: Self::PublicKey, + msg: ByteArray, + sig: ByteArray, + params: Self::VerifyParams, + ) -> Result; +} + +#[derive(Debug)] +pub struct NoArguments; + +impl Display for NoArguments { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{:#?}", self)) + } +} + +#[rocket::async_trait] +impl<'r> FromForm<'r> for NoArguments { + type Context = (); + + fn init(_: form::Options) -> Self::Context {} + + fn push_value(_: &mut Self::Context, _: ValueField<'r>) {} + + async fn push_data(_: &mut Self::Context, _: DataField<'r, '_>) {} + + fn finalize(_: Self::Context) -> form::Result<'r, Self> { + Ok(NoArguments) + } +} + +pub trait TestDefault { + fn default_for_test() -> Self; +} + +impl TestDefault for NoArguments { + fn default_for_test() -> Self { + NoArguments + } } diff --git a/next-gen-signatures/src/crypto.rs b/next-gen-signatures/src/crypto.rs index 511504e..9668fbb 100644 --- a/next-gen-signatures/src/crypto.rs +++ b/next-gen-signatures/src/crypto.rs @@ -5,17 +5,23 @@ pub use fips204::*; pub mod fips204 { use fips204::traits::{SerDes, Signer, Verifier}; - use crate::common::CryptoProvider; + use crate::common::{CryptoProvider, NoArguments}; macro_rules! fips_provider_impl { ($provider_name:ident, $pkg_name:ident) => { pub struct $provider_name; impl CryptoProvider for $provider_name { + type GenParams = NoArguments; + type SignParams = NoArguments; + type VerifyParams = NoArguments; + type PublicKey = fips204::$pkg_name::PublicKey; type SecretKey = fips204::$pkg_name::PrivateKey; - fn gen_keypair() -> anyhow::Result<(Self::PublicKey, Self::SecretKey)> { + fn gen_keypair( + _: Self::GenParams, + ) -> anyhow::Result<(Self::PublicKey, Self::SecretKey)> { fips204::$pkg_name::try_keygen().map_err(|err| anyhow::anyhow!(err)) } @@ -58,6 +64,7 @@ pub mod fips204 { fn sign( sk: &Self::SecretKey, msg: crate::common::ByteArray, + _: Self::SignParams, ) -> anyhow::Result { sk.try_sign(&msg) .map(|res| res.to_vec()) @@ -65,9 +72,10 @@ pub mod fips204 { } fn verify( - pk: &Self::PublicKey, + pk: Self::PublicKey, msg: crate::common::ByteArray, sig: crate::common::ByteArray, + _: Self::VerifyParams, ) -> anyhow::Result { let sig_len = sig.len(); let sig = sig.try_into().map_err(|_| { @@ -97,3 +105,172 @@ pub mod fips204 { test_provider!(Fips204MlDsa87Provider); } } + +#[cfg(feature = "bbs")] +pub use bbs::*; + +#[cfg(feature = "bbs")] +pub mod bbs { + use std::io::Cursor; + + use crate::common::{ByteArray, CryptoProvider, TestDefault}; + use ark_bls12_381::Bls12_381; + use ark_ec::pairing::Pairing; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use base64::Engine; + use bbs_plus::prelude::{ + PublicKeyG1, PublicKeyG2, SecretKey, SignatureG1, SignatureG2, SignatureParamsG1, + SignatureParamsG2, + }; + use blake2::Blake2b512; + use num_bigint::BigUint; + use rand::RngCore; + use rocket::FromForm; + + pub type Digest = Blake2b512; + + fn get_rng() -> impl RngCore { + rand::rngs::OsRng + } + + #[derive(FromForm)] + pub struct GenParams { + nonce: String, + message_count: u32, + } + + pub type SignParams = GenParams; + + pub type VerifyParams = SignParams; + + impl TestDefault for GenParams { + fn default_for_test() -> Self { + GenParams { + nonce: crate::BASE64_URL_SAFE_NO_PAD.encode("nonce"), + message_count: 1, + } + } + } + + macro_rules! fix_arguments { + (G1, $pk:expr, $params:expr) => { + ($pk, $params) + }; + (G2, $pk:expr, $params:expr) => { + (&$pk, &$params) + }; + } + + macro_rules! bbs_plus_provider_impl { + ($g1:ident, $g2:ident, $pairing:ident) => { + paste::item! { + pub struct []; + + impl CryptoProvider for [] { + type GenParams = GenParams; + type SignParams = SignParams; + type VerifyParams = VerifyParams; + + type PublicKey = []<$pairing>; + + type SecretKey = SecretKey<<$pairing as Pairing>::ScalarField>; + + fn gen_keypair( + params: Self::GenParams, + ) -> anyhow::Result<(Self::PublicKey, Self::SecretKey)> { + let nonce = $crate::BASE64_URL_SAFE_NO_PAD.decode(¶ms.nonce)?; + let seed = { + let mut rng = get_rng(); + let mut seed = ByteArray::new(); + rng.fill_bytes(&mut seed); + seed + }; + + let params = []::<$pairing>::new::(&nonce, params.message_count); + + let sk = Self::SecretKey::generate_using_seed::(&seed); + let pk = Self::PublicKey::generate_using_secret_key(&sk, ¶ms); + + Ok((pk, sk)) + } + + fn pk_into_bytes(pk: Self::PublicKey) -> anyhow::Result { + let mut bytes = ByteArray::new(); + pk.serialize_compressed(&mut bytes)?; + Ok(bytes) + } + + fn pk_from_bytes(bytes: crate::common::ByteArray) -> anyhow::Result { + Self::PublicKey::deserialize_compressed(Cursor::new(bytes)).map_err(|err| err.into()) + } + + fn sk_into_bytes(sk: Self::SecretKey) -> anyhow::Result { + let mut bytes = ByteArray::new(); + sk.serialize_compressed(&mut bytes)?; + Ok(bytes) + } + + fn sk_from_bytes(bytes: crate::common::ByteArray) -> anyhow::Result { + Self::SecretKey::deserialize_compressed(Cursor::new(bytes)).map_err(|err| err.into()) + } + + fn sign( + sk: &Self::SecretKey, + msg: crate::common::ByteArray, + params: Self::SignParams, + ) -> anyhow::Result { + let nonce = $crate::BASE64_URL_SAFE_NO_PAD.decode(¶ms.nonce)?; + let params = []::<$pairing>::new::(&nonce, params.message_count); + let sig = []::new( + &mut get_rng(), + &[BigUint::from_bytes_le(&msg).into()], + sk, + ¶ms, + ) + .map_err(|err| anyhow::anyhow!("Signature error: {:?}", err))?; + let mut bytes = ByteArray::new(); + sig.serialize_compressed(&mut bytes)?; + Ok(bytes) + } + + fn verify( + pk: Self::PublicKey, + msg: crate::common::ByteArray, + sig: crate::common::ByteArray, + params: Self::VerifyParams, + ) -> anyhow::Result { + let nonce = $crate::BASE64_URL_SAFE_NO_PAD.decode(¶ms.nonce)?; + let params = []::<$pairing>::new::(&nonce, params.message_count); + let sig = []::<$pairing>::deserialize_compressed(Cursor::new(sig))?; + + let (pk, params) = fix_arguments!($g1, pk, params); + + if !pk.is_valid() || !params.is_valid() { + anyhow::bail!("Invalid params!"); + } + + Ok(sig + .verify( + &[BigUint::from_bytes_le(&msg).into()], + pk, + params + ) + .is_ok()) + } + } + } + }; + } + + bbs_plus_provider_impl!(G1, G2, Bls12_381); + bbs_plus_provider_impl!(G2, G1, Bls12_381); + + #[cfg(test)] + pub mod tests { + use super::*; + use crate::test_provider; + + test_provider!(BbsPlusG1Provider); + test_provider!(BbsPlusG2Provider); + } +} diff --git a/next-gen-signatures/src/macros.rs b/next-gen-signatures/src/macros.rs index cb39714..1cb8ca8 100644 --- a/next-gen-signatures/src/macros.rs +++ b/next-gen-signatures/src/macros.rs @@ -8,11 +8,11 @@ macro_rules! test_provider { #[allow(non_snake_case)] mod [<$provider _tests>] { use super::*; - use $crate::common::CryptoProvider; + use $crate::common::{CryptoProvider, TestDefault}; #[test] fn test_pk_roundtrip() -> anyhow::Result<()> { - let (pk, _) = $provider::gen_keypair()?; + let (pk, _) = $provider::gen_keypair(<$provider as CryptoProvider>::GenParams::default_for_test())?; let bytes = $provider::pk_into_bytes(pk)?; let pk2 = $provider::pk_from_bytes(bytes.clone())?; let bytes2 = $provider::pk_into_bytes(pk2)?; @@ -24,7 +24,7 @@ macro_rules! test_provider { #[test] fn test_sk_roundtrip() -> anyhow::Result<()> { - let (_, sk) = $provider::gen_keypair()?; + let (_, sk) = $provider::gen_keypair(<$provider as CryptoProvider>::GenParams::default_for_test())?; let bytes = $provider::sk_into_bytes(sk)?; let sk2 = $provider::sk_from_bytes(bytes.clone())?; let bytes2 = $provider::sk_into_bytes(sk2)?; @@ -36,11 +36,20 @@ macro_rules! test_provider { #[test] fn test_sign_verify_roundtrip() -> anyhow::Result<()> { - let (pk, sk) = $provider::gen_keypair()?; + let (pk, sk) = $provider::gen_keypair(<$provider as CryptoProvider>::GenParams::default_for_test())?; let msg = b"Hello, World".to_vec(); - let sig = $provider::sign(&sk, msg.clone())?; - let valid = $provider::verify(&pk, msg, sig)?; + let sig = $provider::sign( + &sk, + msg.clone(), + <$provider as CryptoProvider>::SignParams::default_for_test() + )?; + let valid = $provider::verify( + pk, + msg, + sig, + <$provider as CryptoProvider>::VerifyParams::default_for_test() + )?; assert!(valid); @@ -56,37 +65,54 @@ macro_rules! test_provider { macro_rules! generate_crypto_routes { ($provider:ident) => { $crate::macros::paste::item! { - #[get("/keypair")] + #[get("/keypair?")] #[allow(non_snake_case)] - pub(crate) fn [<$provider _gen_keypair>]() -> Json { - let (pk, sk) = $crate::crypto::$provider::gen_keypair().unwrap(); - let pk = $crate::crypto::$provider::pk_into_bytes(pk).unwrap(); - let sk = $crate::crypto::$provider::sk_into_bytes(sk).unwrap(); + pub(crate) fn [<$provider _gen_keypair>]( + params: <$crate::crypto::$provider as $crate::common::CryptoProvider>::GenParams + ) -> anyhow::Result> { + use next_gen_signatures::common::CryptoProvider; + + let (pk, sk) = $crate::crypto::$provider::gen_keypair(params)?; + let pk = $crate::crypto::$provider::pk_into_bytes(pk)?; + let sk = $crate::crypto::$provider::sk_into_bytes(sk)?; let pk = BASE64_URL_SAFE_NO_PAD.encode(pk); let sk = BASE64_URL_SAFE_NO_PAD.encode(sk); - Json(KeyPair { public_key: pk, secret_key: sk }) + Ok(Json(KeyPair { public_key: pk, secret_key: sk })) } - #[get("/sign?&")] + #[get("/sign?&&")] #[allow(non_snake_case)] - pub(crate) fn [<$provider _sign>](secret_key: &str, message: &str) -> Json { + pub(crate) fn [<$provider _sign>]( + secret_key: &str, + message: &str, + params: <$crate::crypto::$provider as $crate::common::CryptoProvider>::GenParams + ) -> anyhow::Result> { + use next_gen_signatures::common::CryptoProvider; + let sk = BASE64_URL_SAFE_NO_PAD.decode(secret_key).unwrap(); let sk = $crate::crypto::$provider::sk_from_bytes(sk).unwrap(); let msg = BASE64_URL_SAFE_NO_PAD.decode(message).unwrap(); - let sig = $crate::crypto::$provider::sign(&sk, msg).unwrap(); - Json(BASE64_URL_SAFE_NO_PAD.encode(sig)) + let sig = $crate::crypto::$provider::sign(&sk, msg, params).unwrap(); + Ok(Json(BASE64_URL_SAFE_NO_PAD.encode(sig))) } - #[get("/verify?&&")] + #[get("/verify?&&&")] #[allow(non_snake_case)] - pub(crate) fn [<$provider _verify>](public_key: &str, message: &str, signature: &str) -> Json { + pub(crate) fn [<$provider _verify>]( + public_key: &str, + message: &str, + signature: &str, + params: <$crate::crypto::$provider as $crate::common::CryptoProvider>::GenParams + ) -> Json { + use next_gen_signatures::common::CryptoProvider; + let pk = BASE64_URL_SAFE_NO_PAD.decode(public_key).unwrap(); let pk = $crate::crypto::$provider::pk_from_bytes(pk).unwrap(); let msg = BASE64_URL_SAFE_NO_PAD.decode(message).unwrap(); let sig = BASE64_URL_SAFE_NO_PAD.decode(signature).unwrap(); - let valid = $crate::crypto::$provider::verify(&pk, msg, sig).unwrap(); + let valid = $crate::crypto::$provider::verify(pk, msg, sig, params).unwrap(); Json(valid) } diff --git a/next-gen-signing-service/Cargo.toml b/next-gen-signing-service/Cargo.toml index 31643a5..46d5706 100644 --- a/next-gen-signing-service/Cargo.toml +++ b/next-gen-signing-service/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "next-gen-signing-service" -version = "0.0.2" +version = "0.0.3" edition = "2021" [dependencies] next-gen-signatures = { path = "../next-gen-signatures", features = [ "all-algos", ] } +paste = "1.0.15" rocket = { version = "0.5.1", features = ["json"] } -serde = "1.0.210" +rocket-errors = "0.1.0" diff --git a/next-gen-signing-service/src/main.rs b/next-gen-signing-service/src/main.rs index c6dc435..0bc8d7f 100644 --- a/next-gen-signing-service/src/main.rs +++ b/next-gen-signing-service/src/main.rs @@ -1,7 +1,10 @@ use rocket::{get, launch, routes, serde::json::Json}; -#[derive(Debug, serde::Serialize)] -#[serde(rename_all = "camelCase")] +use next_gen_signatures::{Engine, BASE64_URL_SAFE_NO_PAD}; +use rocket_errors::anyhow; + +#[derive(Debug, rocket::serde::Serialize, rocket::serde::Deserialize)] +#[serde(crate = "rocket::serde")] pub struct KeyPair { pub public_key: String, pub secret_key: String, @@ -9,15 +12,21 @@ pub struct KeyPair { mod fips204_routes { use super::*; - use next_gen_signatures::common::CryptoProvider; use next_gen_signatures::generate_crypto_routes; - use next_gen_signatures::{Engine, BASE64_URL_SAFE_NO_PAD}; generate_crypto_routes!(Fips204MlDsa44Provider); generate_crypto_routes!(Fips204MlDsa65Provider); generate_crypto_routes!(Fips204MlDsa87Provider); } +mod bbs_plus_routes { + use super::*; + use next_gen_signatures::generate_crypto_routes; + + generate_crypto_routes!(BbsPlusG1Provider); + generate_crypto_routes!(BbsPlusG2Provider); +} + #[get("/")] fn index() -> &'static str { "Hello, World!" @@ -51,6 +60,22 @@ fn rocket() -> _ { fips204_routes::Fips204MlDsa87Provider_verify ], ) + .mount( + "/bbs+/g1/", + routes![ + bbs_plus_routes::BbsPlusG1Provider_gen_keypair, + bbs_plus_routes::BbsPlusG1Provider_sign, + bbs_plus_routes::BbsPlusG1Provider_verify, + ], + ) + .mount( + "/bbs+/g2/", + routes![ + bbs_plus_routes::BbsPlusG2Provider_gen_keypair, + bbs_plus_routes::BbsPlusG2Provider_sign, + bbs_plus_routes::BbsPlusG2Provider_verify, + ], + ) } #[cfg(test)] @@ -59,6 +84,117 @@ mod test { use rocket::local::blocking::Client; use rocket::{http::Status, uri}; + macro_rules! test_roundtrip_fips204 { + ($v:expr) => { + paste::item! { + #[test] + fn []() { + use crate::KeyPair; + use next_gen_signatures::Engine; + + let message = crate::BASE64_URL_SAFE_NO_PAD.encode("Hello, World!"); + + let client = Client::tracked(rocket()).expect("valid rocket instance"); + let response = client + .get(format!( + "/fips204/{v}/keypair", + v = stringify!($v) + )) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let keypair = response.into_json::().unwrap(); + + let response = client + .get(format!( + "/fips204/{v}/sign?secret_key={sk}&message={msg}", + v = stringify!($v), + sk = keypair.secret_key, + msg = message, + )) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let signature = response.into_json::().unwrap(); + let response = client + .get(format!( + "/fips204/{v}/verify?public_key={pk}&signature={sig}&message={msg}", + v = stringify!($v), + pk = keypair.public_key, + sig = signature, + msg = message, + )) + .dispatch(); + + assert_eq!(response.status(), Status::Ok); + + let success = response.into_json::().unwrap(); + + assert!(success); + } + } + }; + } + + macro_rules! test_roundtrip_bbs_plus { + ($g:ident) => { + paste::item! { + #[test] + fn []() { + use next_gen_signatures::Engine; + use crate::KeyPair; + + let nonce = next_gen_signatures::BASE64_URL_SAFE_NO_PAD.encode("nonce"); + let message = next_gen_signatures::BASE64_URL_SAFE_NO_PAD.encode("Hello, World!"); + + let client = Client::tracked(rocket()).expect("valid rocket instance"); + let response = client + .get(format!( + "/bbs+/{g}/keypair?nonce={n}&message_count={c}", + g = stringify!($g), + n = nonce, + c = 1 + )) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let keypair = response.into_json::().unwrap(); + + let response = client + .get(format!( + "/bbs+/{g}/sign?secret_key={sk}&message={msg}&nonce={n}&message_count={c}", + g = stringify!($g), + sk = keypair.secret_key, + msg = message, + n = nonce, + c = 1 + )) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let signature = response.into_json::().unwrap(); + let response = client + .get(format!( + "/bbs+/{g}/verify?public_key={pk}&signature={sig}&message={msg}&nonce={n}&message_count={c}", + g = stringify!($g), + pk = keypair.public_key, + sig = signature, + msg = message, + n = nonce, + c = 1 + )) + .dispatch(); + + assert_eq!(response.status(), Status::Ok); + + let success = response.into_json::().unwrap(); + + assert!(success); + } + } + }; + } + #[test] fn hello_world() { let client = Client::tracked(rocket()).expect("valid rocket instance"); @@ -66,4 +202,11 @@ mod test { assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string().unwrap(), "Hello, World!"); } + + test_roundtrip_fips204!(44); + test_roundtrip_fips204!(65); + test_roundtrip_fips204!(87); + + test_roundtrip_bbs_plus!(g1); + test_roundtrip_bbs_plus!(g2); }