refactor and merge curve and ed crates (#6)

* refactor and merge curve and ed crates

* fmt

* ci

* fmt again

* ci

* Update bench.rs

* fix ubuntu
This commit is contained in:
zz-sol 2026-03-26 08:09:10 -04:00 committed by GitHub
parent 8024e4ed31
commit 1587b4c24f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
104 changed files with 249 additions and 3417 deletions

460
Cargo.lock generated
View file

@ -13,21 +13,6 @@ 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"
@ -67,21 +52,6 @@ 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"
@ -130,15 +100,6 @@ 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"
@ -154,12 +115,6 @@ 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"
@ -245,33 +200,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
@ -306,9 +234,9 @@ dependencies = [
"cast",
"ciborium",
"clap",
"criterion-plot 0.5.0",
"criterion-plot",
"is-terminal",
"itertools 0.10.5",
"itertools",
"num-traits",
"once_cell",
"oorandom",
@ -322,29 +250,6 @@ 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"
@ -352,17 +257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"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",
"itertools",
]
[[package]]
@ -406,16 +301,14 @@ dependencies = [
]
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"zeroize",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
@ -429,22 +322,13 @@ dependencies = [
"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",
"block-buffer",
"crypto-common",
]
@ -455,47 +339,9 @@ 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"
version = "1.15.0"
@ -518,16 +364,6 @@ 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"
@ -552,39 +388,12 @@ 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"
@ -593,7 +402,7 @@ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
"wasi",
]
[[package]]
@ -622,12 +431,6 @@ 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"
@ -645,7 +448,9 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
@ -653,11 +458,6 @@ 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"
@ -701,12 +501,6 @@ 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"
@ -739,15 +533,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.18"
@ -764,12 +549,6 @@ 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"
@ -806,15 +585,6 @@ 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"
@ -844,15 +614,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.4"
@ -865,18 +626,6 @@ 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"
@ -886,12 +635,6 @@ 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"
@ -1062,15 +805,6 @@ 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"
@ -1153,12 +887,6 @@ 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"
@ -1308,19 +1036,6 @@ 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"
@ -1329,16 +1044,7 @@ checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"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",
"digest",
]
[[package]]
@ -1352,20 +1058,36 @@ name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
[[package]]
name = "solana-curve25519-cuda"
version = "0.1.0"
dependencies = [
"cc",
"criterion",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
"rayon",
"solana-ed25519",
"which",
]
[[package]]
name = "solana-curve25519"
name = "solana-ed25519"
version = "0.1.0"
dependencies = [
"bincode",
"cfg-if",
"cpufeatures 0.2.17",
"criterion 0.5.1",
"digest 0.11.2",
"criterion",
"curve25519-dalek-derive",
"der",
"digest",
"ed25519",
"hashbrown 0.15.5",
"hex",
"pkcs8",
"proptest",
"rand 0.10.0",
"rand_core 0.10.0",
@ -1373,35 +1095,11 @@ dependencies = [
"rustcrypto-ff",
"rustcrypto-group",
"serde",
"sha2 0.11.0-rc.5",
"solana-curve25519-derive",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "solana-curve25519-cuda"
version = "0.1.0"
dependencies = [
"cc",
"criterion 0.5.1",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
"rayon",
"solana-curve25519",
"which",
]
[[package]]
name = "solana-curve25519-derive"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "spki"
version = "0.7.3"
@ -1459,35 +1157,6 @@ 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"
@ -1498,47 +1167,6 @@ 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"
@ -1563,18 +1191,6 @@ 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"
@ -1594,12 +1210,6 @@ 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"

View file

@ -1,9 +1,7 @@
[workspace]
members = [
"curve25519/curve25519-cuda",
"curve25519/curve25519",
"curve25519/curve25519-derive",
"curve25519/ed25519-heea",
"curve25519/solana-ed25519",
]
resolver = "2"

View file

@ -7,7 +7,7 @@ authors = ["Anza Cryptography Team"]
[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" }
curve25519 = { path = "../solana-ed25519", package = "solana-ed25519" }
rand = "0.8"
rand_chacha = "0.3"
rand_core = "0.6"

View file

@ -1,8 +0,0 @@
# Changelog
Entries are listed in reverse chronological order per undeprecated
major series.
### 0.1.1
* Copied over license files from [original](https://github.com/koute/unsafe_target_feature/tree/389ae00d34cf0ff589cb8d9b38a85ae1b05ebfdc) repo

View file

@ -1,18 +0,0 @@
[package]
name = "solana-curve25519-derive"
version = "0.1.0"
edition = "2021"
authors = ["Anza Cryptography Team"]
license = "MIT OR Apache-2.0"
readme = "README.md"
description = "Proc-macro helpers for solana-curve25519 (forked from curve25519-derive)"
[lib]
name = "curve25519_derive"
proc-macro = true
[dependencies]
proc-macro2 = "1.0.66"
quote = "1.0.31"
syn = { version = "2.0.27", features = ["full"] }

View file

@ -1,201 +0,0 @@
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.

View file

@ -1,23 +0,0 @@
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.

View file

@ -1,191 +0,0 @@
# A more convenient `#[target_feature]` replacement
To get good performance out of SIMD everything on the SIMD codepath must be inlined.
With how SIMD is currently implemented in Rust one of two things have to be true for
a function using SIMD to be inlinable: (and this includes the SIMD intrinsics themselves)
a) The whole program has to be compiled with the relevant `-C target-cpu` or `-C target-feature` flags.
b) SIMD support must be automatically detected at runtime, and every function on the SIMD codepath must be marked with `#[target_feature]`.
Both have their downsides. Setting the `target-cpu` or `target-features` makes the resulting binary
incompatible with older CPUs, while using `#[target_feature]` is incredibly inconvenient.
This crate is meant to make `#[target_feature]` less painful to use.
## Problems with `#[target_feature]`
When we're not compiling with the relevant `target-cpu`/`target-feature` flags everything on
the SIMD codepath must be marked with the `#[target_feature]` attribute. This is not a problem
when all of your SIMD code is neatly encapsulated inside of a single function, but once you start
to build out more elaborate abstractions it starts to become painful to use.
* It can only be used on `unsafe` functions, so everything on your SIMD codepath now has to be `unsafe`.
In theory this is nice - these functions require the relevant SIMD instructions to be present at runtime,
so calling them without checking is obviously unsafe! But in practice this is rarely what you want. When
you build an abstraction over SIMD code you usually want to assume that *internally* within your module
all of the necessary SIMD instructions are available, and you only want to check this at the boundaries
when you're first entering your module. You do *not* want to infect everything *inside* of the module with
`unsafe` since you've already checked this invariant at the module's API boundary.
* It cannot be used on non-`unsafe` trait methods.
If you're implementing a trait, say for example `std::ops::Add`, then you cannot mark the method `unsafe`
unless the original trait also has it marked as `unsafe`, and usually it doesn't.
* It makes it impossible to abstract over a given SIMD instruction set using a trait.
For example, let's assume you want to abstract over which SIMD instructions you use using a trait in the following way:
```rust
trait Backend {
unsafe fn sum(input: &[u32]) -> u32;
}
struct AVX;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Backend for AVX {
#[target_feature(enable = "avx")]
unsafe fn sum(xs: &[u32]) -> u32 {
// ...
todo!();
}
}
struct AVX2;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
impl Backend for AVX2 {
#[target_feature(enable = "avx2")]
unsafe fn sum(xs: &[u32]) -> u32 {
// ...
todo!();
}
}
// And now you want a have function which calls into that trait:
unsafe fn do_calculations<B>(xs: &[u32]) -> u32 where B: Backend {
let value = B::sum(xs);
// ...do some more calculations here...
value
}
```
We have a problem here. This has to be marked with `#[target_feature]`, and that has to specify the concrete
feature flag for a given SIMD instruction set, but this function is generic so we can't do that!
## How does this crate make it better?
### You can now mark safe functions with `#[target_feature]`
This crate exposes an `#[unsafe_target_feature]` macro which works just like `#[target_feature]` except
it moves the `unsafe` from the function prototype into the macro name, and can be used on safe functions.
```rust
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "avx2")]
unsafe fn func() {}
```
```rust
use curve25519_derive::unsafe_target_feature;
// No `unsafe` on the function itself!
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature("avx2")]
fn func() {}
```
It can also be used to mark functions inside of impls:
```rust,compile_fail
struct S;
impl core::ops::Add for S {
type Output = S;
// ERROR: method `add` has an incompatible type for trait
#[target_feature(enable = "avx2")]
unsafe fn add(self, rhs: S) -> S {
S
}
}
```
```rust
use curve25519_derive::unsafe_target_feature;
struct S;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature("avx2")]
impl core::ops::Add for S {
type Output = S;
// No `unsafe` on the function itself!
fn add(self, rhs: S) -> S {
S
}
}
```
### You can generate specialized copies of a module for each target feature
```rust
use curve25519_derive::unsafe_target_feature_specialize;
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))]
mod simd {
#[for_target_feature("sse2")]
pub const CONSTANT: u32 = 1;
#[for_target_feature("avx2")]
pub const CONSTANT: u32 = 2;
#[for_target_feature("avx512ifma")]
pub const CONSTANT: u32 = 3;
pub fn func() { /* ... */ }
}
# #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn entry_point() {
#[cfg(nightly)]
if std::is_x86_feature_detected!("avx512ifma") {
return simd_avx512ifma::func();
}
if std::is_x86_feature_detected!("avx2") {
return simd_avx2::func();
}
if std::is_x86_feature_detected!("sse2") {
return simd_sse2::func();
}
unimplemented!();
}
```
## How to use `#[unsafe_target_feature]`?
- Can be used on `fn`s, `impl`s and `mod`s.
- When used on a function will only apply to that function; it won't apply to any nested functions, traits, mods, etc.
- When used on an `impl` will only apply to all of the functions directly defined inside of that `impl`.
- When used on a `mod` will only apply to all of the `fn`s and `impl`s directly defined inside of that `mod`.
- Cannot be used on methods which use `self` or `Self`; instead use it on the `impl` in which the method is defined.
## License
Licensed under either of
* Apache License, Version 2.0, [LICENSE-APACHE](LICENSE-APACHE)
* MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View file

@ -1,463 +0,0 @@
#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::spanned::Spanned;
macro_rules! unsupported_if_some {
($value:expr) => {
if let Some(value) = $value {
return syn::Error::new(value.span(), "unsupported by #[unsafe_target_feature(...)]")
.into_compile_error()
.into();
}
};
}
macro_rules! unsupported {
($value: expr) => {
return syn::Error::new(
$value.span(),
"unsupported by #[unsafe_target_feature(...)]",
)
.into_compile_error()
.into()
};
}
mod kw {
syn::custom_keyword!(conditional);
}
enum SpecializeArg {
LitStr(syn::LitStr),
Conditional(Conditional),
}
impl SpecializeArg {
fn lit(&self) -> &syn::LitStr {
match self {
SpecializeArg::LitStr(lit) => lit,
SpecializeArg::Conditional(conditional) => &conditional.lit,
}
}
fn condition(&self) -> Option<&TokenStream2> {
match self {
SpecializeArg::LitStr(..) => None,
SpecializeArg::Conditional(conditional) => Some(&conditional.attr),
}
}
}
struct Conditional {
lit: syn::LitStr,
attr: TokenStream2,
}
impl syn::parse::Parse for Conditional {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lit = input.parse()?;
input.parse::<syn::Token![,]>()?;
let attr = input.parse()?;
Ok(Conditional { lit, attr })
}
}
impl syn::parse::Parse for SpecializeArg {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::conditional) {
input.parse::<kw::conditional>()?;
let content;
syn::parenthesized!(content in input);
let conditional = content.parse()?;
Ok(SpecializeArg::Conditional(conditional))
} else {
Ok(SpecializeArg::LitStr(input.parse()?))
}
}
}
struct SpecializeArgs(syn::punctuated::Punctuated<SpecializeArg, syn::Token![,]>);
impl syn::parse::Parse for SpecializeArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self(syn::punctuated::Punctuated::parse_terminated(input)?))
}
}
#[proc_macro_attribute]
pub fn unsafe_target_feature(attributes: TokenStream, input: TokenStream) -> TokenStream {
let attributes = syn::parse_macro_input!(attributes as syn::LitStr);
let item = syn::parse_macro_input!(input as syn::Item);
process_item(&attributes, item, true)
}
#[proc_macro_attribute]
pub fn unsafe_target_feature_specialize(
attributes: TokenStream,
input: TokenStream,
) -> TokenStream {
let attributes = syn::parse_macro_input!(attributes as SpecializeArgs);
let item_mod = syn::parse_macro_input!(input as syn::ItemMod);
let mut out = Vec::new();
for attributes in attributes.0 {
let features: Vec<_> = attributes
.lit()
.value()
.split(',')
.map(|feature| feature.replace(' ', ""))
.collect();
let name = format!("{}_{}", item_mod.ident, features.join("_"));
let ident = syn::Ident::new(&name, item_mod.ident.span());
let mut attrs = item_mod.attrs.clone();
if let Some(condition) = attributes.condition() {
attrs.push(syn::Attribute {
pound_token: Default::default(),
style: syn::AttrStyle::Outer,
bracket_token: Default::default(),
meta: syn::Meta::List(syn::MetaList {
path: syn::Ident::new("cfg", attributes.lit().span()).into(),
delimiter: syn::MacroDelimiter::Paren(Default::default()),
tokens: condition.clone(),
}),
});
}
let item_mod = process_mod(
attributes.lit(),
syn::ItemMod {
attrs,
ident,
..item_mod.clone()
},
Some(features),
);
out.push(item_mod);
}
quote::quote! {
#(#out)*
}
.into()
}
fn process_item(attributes: &syn::LitStr, item: syn::Item, strict: bool) -> TokenStream {
match item {
syn::Item::Fn(function) => process_function(attributes, function, None),
syn::Item::Impl(item_impl) => process_impl(attributes, item_impl),
syn::Item::Mod(item_mod) => process_mod(attributes, item_mod, None).into(),
item => {
if strict {
unsupported!(item)
} else {
quote::quote! { #item }.into()
}
}
}
}
fn process_mod(
attributes: &syn::LitStr,
mut item_mod: syn::ItemMod,
spec_features: Option<Vec<String>>,
) -> TokenStream2 {
if let Some((_, ref mut content)) = item_mod.content {
'next_item: for item in content {
if let Some(ref spec_features) = spec_features {
match item {
syn::Item::Const(syn::ItemConst { ref mut attrs, .. })
| syn::Item::Enum(syn::ItemEnum { ref mut attrs, .. })
| syn::Item::ExternCrate(syn::ItemExternCrate { ref mut attrs, .. })
| syn::Item::Fn(syn::ItemFn { ref mut attrs, .. })
| syn::Item::ForeignMod(syn::ItemForeignMod { ref mut attrs, .. })
| syn::Item::Impl(syn::ItemImpl { ref mut attrs, .. })
| syn::Item::Macro(syn::ItemMacro { ref mut attrs, .. })
| syn::Item::Mod(syn::ItemMod { ref mut attrs, .. })
| syn::Item::Static(syn::ItemStatic { ref mut attrs, .. })
| syn::Item::Struct(syn::ItemStruct { ref mut attrs, .. })
| syn::Item::Trait(syn::ItemTrait { ref mut attrs, .. })
| syn::Item::TraitAlias(syn::ItemTraitAlias { ref mut attrs, .. })
| syn::Item::Type(syn::ItemType { ref mut attrs, .. })
| syn::Item::Union(syn::ItemUnion { ref mut attrs, .. })
| syn::Item::Use(syn::ItemUse { ref mut attrs, .. }) => {
let mut index = 0;
while index < attrs.len() {
let attr = &attrs[index];
if matches!(attr.style, syn::AttrStyle::Outer) {
match attr.meta {
syn::Meta::List(ref list)
if is_path_eq(&list.path, "for_target_feature") =>
{
let feature: syn::LitStr = match list.parse_args() {
Ok(feature) => feature,
Err(error) => {
return error.into_compile_error();
}
};
let feature = feature.value();
if !spec_features.contains(&feature) {
*item = syn::Item::Verbatim(Default::default());
continue 'next_item;
}
attrs.remove(index);
continue;
}
_ => {}
}
}
index += 1;
continue;
}
}
_ => {
unsupported!(item_mod);
}
}
}
*item = syn::Item::Verbatim(
process_item(
attributes,
std::mem::replace(item, syn::Item::Verbatim(Default::default())),
false,
)
.into(),
);
}
}
quote::quote! {
#item_mod
}
}
fn process_impl(attributes: &syn::LitStr, mut item_impl: syn::ItemImpl) -> TokenStream {
unsupported_if_some!(item_impl.defaultness);
unsupported_if_some!(item_impl.unsafety);
let mut items = Vec::new();
for item in item_impl.items.drain(..) {
match item {
syn::ImplItem::Fn(function) => {
unsupported_if_some!(function.defaultness);
let function = syn::ItemFn {
attrs: function.attrs,
vis: function.vis,
sig: function.sig,
block: Box::new(function.block),
};
let output_item = process_function(
attributes,
function,
Some((item_impl.generics.clone(), item_impl.self_ty.clone())),
);
items.push(syn::ImplItem::Verbatim(output_item.into()));
}
item => items.push(item),
}
}
item_impl.items = items;
quote::quote! {
#item_impl
}
.into()
}
fn is_path_eq(path: &syn::Path, ident: &str) -> bool {
let segments: Vec<_> = ident.split("::").collect();
path.segments.len() == segments.len()
&& path
.segments
.iter()
.zip(segments.iter())
.all(|(segment, expected)| segment.ident == expected && segment.arguments.is_none())
}
fn process_function(
attributes: &syn::LitStr,
function: syn::ItemFn,
outer: Option<(syn::Generics, Box<syn::Type>)>,
) -> TokenStream {
if function.sig.unsafety.is_some() {
return quote::quote! {
#[target_feature(enable = #attributes)]
#function
}
.into();
}
unsupported_if_some!(function.sig.constness);
unsupported_if_some!(function.sig.asyncness);
unsupported_if_some!(function.sig.abi);
unsupported_if_some!(function.sig.variadic);
let function_visibility = function.vis;
let function_name = function.sig.ident;
let function_return = function.sig.output;
let function_inner_name =
syn::Ident::new(&format!("_impl_{}", function_name), function_name.span());
let function_args = function.sig.inputs;
let function_body = function.block;
let mut function_call_args = Vec::new();
let mut function_args_outer = Vec::new();
let mut function_args_inner = Vec::new();
for (index, arg) in function_args.iter().enumerate() {
match arg {
syn::FnArg::Receiver(receiver) => {
unsupported_if_some!(receiver.attrs.first());
unsupported_if_some!(receiver.colon_token);
if outer.is_none() {
return syn::Error::new(receiver.span(), "unsupported by #[unsafe_target_feature(...)]; put the attribute on the outer `impl`").into_compile_error().into();
}
function_args_inner.push(syn::FnArg::Receiver(receiver.clone()));
function_args_outer.push(syn::FnArg::Receiver(receiver.clone()));
function_call_args.push(syn::Ident::new("self", receiver.self_token.span()));
}
syn::FnArg::Typed(ty) => {
unsupported_if_some!(ty.attrs.first());
match &*ty.pat {
syn::Pat::Ident(pat_ident) => {
unsupported_if_some!(pat_ident.attrs.first());
function_args_inner.push(arg.clone());
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
attrs: Vec::new(),
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: Vec::new(),
by_ref: None,
mutability: None,
ident: pat_ident.ident.clone(),
subpat: None,
})),
colon_token: ty.colon_token,
ty: ty.ty.clone(),
}));
function_call_args.push(pat_ident.ident.clone());
}
syn::Pat::Wild(pat_wild) => {
unsupported_if_some!(pat_wild.attrs.first());
let ident = syn::Ident::new(
&format!("__arg_{}__", index),
pat_wild.underscore_token.span(),
);
function_args_inner.push(arg.clone());
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
attrs: Vec::new(),
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: Vec::new(),
by_ref: None,
mutability: None,
ident: ident.clone(),
subpat: None,
})),
colon_token: ty.colon_token,
ty: ty.ty.clone(),
}));
function_call_args.push(ident);
}
_ => unsupported!(arg),
}
}
}
}
let mut maybe_inline = quote::quote! {};
let mut maybe_outer_attributes = Vec::new();
let mut maybe_cfg = quote::quote! {};
for attribute in function.attrs {
match &attribute.meta {
syn::Meta::Path(path) if is_path_eq(path, "inline") => {
maybe_inline = quote::quote! { #[inline] };
}
syn::Meta::Path(path) if is_path_eq(path, "test") => {
maybe_outer_attributes.push(attribute);
maybe_cfg = quote::quote! { #[cfg(target_feature = #attributes)] };
}
syn::Meta::List(syn::MetaList { path, tokens, .. })
if is_path_eq(path, "inline") && tokens.to_string() == "always" =>
{
maybe_inline = quote::quote! { #[inline] };
}
syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if is_path_eq(path, "doc") => {
maybe_outer_attributes.push(attribute);
}
syn::Meta::List(syn::MetaList { path, .. })
if is_path_eq(path, "cfg")
|| is_path_eq(path, "allow")
|| is_path_eq(path, "deny") =>
{
maybe_outer_attributes.push(attribute);
}
syn::Meta::Path(path) if is_path_eq(path, "rustfmt::skip") => {
maybe_outer_attributes.push(attribute);
}
_ => unsupported!(attribute),
}
}
let (fn_impl_generics, fn_ty_generics, fn_where_clause) =
function.sig.generics.split_for_impl();
let fn_call_generics = fn_ty_generics.as_turbofish();
if let Some((generics, self_ty)) = outer {
let (outer_impl_generics, outer_ty_generics, outer_where_clause) =
generics.split_for_impl();
let trait_ident =
syn::Ident::new(&format!("__Impl_{}__", function_name), function_name.span());
let item_trait = quote::quote! {
#[allow(non_camel_case_types)]
trait #trait_ident #outer_impl_generics #outer_where_clause {
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause;
}
};
let item_trait_impl = quote::quote! {
impl #outer_impl_generics #trait_ident #outer_ty_generics for #self_ty #outer_where_clause {
#[target_feature(enable = #attributes)]
#maybe_inline
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
}
};
quote::quote! {
#[inline(always)]
#(#maybe_outer_attributes)*
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
#item_trait
#item_trait_impl
unsafe {
<Self as #trait_ident #outer_ty_generics> ::#function_inner_name #fn_call_generics (#(#function_call_args),*)
}
}
}.into()
} else {
quote::quote! {
#[inline(always)]
#maybe_cfg
#(#maybe_outer_attributes)*
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
#[target_feature(enable = #attributes)]
#maybe_inline
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
unsafe {
#function_inner_name #fn_call_generics (#(#function_call_args),*)
}
}
}.into()
}
}

View file

@ -1,152 +0,0 @@
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#![allow(dead_code)]
#![allow(unused_imports)]
use curve25519_derive::{unsafe_target_feature, unsafe_target_feature_specialize};
#[unsafe_target_feature("sse2")]
/// A doc comment.
fn function(a: u32, b: u32) -> u32 {
a - b
}
#[unsafe_target_feature("sse2")]
fn function_with_const_arg<const N: u32>(b: u32) -> u32 {
N - b
}
#[unsafe_target_feature("sse2")]
fn function_with_where_clause<T>(a: T, b: T) -> T::Output
where
T: Copy + core::ops::Sub,
{
a - b
}
#[unsafe_target_feature("sse2")]
#[rustfmt::skip]
fn function_with_rustfmt_skip() {}
struct Struct {
a: u32,
}
#[unsafe_target_feature("sse2")]
impl Struct {
#[allow(unused_mut)]
fn member_function(&self, mut b: u32) -> u32 {
self.a - b
}
fn member_function_with_const_arg<const N: u32>(self) -> u32 {
self.a - N
}
}
struct StructWithGenerics<T>
where
T: Copy + core::ops::Sub,
{
a: T,
}
#[unsafe_target_feature("sse2")]
impl<T> StructWithGenerics<T>
where
T: Copy + core::ops::Sub,
{
#[inline]
fn member_function(&self, b: T) -> T::Output {
self.a - b
}
}
struct StructWithGenericsNoWhere<T: Copy + core::ops::Sub> {
a: T,
}
#[unsafe_target_feature("sse2")]
impl<T: Copy + core::ops::Sub> StructWithGenericsNoWhere<T> {
#[inline(always)]
fn member_function(&self, b: T) -> T::Output {
self.a - b
}
}
#[unsafe_target_feature("sse2")]
#[allow(dead_code)]
impl<'a> From<&'a Struct> for () {
fn from(_: &'a Struct) -> Self {}
}
#[unsafe_target_feature("sse2")]
mod inner {
fn inner_function(a: u32, b: u32) -> u32 {
a - b
}
}
#[unsafe_target_feature_specialize("sse2", "avx2")]
mod inner_spec {
#[for_target_feature("sse2")]
const CONST: u32 = 1;
#[for_target_feature("avx2")]
const CONST: u32 = 2;
pub fn spec_function(a: u32, b: u32) -> u32 {
a - b - CONST
}
#[for_target_feature("sse2")]
const IS_AVX2: bool = false;
#[for_target_feature("avx2")]
const IS_AVX2: bool = true;
#[test]
fn test_specialized() {
#[allow(clippy::assertions_on_constants)]
{
assert!(!IS_AVX2);
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_specialized_inner() {
#[allow(clippy::assertions_on_constants)]
{
assert!(!super::IS_AVX2);
}
}
}
}
#[unsafe_target_feature("sse2")]
#[test]
fn test_sse2_only() {}
// it turns out that for compilation to succeed, the feature needs be supported by rustc. For this
// test actually verify what happens when the target_feature is not enabled, this needs to be a
// pretty esoteric feature. Looking at the table of supported avx512 features at
// https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512 it seems avx512vp2intersect is one of the
// most unusual ones that has rustc knows about
#[unsafe_target_feature("avx512vp2intersect")]
#[test]
fn test_unset_target_feature() {
compile_error!("When an unknown target_feature is set on a test, unsafe_target_feature is expected remove the function");
}
#[test]
fn test_function() {
assert_eq!(function(10, 3), 7);
assert_eq!(function_with_where_clause(10, 3), 7);
assert_eq!(function_with_const_arg::<10>(3), 7);
assert_eq!(Struct { a: 10 }.member_function(3), 7);
assert_eq!(StructWithGenerics { a: 10 }.member_function(3), 7);
assert_eq!(StructWithGenericsNoWhere { a: 10 }.member_function(3), 7);
assert_eq!(inner_spec_sse2::spec_function(10, 3), 6);
assert_eq!(inner_spec_avx2::spec_function(10, 3), 5);
}

View file

@ -1,136 +0,0 @@
# 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.

File diff suppressed because it is too large Load diff

View file

@ -1,79 +0,0 @@
[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

View file

@ -1,44 +0,0 @@
## 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.

View file

@ -1,203 +0,0 @@
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.

View file

@ -1,20 +0,0 @@
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.

View file

@ -1,139 +0,0 @@
# 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

View file

@ -1,5 +1,5 @@
[package]
name = "solana-curve25519"
name = "solana-ed25519"
version = "0.1.0"
edition = "2024"
rust-version = "1.85.0"
@ -30,7 +30,6 @@ rustdoc-args = [
all-features = true
[dev-dependencies]
sha2 = { version = "0.11.0-rc.3", default-features = false }
bincode = "1"
criterion = { version = "0.5", features = ["html_reports"] }
hex = "0.4.2"
@ -43,39 +42,46 @@ rustc_version = "0.4.0"
[[bench]]
name = "dalek_benchmarks"
harness = false
required-features = ["alloc", "rand_core"]
required-features = ["alloc"]
[dependencies]
cfg-if = "1"
ed25519 = { version = "2.2.3", default-features = false }
ff = { version = "=0.14.0-pre.0", package = "rustcrypto-ff", default-features = false, optional = true }
group = { version = "=0.14.0-pre.0", package = "rustcrypto-group", default-features = false, optional = true }
rand_core = { version = "0.10.0-rc-3", default-features = false, optional = true }
digest = { version = "0.11.0-rc.4", default-features = false, optional = true, features = [
"block-api",
] }
hashbrown = { version = "0.15" }
der = { version = "0.7.9", optional = true }
pkcs8 = { version = "0.10.1", optional = true, features = ["pem"] }
sha2 = { version = "0.11.0-rc.3", default-features = false }
subtle = { version = "2.6.0", default-features = false, features = [
"const-generics",
] }
serde = { version = "1.0", default-features = false, optional = true, features = [
"derive",
] }
zeroize = { version = "1", default-features = false, optional = true }
zeroize = { version = "1", default-features = false, optional = true, features = ["derive"] }
# Keep the dependency key as "curve25519-derive" so existing #[curve25519_derive::…]
# attributes in source files remain valid; the actual package is curve25519-derive-sol.
[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2.17"
curve25519-derive = { path = "../curve25519-derive", package = "solana-curve25519-derive" }
curve25519-dalek-derive = "0.1.1"
[features]
default = ["alloc", "precomputed-tables", "zeroize"]
default = ["alloc", "precomputed-tables", "zeroize", "rand_core", "digest"]
alloc = ["zeroize?/alloc"]
precomputed-tables = []
legacy_compatibility = []
rand_core = ["dep:rand_core"]
std = ["alloc"]
group = ["dep:group", "rand_core"]
group-bits = ["group", "ff/bits"]
digest = ["dep:digest"]
lizard = ["digest"]
pkcs8 = ["dep:pkcs8", "ed25519/pkcs8", "alloc"]
pem = ["pkcs8", "dep:der", "ed25519/pem"]
[lints.rust.unexpected_cfgs]
level = "warn"

View file

@ -48,12 +48,86 @@ have been removed.
---
## Ed25519 Signatures (`ed_sigs`)
This crate includes a **ZIP-215-compliant Ed25519 signature implementation** in the
`ed_sigs` module, forked from [ed25519-zebra] and extended with HEEA-accelerated
verification.
> For the original ed25519-zebra documentation see [README_zebra.md](README_zebra.md).
### `verify_heea`: fast-path signature verification
A new method `VerificationKey::verify_heea` sits alongside the existing `verify`.
Both accept the same arguments and produce identical results — `verify_heea` is a
**drop-in accelerated replacement** for `verify`.
The HEEA method (TCHES 2025) 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.
### 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 [ZIP 215] for full details.
---
## Use
```toml
curve25519-sol = { git = "https://github.com/zz-sol/ed25519-sol" }
```
### Ed25519 signing and verification
```rust,no_run
use core::convert::TryFrom;
use curve25519::ed_sigs::{SigningKey, VerificationKey};
let msg = b"curve25519-sol";
// Generate key and sign
let sk = SigningKey::new(rand::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
```rust,ignore
use curve25519::ed_sigs::batch;
let mut verifier = batch::Verifier::new();
for (vk_bytes, sig, msg) in items {
verifier.queue((vk_bytes, sig, msg));
}
verifier.verify(rand::rng()).expect("all valid");
```
### HEEA decomposition example
```rust,ignore
@ -73,16 +147,17 @@ let (rho, tau, flip_h) = h.heea_decompose();
## Feature Flags
The feature flags are inherited from upstream with no additions:
| Feature | Default? | Description |
|---|:---:|---|
| `alloc` | ✓ | Multiscalar multiplication, batch inversion, batch compress. |
| `alloc` | ✓ | Multiscalar multiplication, batch inversion, batch compress, batch Ed25519 verification. |
| `zeroize` | ✓ | `Zeroize` for all scalar and point types. |
| `precomputed-tables` | ✓ | Precomputed basepoint tables (~400 KB, ~4× faster basepoint mul). |
| `rand_core` | | `Scalar::random`, `RistrettoPoint::random`. |
| `digest` | | Hash-to-curve and `Scalar::from_hash`. |
| `serde` | | Serialization for all point and scalar types. |
| `rand_core` | ✓ | `Scalar::random`, `RistrettoPoint::random`, `SigningKey::new`. |
| `digest` | ✓ | Hash-to-curve, `Scalar::from_hash`, and Ed25519 hashing. |
| `std` | | Enables `std::error::Error` impl on `ed_sigs::Error`. |
| `serde` | | Serialization for all point, scalar, and key types. |
| `pkcs8` | | PKCS#8 DER encoding/decoding for Ed25519 keys. |
| `pem` | | PEM encoding/decoding for Ed25519 keys (requires `pkcs8`). |
| `legacy_compatibility` | | `Scalar::from_bits` (broken arithmetic, use only if required). |
| `group` | | `group` and `ff` crate trait impls. |
| `group-bits` | | `ff::PrimeFieldBits` for `Scalar`. |
@ -135,10 +210,15 @@ Rust **1.85.0** (Edition 2024).
## References
- [TCHES 2025 paper] _Accelerating EdDSA Signature Verification with Faster Scalar Size Halving_
- [curve25519-dalek] upstream library (isis lovecruft, Henry de Valence)
- [curve25519-dalek] upstream curve25519 library (isis lovecruft, Henry de Valence)
- [ed25519-zebra] upstream Ed25519 library (Zcash Foundation)
- [ZIP 215] Ed25519 validation rules for Zcash
- [Original curve25519-dalek README](README_dalek.md)
- [Original ed25519-zebra README](README_zebra.md)
[TCHES 2025 paper]: https://tches.iacr.org/index.php/TCHES/article/view/11971
[curve25519-dalek]: https://github.com/dalek-cryptography/curve25519-dalek
[ed25519-zebra]: https://github.com/ZcashFoundation/ed25519-zebra
[ZIP 215]: https://zips.z.cash/zip-0215
[SPPARK]: https://github.com/supranational/sppark
[subtle]: https://docs.rs/subtle

View file

@ -1,12 +1,10 @@
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use core::convert::TryFrom;
use ed25519_heea::*;
use rand::thread_rng;
use curve25519::ed_sigs::*;
fn sigs_with_distinct_pubkeys() -> impl Iterator<Item = (VerificationKeyBytes, Signature)> {
std::iter::repeat_with(|| {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let pk_bytes = VerificationKeyBytes::from(&sk);
let sig = sk.sign(b"");
(pk_bytes, sig)
@ -14,7 +12,7 @@ fn sigs_with_distinct_pubkeys() -> impl Iterator<Item = (VerificationKeyBytes, S
}
fn sigs_with_same_pubkey() -> impl Iterator<Item = (VerificationKeyBytes, Signature)> {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let pk_bytes = VerificationKeyBytes::from(&sk);
std::iter::repeat_with(move || {
let sig = sk.sign(b"");
@ -30,11 +28,11 @@ fn bench_batch_verify(c: &mut Criterion) {
group.bench_with_input(
BenchmarkId::new("Unbatched verification", n),
&sigs,
|b, sigs| {
|b, sigs: &Vec<(VerificationKeyBytes, Signature)>| {
b.iter(|| {
for (vk_bytes, sig) in sigs.iter() {
let _ =
VerificationKey::try_from(*vk_bytes).and_then(|vk| vk.verify(sig, b""));
let _ = VerificationKey::try_from(*vk_bytes)
.and_then(|vk: VerificationKey| vk.verify(sig, b""));
}
})
},
@ -43,13 +41,13 @@ fn bench_batch_verify(c: &mut Criterion) {
group.bench_with_input(
BenchmarkId::new("Signatures with Distinct Pubkeys", n),
&sigs,
|b, sigs| {
|b, sigs: &Vec<(VerificationKeyBytes, Signature)>| {
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())
batch.verify(rand::rng())
})
},
);
@ -59,13 +57,13 @@ fn bench_batch_verify(c: &mut Criterion) {
group.bench_with_input(
BenchmarkId::new("Signatures with the Same Pubkey", n),
&sigs,
|b, sigs| {
|b, sigs: &Vec<(VerificationKeyBytes, Signature)>| {
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())
batch.verify(rand::rng())
})
},
);
@ -77,7 +75,7 @@ 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 sk = SigningKey::new(rand::rng());
let vk = VerificationKey::from(&sk);
let sig = sk.sign(b"");
b.iter(|| {
@ -86,7 +84,7 @@ fn bench_single_verify(c: &mut Criterion) {
});
group.bench_function("ed25519_hEEA", |b| {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let vk = VerificationKey::from(&sk);
let sig = sk.sign(b"");
b.iter(|| {

View file

@ -1,6 +1,7 @@
#![allow(non_snake_case)]
use rand::{RngCore, TryRngCore, rng, rngs::SysRng};
use rand::rng;
use rand_core::Rng as RngCore;
use criterion::{
BatchSize, BenchmarkGroup, BenchmarkId, Criterion, criterion_main, measurement::Measurement,
@ -31,10 +32,10 @@ mod edwards_benches {
BenchmarkId::new("Batch EdwardsPoint compression", batch_size),
&batch_size,
|b, &size| {
let mut rng = SysRng.unwrap_err();
let mut rng = rng();
let points: Vec<EdwardsPoint> =
(0..size).map(|_| EdwardsPoint::random(&mut rng)).collect();
b.iter(|| EdwardsPoint::compress_batch_alloc(&points));
b.iter(|| EdwardsPoint::compress_batch(&points));
},
);
}
@ -301,9 +302,9 @@ mod ristretto_benches {
BenchmarkId::new("Batch Ristretto double-and-encode", *batch_size),
&batch_size,
|b, &&size| {
let mut rng = SysRng;
let mut rng = rng();
let points: Vec<RistrettoPoint> = (0..size)
.map(|_| RistrettoPoint::try_from_rng(&mut rng).unwrap())
.map(|_| RistrettoPoint::random(&mut rng))
.collect();
b.iter(|| RistrettoPoint::double_and_compress_batch(&points));
},
@ -388,7 +389,7 @@ mod scalar_benches {
BenchmarkId::new("Batch scalar inversion", *batch_size),
&batch_size,
|b, &&size| {
let mut rng = SysRng.unwrap_err();
let mut rng = rng();
let scalars: Vec<Scalar> =
(0..size).map(|_| Scalar::random(&mut rng)).collect();
b.iter(|| {

View file

@ -40,7 +40,7 @@ use core::ops::{Add, Neg, Sub};
use subtle::Choice;
use subtle::ConditionallySelectable;
use curve25519_derive::unsafe_target_feature;
use curve25519_dalek_derive::unsafe_target_feature;
use crate::edwards;
use crate::window::{LookupTable, NafLookupTable5};

View file

@ -53,7 +53,7 @@ use crate::backend::vector::avx2::constants::{
P_TIMES_2_HI, P_TIMES_2_LO, P_TIMES_16_HI, P_TIMES_16_LO,
};
use curve25519_derive::unsafe_target_feature;
use curve25519_dalek_derive::unsafe_target_feature;
/// Unpack 32-bit lanes into 64-bit lanes:
/// ```ascii,no_run

View file

@ -16,7 +16,7 @@
//! by the callers of this code.
use core::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Sub};
use curve25519_derive::unsafe_target_feature;
use curve25519_dalek_derive::unsafe_target_feature;
macro_rules! impl_shared {
(

View file

@ -9,7 +9,7 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
use alloc::vec::Vec;

View file

@ -11,7 +11,7 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
use alloc::vec::Vec;

View file

@ -11,7 +11,7 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
use alloc::vec::Vec;

View file

@ -1,6 +1,6 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
#[for_target_feature("avx2")]

View file

@ -11,7 +11,7 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
use core::cmp::Ordering;

View file

@ -7,7 +7,7 @@
#![allow(non_snake_case)]
#[curve25519_derive::unsafe_target_feature_specialize("avx2")]
#[curve25519_dalek_derive::unsafe_target_feature_specialize("avx2")]
pub mod spec {
use core::cmp::Ordering;

View file

@ -1,11 +1,7 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
//! Ed25519 signing and verification (ZIP-215 / HEEA-accelerated).
//! Docs require the `nightly` feature until RFC 1990 lands.
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(test)]
mod tests;
#[cfg(feature = "alloc")]
pub mod batch;
@ -14,8 +10,8 @@ mod signing_key;
mod verification_key;
// Allows importing traits used by `Signature`.
pub use ed25519;
pub use ed25519::Signature;
pub use ::ed25519;
pub use ::ed25519::Signature;
pub use error::Error;
pub use signing_key::SigningKey;
pub use verification_key::{VerificationKey, VerificationKeyBytes};

View file

@ -34,16 +34,16 @@
//!
//! # Example
//! ```
//! # use ed25519_heea::*;
//! # use curve25519::ed_sigs::*;
//! let mut batch = batch::Verifier::new();
//! for _ in 0..32 {
//! let sk = SigningKey::new(rand::thread_rng());
//! let sk = SigningKey::new(rand::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());
//! assert!(batch.verify(rand::rng()).is_ok());
//! ```
//!
//! [ZIP215]: https://zips.z.cash/zip-0215
@ -51,20 +51,20 @@
use alloc::vec::Vec;
use core::convert::TryFrom;
use curve25519::{
use crate::{
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
traits::{IsIdentity, VartimeMultiscalarMul},
};
use hashbrown::HashMap;
use rand_core::{CryptoRng, RngCore};
use rand_core::{CryptoRng, Rng};
use sha2::{Sha512, digest::Update};
use crate::{Error, VerificationKey, VerificationKeyBytes};
use super::{Error, VerificationKey, VerificationKeyBytes};
use ed25519::Signature;
// Shim to generate a u128 without importing `rand`.
fn gen_u128<R: RngCore + CryptoRng>(mut rng: R) -> u128 {
fn gen_u128<R: Rng + CryptoRng>(mut rng: R) -> u128 {
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes[..]);
u128::from_le_bytes(bytes)
@ -142,7 +142,7 @@ impl Verifier {
/// 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> {
pub fn verify<R: Rng + 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.
@ -199,8 +199,8 @@ impl Verifier {
A_coeffs.push(A_coeff);
}
use crate::constants::ED25519_BASEPOINT_POINT as B;
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()),

View file

@ -6,14 +6,14 @@ const ALGORITHM_ID: AlgorithmIdentifierRef = AlgorithmIdentifierRef {
parameters: None,
};
use crate::Error;
use super::Error;
use crate::{constants, scalar::Scalar};
#[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 rand_core::{CryptoRng, Rng};
use sha2::{Digest, Sha512, digest::Update};
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
@ -40,7 +40,7 @@ use zeroize::Zeroizing;
#[cfg(all(feature = "pem", feature = "pkcs8"))]
use pkcs8::der::pem::PemLabel;
use crate::{VerificationKey, VerificationKeyBytes};
use super::{VerificationKey, VerificationKeyBytes};
/// The length of a ed25519 `SecretKey`, in bytes.
pub const SECRET_KEY_LENGTH: usize = 32;
@ -68,7 +68,7 @@ pub struct SigningKey {
}
impl core::fmt::Debug for SigningKey {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fmt.debug_struct("SigningKey")
.field("vk", &self.vk)
.finish()
@ -317,7 +317,7 @@ impl SigningKey {
}
/// Generate a new signing key.
pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> SigningKey {
pub fn new<R: Rng + CryptoRng>(mut rng: R) -> SigningKey {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes[..]);
bytes.into()

View file

@ -0,0 +1,9 @@
pub mod util;
mod batch;
mod decoding;
mod encoding;
mod heea;
mod rfc8032;
mod small_order;
mod unit_tests;

View file

@ -1,20 +1,19 @@
#![cfg(feature = "alloc")]
use rand::thread_rng;
use ed25519_heea::*;
use crate::ed_sigs::*;
use alloc::vec::Vec;
#[test]
fn batch_verify() {
let mut batch = batch::Verifier::new();
for _ in 0..32 {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::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());
assert!(batch.verify(rand::rng()).is_ok());
}
#[test]
@ -23,7 +22,7 @@ fn batch_verify_with_one_bad_sig() {
let mut batch = batch::Verifier::new();
let mut items = Vec::new();
for i in 0..32 {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let pk_bytes = VerificationKeyBytes::from(&sk);
let msg = b"BatchVerifyTest";
let sig = if i != bad_index {
@ -35,7 +34,7 @@ fn batch_verify_with_one_bad_sig() {
items.push(item.clone());
batch.queue(item);
}
assert!(batch.verify(thread_rng()).is_err());
assert!(batch.verify(rand::rng()).is_err());
for (i, item) in items.drain(..).enumerate() {
if i != bad_index {
assert!(item.verify_single().is_ok());

View file

@ -1,5 +1,5 @@
#[cfg(any(feature = "pem", feature = "pkcs8"))]
use ed25519_heea::*;
use crate::ed_sigs::*;
#[cfg(feature = "pkcs8")]
use pkcs8::{DecodePrivateKey, DecodePublicKey};

View file

@ -1,7 +1,7 @@
#[cfg(any(feature = "pem", feature = "pkcs8"))]
use crate::ed_sigs::*;
#[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,

View file

@ -1,11 +1,9 @@
use ed25519_heea::SigningKey;
use ed25519_heea::VerificationKey;
use rand::thread_rng;
use crate::ed_sigs::SigningKey;
use crate::ed_sigs::VerificationKey;
#[test]
fn test_verify_heea_invalid_signature() {
let mut rng = thread_rng();
let mut rng = rand::rng();
let signing_key = SigningKey::new(&mut rng);
let verification_key = VerificationKey::from(&signing_key);
@ -31,7 +29,7 @@ fn test_verify_heea_invalid_signature() {
#[test]
fn test_verify_heea_multiple_signatures() {
let mut rng = thread_rng();
let mut rng = rand::rng();
for i in 0..100 {
let signing_key = SigningKey::new(&mut rng);

View file

@ -5,8 +5,8 @@
//! in consensus.rs.
#![cfg(feature = "serde")]
use crate::ed_sigs::*;
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");

View file

@ -1,14 +1,14 @@
#![cfg(feature = "std")]
use color_eyre::Report;
use curve25519::{
use crate::{
constants::EIGHT_TORSION, digest::Update, edwards::CompressedEdwardsY, scalar::Scalar,
traits::IsIdentity,
};
use color_eyre::Report;
use once_cell::sync::Lazy;
use sha2::Sha512;
mod util;
use super::util;
use util::TestCase;
#[allow(non_snake_case)]
@ -86,9 +86,9 @@ fn conformance() -> Result<(), Report> {
#[cfg(feature = "alloc")]
#[test]
fn individual_matches_batch_verification() -> Result<(), Report> {
use crate::ed_sigs::{VerificationKey, VerificationKeyBytes, batch};
use core::convert::TryFrom;
use ed25519::Signature;
use ed25519_heea::{VerificationKey, VerificationKeyBytes, batch};
for case in SMALL_ORDER_SIGS.iter() {
let msg = b"Zcash";
@ -98,7 +98,7 @@ fn individual_matches_batch_verification() -> Result<(), Report> {
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());
let batch_verification = bv.verify(rand::rng());
assert_eq!(individual_verification.is_ok(), batch_verification.is_ok());
}
Ok(())

View file

@ -2,13 +2,11 @@
use core::convert::TryFrom;
use rand::thread_rng;
use ed25519_heea::{Signature, SigningKey, VerificationKey, VerificationKeyBytes};
use crate::ed_sigs::{Signature, SigningKey, VerificationKey, VerificationKeyBytes};
#[test]
fn parsing() {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let pk = VerificationKey::from(&sk);
let pkb = VerificationKeyBytes::from(&sk);
let sig = sk.sign(b"test");
@ -39,7 +37,7 @@ fn parsing() {
#[test]
fn sign_and_verify() {
let sk = SigningKey::new(thread_rng());
let sk = SigningKey::new(rand::rng());
let pk = VerificationKey::from(&sk);
let msg = b"ed25519-zebra test message";

View file

@ -2,9 +2,9 @@
#![allow(dead_code)]
#![cfg(feature = "std")]
use crate::ed_sigs as ed25519_heea_zip215;
use crate::edwards::{CompressedEdwardsY, EdwardsPoint};
use color_eyre::{Report, eyre::eyre};
use curve25519::edwards::{CompressedEdwardsY, EdwardsPoint};
use ed25519_heea as ed25519_heea_zip215;
use core::convert::TryFrom;
@ -170,7 +170,7 @@ fn print_non_canonical_points() {
}
pub fn order(point: EdwardsPoint) -> &'static str {
use curve25519::traits::IsIdentity;
use crate::traits::IsIdentity;
if point.is_small_order() {
let point2 = point + point;
let point4 = point2 + point2;

View file

@ -12,12 +12,12 @@
// 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::{
use crate::{
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
traits::{HEEADecomposition, IsIdentity},
};
use core::convert::{TryFrom, TryInto};
use sha2::{Sha512, digest::Update};
use zeroize::DefaultIsZeroes;
@ -32,7 +32,7 @@ use pkcs8::spki::{
#[cfg(feature = "pkcs8")]
use pkcs8::{Document, ObjectIdentifier};
use crate::Error;
use super::Error;
/// The length of an ed25519 `VerificationKey`, in bytes.
pub const VERIFICATION_KEY_LENGTH: usize = 32;
@ -48,10 +48,9 @@ pub const VERIFICATION_KEY_LENGTH: usize = 32;
/// following idiom:
/// ```
/// use core::convert::TryFrom;
/// # use rand::thread_rng;
/// # use ed25519_heea::*;
/// # use curve25519::ed_sigs::*;
/// # let msg = b"Zcash";
/// # let sk = SigningKey::new(thread_rng());
/// # let sk = SigningKey::new(rand::rng());
/// # let sig = sk.sign(msg);
/// # let vk_bytes = VerificationKeyBytes::from(&sk);
/// VerificationKey::try_from(vk_bytes)
@ -62,7 +61,7 @@ pub const VERIFICATION_KEY_LENGTH: usize = 32;
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 {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fmt.debug_tuple("VerificationKeyBytes")
.field(&self.0)
.finish()

View file

@ -113,12 +113,12 @@ use digest::{
#[cfg(feature = "group")]
use {
group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup},
rand_core::TryRngCore,
rand_core::TryRng,
subtle::CtOption,
};
#[cfg(feature = "rand_core")]
use rand_core::RngCore;
use rand_core::Rng as RngCore;
use subtle::Choice;
use subtle::ConditionallyNegatable;
@ -1481,7 +1481,7 @@ impl Debug for EdwardsPoint {
impl group::Group for EdwardsPoint {
type Scalar = Scalar;
fn try_from_rng<R: TryRngCore + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
fn try_from_rng<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
let mut repr = CompressedEdwardsY([0u8; 32]);
loop {
rng.try_fill_bytes(&mut repr.0)?;
@ -1733,7 +1733,7 @@ impl Zeroize for SubgroupPoint {
impl group::Group for SubgroupPoint {
type Scalar = Scalar;
fn try_from_rng<R: TryRngCore + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
fn try_from_rng<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
use group::ff::Field;
// This will almost never loop, but `Group::random` is documented as returning a

View file

@ -78,6 +78,12 @@ pub mod constants;
// External (and internal) traits.
pub mod traits;
//------------------------------------------------------------------------
// ed25519 public modules
//------------------------------------------------------------------------
pub mod ed_sigs;
//------------------------------------------------------------------------
// curve25519 internal modules
//------------------------------------------------------------------------

View file

@ -180,7 +180,7 @@ use crate::field::FieldElement;
#[cfg(feature = "group")]
use {
group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup},
rand_core::TryRngCore,
rand_core::TryRng,
subtle::CtOption,
};
@ -543,12 +543,11 @@ impl RistrettoPoint {
#[cfg_attr(feature = "rand_core", doc = "```")]
#[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
/// # use curve25519::ristretto::RistrettoPoint;
/// use rand::{rngs::SysRng, TryRngCore};
///
/// # // Need fn main() here in comment so the doctest compiles
/// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
/// # fn main() {
/// let mut rng = SysRng.unwrap_err();
/// let mut rng = rand::rng();
///
/// let points: Vec<RistrettoPoint> =
/// (0..32).map(|_| RistrettoPoint::random(&mut rng)).collect();
@ -1179,7 +1178,7 @@ impl Debug for RistrettoPoint {
impl group::Group for RistrettoPoint {
type Scalar = Scalar;
fn try_from_rng<R: TryRngCore + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
fn try_from_rng<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
// NOTE: this is duplicated due to different `rng` bounds
let mut uniform_bytes = [0u8; 64];
rng.try_fill_bytes(&mut uniform_bytes)?;
@ -1277,8 +1276,8 @@ mod test {
use crate::edwards::CompressedEdwardsY;
#[cfg(feature = "group")]
use proptest::prelude::*;
#[cfg(feature = "rand_core")]
use rand::{TryRngCore, rngs::SysRng};
#[cfg(all(feature = "rand_core", feature = "group"))]
use rand::rngs::SysRng;
#[test]
#[cfg(feature = "serde")]
@ -1477,7 +1476,7 @@ mod test {
#[cfg(feature = "rand_core")]
#[test]
fn four_torsion_random() {
let mut rng = SysRng.unwrap_err();
let mut rng = rand::rng();
let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
let P_coset = P.coset4();
for point in P_coset {
@ -1488,11 +1487,13 @@ mod test {
#[cfg(feature = "rand_core")]
#[test]
fn random_roundtrip() {
let mut rng = SysRng.unwrap_err();
let mut rng = rand::rng();
for _ in 0..100 {
let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
let compressed_P = P.compress();
let Q = compressed_P.decompress().unwrap();
let Q = compressed_P
.decompress()
.expect("compression round-trip should decompress");
assert_eq!(P, Q);
}
}

View file

@ -126,7 +126,7 @@ use group::ff::{Field, FromUniformBytes, PrimeField};
use group::ff::{FieldBits, PrimeFieldBits};
#[cfg(feature = "group")]
use rand_core::TryRngCore;
use rand_core::TryRng;
#[cfg(feature = "rand_core")]
use rand_core::CryptoRng;
@ -1263,7 +1263,7 @@ impl Field for Scalar {
const ZERO: Self = Self::ZERO;
const ONE: Self = Self::ONE;
fn try_from_rng<R: TryRngCore + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
fn try_from_rng<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
// NOTE: this is duplicated due to different `rng` bounds
let mut scalar_bytes = [0u8; 64];
rng.try_fill_bytes(&mut scalar_bytes)?;

View file

@ -423,7 +423,7 @@ mod tests {
use crate::{Scalar, digest::Update, traits::HEEADecomposition};
#[cfg(feature = "rand_core")]
use rand::RngCore;
use rand_core::Rng;
#[test]
#[cfg(all(feature = "rand_core", feature = "digest"))]

Some files were not shown because too many files have changed in this diff Show more