mirror of
https://github.com/saymrwulf/anza-cryptography-source.git
synced 2026-09-04 20:24:04 +00:00
integrate heea (#4)
* Initial commit * skeleton * integrate heea * ci * fix nits * Update signing_key.rs
This commit is contained in:
parent
5e70ebe267
commit
e56e48a473
33 changed files with 4345 additions and 63 deletions
550
Cargo.lock
generated
550
Cargo.lock
generated
|
|
@ -13,6 +13,21 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
|
|
@ -22,6 +37,12 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
|
|
@ -30,9 +51,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
|||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
|
|
@ -46,6 +67,27 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
|
|
@ -88,6 +130,15 @@ dependencies = [
|
|||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.0"
|
||||
|
|
@ -103,6 +154,12 @@ version = "3.20.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
|
|
@ -165,18 +222,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.60"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.60"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
|
|
@ -184,9 +241,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-spantrace"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
|
|
@ -216,9 +306,9 @@ dependencies = [
|
|||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"criterion-plot 0.5.0",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
|
|
@ -232,6 +322,29 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot 0.6.0",
|
||||
"itertools 0.13.0",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.5.0"
|
||||
|
|
@ -239,7 +352,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -283,18 +406,95 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.1"
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "285743a676ccb6b3e116bc14cc69319b957867930ae9c4822f8e0f54509d7243"
|
||||
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"byteorder",
|
||||
"digest 0.9.0",
|
||||
"rand_core 0.5.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"pkcs8",
|
||||
"serde",
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-heea"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"color-eyre",
|
||||
"criterion 0.7.0",
|
||||
"der",
|
||||
"ed25519",
|
||||
"ed25519-zebra",
|
||||
"hashbrown 0.16.1",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"pkcs8",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"solana-curve25519",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-zebra"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85a1fff1b329c96789427b09d4d5949b2d2f717851fd1e65a18878bee19d1ff"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"hex",
|
||||
"rand_core 0.5.1",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
|
|
@ -318,6 +518,16 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
|
|
@ -342,12 +552,39 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
|
|
@ -356,7 +593,7 @@ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -385,6 +622,12 @@ dependencies = [
|
|||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.7.1"
|
||||
|
|
@ -402,7 +645,7 @@ version = "0.15.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -410,6 +653,11 @@ name = "hashbrown"
|
|||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
|
@ -453,6 +701,12 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
|
|
@ -486,10 +740,19 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
|
|
@ -501,6 +764,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
|
|
@ -537,6 +806,15 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.3.3"
|
||||
|
|
@ -567,10 +845,19 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
|
|
@ -578,6 +865,43 @@ version = "11.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.7"
|
||||
|
|
@ -738,6 +1062,15 @@ dependencies = [
|
|||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
|
|
@ -820,6 +1153,12 @@ version = "0.8.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
|
@ -969,6 +1308,19 @@ dependencies = [
|
|||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0-rc.5"
|
||||
|
|
@ -977,7 +1329,16 @@ checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -986,6 +1347,15 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-curve25519"
|
||||
version = "0.1.0"
|
||||
|
|
@ -993,8 +1363,8 @@ dependencies = [
|
|||
"bincode",
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"criterion",
|
||||
"digest",
|
||||
"criterion 0.5.1",
|
||||
"digest 0.11.2",
|
||||
"hex",
|
||||
"proptest",
|
||||
"rand 0.10.0",
|
||||
|
|
@ -1003,7 +1373,7 @@ dependencies = [
|
|||
"rustcrypto-ff",
|
||||
"rustcrypto-group",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"solana-curve25519-derive",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
|
|
@ -1014,7 +1384,7 @@ name = "solana-curve25519-cuda"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"criterion",
|
||||
"criterion 0.5.1",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
|
|
@ -1032,6 +1402,16 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
|
@ -1068,9 +1448,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.26.0"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.2",
|
||||
|
|
@ -1079,6 +1459,35 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
|
|
@ -1089,6 +1498,47 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||
dependencies = [
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
|
|
@ -1113,6 +1563,18 @@ version = "0.2.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
|
|
@ -1132,6 +1594,12 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
|
|
@ -1453,18 +1921,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.42"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.42"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -1476,6 +1944,20 @@ 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 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ use core::ops::{Mul, MulAssign};
|
|||
|
||||
#[cfg(feature = "digest")]
|
||||
use digest::{
|
||||
FixedOutput, HashMarker, array::typenum::U64, consts::True, crypto_common::BlockSizeUser,
|
||||
FixedOutput, HashMarker, array::typenum::U64, block_api::BlockSizeUser, consts::True,
|
||||
typenum::IsGreater,
|
||||
};
|
||||
|
||||
|
|
@ -2517,9 +2517,12 @@ mod test {
|
|||
|
||||
#[cfg(all(feature = "alloc", feature = "digest"))]
|
||||
fn hex_str_to_fe(hex_str: &str) -> FieldElement {
|
||||
let mut bytes = hex::decode(hex_str).unwrap().to_vec();
|
||||
let mut bytes = hex::decode(hex_str).expect("RFC test vector hex must decode");
|
||||
bytes.reverse();
|
||||
FieldElement::from_bytes(&bytes.try_into().unwrap())
|
||||
let bytes: [u8; 32] = bytes
|
||||
.try_into()
|
||||
.expect("RFC test vector field element must be 32 bytes");
|
||||
FieldElement::from_bytes(&bytes)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2535,7 +2538,7 @@ mod test {
|
|||
AffinePoint { x, y }.to_edwards()
|
||||
};
|
||||
|
||||
let computed = EdwardsPoint::encode_to_curve::<sha2::Sha512>(&[&input], &[dst]);
|
||||
let computed = EdwardsPoint::encode_to_curve::<sha2::Sha512>(&[input], &[dst]);
|
||||
assert_eq!(computed, expected_output, "Failed in test {}", index);
|
||||
}
|
||||
}
|
||||
|
|
@ -2594,7 +2597,7 @@ mod test {
|
|||
AffinePoint { x, y }.to_edwards()
|
||||
};
|
||||
|
||||
let computed = EdwardsPoint::hash_to_curve::<sha2::Sha512>(&[&input], &[dst]);
|
||||
let computed = EdwardsPoint::hash_to_curve::<sha2::Sha512>(&[input], &[dst]);
|
||||
|
||||
assert_eq!(expected_output, computed, "Failed in test {}", index);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -758,36 +758,46 @@ mod test {
|
|||
fn from_bytes_wide() {
|
||||
// Do the 64-byte input ones first
|
||||
for (input_bytes, expected_reduced) in FROM_BYTES_WIDE_KAT_BIG {
|
||||
let reduce_fe = FieldElement::from_bytes_wide(
|
||||
&hex::decode(input_bytes)
|
||||
.unwrap()
|
||||
let decoded_input =
|
||||
hex::decode(input_bytes).expect("64-byte reduction test vector hex must decode");
|
||||
let decoded_input: [u8; 64] = decoded_input
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
.expect("64-byte reduction test vector must decode to 64 bytes");
|
||||
let reduce_fe = FieldElement::from_bytes_wide(&decoded_input);
|
||||
assert_eq!(
|
||||
&reduce_fe.to_bytes(),
|
||||
hex::decode(expected_reduced).unwrap().as_slice()
|
||||
hex::decode(expected_reduced)
|
||||
.expect("reduction result test vector hex must decode")
|
||||
.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
// Now do the 48-byte inputs
|
||||
for (input_bytes, expected_reduced) in FROM_BYTES_WIDE_KAT_MEDIUM {
|
||||
let mut padded_input_bytes = [0u8; 64];
|
||||
padded_input_bytes[..48].copy_from_slice(&hex::decode(input_bytes).unwrap());
|
||||
padded_input_bytes[..48].copy_from_slice(
|
||||
&hex::decode(input_bytes).expect("48-byte reduction test vector hex must decode"),
|
||||
);
|
||||
let reduce_fe = FieldElement::from_bytes_wide(&padded_input_bytes);
|
||||
assert_eq!(
|
||||
&reduce_fe.to_bytes(),
|
||||
hex::decode(expected_reduced).unwrap().as_slice()
|
||||
hex::decode(expected_reduced)
|
||||
.expect("reduction result test vector hex must decode")
|
||||
.as_slice()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "digest")]
|
||||
fn fe_from_test_vector(expected_hex: &str) -> FieldElement {
|
||||
let mut expected_hash = hex::decode(expected_hex).unwrap();
|
||||
let mut expected_hash =
|
||||
hex::decode(expected_hex).expect("hash-to-field test vector hex must decode");
|
||||
expected_hash.reverse();
|
||||
FieldElement::from_bytes(&expected_hash.try_into().unwrap())
|
||||
let expected_hash: [u8; 32] = expected_hash
|
||||
.try_into()
|
||||
.expect("hash-to-field test vector must decode to 32 bytes");
|
||||
FieldElement::from_bytes(&expected_hash)
|
||||
}
|
||||
|
||||
/// Hash to field test vectors from
|
||||
|
|
|
|||
136
ed25519-heea/CHANGELOG.md
Normal file
136
ed25519-heea/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# CHANGELOG
|
||||
|
||||
Entries are listed in reverse chronological order.
|
||||
|
||||
# 4.2.0
|
||||
|
||||
* Bump MSRV from 1.65 to 1.85.
|
||||
* Bump `hashbrown` from 0.15 to 0.16.
|
||||
|
||||
# 4.1.0
|
||||
|
||||
* Implement `PartialEq` and `Eq` in `SigningKey` and `VerificationKey` (#102)
|
||||
* Add `alloc` feature by @nazar-pc in (#161, #174)
|
||||
* Add helper methods by @conradoplg in (#179)
|
||||
|
||||
Note: to use Rust older than `1.85`, you will need to downgrade the `base64ct`
|
||||
crate:
|
||||
|
||||
```
|
||||
cargo update base64ct --precise 1.6.0
|
||||
```
|
||||
|
||||
# 4.0.3
|
||||
|
||||
* Update `curve25519` to `4.1.0`
|
||||
|
||||
# 4.0.2
|
||||
|
||||
* Update `curve25519` to `4.0.0`
|
||||
|
||||
# 4.0.1
|
||||
|
||||
* Fix no-std build with serde activated (#87)
|
||||
* Update `curve25519` to `4.0.0-rc.3`
|
||||
|
||||
# 4.0.0
|
||||
|
||||
* `Signature` is now an alias for `ed25519::Signature`
|
||||
* `impl From<Signature> for [u8; 64]` no longer exists; use `to_bytes()` instead.
|
||||
* `signature::{Signer, Verifier} is now implemented for `SigningKey` and `VerificationKey`.
|
||||
* Updates `sha2` version to `0.10` and `curve25519` version to `4.0.0-rc.2`.
|
||||
* Add DER & PEM support for SigningKeySeed and VerificationKeyBytes (RFC 8410) #46 https://github.com/ZcashFoundation/ed25519-zebra/pull/46
|
||||
* This is under the non-default `pem` and `pkcs8` features
|
||||
|
||||
MSRV increased to `1.65.0`.
|
||||
|
||||
# 3.1.0
|
||||
|
||||
* Add no_std support by @pvdrz in https://github.com/ZcashFoundation/ed25519-zebra/pull/57
|
||||
|
||||
# 3.0.0
|
||||
|
||||
* Fix typo by @rex4539 in https://github.com/ZcashFoundation/ed25519-zebra/pull/32
|
||||
* Add Zeroize impl for SigningKey by @kim in https://github.com/ZcashFoundation/ed25519-zebra/pull/34
|
||||
* Add JNI code for ed25519-zebra by @droark in https://github.com/ZcashFoundation/ed25519-zebra/pull/37
|
||||
* Update rand_core to 0.6 and rand to 0.8 by @dconnolly in https://github.com/ZcashFoundation/ed25519-zebra/pull/44
|
||||
* dependencies: update zeroize to 1.2 by @FintanH in https://github.com/ZcashFoundation/ed25519-zebra/pull/52
|
||||
|
||||
# 2.2.0
|
||||
|
||||
* Add `PartialOrd`, `Ord` implementations for `VerificationKeyBytes`. While
|
||||
the derived ordering is not cryptographically meaningful, deriving these
|
||||
traits is useful because it allows, e.g., using `VerificationKeyBytes` as the
|
||||
key to a `BTreeMap` (contributed by @cloudhead).
|
||||
|
||||
# 2.1.2
|
||||
|
||||
* Updates `sha2` version to `0.9` and `curve25519` version to `3`.
|
||||
|
||||
# 2.1.1
|
||||
|
||||
* Add a missing multiplication by the cofactor in batch verification and test
|
||||
that individual and batch verification agree. This corrects an omission that
|
||||
should have been included in `2.0.0`.
|
||||
|
||||
# 2.1.0
|
||||
|
||||
* Implements `Clone + Debug` for `batch::Item` and provides
|
||||
`batch::Item::verify_single` to perform fallback verification in case
|
||||
of batch failure.
|
||||
|
||||
# 2.0.0
|
||||
|
||||
* Implements ZIP 215, so that batched and individual verification
|
||||
agree on whether signatures are valid.
|
||||
|
||||
# 1.0.0
|
||||
|
||||
* Adds `impl TryFrom<&[u8]>` for all types.
|
||||
|
||||
# 1.0.0-pre.0
|
||||
|
||||
* Add a note about versioning to handle ZIP 215.
|
||||
|
||||
# 0.4.1
|
||||
|
||||
* Change `docs.rs` configuration in `Cargo.toml` to not refer to the removed
|
||||
`batch` feature so that the docs render correctly on `docs.rs`.
|
||||
|
||||
# 0.4.0
|
||||
|
||||
* The sync batch verification api is changed to remove a dependence on the
|
||||
message lifetime that made it difficult to use in async contexts.
|
||||
|
||||
# 0.3.0
|
||||
|
||||
* Change terminology from secret and public keys to signing and verification
|
||||
keys.
|
||||
* Remove async batch verification in favor of a sync api; the async approach is
|
||||
to be developed in another crate.
|
||||
|
||||
# 0.2.3
|
||||
|
||||
* The previous implementation exactly matched the behavior of `libsodium`
|
||||
`1.0.15` with the `ED25519_COMPAT` configuration, but this configuration
|
||||
wasn't used by `zcashd`. This commit changes the validation rules to exactly
|
||||
match without `ED25519_COMPAT`, and highlights the remaining inconsistencies
|
||||
with the Zcash specification that were not addressed in the previous spec
|
||||
fix.
|
||||
|
||||
# 0.2.2
|
||||
|
||||
* Adds `impl AsRef<[u8]> for PublicKey`.
|
||||
* Adds `impl AsRef<[u8]> for SecretKey`.
|
||||
|
||||
# 0.2.1
|
||||
|
||||
* Adds `impl AsRef<[u8]> for PublicKeyBytes`.
|
||||
|
||||
# 0.2.0
|
||||
|
||||
* Adds experimental futures-based batch verification API, gated by the `batch` feature.
|
||||
|
||||
# 0.1.0
|
||||
|
||||
Initial release, attempting to match the actual `zcashd` behavior.
|
||||
1190
ed25519-heea/Cargo.lock
generated
Normal file
1190
ed25519-heea/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,79 @@
|
|||
[package]
|
||||
name = "ed25519-heea"
|
||||
version = "0.1.0"
|
||||
rust-version = "1.85.0"
|
||||
authors = [
|
||||
"Henry de Valence <hdevalence@hdevalence.ca>",
|
||||
"Zcash Foundation <zebra@zfnd.org>",
|
||||
"Anza Cryptography Team",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2024"
|
||||
description = "ZIP-215-compliant Ed25519 with HEEA-accelerated verification (forked from ed25519-zebra)"
|
||||
resolver = "2"
|
||||
include = [
|
||||
"/src",
|
||||
"/README.md",
|
||||
"/CHANGELOG.md",
|
||||
"/LICENSE-APACHE",
|
||||
"/LICENSE-MIT",
|
||||
"/tests",
|
||||
"/benches",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["nightly"]
|
||||
|
||||
[dependencies]
|
||||
# Keep the dependency key as "curve25519" so that all existing `use curve25519::…`
|
||||
# references in source files remain valid; the actual package is curve25519-sol.
|
||||
curve25519 = { path = "../curve25519", package = "solana-curve25519", default-features = false, features = ["digest", "zeroize", "precomputed-tables"] }
|
||||
der = { version = "0.7.9", optional = true }
|
||||
ed25519 = { version = "2.2.3", default-features = false }
|
||||
hashbrown = { version = "0.16", optional = true }
|
||||
pkcs8 = { version = "0.10.1", optional = true, features = ["pem"] }
|
||||
rand_core = "0.6"
|
||||
serde = { version = "1", default-features = false, optional = true, features = ["derive"] }
|
||||
sha2 = { version = "0.11.0-rc.3", default-features = false }
|
||||
subtle = { version = "2.6.1", default-features = false }
|
||||
zeroize = { version = "1.8", default-features = false, features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
bincode = "1"
|
||||
criterion = "0.7"
|
||||
ed25519-zebra-legacy = { package = "ed25519-zebra", version = "1" }
|
||||
color-eyre = "0.6"
|
||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||
once_cell = "1.21"
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
default = ["serde", "std"]
|
||||
alloc = [
|
||||
"curve25519/alloc",
|
||||
"ed25519/alloc",
|
||||
"hashbrown",
|
||||
"pkcs8?/alloc",
|
||||
"zeroize/alloc",
|
||||
]
|
||||
pem = ["pkcs8", "dep:der", "ed25519/pem"]
|
||||
pkcs8 = ["dep:pkcs8", "ed25519/pkcs8", "alloc"]
|
||||
serde = ["dep:serde", "ed25519/serde"]
|
||||
std = ["alloc", "ed25519/std", "subtle/std"]
|
||||
|
||||
[[test]]
|
||||
name = "rfc8032"
|
||||
|
||||
[[test]]
|
||||
name = "unit_tests"
|
||||
|
||||
[[test]]
|
||||
name = "batch"
|
||||
|
||||
[[test]]
|
||||
name = "heea"
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
|
|
|
|||
44
ed25519-heea/DEVELOPERS.md
Normal file
44
ed25519-heea/DEVELOPERS.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
## Release Checklist
|
||||
|
||||
- Run `cargo semver-checks` to see if a major version bump is required
|
||||
- Bump version in Cargo.toml
|
||||
- If major version was bumped, update README.md
|
||||
- Update CHANGELOG.md
|
||||
- Ensure the MSRV in Cargo.toml (`rust-version` key) is equal to the MSRV being
|
||||
tested (main.yml)
|
||||
- Update locked dependencies: `cargo update`. Run `cargo test --all-features`
|
||||
to check if anything breaks. If that happens, see next section.
|
||||
- Test if it's publishable: `cargo publish --dry-run`
|
||||
- Open a PR with the version bump and changelog update, wait for review and merge
|
||||
- Tag a new release in GitHub: https://github.com/ZcashFoundation/ed25519-zebra/releases/new
|
||||
- Create a tag with the version (e.g. `4.0.3`)
|
||||
- Name: e.g. `ed25519-zebra 4.0.3`
|
||||
- Paste the changelog for the version
|
||||
- Publish: `cargo publish`
|
||||
|
||||
## If something breaks
|
||||
|
||||
If testing broke after running `cargo update`, first determine if it's a
|
||||
test-only dependency or not. Run `cargo build --all-features`. If that works,
|
||||
then it's probably a test-only dependency, and you can avoid updating that
|
||||
specific dependency (leave a old version in the lockfile). Otherwise investigate
|
||||
why it caused build to fail.
|
||||
|
||||
If the "test on nightly" test failed, then either there is some bug in the code
|
||||
or some dependency update caused it to fail. Investigate and if it's the latter,
|
||||
you can either downgrade in the lockfile or try to workaround it.
|
||||
|
||||
If the "build with no_std" test failed, then some change was introduced that
|
||||
depended on the std-library. You will probably need to fix this by changing
|
||||
to some no_std dependency, or gating the code so it only compiles when
|
||||
`std` is enabled.
|
||||
|
||||
If one of the dependencies bumped its MSRV, we might require a MSRV bump too:
|
||||
|
||||
- Double check if the dependency is not a test-only dependency. (The MSRV
|
||||
test in CI only builds the library but does not test it, to prevent
|
||||
a test-only dependency MSRV bump from breaking it.)
|
||||
- If it's not a test-only dependency, check if the main consumers of the
|
||||
library are OK with a MSRV bump. I usually ask ECC devs.
|
||||
- If it's OK, bump it in Cargo.toml and main.yml.
|
||||
- If not, you will need to find some workaround.
|
||||
203
ed25519-heea/LICENSE-APACHE
Normal file
203
ed25519-heea/LICENSE-APACHE
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
Copyright (c) 2020 Zcash Foundation
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
20
ed25519-heea/LICENSE-MIT
Normal file
20
ed25519-heea/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2020 Zcash Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
139
ed25519-heea/README.md
Normal file
139
ed25519-heea/README.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# ed25519-heea
|
||||
|
||||
**ZIP-215-compliant Ed25519 signatures with HEEA-accelerated verification, forked from
|
||||
[ed25519-zebra].**
|
||||
|
||||
> For the original ed25519-zebra documentation see [README_zebra.md](README_zebra.md).
|
||||
|
||||
This crate is part of the [curve25519-sol](../README.md) workspace.
|
||||
|
||||
---
|
||||
|
||||
## Changes from ed25519-zebra
|
||||
|
||||
### `verify_heea`: fast-path signature verification
|
||||
|
||||
A new method `VerificationKey::verify_heea` has been added alongside the existing `verify`.
|
||||
Both methods accept the same arguments and produce identical results — `verify_heea` is a
|
||||
**drop-in accelerated replacement** for `verify`.
|
||||
|
||||
The HEEA method (from the TCHES 2025 paper _"Accelerating EdDSA Signature Verification with
|
||||
Faster Scalar Size Halving"_) transforms the standard 2-point MSM:
|
||||
|
||||
```text
|
||||
[8][s]B = [8]R + [8][h]A (standard)
|
||||
```
|
||||
|
||||
into a 4-point MSM over half-size (~128-bit) scalars:
|
||||
|
||||
```text
|
||||
τs_lo·B + τs_hi·(2¹²⁸·B) = τ·R + ρ·A (HEEA)
|
||||
```
|
||||
|
||||
where `ρ ≡ ±τ·h (mod ℓ)` and `τs = τs_hi·2¹²⁸ + τs_lo`. All four scalars are ≤128 bits,
|
||||
and the two basepoints (`B` and `2¹²⁸B`) use precomputed lookup tables, giving approximately
|
||||
**~15% faster** verification compared to the standard path.
|
||||
|
||||
### Dependencies
|
||||
|
||||
`ed25519-zebra` was updated to depend on this fork's `curve25519` crate instead of
|
||||
`curve25519-dalek`, in order to access `HEEADecomposition` and `vartime_triple_scalar_mul_basepoint`.
|
||||
|
||||
---
|
||||
|
||||
## ZIP 215
|
||||
|
||||
ZIP-215-compliant Ed25519 validation rules are fully preserved from ed25519-zebra:
|
||||
|
||||
- Non-canonical point encodings are accepted for `A` and `R`.
|
||||
- `s` must be a canonical integer less than the group order `ℓ`.
|
||||
- The cofactor-cleared equation `[8][s]B = [8]R + [8][h]A` is used (not the RFC 8032 variant).
|
||||
|
||||
See [README_zebra.md](README_zebra.md) and [ZIP 215] for full details.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ed25519-heea = { git = "https://github.com/zz-sol/ed25519-sol", package = "ed25519-heea" }
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```rust,no_run
|
||||
use core::convert::TryFrom;
|
||||
use rand::thread_rng;
|
||||
use ed25519_heea::{SigningKey, VerificationKey};
|
||||
|
||||
let msg = b"curve25519-sol";
|
||||
|
||||
// Generate key and sign
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let sig = sk.sign(msg);
|
||||
let vk = VerificationKey::from(&sk);
|
||||
|
||||
// Standard ZIP-215 verification (from ed25519-zebra)
|
||||
vk.verify(&sig, msg).expect("valid signature");
|
||||
|
||||
// HEEA-accelerated verification (same result, ~15% faster)
|
||||
vk.verify_heea(&sig, msg).expect("valid signature");
|
||||
```
|
||||
|
||||
### Batch verification
|
||||
|
||||
Batch verification is unchanged from ed25519-zebra and uses a randomised linear combination to
|
||||
check multiple signatures in one pass:
|
||||
|
||||
```rust,ignore
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
use ed25519_heea::batch;
|
||||
|
||||
let mut verifier = batch::Verifier::new();
|
||||
for (vk_bytes, sig, msg) in items {
|
||||
verifier.queue((vk_bytes, sig, msg));
|
||||
}
|
||||
verifier.verify(thread_rng()).expect("all valid");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Default? | Description |
|
||||
|---|:---:|---|
|
||||
| `std` | ✓ | Enables `std`; without it the crate is `no_std` + `alloc`. |
|
||||
| `alloc` | ✓ | Enables batch verification. |
|
||||
| `serde` | | Serialization for key and signature types. |
|
||||
| `pkcs8` | | PKCS#8 DER encoding/decoding for `VerificationKey`. |
|
||||
|
||||
---
|
||||
|
||||
## MSRV
|
||||
|
||||
Rust **1.85.0** (Edition 2024).
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [TCHES 2025 paper] – _Accelerating EdDSA Signature Verification with Faster Scalar Size Halving_
|
||||
- [ed25519-zebra] – upstream library (Zcash Foundation)
|
||||
- [ZIP 215] – Ed25519 validation rules for Zcash
|
||||
- [Original ed25519-zebra README](README_zebra.md)
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||
|
||||
at your option.
|
||||
|
||||
[TCHES 2025 paper]: https://tches.iacr.org/index.php/TCHES/article/view/11971
|
||||
[ed25519-zebra]: https://github.com/ZcashFoundation/ed25519-zebra
|
||||
[ZIP 215]: https://zips.z.cash/zip-0215
|
||||
93
ed25519-heea/README_zebra.md
Normal file
93
ed25519-heea/README_zebra.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
[](https://github.com/ZcashFoundation/ed25519-zebra/actions/workflows/main.yml?query=branch%3Amain)
|
||||
[](https://deps.rs/repo/github/ZcashFoundation/ed25519-zebra)
|
||||
|
||||
Zcash-flavored Ed25519 for use in [Zebra][zebra].
|
||||
|
||||
Zcash uses Ed25519 for [JoinSplit signatures][zcash_protocol_jssig] with
|
||||
particular validation rules around edge cases in Ed25519 signatures. Ed25519,
|
||||
as specified in [RFC8032], does not specify behaviour around these edge cases
|
||||
and so does not require conformant implementations to agree on whether a
|
||||
signature is valid. For most applications, these edge cases are irrelevant,
|
||||
but in Zcash, nodes must be able to reach consensus on which signatures would
|
||||
be valid, so these validation behaviors are *consensus-critical*.
|
||||
|
||||
Because the Ed25519 validation rules are consensus-critical for Zcash, Zebra
|
||||
requires an Ed25519 library that implements the Zcash-flavored validation rules
|
||||
specifically, and since it is unreasonable to expect an upstream dependency to
|
||||
maintain Zcash-specific behavior, this crate provides an Ed25519 implementation
|
||||
matching the Zcash consensus rules exactly.
|
||||
|
||||
However, this library may be of independent interest, as it implements
|
||||
ZIP215, a set of precisely specified validation rules for Ed25519 that make
|
||||
individual verification consistent with batch verification and are
|
||||
backwards-compatible with all existing Ed25519 signatures. Any non-Zcash users
|
||||
should use the ZIP215 rules:
|
||||
```toml
|
||||
ed25519-zebra = "4"
|
||||
```
|
||||
|
||||
## ZIP 215 and changes to Zcash-flavored Ed25519
|
||||
|
||||
[Zcash Improvement Proposal 215][ZIP215] changes validation criteria for
|
||||
Ed25519 signatures in Zcash after its activation (which occurred in the
|
||||
Canopy network upgrade at mainnet block height 1046400). These changes remove
|
||||
the dependence on validation rules inherited from a specific point release of
|
||||
`libsodium` and make individual verification consistent with batch
|
||||
verification. More details and motivation are available in the text of [ZIP215].
|
||||
|
||||
The `1.x` series of this crate implements the legacy, pre-ZIP-215 validation
|
||||
criteria; the `2.x+` series of this crate implements the post-ZIP-215
|
||||
validation criteria. Users (like Zebra or zcashd) who need to handle the
|
||||
upgrade can use both versions simultaneously using cargo renaming, e.g.,
|
||||
```toml
|
||||
ed25519-zebra-legacy = { package = "ed25519-zebra", version = "1" }
|
||||
ed25519-zebra-zip215 = { package = "ed25519-zebra", version = "4" }
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```
|
||||
use std::convert::TryFrom;
|
||||
use rand::thread_rng;
|
||||
use ed25519_zebra::*;
|
||||
|
||||
let msg = b"Zcash";
|
||||
|
||||
// Signer's context
|
||||
let (vk_bytes, sig_bytes) = {
|
||||
// Generate a signing key and sign the message
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let sig = sk.sign(msg);
|
||||
|
||||
// Types can be converted to raw byte arrays with From/Into
|
||||
let sig_bytes: [u8; 64] = sig.into();
|
||||
let vk_bytes: [u8; 32] = VerificationKey::from(&sk).into();
|
||||
|
||||
(vk_bytes, sig_bytes)
|
||||
};
|
||||
|
||||
// Verify the signature
|
||||
assert!(
|
||||
VerificationKey::try_from(vk_bytes)
|
||||
.and_then(|vk| vk.verify(&sig_bytes.into(), msg))
|
||||
.is_ok()
|
||||
);
|
||||
```
|
||||
|
||||
## Developers guide
|
||||
|
||||
|
||||
See [DEVELOPERS.md](DEVELOPERS.md).
|
||||
|
||||
## License
|
||||
|
||||
ed25519-zebra is distributed under the terms of both the MIT license
|
||||
and the Apache License (Version 2.0).
|
||||
|
||||
See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT).
|
||||
|
||||
|
||||
[zcash_protocol_jssig]: https://zips.z.cash/protocol/protocol.pdf#concretejssig
|
||||
[RFC8032]: https://tools.ietf.org/html/rfc8032
|
||||
[zebra]: https://github.com/ZcashFoundation/zebra
|
||||
[ZIP215]: https://zips.z.cash/zip-0215
|
||||
101
ed25519-heea/benches/bench.rs
Normal file
101
ed25519-heea/benches/bench.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use ed25519_heea::*;
|
||||
use rand::thread_rng;
|
||||
|
||||
fn sigs_with_distinct_pubkeys() -> impl Iterator<Item = (VerificationKeyBytes, Signature)> {
|
||||
std::iter::repeat_with(|| {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk_bytes = VerificationKeyBytes::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
(pk_bytes, sig)
|
||||
})
|
||||
}
|
||||
|
||||
fn sigs_with_same_pubkey() -> impl Iterator<Item = (VerificationKeyBytes, Signature)> {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk_bytes = VerificationKeyBytes::from(&sk);
|
||||
std::iter::repeat_with(move || {
|
||||
let sig = sk.sign(b"");
|
||||
(pk_bytes, sig)
|
||||
})
|
||||
}
|
||||
|
||||
fn bench_batch_verify(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Batch Verification");
|
||||
for n in [8usize, 16, 24, 32, 40, 48, 56, 64].iter() {
|
||||
group.throughput(Throughput::Elements(*n as u64));
|
||||
let sigs = sigs_with_distinct_pubkeys().take(*n).collect::<Vec<_>>();
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("Unbatched verification", n),
|
||||
&sigs,
|
||||
|b, sigs| {
|
||||
b.iter(|| {
|
||||
for (vk_bytes, sig) in sigs.iter() {
|
||||
let _ =
|
||||
VerificationKey::try_from(*vk_bytes).and_then(|vk| vk.verify(sig, b""));
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("Signatures with Distinct Pubkeys", n),
|
||||
&sigs,
|
||||
|b, sigs| {
|
||||
b.iter(|| {
|
||||
let mut batch = batch::Verifier::new();
|
||||
for (vk_bytes, sig) in sigs.iter().cloned() {
|
||||
batch.queue((vk_bytes, sig, b""));
|
||||
}
|
||||
batch.verify(thread_rng())
|
||||
})
|
||||
},
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
let sigs = sigs_with_same_pubkey().take(*n).collect::<Vec<_>>();
|
||||
#[cfg(feature = "alloc")]
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("Signatures with the Same Pubkey", n),
|
||||
&sigs,
|
||||
|b, sigs| {
|
||||
b.iter(|| {
|
||||
let mut batch = batch::Verifier::new();
|
||||
for (vk_bytes, sig) in sigs.iter().cloned() {
|
||||
batch.queue((vk_bytes, sig, b""));
|
||||
}
|
||||
batch.verify(thread_rng())
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_single_verify(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Single Verification");
|
||||
|
||||
group.bench_function("ed25519", |b| {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let vk = VerificationKey::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
b.iter(|| {
|
||||
let _ = vk.verify(&sig, b"");
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("ed25519_hEEA", |b| {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let vk = VerificationKey::from(&sk);
|
||||
let sig = sk.sign(b"");
|
||||
b.iter(|| {
|
||||
let _ = vk.verify_heea(&sig, b"");
|
||||
})
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_single_verify, bench_batch_verify,);
|
||||
criterion_main!(benches);
|
||||
215
ed25519-heea/src/batch.rs
Normal file
215
ed25519-heea/src/batch.rs
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
//! Performs batch Ed25519 signature verification.
|
||||
//!
|
||||
//! Batch verification asks whether *all* signatures in some set are valid,
|
||||
//! rather than asking whether *each* of them is valid. This allows sharing
|
||||
//! computations among all signature verifications, performing less work overall
|
||||
//! at the cost of higher latency (the entire batch must complete), complexity of
|
||||
//! caller code (which must assemble a batch of signatures across work-items),
|
||||
//! and loss of the ability to easily pinpoint failing signatures.
|
||||
//!
|
||||
//! In addition to these general tradeoffs, design flaws in Ed25519 specifically
|
||||
//! mean that batched verification may not agree with individual verification.
|
||||
//! Some signatures may verify as part of a batch but not on their own.
|
||||
//! This problem is fixed by [ZIP215], a precise specification for edge cases
|
||||
//! in Ed25519 signature validation that ensures that batch verification agrees
|
||||
//! with individual verification in all cases.
|
||||
//!
|
||||
//! This crate implements ZIP215, so batch verification always agrees with
|
||||
//! individual verification, but this is not guaranteed by other implementations.
|
||||
//! **Be extremely careful when using Ed25519 in a consensus-critical context
|
||||
//! like a blockchain.**
|
||||
//!
|
||||
//! This batch verification implementation is adaptive in the sense that it
|
||||
//! detects multiple signatures created with the same verification key and
|
||||
//! automatically coalesces terms in the final verification equation. In the
|
||||
//! limiting case where all signatures in the batch are made with the same
|
||||
//! verification key, coalesced batch verification runs twice as fast as ordinary
|
||||
//! batch verification.
|
||||
//!
|
||||
//! 
|
||||
//!
|
||||
//! This optimization doesn't help much with Zcash, where public keys are random,
|
||||
//! but could be useful in proof-of-stake systems where signatures come from a
|
||||
//! set of validators (provided that system uses the ZIP215 rules).
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! # use ed25519_heea::*;
|
||||
//! let mut batch = batch::Verifier::new();
|
||||
//! for _ in 0..32 {
|
||||
//! let sk = SigningKey::new(rand::thread_rng());
|
||||
//! let vk_bytes = VerificationKeyBytes::from(&sk);
|
||||
//! let msg = b"BatchVerifyTest";
|
||||
//! let sig = sk.sign(&msg[..]);
|
||||
//! batch.queue((vk_bytes, sig, &msg[..]));
|
||||
//! }
|
||||
//! assert!(batch.verify(rand::thread_rng()).is_ok());
|
||||
//! ```
|
||||
//!
|
||||
//! [ZIP215]: https://zips.z.cash/zip-0215
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use curve25519::{
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
scalar::Scalar,
|
||||
traits::{IsIdentity, VartimeMultiscalarMul},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Sha512, digest::Update};
|
||||
|
||||
use crate::{Error, VerificationKey, VerificationKeyBytes};
|
||||
use ed25519::Signature;
|
||||
|
||||
// Shim to generate a u128 without importing `rand`.
|
||||
fn gen_u128<R: RngCore + CryptoRng>(mut rng: R) -> u128 {
|
||||
let mut bytes = [0u8; 16];
|
||||
rng.fill_bytes(&mut bytes[..]);
|
||||
u128::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
/// A batch verification item.
|
||||
///
|
||||
/// This struct exists to allow batch processing to be decoupled from the
|
||||
/// lifetime of the message. This is useful when using the batch verification API
|
||||
/// in an async context.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Item {
|
||||
vk_bytes: VerificationKeyBytes,
|
||||
sig: Signature,
|
||||
k: Scalar,
|
||||
}
|
||||
|
||||
impl<'msg, M: AsRef<[u8]> + ?Sized> From<(VerificationKeyBytes, Signature, &'msg M)> for Item {
|
||||
fn from(tup: (VerificationKeyBytes, Signature, &'msg M)) -> Self {
|
||||
let (vk_bytes, sig, msg) = tup;
|
||||
// Compute k now to avoid dependency on the msg lifetime.
|
||||
let k = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&sig.r_bytes()[..])
|
||||
.chain(&vk_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
Self { vk_bytes, sig, k }
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
/// Perform non-batched verification of this `Item`.
|
||||
///
|
||||
/// This is useful (in combination with `Item::clone`) for implementing fallback
|
||||
/// logic when batch verification fails. In contrast to
|
||||
/// [`VerificationKey::verify`](crate::VerificationKey::verify), which requires
|
||||
/// borrowing the message data, the `Item` type is unlinked from the lifetime of
|
||||
/// the message.
|
||||
pub fn verify_single(self) -> Result<(), Error> {
|
||||
VerificationKey::try_from(self.vk_bytes)
|
||||
.and_then(|vk| vk.verify_prehashed(&self.sig, self.k))
|
||||
}
|
||||
}
|
||||
|
||||
/// A batch verification context.
|
||||
#[derive(Default)]
|
||||
pub struct Verifier {
|
||||
/// Signature data queued for verification.
|
||||
signatures: HashMap<VerificationKeyBytes, Vec<(Scalar, Signature)>>,
|
||||
/// Caching this count avoids a hash traversal to figure out
|
||||
/// how much to preallocate.
|
||||
batch_size: usize,
|
||||
}
|
||||
|
||||
impl Verifier {
|
||||
/// Construct a new batch verifier.
|
||||
pub fn new() -> Verifier {
|
||||
Verifier::default()
|
||||
}
|
||||
|
||||
/// Queue a (key, signature, message) tuple for verification.
|
||||
pub fn queue<I: Into<Item>>(&mut self, item: I) {
|
||||
let Item { vk_bytes, sig, k } = item.into();
|
||||
|
||||
self.signatures
|
||||
.entry(vk_bytes)
|
||||
// The common case is 1 signature per public key.
|
||||
// We could also consider using a smallvec here.
|
||||
.or_insert_with(|| Vec::with_capacity(1))
|
||||
.push((k, sig));
|
||||
self.batch_size += 1;
|
||||
}
|
||||
|
||||
/// Perform batch verification, returning `Ok(())` if all signatures were
|
||||
/// valid and `Err` otherwise.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
|
||||
// The batch verification equation is
|
||||
//
|
||||
// 8*[-sum(z_i * s_i)]B + 8*sum([z_i]R_i) + 8*sum([z_i * k_i]A_i) = 0.
|
||||
//
|
||||
// where for each signature i,
|
||||
// - A_i is the verification key;
|
||||
// - R_i is the signature's R value;
|
||||
// - s_i is the signature's s value;
|
||||
// - k_i is the hash of the message and other data;
|
||||
// - z_i is a random 128-bit Scalar.
|
||||
//
|
||||
// Normally n signatures would require a multiscalar multiplication of
|
||||
// size 2*n + 1, together with 2*n point decompressions (to obtain A_i
|
||||
// and R_i). However, because we store batch entries in a HashMap
|
||||
// indexed by the verification key, we can "coalesce" all z_i * k_i
|
||||
// terms for each distinct verification key into a single coefficient.
|
||||
//
|
||||
// For n signatures from m verification keys, this approach instead
|
||||
// requires a multiscalar multiplication of size n + m + 1 together with
|
||||
// n + m point decompressions. When m = n, so all signatures are from
|
||||
// distinct verification keys, this is as efficient as the usual method.
|
||||
// However, when m = 1 and all signatures are from a single verification
|
||||
// key, this is nearly twice as fast.
|
||||
|
||||
let m = self.signatures.keys().count();
|
||||
|
||||
let mut A_coeffs = Vec::with_capacity(m);
|
||||
let mut As = Vec::with_capacity(m);
|
||||
let mut R_coeffs = Vec::with_capacity(self.batch_size);
|
||||
let mut Rs = Vec::with_capacity(self.batch_size);
|
||||
let mut B_coeff = Scalar::ZERO;
|
||||
|
||||
for (vk_bytes, sigs) in self.signatures.iter() {
|
||||
let A = CompressedEdwardsY(vk_bytes.0)
|
||||
.decompress()
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
|
||||
let mut A_coeff = Scalar::ZERO;
|
||||
|
||||
for (k, sig) in sigs.iter() {
|
||||
let R = CompressedEdwardsY(*sig.r_bytes())
|
||||
.decompress()
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(*sig.s_bytes()))
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
let z = Scalar::from(gen_u128(&mut rng));
|
||||
B_coeff -= z * s;
|
||||
Rs.push(R);
|
||||
R_coeffs.push(z);
|
||||
A_coeff += z * k;
|
||||
}
|
||||
|
||||
As.push(A);
|
||||
A_coeffs.push(A_coeff);
|
||||
}
|
||||
|
||||
use core::iter::once;
|
||||
use curve25519::constants::ED25519_BASEPOINT_POINT as B;
|
||||
let check = EdwardsPoint::vartime_multiscalar_mul(
|
||||
once(&B_coeff).chain(A_coeffs.iter()).chain(R_coeffs.iter()),
|
||||
once(&B).chain(As.iter()).chain(Rs.iter()),
|
||||
);
|
||||
|
||||
if check.mul_by_cofactor().is_identity() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidSignature)
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ed25519-heea/src/error.rs
Normal file
30
ed25519-heea/src/error.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use core::fmt;
|
||||
|
||||
/// An error related to Ed25519 signatures.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
/// The encoding of a secret key was malformed.
|
||||
MalformedSecretKey,
|
||||
/// The encoding of a public key was malformed.
|
||||
MalformedPublicKey,
|
||||
/// Signature verification failed.
|
||||
InvalidSignature,
|
||||
/// A byte slice of the wrong length was supplied during parsing.
|
||||
InvalidSliceLength,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let msg = match self {
|
||||
Self::MalformedSecretKey => "Malformed secret key encoding.",
|
||||
Self::MalformedPublicKey => "Malformed public key encoding.",
|
||||
Self::InvalidSignature => "Invalid signature.",
|
||||
Self::InvalidSliceLength => "Invalid length when parsing byte slice.",
|
||||
};
|
||||
|
||||
msg.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
|
@ -1,14 +1,21 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
#![deny(missing_docs)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
//! Docs require the `nightly` feature until RFC 1990 lands.
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod batch;
|
||||
mod error;
|
||||
mod signing_key;
|
||||
mod verification_key;
|
||||
|
||||
// Allows importing traits used by `Signature`.
|
||||
pub use ed25519;
|
||||
pub use ed25519::Signature;
|
||||
pub use error::Error;
|
||||
pub use signing_key::SigningKey;
|
||||
pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
||||
|
|
|
|||
383
ed25519-heea/src/signing_key.rs
Normal file
383
ed25519-heea/src/signing_key.rs
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
#[cfg(feature = "pkcs8")]
|
||||
const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112"); // RFC 8410
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const ALGORITHM_ID: AlgorithmIdentifierRef = AlgorithmIdentifierRef {
|
||||
oid: OID,
|
||||
parameters: None,
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
#[cfg(all(feature = "pem", feature = "pkcs8"))]
|
||||
use alloc::string::String;
|
||||
use core::convert::TryFrom;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use core::convert::TryInto;
|
||||
use curve25519::{constants, scalar::Scalar};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Digest, Sha512, digest::Update};
|
||||
use subtle::ConstantTimeEq;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use ed25519::{Signature, signature::Signer};
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use ed25519::KeypairBytes;
|
||||
#[cfg(feature = "pem")]
|
||||
use ed25519::PublicKeyBytes;
|
||||
|
||||
#[cfg(all(feature = "pem", feature = "pkcs8"))]
|
||||
use der::pem::LineEnding;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::der::SecretDocument;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::{
|
||||
DecodePrivateKey, DecodePublicKey, Document, EncodePrivateKey, EncodePublicKey,
|
||||
ObjectIdentifier, PrivateKeyInfo, spki::AlgorithmIdentifierRef,
|
||||
};
|
||||
#[cfg(all(feature = "pem", feature = "pkcs8"))]
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
#[cfg(all(feature = "pem", feature = "pkcs8"))]
|
||||
use pkcs8::der::pem::PemLabel;
|
||||
|
||||
use crate::{VerificationKey, VerificationKeyBytes};
|
||||
|
||||
/// The length of a ed25519 `SecretKey`, in bytes.
|
||||
pub const SECRET_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// ed25519 secret key as defined in [RFC8032 § 5.1.5]:
|
||||
///
|
||||
/// > The private key is 32 octets (256 bits, corresponding to b) of
|
||||
/// > cryptographically secure random data.
|
||||
///
|
||||
/// [RFC8032 § 5.1.5]: https://www.rfc-editor.org/rfc/rfc8032#section-5.1.5
|
||||
pub type SecretKey = [u8; SECRET_KEY_LENGTH];
|
||||
|
||||
/// An Ed25519 signing key.
|
||||
///
|
||||
/// This is also called a secret key by other implementations.
|
||||
#[derive(Copy, Clone, Zeroize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(from = "SerdeHelper"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))]
|
||||
pub struct SigningKey {
|
||||
seed: SecretKey,
|
||||
s: Scalar,
|
||||
prefix: [u8; 32],
|
||||
vk: VerificationKey,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for SigningKey {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
fmt.debug_struct("SigningKey")
|
||||
.field("vk", &self.vk)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SigningKey> for VerificationKey {
|
||||
fn from(sk: &'a SigningKey) -> VerificationKey {
|
||||
sk.vk
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a SigningKey> for VerificationKeyBytes {
|
||||
fn from(sk: &'a SigningKey) -> VerificationKeyBytes {
|
||||
sk.vk.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SigningKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.seed[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SigningKey> for SecretKey {
|
||||
fn from(sk: SigningKey) -> SecretKey {
|
||||
sk.seed
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for SigningKey {
|
||||
type Error = Error;
|
||||
fn try_from(slice: &[u8]) -> Result<SigningKey, Self::Error> {
|
||||
if slice.len() == 32 {
|
||||
let mut bytes = [0u8; 32];
|
||||
bytes[..].copy_from_slice(slice);
|
||||
Ok(bytes.into())
|
||||
} else {
|
||||
Err(Self::Error::InvalidSliceLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecretKey> for SigningKey {
|
||||
#[allow(non_snake_case)]
|
||||
fn from(seed: [u8; 32]) -> SigningKey {
|
||||
// Expand the seed to a 64-byte array with SHA512.
|
||||
let h = Sha512::digest(&seed[..]);
|
||||
|
||||
// Convert the low half to a scalar with Ed25519 "clamping"
|
||||
let s = {
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
scalar_bytes[..].copy_from_slice(&h[0..32]);
|
||||
scalar_bytes[0] &= 248;
|
||||
scalar_bytes[31] &= 127;
|
||||
scalar_bytes[31] |= 64;
|
||||
Scalar::from_bytes_mod_order(scalar_bytes)
|
||||
};
|
||||
|
||||
// Extract and cache the high half.
|
||||
let prefix = {
|
||||
let mut prefix = [0u8; 32];
|
||||
prefix[..].copy_from_slice(&h[32..64]);
|
||||
prefix
|
||||
};
|
||||
|
||||
// Compute the public key as A = [s]B.
|
||||
let A = &s * constants::ED25519_BASEPOINT_TABLE;
|
||||
|
||||
SigningKey {
|
||||
seed,
|
||||
s,
|
||||
prefix,
|
||||
vk: VerificationKey {
|
||||
minus_A: -A,
|
||||
A_bytes: VerificationKeyBytes(A.compress().to_bytes()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for SigningKey {
|
||||
fn ct_eq(&self, other: &Self) -> subtle::Choice {
|
||||
self.seed.ct_eq(&other.seed)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SigningKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SigningKey {}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl<'a> TryFrom<PrivateKeyInfo<'a>> for SigningKey {
|
||||
type Error = Error;
|
||||
fn try_from(pki: PrivateKeyInfo) -> Result<Self, Self::Error> {
|
||||
if pki.algorithm == ALGORITHM_ID {
|
||||
SigningKey::try_from(pki.private_key)
|
||||
} else {
|
||||
Err(Self::Error::MalformedSecretKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl EncodePublicKey for SigningKey {
|
||||
/// Serialize the public key for a [`SigningKey`] to an ASN.1 DER-encoded document.
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
self.vk.to_public_key_der()
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer<Signature> for SigningKey {
|
||||
/// Generate a [`Signature`] using a given [`SigningKey`].
|
||||
fn try_sign(&self, message: &[u8]) -> Result<Signature, ed25519::signature::Error> {
|
||||
Ok(self.sign(message))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: KeypairBytes) -> pkcs8::Result<Self> {
|
||||
SigningKey::try_from(&pkcs8_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl TryFrom<&KeypairBytes> for SigningKey {
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(pkcs8_key: &KeypairBytes) -> pkcs8::Result<Self> {
|
||||
let signing_key = SigningKey::from_der(&pkcs8_key.secret_key);
|
||||
|
||||
// Validate the public key in the PKCS#8 document if present
|
||||
if let Some(public_bytes) = &pkcs8_key.public_key {
|
||||
let expected_verifying_key =
|
||||
VerificationKey::from_public_key_der(public_bytes.as_ref())
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?;
|
||||
|
||||
if VerificationKey::from(&signing_key.unwrap()).A_bytes != expected_verifying_key.into()
|
||||
{
|
||||
return Err(pkcs8::Error::KeyMalformed);
|
||||
}
|
||||
}
|
||||
|
||||
signing_key
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pem")]
|
||||
impl From<SigningKey> for KeypairBytes {
|
||||
fn from(signing_key: SigningKey) -> KeypairBytes {
|
||||
KeypairBytes::from(&signing_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pem")]
|
||||
impl From<&SigningKey> for KeypairBytes {
|
||||
fn from(signing_key: &SigningKey) -> KeypairBytes {
|
||||
KeypairBytes {
|
||||
secret_key: signing_key.seed,
|
||||
public_key: Some(PublicKeyBytes(signing_key.vk.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl EncodePrivateKey for SigningKey {
|
||||
/// Serialize [`SigningKey`] to an ASN.1 DER-encoded secret document. Note that this
|
||||
/// will generate a v2 (RFC 5958) DER encoding with a public key.
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
|
||||
// In RFC 8410, the octet string containing the private key is encapsulated by
|
||||
// another octet string. Just add octet string bytes to the key when building
|
||||
// the document.
|
||||
let mut final_key = [0u8; 34];
|
||||
final_key[..2].copy_from_slice(&[0x04, 0x20]);
|
||||
final_key[2..].copy_from_slice(&self.seed);
|
||||
SecretDocument::try_from(PrivateKeyInfo {
|
||||
algorithm: ALGORITHM_ID,
|
||||
private_key: &final_key,
|
||||
public_key: Some(self.vk.A_bytes.0.as_slice()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl DecodePrivateKey for SigningKey {
|
||||
/// Create a [`SigningKey`] from an ASN.1 DER-encoded bytes. The bytes may include an
|
||||
/// accompanying public key, as defined in RFC 5958 (v1 and v2), but the call will
|
||||
/// fail if the public key doesn't match the private key's true accompanying public
|
||||
/// key.
|
||||
fn from_pkcs8_der(bytes: &[u8]) -> pkcs8::Result<Self> {
|
||||
let keypair = KeypairBytes::from_pkcs8_der(bytes).unwrap();
|
||||
let sk = SigningKey::from(keypair.secret_key);
|
||||
match keypair.public_key {
|
||||
Some(vk2) => {
|
||||
if sk.vk.A_bytes.0 == vk2.to_bytes() {
|
||||
Ok(sk)
|
||||
} else {
|
||||
Err(pkcs8::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
None => Ok(sk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
struct SerdeHelper([u8; 32]);
|
||||
|
||||
impl From<SerdeHelper> for SigningKey {
|
||||
fn from(helper: SerdeHelper) -> SigningKey {
|
||||
helper.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SigningKey> for SerdeHelper {
|
||||
fn from(sk: SigningKey) -> Self {
|
||||
Self(sk.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningKey {
|
||||
/// Construct a [`SigningKey`] from a `SecretKey`
|
||||
///
|
||||
#[inline]
|
||||
pub fn from_bytes(secret_key: &SecretKey) -> Self {
|
||||
(*secret_key).into()
|
||||
}
|
||||
|
||||
/// Convert this [`SigningKey`] into a `SecretKey`
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> SecretKey {
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
/// Convert this [`SigningKey`] into a `SecretKey` reference
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &SecretKey {
|
||||
&self.seed
|
||||
}
|
||||
|
||||
/// Generate a new signing key.
|
||||
pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> SigningKey {
|
||||
let mut bytes = [0u8; 32];
|
||||
rng.fill_bytes(&mut bytes[..]);
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
/// Get the [`VerificationKey`] for this [`SigningKey`].
|
||||
pub fn verification_key(&self) -> VerificationKey {
|
||||
self.into()
|
||||
}
|
||||
|
||||
/// Create a signature on `msg` using this key.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn sign(&self, msg: &[u8]) -> Signature {
|
||||
let r = Scalar::from_hash(Sha512::default().chain(&self.prefix[..]).chain(msg));
|
||||
|
||||
let R_bytes = (&r * constants::ED25519_BASEPOINT_TABLE)
|
||||
.compress()
|
||||
.to_bytes();
|
||||
|
||||
let k = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&R_bytes[..])
|
||||
.chain(&self.vk.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
|
||||
let s_bytes = (r + k * self.s).to_bytes();
|
||||
|
||||
Signature::from_components(R_bytes, s_bytes)
|
||||
}
|
||||
|
||||
/// Parse [`SigningKey`] from ASN.1 DER bytes.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
pub fn from_der(bytes: &[u8]) -> pkcs8::Result<Self> {
|
||||
bytes
|
||||
.try_into()
|
||||
.map_err(|_| pkcs8::Error::ParametersMalformed)
|
||||
}
|
||||
|
||||
/// Serialize [`SigningKey`] to an ASN.1 DER-encoded secret document. Note that this
|
||||
/// will generate a v1 (RFC 5958) DER encoding without a public key.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
pub fn to_pkcs8_der_v1(&self) -> pkcs8::Result<SecretDocument> {
|
||||
// In RFC 8410, the octet string containing the private key is encapsulated by
|
||||
// another octet string. Just add octet string bytes to the key when building
|
||||
// the document.
|
||||
let mut final_key = [0u8; 34];
|
||||
final_key[..2].copy_from_slice(&[0x04, 0x20]);
|
||||
final_key[2..].copy_from_slice(&self.seed);
|
||||
SecretDocument::try_from(PrivateKeyInfo::new(ALGORITHM_ID, &final_key))
|
||||
}
|
||||
|
||||
/// Serialize [`SigningKey`] as a PEM-encoded PKCS#8 string. Note that this
|
||||
/// will generate a v1 (RFC 5958) PEM encoding without a public key.
|
||||
#[cfg(all(feature = "pem", feature = "pkcs8"))]
|
||||
pub fn to_pkcs8_pem_v1(
|
||||
&self,
|
||||
line_ending: LineEnding,
|
||||
) -> Result<Zeroizing<String>, pkcs8::Error> {
|
||||
let doc = self.to_pkcs8_der_v1()?;
|
||||
Ok(doc.to_pem(PrivateKeyInfo::PEM_LABEL, line_ending)?)
|
||||
}
|
||||
}
|
||||
360
ed25519-heea/src/verification_key.rs
Normal file
360
ed25519-heea/src/verification_key.rs
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of ed25519-heea, a fork of ed25519-zebra.
|
||||
// Original ed25519-zebra code: Copyright (c) Zcash Foundation contributors
|
||||
// Modifications for HEEA: Copyright (c) 2025 curve25519-sol contributors
|
||||
// See LICENSE-APACHE and LICENSE-MIT for licensing information.
|
||||
//
|
||||
// Modifications from ed25519-zebra:
|
||||
// - Added `verify_heea`, an accelerated verification path using the HEEA
|
||||
// scalar decomposition from curve25519-sol's `HEEADecomposition` trait.
|
||||
// See "Accelerating EdDSA Signature Verification with Faster Scalar Size
|
||||
// Halving" (TCHES 2025) for the algorithm.
|
||||
// - `verify` and all ZIP-215 consensus logic are unchanged from ed25519-zebra.
|
||||
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use curve25519::{
|
||||
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||
scalar::Scalar,
|
||||
traits::{HEEADecomposition, IsIdentity},
|
||||
};
|
||||
use sha2::{Sha512, digest::Update};
|
||||
use zeroize::DefaultIsZeroes;
|
||||
|
||||
use ed25519::{Signature, signature::Verifier};
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::der::asn1::BitStringRef;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::spki::{
|
||||
AlgorithmIdentifierRef, DecodePublicKey, EncodePublicKey, SubjectPublicKeyInfoRef,
|
||||
};
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::{Document, ObjectIdentifier};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// The length of an ed25519 `VerificationKey`, in bytes.
|
||||
pub const VERIFICATION_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// A refinement type for `[u8; 32]` indicating that the bytes represent an
|
||||
/// encoding of an Ed25519 verification key.
|
||||
///
|
||||
/// This is useful for representing an encoded verification key, while the
|
||||
/// [`VerificationKey`] type in this library caches other decoded state used in
|
||||
/// signature verification.
|
||||
///
|
||||
/// A `VerificationKeyBytes` can be used to verify a single signature using the
|
||||
/// following idiom:
|
||||
/// ```
|
||||
/// use core::convert::TryFrom;
|
||||
/// # use rand::thread_rng;
|
||||
/// # use ed25519_heea::*;
|
||||
/// # let msg = b"Zcash";
|
||||
/// # let sk = SigningKey::new(thread_rng());
|
||||
/// # let sig = sk.sign(msg);
|
||||
/// # let vk_bytes = VerificationKeyBytes::from(&sk);
|
||||
/// VerificationKey::try_from(vk_bytes)
|
||||
/// .and_then(|vk| vk.verify(&sig, msg));
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct VerificationKeyBytes(pub(crate) [u8; VERIFICATION_KEY_LENGTH]);
|
||||
|
||||
impl core::fmt::Debug for VerificationKeyBytes {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
fmt.debug_tuple("VerificationKeyBytes")
|
||||
.field(&self.0)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for VerificationKeyBytes {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for VerificationKeyBytes {
|
||||
type Error = Error;
|
||||
fn try_from(slice: &[u8]) -> Result<VerificationKeyBytes, Self::Error> {
|
||||
if slice.len() == 32 {
|
||||
let mut bytes = [0u8; 32];
|
||||
bytes[..].copy_from_slice(slice);
|
||||
Ok(bytes.into())
|
||||
} else {
|
||||
Err(Error::InvalidSliceLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for VerificationKeyBytes {
|
||||
fn from(bytes: [u8; 32]) -> VerificationKeyBytes {
|
||||
VerificationKeyBytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerificationKeyBytes> for [u8; 32] {
|
||||
fn from(refined: VerificationKeyBytes) -> [u8; 32] {
|
||||
refined.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl<'a> TryFrom<SubjectPublicKeyInfoRef<'a>> for VerificationKeyBytes {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(spki: SubjectPublicKeyInfoRef) -> Result<VerificationKeyBytes, Error> {
|
||||
Ok(VerificationKeyBytes::try_from(spki.subject_public_key.as_bytes().unwrap()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// A valid Ed25519 verification key.
|
||||
///
|
||||
/// This is also called a public key by other implementations.
|
||||
///
|
||||
/// This type holds decompressed state used in signature verification; if the
|
||||
/// verification key may not be used immediately, it is probably better to use
|
||||
/// [`VerificationKeyBytes`], which is a refinement type for `[u8; 32]`.
|
||||
///
|
||||
/// ## Zcash-specific consensus properties
|
||||
///
|
||||
/// Ed25519 checks are described in [§5.4.5][ps] of the Zcash protocol specification and in
|
||||
/// [ZIP 215]. The verification criteria for an (encoded) verification key `A_bytes` are:
|
||||
///
|
||||
/// * `A_bytes` MUST be an encoding of a point `A` on the twisted Edwards form of
|
||||
/// Curve25519, and non-canonical encodings MUST be accepted;
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "VerificationKeyBytes"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes"))]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct VerificationKey {
|
||||
pub(crate) A_bytes: VerificationKeyBytes,
|
||||
pub(crate) minus_A: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl From<VerificationKey> for VerificationKeyBytes {
|
||||
fn from(vk: VerificationKey) -> VerificationKeyBytes {
|
||||
vk.A_bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for VerificationKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.A_bytes.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VerificationKey {
|
||||
fn default() -> VerificationKey {
|
||||
let identity: EdwardsPoint = Default::default();
|
||||
let identity_bytes = identity.compress().to_bytes();
|
||||
|
||||
VerificationKey {
|
||||
A_bytes: VerificationKeyBytes::from(identity_bytes),
|
||||
minus_A: -identity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultIsZeroes for VerificationKey {}
|
||||
|
||||
impl From<VerificationKey> for [u8; 32] {
|
||||
fn from(vk: VerificationKey) -> [u8; 32] {
|
||||
vk.A_bytes.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<VerificationKeyBytes> for VerificationKey {
|
||||
type Error = Error;
|
||||
#[allow(non_snake_case)]
|
||||
fn try_from(bytes: VerificationKeyBytes) -> Result<Self, Self::Error> {
|
||||
// * `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` respectively on the
|
||||
// twisted Edwards form of Curve25519, and non-canonical encodings MUST be accepted;
|
||||
let A = CompressedEdwardsY(bytes.0)
|
||||
.decompress()
|
||||
.ok_or(Error::MalformedPublicKey)?;
|
||||
|
||||
Ok(VerificationKey {
|
||||
A_bytes: bytes,
|
||||
minus_A: -A,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for VerificationKey {
|
||||
type Error = Error;
|
||||
fn try_from(slice: &[u8]) -> Result<VerificationKey, Error> {
|
||||
VerificationKeyBytes::try_from(slice).and_then(|vkb| vkb.try_into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<[u8; 32]> for VerificationKey {
|
||||
type Error = Error;
|
||||
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
||||
VerificationKeyBytes::from(bytes).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl EncodePublicKey for VerificationKey {
|
||||
/// Serialize [`VerificationKey`] to an ASN.1 DER-encoded document.
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
|
||||
let alg_info = AlgorithmIdentifierRef {
|
||||
oid: ObjectIdentifier::new_unwrap("1.3.101.112"), // RFC 8410
|
||||
parameters: None,
|
||||
};
|
||||
SubjectPublicKeyInfoRef {
|
||||
algorithm: alg_info,
|
||||
subject_public_key: BitStringRef::from_bytes(&self.A_bytes.0[..])?,
|
||||
}
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
impl DecodePublicKey for VerificationKey {
|
||||
/// Deserialize [`VerificationKey`] from ASN.1 DER bytes (32 bytes).
|
||||
fn from_public_key_der(bytes: &[u8]) -> Result<Self, pkcs8::spki::Error> {
|
||||
let spki = SubjectPublicKeyInfoRef::try_from(bytes).unwrap();
|
||||
let pk_bytes = spki.subject_public_key.as_bytes().unwrap();
|
||||
Ok(Self::try_from(pk_bytes).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<Signature> for VerificationKey {
|
||||
/// Verify a [`Signature`] object against a given [`VerificationKey`].
|
||||
fn verify(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &Signature,
|
||||
) -> Result<(), ed25519::signature::Error> {
|
||||
self.verify(signature, message)
|
||||
.map_err(|_| ed25519::signature::Error::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl VerificationKey {
|
||||
/// Verify a purported `signature` on the given `msg`.
|
||||
///
|
||||
/// ## Zcash-specific consensus properties
|
||||
///
|
||||
/// Ed25519 checks are described in [§5.4.5][ps] of the Zcash protocol specification and in
|
||||
/// [ZIP215]. The verification criteria for an (encoded) signature `(R_bytes, s_bytes)` with
|
||||
/// (encoded) verification key `A_bytes` are:
|
||||
///
|
||||
/// * `A_bytes` and `R_bytes` MUST be encodings of points `A` and `R` respectively on the
|
||||
/// twisted Edwards form of Curve25519, and non-canonical encodings MUST be accepted;
|
||||
///
|
||||
/// * `s_bytes` MUST represent an integer `s` less than `l`, the order of the prime-order
|
||||
/// subgroup of Curve25519;
|
||||
///
|
||||
/// * the verification equation `[8][s]B = [8]R + [8][k]A` MUST be satisfied;
|
||||
///
|
||||
/// * the alternate verification equation `[s]B = R + [k]A`, allowed by RFC 8032, MUST NOT be
|
||||
/// used.
|
||||
///
|
||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#concreteed25519
|
||||
/// [ZIP215]: https://zips.z.cash/zip-0215
|
||||
pub fn verify(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
let k = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&signature.r_bytes()[..])
|
||||
.chain(&self.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
self.verify_prehashed(signature, k)
|
||||
}
|
||||
|
||||
/// Verify a signature using the heea half-size scalar optimization.
|
||||
///
|
||||
/// This implements the algorithm from "Accelerating EdDSA Signature Verification
|
||||
/// with Faster Scalar Size Halving" (TCHES 2025).
|
||||
///
|
||||
/// The standard verification equation sB = R + hA is transformed to:
|
||||
/// τsB = τR + ρA where ρ ≡ τh (mod ℓ)
|
||||
///
|
||||
/// Both ρ and τ are approximately half the size of h.
|
||||
///
|
||||
/// We then decompose τs into two 128-bit scalars:
|
||||
/// τs = τs_hi * 2^128 + τs_lo
|
||||
///
|
||||
/// The verification equation becomes:
|
||||
/// τs_lo B + τs_hi (2^128 B) = τR + ρA
|
||||
/// which can be done via 4-variable MSM with half-size scalars.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn verify_heea(&self, signature: &Signature, msg: &[u8]) -> Result<(), Error> {
|
||||
// Compute the hash scalar h (called k in the standard implementation)
|
||||
let h = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&signature.r_bytes()[..])
|
||||
.chain(&self.A_bytes.0[..])
|
||||
.chain(msg),
|
||||
);
|
||||
|
||||
// Generate half-size scalars ρ and τ such that ρ ≡ τh (mod ℓ)
|
||||
// in order to have rho and tau approximately half the size of h
|
||||
// it is possible that we compute ρ ≡ -τh (mod ℓ)
|
||||
// this is indicated by `flip_h` flag being true,
|
||||
// in which case we will need to negate A later
|
||||
// let (rho, tau, flip_h) = crate::heea::generate_half_size_scalars(&h);
|
||||
let (rho, tau, flip_h) = h.heea_decompose();
|
||||
|
||||
// Extract s from the signature
|
||||
let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(*signature.s_bytes()))
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
|
||||
// Decode R from the signature
|
||||
let neg_R = -CompressedEdwardsY(*signature.r_bytes())
|
||||
.decompress()
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
|
||||
// Standard verification checks: sB = R + hA
|
||||
// Transformed verification: -τsB + τR + ρA == 0
|
||||
//
|
||||
// We verify:
|
||||
// [8] τs B + [8] τ (-R) + [8] ρ (-A) == 0
|
||||
|
||||
// Compute τs
|
||||
let ts = tau * s;
|
||||
let A = if flip_h { -self.minus_A } else { self.minus_A };
|
||||
// Compute the multi-scalar multiplication
|
||||
let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&tau, &neg_R, &rho, &A, &ts);
|
||||
|
||||
// Check if [8] τs B + [8] τ (-R) + [8] ρ (-A) == 0
|
||||
if result.mul_by_cofactor().is_identity() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidSignature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a signature with a prehashed `k` value. Note that this is not the
|
||||
/// same as "prehashing" in RFC8032.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn verify_prehashed(&self, signature: &Signature, k: Scalar) -> Result<(), Error> {
|
||||
// `s_bytes` MUST represent an integer less than the prime `l`.
|
||||
let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(*signature.s_bytes()))
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
// `R_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519.
|
||||
let R = CompressedEdwardsY(*signature.r_bytes())
|
||||
.decompress()
|
||||
.ok_or(Error::InvalidSignature)?;
|
||||
// We checked the encoding of A_bytes when constructing `self`.
|
||||
|
||||
// [8][s]B = [8]R + [8][k]A
|
||||
// <=> [8]R = [8][s]B - [8][k]A
|
||||
// <=> 0 = [8](R - ([s]B - [k]A))
|
||||
// <=> 0 = [8](R - R') where R' = [s]B - [k]A
|
||||
let R_prime = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &self.minus_A, &s);
|
||||
|
||||
if (R - R_prime).mul_by_cofactor().is_identity() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidSignature)
|
||||
}
|
||||
}
|
||||
}
|
||||
46
ed25519-heea/tests/batch.rs
Normal file
46
ed25519-heea/tests/batch.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#![cfg(feature = "alloc")]
|
||||
|
||||
use rand::thread_rng;
|
||||
|
||||
use ed25519_heea::*;
|
||||
|
||||
#[test]
|
||||
fn batch_verify() {
|
||||
let mut batch = batch::Verifier::new();
|
||||
for _ in 0..32 {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk_bytes = VerificationKeyBytes::from(&sk);
|
||||
let msg = b"BatchVerifyTest";
|
||||
let sig = sk.sign(&msg[..]);
|
||||
batch.queue((pk_bytes, sig, msg));
|
||||
}
|
||||
assert!(batch.verify(thread_rng()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_verify_with_one_bad_sig() {
|
||||
let bad_index = 10;
|
||||
let mut batch = batch::Verifier::new();
|
||||
let mut items = Vec::new();
|
||||
for i in 0..32 {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk_bytes = VerificationKeyBytes::from(&sk);
|
||||
let msg = b"BatchVerifyTest";
|
||||
let sig = if i != bad_index {
|
||||
sk.sign(&msg[..])
|
||||
} else {
|
||||
sk.sign(b"badmsg")
|
||||
};
|
||||
let item: batch::Item = (pk_bytes, sig, msg).into();
|
||||
items.push(item.clone());
|
||||
batch.queue(item);
|
||||
}
|
||||
assert!(batch.verify(thread_rng()).is_err());
|
||||
for (i, item) in items.drain(..).enumerate() {
|
||||
if i != bad_index {
|
||||
assert!(item.verify_single().is_ok());
|
||||
} else {
|
||||
assert!(item.verify_single().is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
87
ed25519-heea/tests/decoding.rs
Normal file
87
ed25519-heea/tests/decoding.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#[cfg(any(feature = "pem", feature = "pkcs8"))]
|
||||
use ed25519_heea::*;
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
use pkcs8::{DecodePrivateKey, DecodePublicKey};
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PKCS8_V1_PEM: &str = include_str!("examples/pkcs8-v1.pem");
|
||||
|
||||
/// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PKCS8_V2_PEM: &str = include_str!("examples/pkcs8-v2.pem");
|
||||
|
||||
/// Ed25519 PKCS#8 v2 private key + mismatched public key encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PKCS8_V2_DER_BAD: &[u8] = include_bytes!("examples/pkcs8-v2-bad-ver-key.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v2 private key + mismatched public key encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PKCS8_V2_PEM_BAD: &str = include_str!("examples/pkcs8-v2-bad-ver-key.pem");
|
||||
|
||||
/// Ed25519 SubjectPublicKeyInfo encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der");
|
||||
|
||||
/// Ed25519 SubjectPublicKeyInfo encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PUBLIC_KEY_PEM: &str = include_str!("examples/pubkey.pem");
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pkcs8")]
|
||||
fn decode_der_to_signing_key() {
|
||||
// Test against a v1 DER key.
|
||||
let sk1 = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap();
|
||||
let sk_bytes_string_1 = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
assert_eq!(hex::decode(sk_bytes_string_1).unwrap(), sk1.as_ref());
|
||||
|
||||
// Test against a v2 DER key.
|
||||
let sk2 = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap();
|
||||
let sk_bytes_string_2 = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
assert_eq!(hex::decode(sk_bytes_string_2).unwrap(), sk2.as_ref());
|
||||
|
||||
// Test against a v2 DER key with a mismatched public key.
|
||||
assert!(SigningKey::from_pkcs8_der(PKCS8_V2_DER_BAD).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pem")]
|
||||
fn decode_doc_to_signing_key() {
|
||||
// Test against a v1 PEM key.
|
||||
let sk1 = SigningKey::from_pkcs8_pem(PKCS8_V1_PEM).unwrap();
|
||||
let sk_bytes_string_1 = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
assert_eq!(hex::decode(sk_bytes_string_1).unwrap(), sk1.as_ref());
|
||||
|
||||
// Test against a valid v2 PEM key.
|
||||
let sk2 = SigningKey::from_pkcs8_pem(PKCS8_V2_PEM).unwrap();
|
||||
let sk_bytes_string_2 = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
assert_eq!(hex::decode(sk_bytes_string_2).unwrap(), sk2.as_ref());
|
||||
|
||||
// Test against a v2 DER key with a mismatched public key.
|
||||
assert!(SigningKey::from_pkcs8_pem(PKCS8_V2_PEM_BAD).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pkcs8")]
|
||||
fn decode_der_to_verification_key() {
|
||||
let vk = VerificationKey::from_public_key_der(PUBLIC_KEY_DER).unwrap();
|
||||
let vk_bytes_string = "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1";
|
||||
assert_eq!(hex::decode(vk_bytes_string).unwrap(), vk.as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pem")]
|
||||
fn decode_doc_to_verification_key() {
|
||||
let vk = VerificationKey::from_public_key_pem(PUBLIC_KEY_PEM).unwrap();
|
||||
let vk_bytes_string = "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1";
|
||||
assert_eq!(hex::decode(vk_bytes_string).unwrap(), vk.as_ref());
|
||||
}
|
||||
95
ed25519-heea/tests/encoding.rs
Normal file
95
ed25519-heea/tests/encoding.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#[cfg(feature = "pem")]
|
||||
use der::pem::LineEnding;
|
||||
#[cfg(any(feature = "pem", feature = "pkcs8"))]
|
||||
use ed25519_heea::*;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
pub use pkcs8::{
|
||||
EncodePrivateKey, EncodePublicKey, ObjectIdentifier, PrivateKeyInfo,
|
||||
spki::AlgorithmIdentifierRef,
|
||||
};
|
||||
#[cfg(any(feature = "pem", feature = "pkcs8"))]
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PKCS8_V1_PEM: &str = include_str!("examples/pkcs8-v1.pem");
|
||||
|
||||
/// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der");
|
||||
|
||||
/// Ed25519 PKCS#8 v1 private key encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PKCS8_V2_PEM: &str = include_str!("examples/pkcs8-v2.pem");
|
||||
|
||||
/// Ed25519 SubjectPublicKeyInfo encoded as ASN.1 DER.
|
||||
#[cfg(feature = "pkcs8")]
|
||||
const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der");
|
||||
|
||||
/// Ed25519 SubjectPublicKeyInfo encoded as PEM.
|
||||
#[cfg(feature = "pem")]
|
||||
const PUBLIC_KEY_PEM: &str = include_str!("examples/pubkey.pem");
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pkcs8")]
|
||||
fn encode_signing_key_to_der() {
|
||||
let sk_bytes_string = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
let mut sk_array = [0u8; 32];
|
||||
hex::decode_to_slice(sk_bytes_string, &mut sk_array as &mut [u8]).ok();
|
||||
|
||||
let sk = SigningKey::from(sk_array);
|
||||
let vk = sk.to_public_key_der().unwrap();
|
||||
assert_eq!(sk.to_pkcs8_der_v1().unwrap().as_bytes(), PKCS8_V1_DER);
|
||||
assert_eq!(sk.to_pkcs8_der().unwrap().as_bytes(), PKCS8_V2_DER);
|
||||
assert_eq!(vk.as_bytes(), PUBLIC_KEY_DER);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pem")]
|
||||
fn encode_signing_key_to_pem() {
|
||||
let sk_bytes_string = "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842";
|
||||
let mut sk_array = [0u8; 32];
|
||||
hex::decode_to_slice(sk_bytes_string, &mut sk_array as &mut [u8]).ok();
|
||||
|
||||
let sk = SigningKey::from(sk_array);
|
||||
let vk = sk.to_public_key_pem(LineEnding::default()).unwrap();
|
||||
assert_eq!(
|
||||
sk.to_pkcs8_pem_v1(LineEnding::default())
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
PKCS8_V1_PEM.as_bytes()
|
||||
);
|
||||
assert_eq!(
|
||||
sk.to_pkcs8_pem(LineEnding::default()).unwrap().as_bytes(),
|
||||
PKCS8_V2_PEM.as_bytes()
|
||||
);
|
||||
assert_eq!(vk, PUBLIC_KEY_PEM);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pkcs8")]
|
||||
fn encode_verification_key_to_der() {
|
||||
let vk_bytes_string = "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1";
|
||||
let mut vk_array = [0u8; 32];
|
||||
hex::decode_to_slice(vk_bytes_string, &mut vk_array as &mut [u8]).ok();
|
||||
|
||||
let vk = VerificationKey::try_from(vk_array).unwrap();
|
||||
let pkd = vk.to_public_key_der().unwrap();
|
||||
assert_eq!(pkd.as_ref(), PUBLIC_KEY_DER);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "pem")]
|
||||
fn encode_verification_key_to_pem() {
|
||||
let vk_bytes_string = "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1";
|
||||
let mut vk_array = [0u8; 32];
|
||||
hex::decode_to_slice(vk_bytes_string, &mut vk_array as &mut [u8]).ok();
|
||||
|
||||
let vk = VerificationKey::try_from(vk_array).unwrap();
|
||||
let pem = vk.to_public_key_pem(LineEnding::default()).unwrap();
|
||||
assert_eq!(pem, PUBLIC_KEY_PEM);
|
||||
}
|
||||
BIN
ed25519-heea/tests/examples/pkcs8-v1.der
Normal file
BIN
ed25519-heea/tests/examples/pkcs8-v1.der
Normal file
Binary file not shown.
3
ed25519-heea/tests/examples/pkcs8-v1.pem
Normal file
3
ed25519-heea/tests/examples/pkcs8-v1.pem
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
|
||||
-----END PRIVATE KEY-----
|
||||
BIN
ed25519-heea/tests/examples/pkcs8-v2-bad-ver-key.der
Normal file
BIN
ed25519-heea/tests/examples/pkcs8-v2-bad-ver-key.der
Normal file
Binary file not shown.
5
ed25519-heea/tests/examples/pkcs8-v2-bad-ver-key.pem
Normal file
5
ed25519-heea/tests/examples/pkcs8-v2-bad-ver-key.pem
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
|
||||
oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
|
||||
Z9xLlshQhqowtrbLDFw4rXAxZuE=
|
||||
-----END PRIVATE KEY-----
|
||||
BIN
ed25519-heea/tests/examples/pkcs8-v2.der
Normal file
BIN
ed25519-heea/tests/examples/pkcs8-v2.der
Normal file
Binary file not shown.
4
ed25519-heea/tests/examples/pkcs8-v2.pem
Normal file
4
ed25519-heea/tests/examples/pkcs8-v2.pem
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MFECAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
|
||||
gSEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
|
||||
-----END PRIVATE KEY-----
|
||||
BIN
ed25519-heea/tests/examples/pubkey.der
Normal file
BIN
ed25519-heea/tests/examples/pubkey.der
Normal file
Binary file not shown.
3
ed25519-heea/tests/examples/pubkey.pem
Normal file
3
ed25519-heea/tests/examples/pubkey.pem
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
|
||||
-----END PUBLIC KEY-----
|
||||
57
ed25519-heea/tests/heea.rs
Normal file
57
ed25519-heea/tests/heea.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use ed25519_heea::SigningKey;
|
||||
use ed25519_heea::VerificationKey;
|
||||
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn test_verify_heea_invalid_signature() {
|
||||
let mut rng = thread_rng();
|
||||
let signing_key = SigningKey::new(&mut rng);
|
||||
let verification_key = VerificationKey::from(&signing_key);
|
||||
|
||||
let msg = b"Original message";
|
||||
let signature = signing_key.sign(msg);
|
||||
|
||||
// Try to verify with different message
|
||||
let wrong_msg = b"Different message";
|
||||
|
||||
let result_standard = verification_key.verify(&signature, wrong_msg);
|
||||
let result_heea = verification_key.verify_heea(&signature, wrong_msg);
|
||||
|
||||
// Both should fail
|
||||
assert!(
|
||||
result_standard.is_err(),
|
||||
"Standard verification should fail for wrong message"
|
||||
);
|
||||
assert!(
|
||||
result_heea.is_err(),
|
||||
"heea verification should fail for wrong message"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_heea_multiple_signatures() {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for i in 0..100 {
|
||||
let signing_key = SigningKey::new(&mut rng);
|
||||
let verification_key = VerificationKey::from(&signing_key);
|
||||
|
||||
let msg = format!("Message number {}", i);
|
||||
let signature = signing_key.sign(msg.as_bytes());
|
||||
|
||||
let result_standard = verification_key.verify(&signature, msg.as_bytes());
|
||||
let result_heea = verification_key.verify_heea(&signature, msg.as_bytes());
|
||||
|
||||
assert!(
|
||||
result_standard.is_ok(),
|
||||
"Standard verification should succeed for signature {}",
|
||||
i
|
||||
);
|
||||
assert!(
|
||||
result_heea.is_ok(),
|
||||
"heea verification should succeed for signature {}",
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
73
ed25519-heea/tests/rfc8032.rs
Normal file
73
ed25519-heea/tests/rfc8032.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//! RFC 8032 test vectors.
|
||||
//!
|
||||
//! Note that RFC 8032 does not actually specify validation criteria for Ed25519,
|
||||
//! so these are basic sanity checks, rather than the more detailed test vectors
|
||||
//! in consensus.rs.
|
||||
#![cfg(feature = "serde")]
|
||||
|
||||
use ed25519::Signature;
|
||||
use ed25519_heea::*;
|
||||
|
||||
fn rfc8032_test_case(sk_bytes: Vec<u8>, pk_bytes: Vec<u8>, sig_bytes: Vec<u8>, msg: Vec<u8>) {
|
||||
let sk: SigningKey = bincode::deserialize(&sk_bytes).expect("sk should deserialize");
|
||||
let pk: VerificationKey = bincode::deserialize(&pk_bytes).expect("pk should deserialize");
|
||||
let sig: Signature =
|
||||
Signature::from_slice(sig_bytes.as_slice()).expect("sig should deserialize");
|
||||
|
||||
assert!(pk.verify(&sig, &msg).is_ok(), "verification failed");
|
||||
|
||||
let pk_from_sk = VerificationKey::from(&sk);
|
||||
assert_eq!(
|
||||
VerificationKeyBytes::from(pk),
|
||||
VerificationKeyBytes::from(pk_from_sk),
|
||||
"regenerated pubkey did not match test vector pubkey"
|
||||
);
|
||||
|
||||
let sig_from_sk = sk.sign(&msg);
|
||||
assert_eq!(
|
||||
sig, sig_from_sk,
|
||||
"regenerated signature did not match test vector"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc8032_test_1() {
|
||||
rfc8032_test_case(
|
||||
hex::decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("")
|
||||
.expect("hex should decode"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc8032_test_2() {
|
||||
rfc8032_test_case(
|
||||
hex::decode("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("72")
|
||||
.expect("hex should decode"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc8032_test_3() {
|
||||
rfc8032_test_case(
|
||||
hex::decode("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a")
|
||||
.expect("hex should decode"),
|
||||
hex::decode("af82")
|
||||
.expect("hex should decode"),
|
||||
);
|
||||
}
|
||||
105
ed25519-heea/tests/small_order.rs
Normal file
105
ed25519-heea/tests/small_order.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#![cfg(feature = "std")]
|
||||
|
||||
use color_eyre::Report;
|
||||
use curve25519::{
|
||||
constants::EIGHT_TORSION, digest::Update, edwards::CompressedEdwardsY, scalar::Scalar,
|
||||
traits::IsIdentity,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use sha2::Sha512;
|
||||
|
||||
mod util;
|
||||
use util::TestCase;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub static SMALL_ORDER_SIGS: Lazy<Vec<TestCase>> = Lazy::new(|| {
|
||||
let mut tests = Vec::new();
|
||||
let s = Scalar::ZERO;
|
||||
|
||||
// Use all the canonical encodings of the 8-torsion points,
|
||||
// and the low-order non-canonical encodings.
|
||||
let encodings = EIGHT_TORSION
|
||||
.iter()
|
||||
.map(|point| point.compress().to_bytes())
|
||||
.chain(util::non_canonical_point_encodings().into_iter().take(6))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
/*
|
||||
for (i, e) in encodings.iter().enumerate() {
|
||||
println!("{}: {}", i, hex::encode(e));
|
||||
}
|
||||
*/
|
||||
|
||||
for A_bytes in &encodings {
|
||||
let A = CompressedEdwardsY(*A_bytes).decompress().unwrap();
|
||||
for R_bytes in &encodings {
|
||||
let R = CompressedEdwardsY(*R_bytes).decompress().unwrap();
|
||||
let sig_bytes = {
|
||||
let mut bytes = [0u8; 64];
|
||||
bytes[0..32].copy_from_slice(&R_bytes[..]);
|
||||
bytes[32..64].copy_from_slice(s.as_bytes());
|
||||
bytes
|
||||
};
|
||||
let vk_bytes = *A_bytes;
|
||||
// The verification equation is [8][s]B = [8]R + [8][k]A.
|
||||
// If R, A are torsion points the LHS is 0, setting s = 0 makes RHS 0.
|
||||
let valid_zip215 = true;
|
||||
// In the legacy equation the RHS is 0 and the LHS is R + [k]A.
|
||||
// This will be valid only if:
|
||||
// * A is not all zeros.
|
||||
// * R is not an excluded point
|
||||
// * R + [k]A = 0
|
||||
// * R is canonically encoded (because the check recomputes R)
|
||||
let k = Scalar::from_hash(
|
||||
Sha512::default()
|
||||
.chain(&sig_bytes[0..32])
|
||||
.chain(vk_bytes)
|
||||
.chain(b"Zcash"),
|
||||
);
|
||||
let check = R + k * A;
|
||||
let non_canonical_R = R.compress().as_bytes() != R_bytes;
|
||||
let valid_legacy = !(vk_bytes == [0; 32]
|
||||
|| util::EXCLUDED_POINT_ENCODINGS.contains(R.compress().as_bytes())
|
||||
|| !check.is_identity()
|
||||
|| non_canonical_R);
|
||||
|
||||
tests.push(TestCase {
|
||||
vk_bytes,
|
||||
sig_bytes,
|
||||
valid_legacy,
|
||||
valid_zip215,
|
||||
})
|
||||
}
|
||||
}
|
||||
tests
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn conformance() -> Result<(), Report> {
|
||||
for case in SMALL_ORDER_SIGS.iter() {
|
||||
case.check()?;
|
||||
}
|
||||
println!("{:#?}", *SMALL_ORDER_SIGS);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
fn individual_matches_batch_verification() -> Result<(), Report> {
|
||||
use core::convert::TryFrom;
|
||||
use ed25519::Signature;
|
||||
use ed25519_heea::{VerificationKey, VerificationKeyBytes, batch};
|
||||
|
||||
for case in SMALL_ORDER_SIGS.iter() {
|
||||
let msg = b"Zcash";
|
||||
let sig = Signature::from(case.sig_bytes);
|
||||
let vkb = VerificationKeyBytes::from(case.vk_bytes);
|
||||
let individual_verification =
|
||||
VerificationKey::try_from(vkb).and_then(|vk| vk.verify(&sig, msg));
|
||||
let mut bv = batch::Verifier::new();
|
||||
bv.queue((vkb, sig, msg));
|
||||
let batch_verification = bv.verify(rand::thread_rng());
|
||||
assert_eq!(individual_verification.is_ok(), batch_verification.is_ok());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
50
ed25519-heea/tests/unit_tests.rs
Normal file
50
ed25519-heea/tests/unit_tests.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#![cfg(feature = "serde")]
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use rand::thread_rng;
|
||||
|
||||
use ed25519_heea::{Signature, SigningKey, VerificationKey, VerificationKeyBytes};
|
||||
|
||||
#[test]
|
||||
fn parsing() {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk = VerificationKey::from(&sk);
|
||||
let pkb = VerificationKeyBytes::from(&sk);
|
||||
let sig = sk.sign(b"test");
|
||||
|
||||
let sk_array: [u8; 32] = sk.into();
|
||||
let pk_array: [u8; 32] = pk.into();
|
||||
let pkb_array: [u8; 32] = pkb.into();
|
||||
let sig_array: [u8; 64] = sig.into();
|
||||
|
||||
let sk2 = SigningKey::from(sk_array);
|
||||
let pk2 = VerificationKey::try_from(pk_array).unwrap();
|
||||
let pkb2 = VerificationKeyBytes::from(pkb_array);
|
||||
let sig2 = Signature::from(sig_array);
|
||||
|
||||
assert_eq!(sk, sk2);
|
||||
assert_eq!(pk, pk2);
|
||||
assert_eq!(pkb, pkb2);
|
||||
assert_eq!(sig, sig2);
|
||||
|
||||
let sk3: SigningKey = bincode::deserialize(sk.as_ref()).unwrap();
|
||||
let pk3: VerificationKey = bincode::deserialize(pk.as_ref()).unwrap();
|
||||
let pkb3: VerificationKeyBytes = bincode::deserialize(pkb.as_ref()).unwrap();
|
||||
|
||||
assert_eq!(sk, sk3);
|
||||
assert_eq!(pk, pk3);
|
||||
assert_eq!(pkb, pkb3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify() {
|
||||
let sk = SigningKey::new(thread_rng());
|
||||
let pk = VerificationKey::from(&sk);
|
||||
|
||||
let msg = b"ed25519-zebra test message";
|
||||
|
||||
let sig = sk.sign(&msg[..]);
|
||||
|
||||
assert_eq!(pk.verify(&sig, &msg[..]), Ok(()))
|
||||
}
|
||||
265
ed25519-heea/tests/util/mod.rs
Normal file
265
ed25519-heea/tests/util/mod.rs
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// functions are used in small_order but not recognized as such?
|
||||
#![allow(dead_code)]
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use color_eyre::{Report, eyre::eyre};
|
||||
use curve25519::edwards::{CompressedEdwardsY, EdwardsPoint};
|
||||
use ed25519_heea as ed25519_heea_zip215;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
pub struct TestCase {
|
||||
pub vk_bytes: [u8; 32],
|
||||
pub sig_bytes: [u8; 64],
|
||||
pub valid_legacy: bool,
|
||||
pub valid_zip215: bool,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for TestCase {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
fmt.debug_struct("TestCase")
|
||||
.field("vk_bytes", &hex::encode(&self.vk_bytes[..]))
|
||||
.field("sig_bytes", &hex::encode(&self.sig_bytes[..]))
|
||||
.field("valid_legacy", &self.valid_legacy)
|
||||
.field("valid_zip215", &self.valid_zip215)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
pub fn check(&self) -> Result<(), Report> {
|
||||
match (self.valid_legacy, self.check_legacy()) {
|
||||
(false, Err(_)) => Ok(()),
|
||||
(true, Ok(())) => Ok(()),
|
||||
(false, Ok(())) => Err(eyre!(
|
||||
"legacy-invalid signature case validated under legacy rules"
|
||||
)),
|
||||
(true, Err(e)) => {
|
||||
Err(e.wrap_err("legacy-valid signature case was rejected under legacy rules"))
|
||||
}
|
||||
}?;
|
||||
match (self.valid_zip215, self.check_zip215()) {
|
||||
(false, Err(_)) => Ok(()),
|
||||
(true, Ok(())) => Ok(()),
|
||||
(false, Ok(())) => Err(eyre!(
|
||||
"zip215-invalid signature case validated under zip215 rules"
|
||||
)),
|
||||
(true, Err(e)) => {
|
||||
Err(e.wrap_err("zip215-valid signature case was rejected under zip215 rules"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_legacy(&self) -> Result<(), Report> {
|
||||
use ed25519_zebra_legacy::{Signature, VerificationKey};
|
||||
let sig = Signature::from(self.sig_bytes);
|
||||
VerificationKey::try_from(self.vk_bytes).and_then(|vk| vk.verify(&sig, b"Zcash"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_zip215(&self) -> Result<(), Report> {
|
||||
use ed25519_heea_zip215::{Signature, VerificationKey};
|
||||
let sig = Signature::from(self.sig_bytes);
|
||||
VerificationKey::try_from(self.vk_bytes).and_then(|vk| vk.verify(&sig, b"Zcash"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn non_canonical_field_encodings() -> Vec<[u8; 32]> {
|
||||
// There are 19 finite field elements which can be represented
|
||||
// non-canonically as x + p with x + p fitting in 255 bits:
|
||||
let mut bytes = [
|
||||
237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127,
|
||||
];
|
||||
let mut encodings = Vec::new();
|
||||
for i in 0..19u8 {
|
||||
bytes[0] = 237 + i;
|
||||
encodings.push(bytes);
|
||||
}
|
||||
encodings
|
||||
}
|
||||
|
||||
// Compute all 25 non-canonical point encodings. The first 5 are low order.
|
||||
pub fn non_canonical_point_encodings() -> Vec<[u8; 32]> {
|
||||
// Points are encoded by the y-coordinate and a bit indicating the
|
||||
// sign of the x-coordinate. There are two ways to construct a
|
||||
// non-canonical point encoding:
|
||||
//
|
||||
// (1) by using a non-canonical encoding of y (cf RFC8032§5.1.3.1)
|
||||
// (2) by selecting y so that both sign choices give the same x.
|
||||
//
|
||||
// Condition (1) can occur only for 19 field elements that can be encoded
|
||||
// non-canonically as y + p with y + p fitting in 255 bits.
|
||||
//
|
||||
// Condition (2) occurs if and only if x = -x, i.e., x = 0.
|
||||
// The curve equation is ax^2 + y^2 = 1 + dx^2 + y^2 so x = 0 => y^2 = 1.
|
||||
// This means y = 1 or y = -1.
|
||||
//
|
||||
// When y = -1, y can only be canonically encoded, so the encodings of (0,-1) are:
|
||||
// * enc(-1) || 0 [canonical]
|
||||
// * enc(-1) || 1 [non-canonical]
|
||||
//
|
||||
// When y = 1, y can be non-canonically encoded, so the encodings of (0,1) are:
|
||||
// * enc(1) || 0 [canonical]
|
||||
// * enc(1) || 1 [non-canonical]
|
||||
// * enc(2^255 - 18) || 0 [non-canonical]
|
||||
// * enc(2^255 - 18) || 1 [non-canonical]
|
||||
//
|
||||
// We pick up the latter two in generation of non-canonically encoded field elements,
|
||||
// and construct the first two explicitly.
|
||||
//
|
||||
// RFC8032§5.1.3.4 requires implementations to perform a field element equality check
|
||||
// on the x value computed inside the decompression routine and abort if x = 0 and
|
||||
// the sign bit was set. However, no implementations do this, and any implementation
|
||||
// that did would then be subtly incompatible with others in a new and different way.
|
||||
//
|
||||
// (This taxonomy was created with pointers from Sean Bowe and NCC Group).
|
||||
let mut encodings = Vec::new();
|
||||
|
||||
// Canonical y with non-canonical sign bits.
|
||||
let y1_noncanonical_sign_bit = [
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 128,
|
||||
];
|
||||
encodings.push(y1_noncanonical_sign_bit);
|
||||
let ym1_noncanonical_sign_bit = [
|
||||
236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
];
|
||||
encodings.push(ym1_noncanonical_sign_bit);
|
||||
|
||||
// Run through non-canonical field elements.
|
||||
// Not all field elements are x-coordinates of curve points, so check:
|
||||
for mut x in non_canonical_field_encodings().into_iter() {
|
||||
if CompressedEdwardsY(x).decompress().is_some() {
|
||||
encodings.push(x);
|
||||
}
|
||||
x[31] |= 128;
|
||||
if CompressedEdwardsY(x).decompress().is_some() {
|
||||
encodings.push(x);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all of the non-canonical points are really non-canonical
|
||||
for &e in &encodings {
|
||||
assert_ne!(
|
||||
e,
|
||||
CompressedEdwardsY(e)
|
||||
.decompress()
|
||||
.unwrap()
|
||||
.compress()
|
||||
.to_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
encodings
|
||||
}
|
||||
|
||||
// Running this reveals that only the first 6 entries on the list have low order.
|
||||
#[test]
|
||||
fn print_non_canonical_points() {
|
||||
for encoding in non_canonical_point_encodings().into_iter() {
|
||||
let point = CompressedEdwardsY(encoding).decompress().unwrap();
|
||||
println!(
|
||||
"encoding {} has order {}",
|
||||
hex::encode(&encoding[..]),
|
||||
order(point)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn order(point: EdwardsPoint) -> &'static str {
|
||||
use curve25519::traits::IsIdentity;
|
||||
if point.is_small_order() {
|
||||
let point2 = point + point;
|
||||
let point4 = point2 + point2;
|
||||
if point.is_identity() {
|
||||
"1"
|
||||
} else if point2.is_identity() {
|
||||
"2"
|
||||
} else if point4.is_identity() {
|
||||
"4"
|
||||
} else {
|
||||
"8"
|
||||
}
|
||||
} else if point.is_torsion_free() {
|
||||
"p"
|
||||
} else {
|
||||
">p"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_valid_excluded_encodings() {
|
||||
for (i, encoding) in EXCLUDED_POINT_ENCODINGS.iter().enumerate() {
|
||||
if let Some(point) = CompressedEdwardsY(*encoding).decompress() {
|
||||
println!("index {} is valid point of order {}", i, order(point));
|
||||
} else {
|
||||
println!("index {} is not a valid encoding", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// These point encodings were specifically blacklisted by libsodium 1.0.15, in
|
||||
/// an apparent (and unsuccessful) attempt to exclude points of low order.
|
||||
///
|
||||
/// To maintain exact compatibility with this version of libsodium, we encode
|
||||
/// them here, following the Zcash protocol specification.
|
||||
pub static EXCLUDED_POINT_ENCODINGS: [[u8; 32]; 11] = [
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
],
|
||||
[
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
],
|
||||
[
|
||||
0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
|
||||
0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
|
||||
0xfc, 0x05,
|
||||
],
|
||||
[
|
||||
0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
|
||||
0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
|
||||
0x03, 0x7a,
|
||||
],
|
||||
[
|
||||
0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
|
||||
0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
|
||||
0xfc, 0x85,
|
||||
],
|
||||
[
|
||||
0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
|
||||
0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
|
||||
0x03, 0xfa,
|
||||
],
|
||||
[
|
||||
0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x7f,
|
||||
],
|
||||
[
|
||||
0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x7f,
|
||||
],
|
||||
[
|
||||
0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x7f,
|
||||
],
|
||||
[
|
||||
0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
],
|
||||
[
|
||||
0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
],
|
||||
];
|
||||
Loading…
Reference in a new issue