Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eccb3af10e | |||
| a7e6844b7d | |||
| db77591bb8 | |||
| 0f29c1466c | |||
| 296b4b834b | |||
| cc02fc56de | |||
| 44d74af159 | |||
| eb47d4c3ac | |||
| 40b4670d80 |
@@ -44,10 +44,8 @@ jobs:
|
||||
echo "Tag is empty"
|
||||
exit 1
|
||||
fi
|
||||
# first, list all tags for logging purposes
|
||||
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
|
||||
# check if there's a matching tag
|
||||
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
|
||||
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
|
||||
if [[ $exists = "true" ]]; then
|
||||
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
|
||||
exit 1
|
||||
@@ -55,5 +53,5 @@ jobs:
|
||||
echo "Version '$TAG' doesn't exist on the remote"
|
||||
else
|
||||
echo "Unknown output '$exists'"
|
||||
exit 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -44,10 +44,8 @@ jobs:
|
||||
echo "Tag is empty"
|
||||
exit 1
|
||||
fi
|
||||
# first, list all tags for logging purposes
|
||||
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
|
||||
# check if there's a matching tag
|
||||
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
|
||||
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
|
||||
if [[ $exists = "true" ]]; then
|
||||
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
|
||||
exit 1
|
||||
@@ -55,5 +53,5 @@ jobs:
|
||||
echo "Version '$TAG' doesn't exist on the remote"
|
||||
else
|
||||
echo "Unknown output '$exists'"
|
||||
exit 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.87
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
|
||||
|
||||
Generated
+21
-500
@@ -1298,16 +1298,6 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "classic-mceliece-rust"
|
||||
version = "3.2.0"
|
||||
source = "git+https://github.com/georgio/classic-mceliece-rust#d2367683c06330b694047132d7bd38924ac97da8"
|
||||
dependencies = [
|
||||
"rand 0.9.2",
|
||||
"sha3",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
@@ -1467,16 +1457,6 @@ version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "core-models"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"pastey",
|
||||
"rand 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmos-sdk-proto"
|
||||
version = "0.26.1"
|
||||
@@ -1898,7 +1878,6 @@ dependencies = [
|
||||
"curve25519-dalek-derive",
|
||||
"digest 0.10.7",
|
||||
"fiat-crypto",
|
||||
"rand_core 0.6.4",
|
||||
"rustc_version 0.4.1",
|
||||
"serde",
|
||||
"subtle 2.6.1",
|
||||
@@ -3162,43 +3141,6 @@ dependencies = [
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86"
|
||||
dependencies = [
|
||||
"hax-lib-macros",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1"
|
||||
dependencies = [
|
||||
"hax-lib-macros-types",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros-types"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdrhistogram"
|
||||
version = "7.5.4"
|
||||
@@ -3308,7 +3250,7 @@ dependencies = [
|
||||
"idna",
|
||||
"ipnet",
|
||||
"once_cell",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.0",
|
||||
"ring",
|
||||
"rustls 0.23.25",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3333,7 +3275,7 @@ dependencies = [
|
||||
"moka",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.0",
|
||||
"resolv-conf",
|
||||
"rustls 0.23.25",
|
||||
"smallvec",
|
||||
@@ -4130,15 +4072,6 @@ dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keystream"
|
||||
version = "1.0.0"
|
||||
@@ -4217,213 +4150,6 @@ version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-chacha20poly1305"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-poly1305",
|
||||
"libcrux-secrets",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-curve25519"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-secrets",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ecdh"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-curve25519",
|
||||
"libcrux-p256",
|
||||
"rand 0.9.2",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ed25519"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-sha2",
|
||||
"rand_core 0.9.1",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-hacl-rs"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-hkdf"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-hmac",
|
||||
"libcrux-secrets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-hmac"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-intrinsics"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"core-models",
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-kem"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-curve25519",
|
||||
"libcrux-ecdh",
|
||||
"libcrux-ml-kem",
|
||||
"libcrux-p256",
|
||||
"libcrux-sha3",
|
||||
"libcrux-traits",
|
||||
"rand 0.9.2",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-macros"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ml-kem"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
"libcrux-secrets",
|
||||
"libcrux-sha3",
|
||||
"libcrux-traits",
|
||||
"rand 0.9.2",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-p256"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-secrets",
|
||||
"libcrux-sha2",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-platform"
|
||||
version = "0.0.2"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-poly1305"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-psq"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-chacha20poly1305",
|
||||
"libcrux-ecdh",
|
||||
"libcrux-ed25519",
|
||||
"libcrux-hkdf",
|
||||
"libcrux-hmac",
|
||||
"libcrux-kem",
|
||||
"libcrux-ml-kem",
|
||||
"libcrux-sha2",
|
||||
"libcrux-traits",
|
||||
"rand 0.9.2",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-secrets"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha2"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-hacl-rs",
|
||||
"libcrux-macros",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha3"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-traits"
|
||||
version = "0.0.3"
|
||||
source = "git+https://github.com/cryspen/libcrux#7cfce8fcf0666dc016f4a8e8ed2c6260285052ac"
|
||||
dependencies = [
|
||||
"libcrux-secrets",
|
||||
"rand 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
@@ -5069,7 +4795,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.60"
|
||||
version = "1.1.61"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -5358,7 +5084,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5440,7 +5166,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -5580,7 +5306,6 @@ dependencies = [
|
||||
"nym-sphinx 0.1.0",
|
||||
"nym-task 0.1.0",
|
||||
"sqlx",
|
||||
"sqlx-pool-guard",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
@@ -5848,7 +5573,6 @@ dependencies = [
|
||||
"nym-ecash-time 0.1.0",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"sqlx-pool-guard",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"zeroize",
|
||||
@@ -6497,35 +6221,6 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-kkt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"arc-swap",
|
||||
"blake3",
|
||||
"bytes",
|
||||
"classic-mceliece-rust",
|
||||
"criterion",
|
||||
"curve25519-dalek",
|
||||
"futures",
|
||||
"libcrux-ecdh",
|
||||
"libcrux-kem",
|
||||
"libcrux-ml-kem",
|
||||
"libcrux-psq",
|
||||
"libcrux-sha3",
|
||||
"libcrux-traits",
|
||||
"nym-crypto 0.4.0",
|
||||
"pin-project",
|
||||
"rand 0.9.2",
|
||||
"strum 0.26.3",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ledger"
|
||||
version = "0.1.0"
|
||||
@@ -6743,7 +6438,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.58"
|
||||
version = "1.1.59"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6793,7 +6488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -6954,7 +6649,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "3.1.0"
|
||||
version = "3.0.0"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -7084,14 +6779,9 @@ dependencies = [
|
||||
"arc-swap",
|
||||
"bytes",
|
||||
"futures",
|
||||
"libcrux-ed25519",
|
||||
"libcrux-kem",
|
||||
"libcrux-psq",
|
||||
"nym-crypto 0.4.0",
|
||||
"nym-noise-keys",
|
||||
"nympsq",
|
||||
"pin-project",
|
||||
"rand 0.9.2",
|
||||
"rand_chacha 0.3.1",
|
||||
"sha2 0.10.9",
|
||||
"snow",
|
||||
@@ -7322,7 +7012,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -8276,24 +7966,9 @@ dependencies = [
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nympsq"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"libcrux-ed25519",
|
||||
"libcrux-kem",
|
||||
"libcrux-psq",
|
||||
"libcrux-traits",
|
||||
"nym-crypto 0.4.0",
|
||||
"rand 0.9.2",
|
||||
"thiserror 2.0.12",
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.22"
|
||||
version = "0.1.23"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -8609,12 +8284,6 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261"
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
version = "0.8.4"
|
||||
@@ -8935,7 +8604,7 @@ dependencies = [
|
||||
"hmac",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.0",
|
||||
"sha2 0.10.9",
|
||||
"stringprep",
|
||||
]
|
||||
@@ -9022,15 +8691,6 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc_pidinfo"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af53dad2390f8df98dda1e4188322bdf2f91c86cf6001f51d10d64451edf463a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.14.0"
|
||||
@@ -9186,7 +8846,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.1",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.0",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls 0.23.25",
|
||||
@@ -9240,12 +8900,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.1",
|
||||
"zerocopy 0.8.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10277,16 +9938,6 @@ dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -10627,19 +10278,6 @@ dependencies = [
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-pool-guard"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc_pidinfo",
|
||||
"sqlx",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"windows 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-postgres"
|
||||
version = "0.8.6"
|
||||
@@ -11261,27 +10899,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
|
||||
dependencies = [
|
||||
"tls_codec_derive",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec_derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.1"
|
||||
@@ -11341,7 +10958,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"postgres-protocol",
|
||||
"postgres-types",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.0",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -12675,28 +12292,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core 0.61.2",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
@@ -12731,30 +12326,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement 0.60.0",
|
||||
"windows-interface 0.59.1",
|
||||
"windows-link",
|
||||
"windows-result 0.3.4",
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
@@ -12777,17 +12348,6 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
@@ -12810,32 +12370,11 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
"windows-link",
|
||||
]
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
@@ -12843,7 +12382,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
|
||||
dependencies = [
|
||||
"windows-result 0.3.4",
|
||||
"windows-result 0.3.1",
|
||||
"windows-strings 0.3.1",
|
||||
"windows-targets 0.53.0",
|
||||
]
|
||||
@@ -12868,9 +12407,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
@@ -12894,15 +12433,6 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
@@ -13001,15 +12531,6 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
|
||||
@@ -66,10 +66,8 @@ members = [
|
||||
"common/nym-id",
|
||||
"common/nym-metrics",
|
||||
"common/nym_offline_compact_ecash",
|
||||
"common/nymkkt",
|
||||
"common/nymnoise",
|
||||
"common/nymnoise/keys",
|
||||
"common/nympsq",
|
||||
"common/nymsphinx",
|
||||
"common/nymsphinx/acknowledgements",
|
||||
"common/nymsphinx/addressing",
|
||||
@@ -128,7 +126,6 @@ members = [
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"sqlx-pool-guard",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
@@ -290,7 +287,6 @@ petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pin-project-lite = "0.2.16"
|
||||
publicsuffix = "2.3.0"
|
||||
proc_pidinfo = "0.1.3"
|
||||
quote = "1"
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::BadGateway;
|
||||
use std::{io, path::PathBuf};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -18,6 +19,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("failed to perform sqlx migration: {source}")]
|
||||
MigrationError {
|
||||
#[source]
|
||||
#[from]
|
||||
source: sqlx::migrate::MigrateError,
|
||||
},
|
||||
@@ -30,6 +32,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("failed to run the SQL query: {source}")]
|
||||
QueryError {
|
||||
#[source]
|
||||
#[from]
|
||||
source: sqlx::error::Error,
|
||||
},
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
client::replies::reply_storage::{fs_backend, CombinedReplyStorage, ReplyStorageBackend},
|
||||
config,
|
||||
config::Config,
|
||||
error::ClientCoreError,
|
||||
use crate::client::replies::reply_storage::{
|
||||
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
};
|
||||
use crate::config;
|
||||
use crate::config::Config;
|
||||
use crate::error::ClientCoreError;
|
||||
use log::{error, info, trace};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_gateways_storage::OnDiskGatewaysDetails;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient};
|
||||
use std::{io, path::Path};
|
||||
use nym_validator_client::nyxd;
|
||||
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
@@ -20,11 +22,11 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
surb_config: &config::ReplySurbs,
|
||||
) -> Result<fs_backend::Backend, ClientCoreError> {
|
||||
info!("Creating fresh surb database");
|
||||
info!("creating fresh surb database");
|
||||
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
|
||||
Ok(backend) => backend,
|
||||
Err(err) => {
|
||||
error!("setup_fresh_backend: Failed to setup persistent storage backend for our reply needs: {err}");
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}");
|
||||
return Err(ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
});
|
||||
@@ -38,15 +40,14 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
surb_config.minimum_reply_surb_storage_threshold,
|
||||
surb_config.maximum_reply_surb_storage_threshold,
|
||||
);
|
||||
match storage_backend.init_fresh(&mem_store).await {
|
||||
Ok(()) => Ok(storage_backend),
|
||||
Err(err) => {
|
||||
storage_backend.shutdown().await;
|
||||
Err(ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
storage_backend
|
||||
.init_fresh(&mem_store)
|
||||
.await
|
||||
.map_err(|err| ClientCoreError::SurbStorageError {
|
||||
source: Box::new(err),
|
||||
})?;
|
||||
|
||||
Ok(storage_backend)
|
||||
}
|
||||
|
||||
// fn setup_inactive_backend(surb_config: &config::ReplySurbs) -> fs_backend::Backend {
|
||||
@@ -57,11 +58,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
||||
// )
|
||||
// }
|
||||
|
||||
async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
|
||||
let db_path = db_path.as_ref();
|
||||
debug_assert!(db_path.exists());
|
||||
|
||||
let now = OffsetDateTime::now_utc().unix_timestamp();
|
||||
|
||||
let suffix = format!("_{now}.corrupted");
|
||||
|
||||
let new_extension =
|
||||
@@ -70,15 +72,11 @@ async fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()
|
||||
} else {
|
||||
suffix
|
||||
};
|
||||
let renamed = db_path.with_extension(new_extension);
|
||||
|
||||
tokio::fs::rename(db_path, &renamed).await.inspect_err(|_| {
|
||||
error!(
|
||||
"Failed to rename corrupt database file: {} to {}",
|
||||
db_path.display(),
|
||||
renamed.display()
|
||||
);
|
||||
})
|
||||
let mut renamed = db_path.to_owned();
|
||||
renamed.set_extension(new_extension);
|
||||
|
||||
fs::rename(db_path, renamed)
|
||||
}
|
||||
|
||||
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
@@ -89,12 +87,13 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
// the existing one
|
||||
let db_path = db_path.as_ref();
|
||||
if db_path.exists() {
|
||||
info!("Loading existing surb database");
|
||||
info!("loading existing surb database");
|
||||
match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await {
|
||||
Ok(backend) => Ok(backend),
|
||||
Err(err) => {
|
||||
error!("setup_fs_reply_surb_backend: Failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
archive_corrupted_database(db_path).await?;
|
||||
error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future");
|
||||
|
||||
archive_corrupted_database(db_path)?;
|
||||
setup_fresh_backend(db_path, surb_config).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,26 +17,15 @@ nym-crypto = { path = "../../crypto", optional = true, default-features = false
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-task = { path = "../../task" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
workspace = true
|
||||
features = ["fs"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
|
||||
path = "../../../sqlx-pool-guard"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
fs-surb-storage = ["sqlx", "nym-crypto", "nym-crypto/hashing"]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{io, path::PathBuf};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -29,6 +30,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("failed to perform sqlx migration: {source}")]
|
||||
MigrationError {
|
||||
#[source]
|
||||
#[from]
|
||||
source: sqlx::migrate::MigrateError,
|
||||
},
|
||||
@@ -41,6 +43,7 @@ pub enum StorageError {
|
||||
|
||||
#[error("failed to run the SQL query: {source}")]
|
||||
QueryError {
|
||||
#[source]
|
||||
#[from]
|
||||
source: sqlx::error::Error,
|
||||
},
|
||||
|
||||
@@ -15,11 +15,9 @@ use sqlx::{
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
use sqlx_pool_guard::SqlitePoolGuard;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StorageManager {
|
||||
connection_pool: SqlitePoolGuard,
|
||||
pub connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
// all SQL goes here
|
||||
@@ -39,7 +37,7 @@ impl StorageManager {
|
||||
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||
.synchronous(SqliteSynchronous::Normal)
|
||||
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||
.filename(&database_path)
|
||||
.filename(database_path)
|
||||
.create_if_missing(fresh)
|
||||
.disable_statement_logging();
|
||||
|
||||
@@ -51,15 +49,11 @@ impl StorageManager {
|
||||
}
|
||||
};
|
||||
|
||||
let connection_pool =
|
||||
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
|
||||
|
||||
if let Err(err) = sqlx::migrate!("./fs_surbs_migrations")
|
||||
.run(&*connection_pool)
|
||||
.run(&connection_pool)
|
||||
.await
|
||||
{
|
||||
error!("Failed to initialize SQLx database: {err}");
|
||||
connection_pool.close().await;
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
@@ -67,43 +61,38 @@ impl StorageManager {
|
||||
Ok(StorageManager { connection_pool })
|
||||
}
|
||||
|
||||
/// Close connection pool waiting for all connections to be closed.
|
||||
pub async fn close_pool(&self) {
|
||||
self.connection_pool.close().await;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn status_table_exists(&self) -> Result<bool, sqlx::Error> {
|
||||
sqlx::query!("SELECT name FROM sqlite_master WHERE type='table' AND name='status'")
|
||||
.fetch_optional(&*self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
.map(|r| r.is_some())
|
||||
}
|
||||
|
||||
pub async fn create_status_table(&self) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("INSERT INTO status(flush_in_progress, previous_flush_timestamp, client_in_use) VALUES (0, 0, 1)")
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_flush_status(&self) -> Result<bool, sqlx::Error> {
|
||||
sqlx::query!("SELECT flush_in_progress FROM status;")
|
||||
.fetch_one(&*self.connection_pool)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|r| r.flush_in_progress > 0)
|
||||
}
|
||||
|
||||
pub async fn set_previous_flush_timestamp(&self, timestamp: i64) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("UPDATE status SET previous_flush_timestamp = ?", timestamp)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_previous_flush_timestamp(&self) -> Result<i64, sqlx::Error> {
|
||||
sqlx::query!("SELECT previous_flush_timestamp FROM status;")
|
||||
.fetch_one(&*self.connection_pool)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|r| r.previous_flush_timestamp)
|
||||
}
|
||||
@@ -111,14 +100,14 @@ impl StorageManager {
|
||||
pub async fn set_flush_status(&self, in_progress: bool) -> Result<(), sqlx::Error> {
|
||||
let in_progress_int = i64::from(in_progress);
|
||||
sqlx::query!("UPDATE status SET flush_in_progress = ?", in_progress_int)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_client_in_use_status(&self) -> Result<bool, sqlx::Error> {
|
||||
sqlx::query!("SELECT client_in_use FROM status;")
|
||||
.fetch_one(&*self.connection_pool)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
.map(|r| r.client_in_use > 0)
|
||||
}
|
||||
@@ -126,21 +115,21 @@ impl StorageManager {
|
||||
pub async fn set_client_in_use_status(&self, in_use: bool) -> Result<(), sqlx::Error> {
|
||||
let in_use_int = i64::from(in_use);
|
||||
sqlx::query!("UPDATE status SET client_in_use = ?", in_use_int)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_all_tags(&self) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM sender_tag;")
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_tags(&self) -> Result<Vec<StoredSenderTag>, sqlx::Error> {
|
||||
sqlx::query_as!(StoredSenderTag, "SELECT * FROM sender_tag;",)
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -152,21 +141,21 @@ impl StorageManager {
|
||||
stored_tag.recipient,
|
||||
stored_tag.tag
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_all_reply_keys(&self) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM reply_key;")
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_reply_keys(&self) -> Result<Vec<StoredReplyKey>, sqlx::Error> {
|
||||
sqlx::query_as!(StoredReplyKey, "SELECT * FROM reply_key;",)
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -182,14 +171,14 @@ impl StorageManager {
|
||||
stored_reply_key.reply_key,
|
||||
stored_reply_key.sent_at_timestamp
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_surb_senders(&self) -> Result<Vec<StoredSurbSender>, sqlx::Error> {
|
||||
sqlx::query_as!(StoredSurbSender, "SELECT * FROM reply_surb_sender;",)
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -204,7 +193,7 @@ impl StorageManager {
|
||||
stored_surb_sender.tag,
|
||||
stored_surb_sender.last_sent_timestamp
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?
|
||||
.last_insert_rowid();
|
||||
Ok(id)
|
||||
@@ -222,17 +211,17 @@ impl StorageManager {
|
||||
"#,
|
||||
sender_id
|
||||
)
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_all_reply_surb_data(&self) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM reply_surb;")
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query!("DELETE FROM reply_surb_sender;")
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -250,7 +239,7 @@ impl StorageManager {
|
||||
stored_reply_surb.reply_surb,
|
||||
stored_reply_surb.encoded_key_rotation
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -264,7 +253,7 @@ impl StorageManager {
|
||||
SELECT min_reply_surb_threshold as "min_reply_surb_threshold: u32", max_reply_surb_threshold as "max_reply_surb_threshold: u32" FROM reply_surb_storage_metadata;
|
||||
"#,
|
||||
)
|
||||
.fetch_one(&*self.connection_pool)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -278,7 +267,7 @@ impl StorageManager {
|
||||
"#,
|
||||
metadata.min_reply_surb_threshold,
|
||||
metadata.max_reply_surb_threshold,
|
||||
).execute(&*self.connection_pool).await?;
|
||||
).execute(&self.connection_pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::backend::fs_backend::manager::StorageManager;
|
||||
use crate::backend::fs_backend::models::{
|
||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
||||
};
|
||||
use crate::surb_storage::ReceivedReplySurbs;
|
||||
use crate::{
|
||||
backend::fs_backend::{
|
||||
manager::StorageManager,
|
||||
models::{
|
||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag,
|
||||
StoredSurbSender,
|
||||
},
|
||||
},
|
||||
surb_storage::ReceivedReplySurbs,
|
||||
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys,
|
||||
UsedSenderTags,
|
||||
CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use log::{debug, error, info, warn};
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -44,17 +41,15 @@ impl Backend {
|
||||
}
|
||||
|
||||
let manager = StorageManager::init(database_path, true).await?;
|
||||
match manager.create_status_table().await {
|
||||
Ok(()) => Ok(Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
manager,
|
||||
}),
|
||||
Err(err) => {
|
||||
manager.close_pool().await;
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
manager.create_status_table().await?;
|
||||
|
||||
let backend = Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
manager,
|
||||
};
|
||||
|
||||
Ok(backend)
|
||||
}
|
||||
|
||||
pub async fn try_load<P: AsRef<Path>>(
|
||||
@@ -69,28 +64,7 @@ impl Backend {
|
||||
}
|
||||
|
||||
let manager = StorageManager::init(database_path, false).await?;
|
||||
match Self::try_load_inner(&manager, fresh_sender_tags).await {
|
||||
Ok(()) => Ok(Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
manager,
|
||||
}),
|
||||
Err(e) => {
|
||||
manager.close_pool().await;
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gracefully close sqlite connection pool and drop backend.
|
||||
pub async fn shutdown(self) {
|
||||
self.manager.close_pool().await
|
||||
}
|
||||
|
||||
async fn try_load_inner(
|
||||
manager: &StorageManager,
|
||||
fresh_sender_tags: bool,
|
||||
) -> Result<(), StorageError> {
|
||||
// the database flush wasn't fully finished and thus the data is in inconsistent state
|
||||
// (we don't really know what's properly saved or what's not)
|
||||
if manager.get_flush_status().await? {
|
||||
@@ -152,11 +126,20 @@ impl Backend {
|
||||
manager.delete_all_tags().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(Backend {
|
||||
temporary_old_path: None,
|
||||
database_path: owned_path,
|
||||
// manager: StorageManagerState::Storage(manager),
|
||||
manager,
|
||||
})
|
||||
}
|
||||
|
||||
async fn close_pool(&mut self) {
|
||||
self.manager.connection_pool.close().await;
|
||||
}
|
||||
|
||||
async fn rotate(&mut self) -> Result<(), StorageError> {
|
||||
self.manager.close_pool().await;
|
||||
self.close_pool().await;
|
||||
|
||||
let new_extension = if let Some(existing_extension) =
|
||||
self.database_path.extension().and_then(|ext| ext.to_str())
|
||||
@@ -169,8 +152,7 @@ impl Backend {
|
||||
let mut temp_old = self.database_path.clone();
|
||||
temp_old.set_extension(new_extension);
|
||||
|
||||
tokio::fs::rename(&self.database_path, &temp_old)
|
||||
.await
|
||||
fs::rename(&self.database_path, &temp_old)
|
||||
.map_err(|err| StorageError::DatabaseRenameError { source: err })?;
|
||||
self.manager = StorageManager::init(&self.database_path, true).await?;
|
||||
self.manager.create_status_table().await?;
|
||||
@@ -179,10 +161,9 @@ impl Backend {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_old(&mut self) -> Result<(), StorageError> {
|
||||
fn remove_old(&mut self) -> Result<(), StorageError> {
|
||||
if let Some(old_path) = self.temporary_old_path.take() {
|
||||
tokio::fs::remove_file(old_path)
|
||||
.await
|
||||
fs::remove_file(old_path)
|
||||
.map_err(|err| StorageError::DatabaseOldFileRemoveError { source: err })
|
||||
} else {
|
||||
warn!("the old database file doesn't seem to exist!");
|
||||
@@ -354,7 +335,7 @@ impl ReplyStorageBackend for Backend {
|
||||
self.dump_reply_surb_storage_metadata(surbs_ref).await?;
|
||||
self.dump_reply_surbs(surbs_ref).await?;
|
||||
|
||||
self.remove_old().await?;
|
||||
self.remove_old()?;
|
||||
self.end_storage_flush().await
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ where
|
||||
self.backend.load_surb_storage().await
|
||||
}
|
||||
|
||||
// this will have to get enabled after merging develop
|
||||
pub async fn flush_on_shutdown(
|
||||
mut self,
|
||||
mem_state: CombinedReplyStorage,
|
||||
@@ -49,6 +50,7 @@ where
|
||||
shutdown.recv().await;
|
||||
|
||||
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
|
||||
info!("you MUST NOT forcefully shutdown now or you risk data corruption!");
|
||||
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
|
||||
error!("failed to flush our reply-related data to the persistent storage: {err}")
|
||||
} else {
|
||||
|
||||
@@ -133,7 +133,7 @@ impl ManagedConnection {
|
||||
debug!("Managed to establish connection to {}", self.address);
|
||||
|
||||
let noise_stream =
|
||||
match upgrade_noise_initiator(stream, &self.noise_config, None).await {
|
||||
match upgrade_noise_initiator(stream, &self.noise_config).await {
|
||||
Ok(noise_stream) => noise_stream,
|
||||
Err(err) => {
|
||||
error!("Failed to perform Noise handshake with {address} - {err}");
|
||||
|
||||
@@ -20,8 +20,6 @@ nym-credentials = { path = "../credentials" }
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx-pool-guard]
|
||||
path = "../../sqlx-pool-guard"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
@@ -33,13 +31,8 @@ features = ["rt-multi-thread", "net", "signal", "fs"]
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { workspace = true, features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
[features]
|
||||
persistent-storage = ["bincode", "serde"]
|
||||
persistent-storage = ["bincode", "serde"]
|
||||
@@ -7,11 +7,10 @@ use crate::models::{
|
||||
};
|
||||
use nym_ecash_time::Date;
|
||||
use sqlx::{Executor, Sqlite, Transaction};
|
||||
use sqlx_pool_guard::SqlitePoolGuard;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SqliteEcashTicketbookManager {
|
||||
connection_pool: SqlitePoolGuard,
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
impl SqliteEcashTicketbookManager {
|
||||
@@ -20,7 +19,7 @@ impl SqliteEcashTicketbookManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connection_pool`: database connection pool to use.
|
||||
pub fn new(connection_pool: SqlitePoolGuard) -> Self {
|
||||
pub fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
SqliteEcashTicketbookManager { connection_pool }
|
||||
}
|
||||
|
||||
@@ -34,7 +33,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"DELETE FROM ecash_ticketbook WHERE expiration_date <= ?",
|
||||
deadline
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -61,7 +60,7 @@ impl SqliteEcashTicketbookManager {
|
||||
data,
|
||||
expiration_date,
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -91,7 +90,7 @@ impl SqliteEcashTicketbookManager {
|
||||
epoch_id,
|
||||
total_tickets,
|
||||
used_tickets,
|
||||
).execute(&*self.connection_pool).await?;
|
||||
).execute(&self.connection_pool).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -106,7 +105,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"#,
|
||||
)
|
||||
.bind(data)
|
||||
.fetch_optional(&*self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await?
|
||||
.is_some();
|
||||
|
||||
@@ -122,7 +121,7 @@ impl SqliteEcashTicketbookManager {
|
||||
FROM ecash_ticketbook
|
||||
"#,
|
||||
)
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -144,7 +143,7 @@ impl SqliteEcashTicketbookManager {
|
||||
ticketbook_id,
|
||||
expected_current_total_spent
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?
|
||||
.rows_affected();
|
||||
Ok(affected > 0)
|
||||
@@ -154,7 +153,7 @@ impl SqliteEcashTicketbookManager {
|
||||
&self,
|
||||
) -> Result<Vec<StoredPendingTicketbook>, sqlx::Error> {
|
||||
sqlx::query_as("SELECT * FROM pending_issuance")
|
||||
.fetch_all(&*self.connection_pool)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -166,7 +165,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"DELETE FROM pending_issuance WHERE deposit_id = ?",
|
||||
pending_id
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,7 +182,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"#,
|
||||
epoch_id
|
||||
)
|
||||
.fetch_optional(&*self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -209,7 +208,7 @@ impl SqliteEcashTicketbookManager {
|
||||
serialisation_revision,
|
||||
epoch_id
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -226,7 +225,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"#,
|
||||
epoch_id
|
||||
)
|
||||
.fetch_optional(&*self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -252,7 +251,7 @@ impl SqliteEcashTicketbookManager {
|
||||
serialisation_revision,
|
||||
epoch_id,
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -270,7 +269,7 @@ impl SqliteEcashTicketbookManager {
|
||||
"#,
|
||||
expiration_date
|
||||
)
|
||||
.fetch_optional(&*self.connection_pool)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -299,7 +298,7 @@ impl SqliteEcashTicketbookManager {
|
||||
serialisation_revision,
|
||||
expiration_date
|
||||
)
|
||||
.execute(&*self.connection_pool)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ use sqlx::{
|
||||
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||
ConnectOptions,
|
||||
};
|
||||
use sqlx_pool_guard::SqlitePoolGuard;
|
||||
use std::path::Path;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
@@ -55,15 +54,15 @@ impl PersistentStorage {
|
||||
/// * `database_path`: path to the database.
|
||||
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
|
||||
debug!(
|
||||
"Attempting to connect to database {}",
|
||||
database_path.as_ref().display()
|
||||
"Attempting to connect to database {:?}",
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||
.synchronous(SqliteSynchronous::Normal)
|
||||
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||
.filename(&database_path)
|
||||
.filename(database_path)
|
||||
.create_if_missing(true)
|
||||
.disable_statement_logging();
|
||||
|
||||
@@ -75,17 +74,13 @@ impl PersistentStorage {
|
||||
}
|
||||
};
|
||||
|
||||
let connection_pool =
|
||||
SqlitePoolGuard::new(database_path.as_ref().to_path_buf(), connection_pool);
|
||||
|
||||
if let Err(err) = sqlx::migrate!("./migrations").run(&*connection_pool).await {
|
||||
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
|
||||
error!("Failed to perform migration on the SQLx database: {err}");
|
||||
connection_pool.close().await;
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
Ok(PersistentStorage {
|
||||
storage_manager: SqliteEcashTicketbookManager::new(connection_pool),
|
||||
storage_manager: SqliteEcashTicketbookManager::new(connection_pool.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ impl PersistentStatsStorage {
|
||||
/// * `database_path`: path to the database.
|
||||
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StatsStorageError> {
|
||||
debug!(
|
||||
"Attempting to connect to database {}",
|
||||
database_path.as_ref().display()
|
||||
"Attempting to connect to database {:?}",
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
// TODO: we can inject here more stuff based on our gateway global config
|
||||
|
||||
@@ -82,8 +82,8 @@ impl GatewayStorage {
|
||||
message_retrieval_limit: i64,
|
||||
) -> Result<Self, GatewayStorageError> {
|
||||
debug!(
|
||||
"Attempting to connect to database {}",
|
||||
database_path.as_ref().display()
|
||||
"Attempting to connect to database {:?}",
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
// TODO: we can inject here more stuff based on our gateway global config
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(feature = "network")]
|
||||
use crate::{ApiUrlConst, DenomDetails, ValidatorDetails};
|
||||
use crate::{DenomDetails, ValidatorDetails};
|
||||
|
||||
pub const NETWORK_NAME: &str = "mainnet";
|
||||
|
||||
@@ -29,36 +29,10 @@ pub const COCONUT_DKG_CONTRACT_ADDRESS: &str =
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
|
||||
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
|
||||
pub const NYXD_WS: &str = "wss://rpc.nymtech.net/websocket";
|
||||
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
|
||||
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
|
||||
#[cfg(feature = "network")]
|
||||
pub const NYM_APIS: &[ApiUrlConst] = &[
|
||||
ApiUrlConst {
|
||||
url: NYM_API,
|
||||
front_hosts: None,
|
||||
},
|
||||
ApiUrlConst {
|
||||
url: "https://nym-fronntdoor.vercel.app/api/",
|
||||
front_hosts: Some(&["vercel.app", "vercel.com"]),
|
||||
},
|
||||
ApiUrlConst {
|
||||
url: "https://nym-frontdoor.global.ssl.fastly.net/api/",
|
||||
front_hosts: Some(&["yelp.global.ssl.fastly.net"]),
|
||||
},
|
||||
];
|
||||
pub const NYM_VPN_API: &str = "https://nymvpn.com/api/";
|
||||
#[cfg(feature = "network")]
|
||||
pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
|
||||
ApiUrlConst {
|
||||
url: NYM_VPN_API,
|
||||
front_hosts: Some(&["vercel.app", "vercel.com"]),
|
||||
},
|
||||
ApiUrlConst {
|
||||
url: "https://nymvpn-frontdoor.global.ssl.fastly.net/api/",
|
||||
front_hosts: Some(&["yelp.global.ssl.fastly.net"]),
|
||||
},
|
||||
];
|
||||
|
||||
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
||||
pub const EXIT_POLICY_URL: &str =
|
||||
|
||||
@@ -37,37 +37,6 @@ pub struct NymNetworkDetails {
|
||||
pub contracts: NymContracts,
|
||||
pub explorer_api: Option<String>,
|
||||
pub nym_vpn_api_url: Option<String>,
|
||||
pub nym_api_urls: Option<Vec<ApiUrl>>,
|
||||
pub nym_vpn_api_urls: Option<Vec<ApiUrl>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ApiUrl {
|
||||
/// Expects a string formatted Url
|
||||
///
|
||||
/// see https://docs.rs/url/latest/url/struct.Url.html
|
||||
pub url: String,
|
||||
/// Optional alternative equivalent hostnames. Each entry must parse as valid Host
|
||||
///
|
||||
/// see https://docs.rs/url/latest/url/enum.Host.html
|
||||
pub front_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
pub struct ApiUrlConst<'a> {
|
||||
pub url: &'a str,
|
||||
pub front_hosts: Option<&'a [&'a str]>,
|
||||
}
|
||||
|
||||
impl From<ApiUrlConst<'_>> for ApiUrl {
|
||||
fn from(value: ApiUrlConst) -> Self {
|
||||
ApiUrl {
|
||||
url: value.url.to_string(),
|
||||
front_hosts: value
|
||||
.front_hosts
|
||||
.map(|slice| slice.iter().map(|s| s.to_string()).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
|
||||
@@ -98,8 +67,6 @@ impl NymNetworkDetails {
|
||||
contracts: Default::default(),
|
||||
explorer_api: Default::default(),
|
||||
nym_vpn_api_url: Default::default(),
|
||||
nym_api_urls: Default::default(),
|
||||
nym_vpn_api_urls: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,8 +154,6 @@ impl NymNetworkDetails {
|
||||
},
|
||||
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
|
||||
nym_vpn_api_url: parse_optional_str(mainnet::NYM_VPN_API),
|
||||
nym_api_urls: None,
|
||||
nym_vpn_api_urls: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
[package]
|
||||
name = "nym-kkt"
|
||||
version = "0.1.0"
|
||||
authors = ["Georgio Nicolas <georgio@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
arc-swap = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
pin-project = { workspace = true }
|
||||
blake3 = { workspace = true }
|
||||
aead = { workspace = true }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
|
||||
|
||||
|
||||
# internal
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"]}
|
||||
|
||||
libcrux-traits = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = ["test-utils"] }
|
||||
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-ml-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", features = ["codec"]}
|
||||
|
||||
rand = "0.9.2"
|
||||
curve25519-dalek = {version = "4.1.3", features = ["rand_core", "serde"] }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f","zeroize"]}
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = {workspace = true}
|
||||
|
||||
[[bench]]
|
||||
name = "benches"
|
||||
harness = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,518 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_kkt::{
|
||||
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, SignatureScheme, KEM},
|
||||
context::KKTMode,
|
||||
frame::KKTFrame,
|
||||
key_utils::{generate_keypair_libcrux, generate_keypair_mceliece, hash_encapsulation_key},
|
||||
session::{
|
||||
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
||||
responder_ingest_message, responder_process,
|
||||
},
|
||||
};
|
||||
use rand::prelude::*;
|
||||
|
||||
pub fn gen_ed25519_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate Ed25519 Keypair", |b| {
|
||||
b.iter(|| {
|
||||
let mut s: [u8; 32] = [0u8; 32];
|
||||
rand::rng().fill_bytes(&mut s);
|
||||
ed25519::KeyPair::from_secret(s, 0)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn gen_mlkem768_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate MlKem768 Keypair", |b| {
|
||||
b.iter(|| {
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rand::rng()).unwrap()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn kkt_benchmark(c: &mut Criterion) {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// generate ed25519 keys
|
||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_initiator);
|
||||
let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0);
|
||||
|
||||
let mut secret_responder: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_responder);
|
||||
let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1);
|
||||
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||
for hash_function in [
|
||||
HashFunction::Blake3,
|
||||
HashFunction::SHA256,
|
||||
HashFunction::SHAKE128,
|
||||
HashFunction::SHAKE256,
|
||||
] {
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
kem,
|
||||
hash_function,
|
||||
SignatureScheme::Ed25519,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// generate kem public keys
|
||||
|
||||
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||
KEM::MlKem768 => (
|
||||
EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
EncapsulationKey::MlKem768(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
),
|
||||
KEM::XWing => (
|
||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
),
|
||||
KEM::X25519 => (
|
||||
EncapsulationKey::X25519(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
EncapsulationKey::X25519(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
),
|
||||
KEM::McEliece => (
|
||||
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||
),
|
||||
};
|
||||
|
||||
let i_kem_key_bytes = initiator_kem_public_key.encode();
|
||||
|
||||
let r_kem_key_bytes = responder_kem_public_key.encode();
|
||||
|
||||
let i_dir_hash = hash_encapsulation_key(
|
||||
&ciphersuite.hash_function(),
|
||||
ciphersuite.hash_len(),
|
||||
&i_kem_key_bytes,
|
||||
);
|
||||
|
||||
let r_dir_hash = hash_encapsulation_key(
|
||||
&ciphersuite.hash_function(),
|
||||
ciphersuite.hash_len(),
|
||||
&r_kem_key_bytes,
|
||||
);
|
||||
|
||||
// Anonymous Initiator, OneWay
|
||||
{
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Generate Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| anonymous_initiator_process(&mut rng, ciphersuite).unwrap());
|
||||
},
|
||||
);
|
||||
|
||||
let (mut i_context, i_frame) =
|
||||
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Encode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| b.iter(|| i_frame.to_bytes()),
|
||||
);
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Decode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
||||
);
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Responder Ingest Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_ingest_message(&r_context, None, None, &i_frame_r).unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let (mut r_context, _) =
|
||||
responder_ingest_message(&r_context, None, None, &i_frame_r).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Responder Generate Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Responder Encode Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| b.iter(|| r_frame.to_bytes()),
|
||||
);
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Anonymous Initiator: Initiator Ingest Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
// Initiator, OneWay
|
||||
{
|
||||
let (mut i_context, i_frame) = initiator_process(
|
||||
&mut rng,
|
||||
KKTMode::OneWay,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Generate Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
initiator_process(
|
||||
&mut rng,
|
||||
KKTMode::OneWay,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Encode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| b.iter(|| i_frame.to_bytes()),
|
||||
);
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Decode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
||||
);
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Responder Ingest Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
None,
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
None,
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(r_obtained_key.is_none());
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Responder Generate Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Responder Encode Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| r_frame.to_bytes());
|
||||
},
|
||||
);
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator OneWay: Initiator Ingest Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let i_obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
|
||||
// Initiator, Mutual
|
||||
{
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Generate Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
initiator_process(
|
||||
&mut rng,
|
||||
KKTMode::Mutual,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
Some(&initiator_kem_public_key),
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let (mut i_context, i_frame) = initiator_process(
|
||||
&mut rng,
|
||||
KKTMode::Mutual,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
Some(&initiator_kem_public_key),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Encode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| i_frame.to_bytes());
|
||||
},
|
||||
);
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Decode Frame - Request",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap());
|
||||
},
|
||||
);
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Responder Ingest Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
Some(&i_dir_hash),
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
Some(&i_dir_hash),
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Responder Generate Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Responder Encode Frame",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
r_frame.to_bytes();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"{}, {} | Initiator Mutual: Initiator Ingest Response",
|
||||
kem, hash_function
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
gen_ed25519_keypair,
|
||||
gen_mlkem768_keypair,
|
||||
kkt_benchmark
|
||||
);
|
||||
criterion_main!(benches);
|
||||
@@ -1,311 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use libcrux_kem::{Algorithm, MlKem768PublicKey};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
|
||||
use crate::error::KKTError;
|
||||
|
||||
pub const HASH_LEN_256: u8 = 32;
|
||||
pub const CIPHERSUITE_ENCODING_LEN: usize = 4;
|
||||
|
||||
pub const CURVE25519_KEY_LEN: usize = 32;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum HashFunction {
|
||||
Blake3,
|
||||
SHAKE128,
|
||||
SHAKE256,
|
||||
SHA256,
|
||||
}
|
||||
impl Display for HashFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
HashFunction::Blake3 => "Blake3",
|
||||
HashFunction::SHAKE128 => "SHAKE128",
|
||||
HashFunction::SHAKE256 => "SHAKE256",
|
||||
HashFunction::SHA256 => "SHA256",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum EncapsulationKey<'a> {
|
||||
MlKem768(libcrux_kem::PublicKey),
|
||||
XWing(libcrux_kem::PublicKey),
|
||||
X25519(libcrux_kem::PublicKey),
|
||||
McEliece(classic_mceliece_rust::PublicKey<'a>),
|
||||
}
|
||||
|
||||
pub enum DecapsulationKey<'a> {
|
||||
MlKem768(libcrux_kem::PrivateKey),
|
||||
XWing(libcrux_kem::PrivateKey),
|
||||
X25519(libcrux_kem::PrivateKey),
|
||||
McEliece(classic_mceliece_rust::SecretKey<'a>),
|
||||
}
|
||||
impl<'a> EncapsulationKey<'a> {
|
||||
pub(crate) fn decode(kem: KEM, bytes: &[u8]) -> Result<Self, KKTError> {
|
||||
match kem {
|
||||
KEM::McEliece => {
|
||||
if bytes.len() != classic_mceliece_rust::CRYPTO_PUBLICKEYBYTES {
|
||||
Err(KKTError::KEMError {
|
||||
info: "Received McEliece Encapsulation Key with Invalid Length",
|
||||
})
|
||||
} else {
|
||||
let mut public_key_bytes =
|
||||
Box::new([0u8; classic_mceliece_rust::CRYPTO_PUBLICKEYBYTES]);
|
||||
// Size must be correct due to KKTFrame::from_bytes(message_bytes)?
|
||||
public_key_bytes.clone_from_slice(bytes);
|
||||
Ok(EncapsulationKey::McEliece(
|
||||
classic_mceliece_rust::PublicKey::from(public_key_bytes),
|
||||
))
|
||||
}
|
||||
}
|
||||
KEM::X25519 => Ok(EncapsulationKey::X25519(libcrux_kem::PublicKey::decode(
|
||||
map_kem_to_libcrux_kem(kem),
|
||||
bytes,
|
||||
)?)),
|
||||
KEM::MlKem768 => Ok(EncapsulationKey::MlKem768(libcrux_kem::PublicKey::decode(
|
||||
map_kem_to_libcrux_kem(kem),
|
||||
bytes,
|
||||
)?)),
|
||||
KEM::XWing => Ok(EncapsulationKey::XWing(libcrux_kem::PublicKey::decode(
|
||||
map_kem_to_libcrux_kem(kem),
|
||||
bytes,
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
EncapsulationKey::XWing(public_key)
|
||||
| EncapsulationKey::MlKem768(public_key)
|
||||
| EncapsulationKey::X25519(public_key) => public_key.encode(),
|
||||
EncapsulationKey::McEliece(public_key) => Vec::from(public_key.as_array()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SignatureScheme {
|
||||
Ed25519,
|
||||
}
|
||||
impl Display for SignatureScheme {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
SignatureScheme::Ed25519 => "Ed25519",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum KEM {
|
||||
MlKem768,
|
||||
XWing,
|
||||
X25519,
|
||||
McEliece,
|
||||
}
|
||||
|
||||
impl Display for KEM {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
KEM::MlKem768 => "MlKem768",
|
||||
KEM::XWing => "XWing",
|
||||
KEM::X25519 => "x25519",
|
||||
KEM::McEliece => "McEliece",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Ciphersuite {
|
||||
hash_function: HashFunction,
|
||||
signature_scheme: SignatureScheme,
|
||||
kem: KEM,
|
||||
hash_length: u8,
|
||||
encapsulation_key_length: usize,
|
||||
signing_key_length: usize,
|
||||
verification_key_length: usize,
|
||||
signature_length: usize,
|
||||
}
|
||||
|
||||
impl Ciphersuite {
|
||||
pub fn kem_key_len(&self) -> usize {
|
||||
self.encapsulation_key_length
|
||||
}
|
||||
|
||||
pub fn signature_len(&self) -> usize {
|
||||
self.signature_length
|
||||
}
|
||||
pub fn signing_key_len(&self) -> usize {
|
||||
self.signing_key_length
|
||||
}
|
||||
pub fn verification_key_len(&self) -> usize {
|
||||
self.verification_key_length
|
||||
}
|
||||
pub fn hash_function(&self) -> HashFunction {
|
||||
self.hash_function
|
||||
}
|
||||
pub fn kem(&self) -> KEM {
|
||||
self.kem
|
||||
}
|
||||
pub fn signature_scheme(&self) -> SignatureScheme {
|
||||
self.signature_scheme
|
||||
}
|
||||
pub fn hash_len(&self) -> usize {
|
||||
self.hash_length as usize
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self::resolve_ciphersuite(
|
||||
KEM::XWing,
|
||||
HashFunction::Blake3,
|
||||
SignatureScheme::Ed25519,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn resolve_ciphersuite(
|
||||
kem: KEM,
|
||||
hash_function: HashFunction,
|
||||
signature_scheme: SignatureScheme,
|
||||
// This should be None 99.9999% of the time
|
||||
custom_hash_length: Option<u8>,
|
||||
) -> Result<Self, KKTError> {
|
||||
let hash_len = match custom_hash_length {
|
||||
Some(l) => {
|
||||
if l < 16 {
|
||||
return Err(KKTError::InsecureHashLen);
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
None => HASH_LEN_256,
|
||||
};
|
||||
Ok(Self {
|
||||
hash_function: hash_function,
|
||||
signature_scheme: signature_scheme,
|
||||
kem: kem,
|
||||
hash_length: hash_len,
|
||||
encapsulation_key_length: match kem {
|
||||
// 1184 bytes
|
||||
KEM::MlKem768 => MlKem768PublicKey::len(),
|
||||
// 1216 bytes = 1184 + 32
|
||||
KEM::XWing => MlKem768PublicKey::len() + CURVE25519_KEY_LEN,
|
||||
// 32 bytes
|
||||
KEM::X25519 => CURVE25519_KEY_LEN,
|
||||
// 524160 bytes
|
||||
KEM::McEliece => classic_mceliece_rust::CRYPTO_PUBLICKEYBYTES,
|
||||
},
|
||||
signing_key_length: match signature_scheme {
|
||||
// 32 bytes
|
||||
SignatureScheme::Ed25519 => ed25519::SECRET_KEY_LENGTH,
|
||||
},
|
||||
verification_key_length: match signature_scheme {
|
||||
// 32 bytes
|
||||
SignatureScheme::Ed25519 => ed25519::PUBLIC_KEY_LENGTH,
|
||||
},
|
||||
signature_length: match signature_scheme {
|
||||
// 64 bytes
|
||||
SignatureScheme::Ed25519 => ed25519::SIGNATURE_LENGTH,
|
||||
},
|
||||
})
|
||||
}
|
||||
pub fn encode(&self) -> [u8; 4] {
|
||||
// [kem, hash, hashlen, sig]
|
||||
[
|
||||
match self.kem {
|
||||
KEM::XWing => 0,
|
||||
KEM::MlKem768 => 1,
|
||||
KEM::McEliece => 2,
|
||||
KEM::X25519 => 255,
|
||||
},
|
||||
match self.hash_function {
|
||||
HashFunction::Blake3 => 0,
|
||||
HashFunction::SHAKE256 => 1,
|
||||
HashFunction::SHAKE128 => 2,
|
||||
HashFunction::SHA256 => 3,
|
||||
},
|
||||
match self.hash_length {
|
||||
HASH_LEN_256 => 0,
|
||||
_ => self.hash_length as u8,
|
||||
},
|
||||
match self.signature_scheme {
|
||||
SignatureScheme::Ed25519 => 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
pub fn decode(encoding: &[u8]) -> Result<Self, KKTError> {
|
||||
if encoding.len() == 4 {
|
||||
let kem = match encoding[0] {
|
||||
0 => KEM::XWing,
|
||||
1 => KEM::MlKem768,
|
||||
2 => KEM::McEliece,
|
||||
255 => KEM::X25519,
|
||||
_ => {
|
||||
return Err(KKTError::CiphersuiteDecodingError {
|
||||
info: format!("Undefined KEM: {}", encoding[0]),
|
||||
})
|
||||
}
|
||||
};
|
||||
let hash_function = match encoding[1] {
|
||||
0 => HashFunction::Blake3,
|
||||
1 => HashFunction::SHAKE256,
|
||||
2 => HashFunction::SHAKE128,
|
||||
3 => HashFunction::SHA256,
|
||||
_ => {
|
||||
return Err(KKTError::CiphersuiteDecodingError {
|
||||
info: format!("Undefined Hash Function: {}", encoding[1]),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let custom_hash_length = match encoding[2] {
|
||||
0 => None,
|
||||
_ => Some(encoding[2]),
|
||||
};
|
||||
|
||||
let signature_scheme = match encoding[3] {
|
||||
0 => SignatureScheme::Ed25519,
|
||||
_ => {
|
||||
return Err(KKTError::CiphersuiteDecodingError {
|
||||
info: format!("Undefined Signature Scheme: {}", encoding[3]),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Self::resolve_ciphersuite(kem, hash_function, signature_scheme, custom_hash_length)
|
||||
} else {
|
||||
return Err(KKTError::CiphersuiteDecodingError {
|
||||
info: format!(
|
||||
"Incorrect Encoding Length: actual: {} != expected: {}",
|
||||
encoding.len(),
|
||||
CIPHERSUITE_ENCODING_LEN
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ciphersuite {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(
|
||||
&format!(
|
||||
"{}_{}({})_{}",
|
||||
self.kem, self.hash_function, self.hash_length, self.signature_scheme
|
||||
)
|
||||
.to_ascii_lowercase(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn map_kem_to_libcrux_kem(kem: KEM) -> Algorithm {
|
||||
match kem {
|
||||
KEM::MlKem768 => Algorithm::MlKem768,
|
||||
KEM::XWing => Algorithm::XWingKemDraft06,
|
||||
KEM::X25519 => Algorithm::X25519,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{ciphersuite::Ciphersuite, error::KKTError, frame::KKT_SESSION_ID_LEN, KKT_VERSION};
|
||||
|
||||
pub const KKT_CONTEXT_LEN: usize = 7;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum KKTStatus {
|
||||
Ok,
|
||||
InvalidRequestFormat,
|
||||
InvalidResponseFormat,
|
||||
InvalidSignature,
|
||||
UnsupportedCiphersuite,
|
||||
UnsupportedKKTVersion,
|
||||
InvalidKey,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl Display for KKTStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
KKTStatus::Ok => "Ok",
|
||||
KKTStatus::InvalidRequestFormat => "Invalid Request Format",
|
||||
KKTStatus::InvalidResponseFormat => "Invalid Response Format",
|
||||
KKTStatus::InvalidSignature => "Invalid Signature",
|
||||
KKTStatus::UnsupportedCiphersuite => "Unsupported Ciphersuite",
|
||||
KKTStatus::UnsupportedKKTVersion => "Unsupported KKT Version",
|
||||
KKTStatus::InvalidKey => "Invalid Key",
|
||||
KKTStatus::Timeout => "Timeout",
|
||||
})
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum KKTRole {
|
||||
Initiator,
|
||||
AnonymousInitiator,
|
||||
Responder,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum KKTMode {
|
||||
OneWay,
|
||||
Mutual,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct KKTContext {
|
||||
version: u8,
|
||||
message_sequence: u8,
|
||||
status: KKTStatus,
|
||||
mode: KKTMode,
|
||||
role: KKTRole,
|
||||
ciphersuite: Ciphersuite,
|
||||
}
|
||||
impl KKTContext {
|
||||
pub fn new(role: KKTRole, mode: KKTMode, ciphersuite: Ciphersuite) -> Result<Self, KKTError> {
|
||||
if role == KKTRole::AnonymousInitiator && mode != KKTMode::OneWay {
|
||||
return Err(KKTError::IncompatibilityError {
|
||||
info: "Anonymous Initiator can only use OneWay mode",
|
||||
});
|
||||
}
|
||||
Ok(Self {
|
||||
version: KKT_VERSION,
|
||||
message_sequence: 0,
|
||||
status: KKTStatus::Ok,
|
||||
mode: mode,
|
||||
role: role,
|
||||
ciphersuite: ciphersuite,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn derive_responder_header(&self) -> Result<Self, KKTError> {
|
||||
let mut responder_header = self.clone();
|
||||
|
||||
responder_header.increment_message_sequence_count()?;
|
||||
responder_header.role = KKTRole::Responder;
|
||||
|
||||
Ok(responder_header)
|
||||
}
|
||||
|
||||
pub fn increment_message_sequence_count(&mut self) -> Result<(), KKTError> {
|
||||
if self.message_sequence + 1 < (1 << 4) {
|
||||
self.message_sequence += 1;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(KKTError::MessageCountLimitReached)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_status(&mut self, status: KKTStatus) {
|
||||
self.status = status;
|
||||
}
|
||||
pub fn version(&self) -> u8 {
|
||||
self.version
|
||||
}
|
||||
pub fn status(&self) -> KKTStatus {
|
||||
self.status
|
||||
}
|
||||
pub fn ciphersuite(&self) -> Ciphersuite {
|
||||
self.ciphersuite
|
||||
}
|
||||
pub fn role(&self) -> KKTRole {
|
||||
self.role
|
||||
}
|
||||
pub fn mode(&self) -> KKTMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
pub fn body_len(&self) -> usize {
|
||||
if self.status != KKTStatus::Ok
|
||||
|| (self.mode == KKTMode::OneWay
|
||||
&& (self.role == KKTRole::Initiator || self.role == KKTRole::AnonymousInitiator))
|
||||
{
|
||||
0
|
||||
} else {
|
||||
self.ciphersuite.kem_key_len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signature_len(&self) -> usize {
|
||||
match self.role {
|
||||
KKTRole::Initiator | KKTRole::Responder => self.ciphersuite.signature_len(),
|
||||
KKTRole::AnonymousInitiator => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header_len(&self) -> usize {
|
||||
KKT_CONTEXT_LEN
|
||||
}
|
||||
|
||||
pub fn session_id_len(&self) -> usize {
|
||||
// match self.role {
|
||||
// KKTRole::Initiator | KKTRole::Responder => SESSION_ID_LENGTH,
|
||||
// It doesn't make sense to send a session_id if we send messages in the clear
|
||||
// KKTRole::AnonymousInitiator => 0,
|
||||
// }
|
||||
KKT_SESSION_ID_LEN
|
||||
}
|
||||
|
||||
pub fn full_message_len(&self) -> usize {
|
||||
self.body_len() + self.signature_len() + self.header_len() + self.session_id_len()
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Result<Vec<u8>, KKTError> {
|
||||
let mut header_bytes: Vec<u8> = Vec::with_capacity(KKT_CONTEXT_LEN);
|
||||
if self.message_sequence >= 1 << 4 {
|
||||
return Err(KKTError::MessageCountLimitReached);
|
||||
}
|
||||
|
||||
header_bytes.push((KKT_VERSION << 4) + self.message_sequence);
|
||||
|
||||
header_bytes.push(
|
||||
match self.status {
|
||||
KKTStatus::Ok => 0,
|
||||
KKTStatus::InvalidRequestFormat => 0b001_000_00,
|
||||
KKTStatus::InvalidResponseFormat => 0b010_000_00,
|
||||
KKTStatus::InvalidSignature => 0b011_000_00,
|
||||
KKTStatus::UnsupportedCiphersuite => 0b100_000_00,
|
||||
KKTStatus::UnsupportedKKTVersion => 0b101_000_00,
|
||||
KKTStatus::InvalidKey => 0b110_000_00,
|
||||
KKTStatus::Timeout => 0b111_000_00,
|
||||
} + match self.mode {
|
||||
KKTMode::OneWay => 0,
|
||||
KKTMode::Mutual => 0b000_001_00,
|
||||
} + match self.role {
|
||||
KKTRole::Initiator => 0,
|
||||
KKTRole::Responder => 1,
|
||||
KKTRole::AnonymousInitiator => 2,
|
||||
},
|
||||
);
|
||||
|
||||
header_bytes.extend_from_slice(&self.ciphersuite.encode());
|
||||
header_bytes.push(0);
|
||||
Ok(header_bytes)
|
||||
}
|
||||
|
||||
pub fn try_decode(header_bytes: &[u8]) -> Result<Self, KKTError> {
|
||||
if header_bytes.len() == KKT_CONTEXT_LEN {
|
||||
let kkt_version = header_bytes[0] & 0b1111_0000;
|
||||
|
||||
let message_sequence_counter = header_bytes[0] & 0b0000_1111;
|
||||
|
||||
// We only check if stuff is valid here, not necessarily if it's compatible
|
||||
|
||||
if (kkt_version >> 4) > KKT_VERSION {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!("Header - Invalid KKT Version: {}", kkt_version >> 4),
|
||||
});
|
||||
}
|
||||
|
||||
let status = match header_bytes[1] & 0b111_000_00 {
|
||||
0 => KKTStatus::Ok,
|
||||
0b001_000_00 => KKTStatus::InvalidRequestFormat,
|
||||
0b010_000_00 => KKTStatus::InvalidResponseFormat,
|
||||
0b011_000_00 => KKTStatus::InvalidSignature,
|
||||
0b100_000_00 => KKTStatus::UnsupportedCiphersuite,
|
||||
0b101_000_00 => KKTStatus::UnsupportedKKTVersion,
|
||||
0b110_000_00 => KKTStatus::InvalidKey,
|
||||
0b111_000_00 => KKTStatus::Timeout,
|
||||
_ => {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Header - Invalid KKT Status: {}",
|
||||
header_bytes[1] & 0b111_000_00
|
||||
),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let role = match header_bytes[1] & 0b000_000_11 {
|
||||
0 => KKTRole::Initiator,
|
||||
1 => KKTRole::Responder,
|
||||
2 => KKTRole::AnonymousInitiator,
|
||||
_ => {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Header - Invalid KKT Role: {}",
|
||||
header_bytes[1] & 0b000_000_11
|
||||
),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let mode = match (header_bytes[1] & 0b000_111_00) >> 2 {
|
||||
0 => KKTMode::OneWay,
|
||||
1 => KKTMode::Mutual,
|
||||
_ => {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Header - Invalid KKT Mode: {}",
|
||||
(header_bytes[1] & 0b000_111_00) >> 2
|
||||
),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Ok(KKTContext {
|
||||
version: kkt_version,
|
||||
status: status,
|
||||
mode: mode,
|
||||
role: role,
|
||||
ciphersuite: Ciphersuite::decode(&header_bytes[2..6])?,
|
||||
message_sequence: message_sequence_counter,
|
||||
})
|
||||
} else {
|
||||
Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Header - Invalid Header Length: actual: {} != expected: {}",
|
||||
header_bytes.len(),
|
||||
KKT_CONTEXT_LEN
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
use core::hash;
|
||||
|
||||
use blake3::{Hash, Hasher};
|
||||
use curve25519_dalek::digest::DynDigest;
|
||||
use libcrux_psq::traits::Ciphertext;
|
||||
use nym_crypto::symmetric::aead::{AeadKey, Nonce};
|
||||
use nym_crypto::{
|
||||
aes::Aes256,
|
||||
asymmetric::x25519::{self, PrivateKey, PublicKey},
|
||||
generic_array::GenericArray,
|
||||
Aes256GcmSiv,
|
||||
};
|
||||
// use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::error::KKTError;
|
||||
|
||||
fn generate_round_trip_symmetric_key<R>(
|
||||
rng: &mut R,
|
||||
remote_public_key: &PublicKey,
|
||||
) -> ([u8; 64], [u8; 32])
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let mut s = x25519::PrivateKey::new(rng);
|
||||
let gs = s.public_key();
|
||||
|
||||
let mut gbs = s.diffie_hellman(remote_public_key);
|
||||
s.zeroize();
|
||||
|
||||
let mut message: [u8; 64] = [0u8; 64];
|
||||
message[0..32].clone_from_slice(gs.as_bytes());
|
||||
|
||||
let mut hasher = Hasher::new();
|
||||
|
||||
hasher.update(&gbs);
|
||||
gbs.zeroize();
|
||||
let key: [u8; 32] = hasher.finalize().as_bytes().to_owned();
|
||||
|
||||
hasher.update(remote_public_key.as_bytes());
|
||||
hasher.update(gs.as_bytes());
|
||||
|
||||
hasher.finalize_into_reset(&mut message[32..64]);
|
||||
|
||||
(message, key)
|
||||
}
|
||||
|
||||
fn extract_shared_secret(b: &PrivateKey, message: &[u8; 64]) -> Result<[u8; 32], KKTError> {
|
||||
let gs = PublicKey::from_bytes(&message[0..32])?;
|
||||
|
||||
let mut gsb = b.diffie_hellman(&gs);
|
||||
|
||||
let mut hasher = Hasher::new();
|
||||
hasher.update(&gsb);
|
||||
gsb.zeroize();
|
||||
let key: [u8; 32] = hasher.finalize().as_bytes().to_owned();
|
||||
|
||||
hasher.update(b.public_key().as_bytes());
|
||||
hasher.update(gs.as_bytes());
|
||||
|
||||
// This runs in constant time
|
||||
if hasher.finalize() == message[32..64] {
|
||||
Ok(key)
|
||||
} else {
|
||||
Err(KKTError::X25519Error {
|
||||
info: format!("Symmetric Key Hash Validation Error"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt(mut key: [u8; 32], message: &[u8]) -> Result<Vec<u8>, KKTError> {
|
||||
// The empty nonce is fine since we use the key once.
|
||||
let nonce = Nonce::<Aes256GcmSiv>::from_slice(&[]);
|
||||
|
||||
let ciphertext =
|
||||
nym_crypto::symmetric::aead::encrypt::<Aes256GcmSiv>(&key.into(), nonce, message)?;
|
||||
|
||||
key.zeroize();
|
||||
|
||||
Ok(ciphertext)
|
||||
}
|
||||
|
||||
fn decrypt(key: [u8; 32], ciphertext: Vec<u8>) -> Vec<u8> {
|
||||
// The empty nonce is fine since we use the key once.
|
||||
let nonce = Nonce::<Aes256>::from_slice(&[]);
|
||||
|
||||
let ciphertext =
|
||||
nym_crypto::symmetric::aead::encrypt::<Aes256GcmSiv>(&key.into(), nonce, message)?;
|
||||
|
||||
key.zeroize();
|
||||
|
||||
Ok(ciphertext)
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::context::KKTStatus;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum KKTError {
|
||||
#[error("Signature constructor error")]
|
||||
SigConstructorError,
|
||||
#[error("Signature verification error")]
|
||||
SigVerifError,
|
||||
#[error("Ciphersuite Decoding Error: {}", info)]
|
||||
CiphersuiteDecodingError { info: String },
|
||||
#[error("Insecure Encapsulation Key Hash Length")]
|
||||
InsecureHashLen,
|
||||
|
||||
#[error("KKT Frame Decoding Error: {}", info)]
|
||||
FrameDecodingError { info: String },
|
||||
|
||||
#[error("KKT Frame Encoding Error: {}", info)]
|
||||
FrameEncodingError { info: String },
|
||||
|
||||
#[error("KKT Incompatibility Error: {}", info)]
|
||||
IncompatibilityError { info: &'static str },
|
||||
|
||||
#[error("KKT Responder Flagged Error: {}", status)]
|
||||
ResponderFlaggedError { status: KKTStatus },
|
||||
|
||||
#[error("KKT Message Count Limit Reached")]
|
||||
MessageCountLimitReached,
|
||||
|
||||
#[error("PSQ KEM Error: {}", info)]
|
||||
KEMError { info: &'static str },
|
||||
|
||||
#[error("Local Function Input Error: {}", info)]
|
||||
FunctionInputError { info: &'static str },
|
||||
|
||||
#[error("{}", info)]
|
||||
X25519Error { info: &'static str },
|
||||
// #[error("Protocol did not complete")]
|
||||
// ProtocolError,
|
||||
|
||||
// #[error("encountered an IO error: {0}")]
|
||||
// IoError(#[from] io::Error),s
|
||||
|
||||
// #[error("Handshake timeout")]
|
||||
// HandshakeTimeout(#[from] tokio::time::error::Elapsed),
|
||||
}
|
||||
|
||||
impl From<libcrux_kem::Error> for KKTError {
|
||||
fn from(err: libcrux_kem::Error) -> Self {
|
||||
match err {
|
||||
libcrux_kem::Error::EcDhError(_) => KKTError::KEMError { info: "ECDH Error" },
|
||||
libcrux_kem::Error::KeyGen => KKTError::KEMError {
|
||||
info: "Key Generation Error",
|
||||
},
|
||||
libcrux_kem::Error::Encapsulate => KKTError::KEMError {
|
||||
info: "Encapsulation Error",
|
||||
},
|
||||
libcrux_kem::Error::Decapsulate => KKTError::KEMError {
|
||||
info: "Decapsulation Error",
|
||||
},
|
||||
libcrux_kem::Error::UnsupportedAlgorithm => KKTError::KEMError {
|
||||
info: "libcrux Unsupported Algorithm",
|
||||
},
|
||||
libcrux_kem::Error::InvalidPrivateKey => KKTError::KEMError {
|
||||
info: "Invalid Private Key",
|
||||
},
|
||||
|
||||
libcrux_kem::Error::InvalidPublicKey => KKTError::KEMError {
|
||||
info: "Invalid Public Key",
|
||||
},
|
||||
libcrux_kem::Error::InvalidCiphertext => KKTError::KEMError {
|
||||
info: "Invalid Ciphertext",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<libcrux_ecdh::Error> for KKTError {
|
||||
fn from(err: libcrux_ecdh::Error) -> Self {
|
||||
match err {
|
||||
libcrux_ecdh::Error::InvalidPoint => KKTError::KEMError {
|
||||
info: "Invalid Remote Public Key",
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// | 0 | 1 | 2, 3, 4, 5 | 6 | 7
|
||||
// [0] => KKT version (4 bits) + Message Sequence Count (4 bits)
|
||||
// [1] => Status (3 bits) + Mode (3 bits) + Role (2 bits)
|
||||
// [2..=5] => Ciphersuite
|
||||
// [6] => Reserved
|
||||
|
||||
use nym_crypto::asymmetric::x25519::PublicKey;
|
||||
|
||||
use crate::{
|
||||
context::{KKTContext, KKT_CONTEXT_LEN},
|
||||
error::KKTError,
|
||||
};
|
||||
|
||||
pub const KKT_SESSION_ID_LEN: usize = 16;
|
||||
|
||||
pub struct KKTFrame {
|
||||
context: Vec<u8>,
|
||||
session_id: Vec<u8>,
|
||||
body: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
}
|
||||
|
||||
// if oneway and message coming from initiator => body is empty, signature contains signature of context + session id (64 bytes).
|
||||
// if message coming from anonymous initiator => body is empty, there is no signature.
|
||||
// if mutual and message coming from initiator => body has the initiator's kem public key and the signature is over the context + body + session_id.
|
||||
// if coming from responder => body has the responder's kem public key and the signature is over the context + body + session_id.
|
||||
|
||||
impl KKTFrame {
|
||||
pub fn new(context: &[u8], body: &[u8], session_id: &[u8], signature: &[u8]) -> Self {
|
||||
Self {
|
||||
context: Vec::from(context),
|
||||
body: Vec::from(body),
|
||||
session_id: Vec::from(session_id),
|
||||
signature: Vec::from(signature),
|
||||
}
|
||||
}
|
||||
pub fn context_ref<'a>(&'a self) -> &'a [u8] {
|
||||
&self.context
|
||||
}
|
||||
pub fn signature_ref<'a>(&'a self) -> &'a [u8] {
|
||||
&self.signature
|
||||
}
|
||||
pub fn body_ref<'a>(&'a self) -> &'a [u8] {
|
||||
&self.body
|
||||
}
|
||||
|
||||
pub fn session_id_ref<'a>(&'a self) -> &'a [u8] {
|
||||
&self.session_id
|
||||
}
|
||||
pub fn signature_mut<'a>(&'a mut self) -> &'a mut [u8] {
|
||||
&mut self.signature
|
||||
}
|
||||
pub fn body_mut<'a>(&'a mut self) -> &'a mut [u8] {
|
||||
&mut self.body
|
||||
}
|
||||
|
||||
pub fn session_id_mut<'a>(&'a mut self) -> &'a mut [u8] {
|
||||
&mut self.session_id
|
||||
}
|
||||
|
||||
pub fn frame_length(&self) -> usize {
|
||||
self.context.len() + self.session_id.len() + self.body.len() + self.signature.len()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(self.frame_length());
|
||||
bytes.extend_from_slice(&self.context);
|
||||
bytes.extend_from_slice(&self.body);
|
||||
bytes.extend_from_slice(&self.session_id);
|
||||
bytes.extend_from_slice(&self.signature);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<(Self, KKTContext), KKTError> {
|
||||
if bytes.len() < KKT_CONTEXT_LEN {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Frame is shorter than expected context length: actual {} != expected {}",
|
||||
bytes.len(),
|
||||
KKT_CONTEXT_LEN
|
||||
),
|
||||
});
|
||||
} else {
|
||||
let context_bytes = Vec::from(&bytes[0..KKT_CONTEXT_LEN]);
|
||||
|
||||
let context = KKTContext::try_decode(&context_bytes)?;
|
||||
|
||||
let (mut session_id, mut body, mut signature): (Vec<u8>, Vec<u8>, Vec<u8>) =
|
||||
(vec![], vec![], vec![]);
|
||||
|
||||
if bytes.len() == context.full_message_len() {
|
||||
if context.body_len() > 0 {
|
||||
body.extend_from_slice(
|
||||
&bytes[KKT_CONTEXT_LEN..KKT_CONTEXT_LEN + context.body_len()],
|
||||
);
|
||||
}
|
||||
if context.session_id_len() > 0 {
|
||||
session_id.extend_from_slice(
|
||||
&bytes[KKT_CONTEXT_LEN + context.body_len()
|
||||
..KKT_CONTEXT_LEN + context.body_len() + context.session_id_len()],
|
||||
);
|
||||
}
|
||||
if context.signature_len() > 0 {
|
||||
signature.extend_from_slice(
|
||||
&bytes[KKT_CONTEXT_LEN + context.body_len() + context.session_id_len()
|
||||
..KKT_CONTEXT_LEN
|
||||
+ context.body_len()
|
||||
+ context.session_id_len()
|
||||
+ context.signature_len()],
|
||||
);
|
||||
}
|
||||
|
||||
Ok((
|
||||
KKTFrame::new(&context_bytes, &body, &session_id, &signature),
|
||||
context,
|
||||
))
|
||||
} else {
|
||||
return Err(KKTError::FrameDecodingError {
|
||||
info: format!(
|
||||
"Frame is shorter than expected: actual {} != expected {}",
|
||||
bytes.len(),
|
||||
context.full_message_len()
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EncryptedKKTRequest {
|
||||
ephermeral_key: PublicKey,
|
||||
body: Vec<u8>,
|
||||
}
|
||||
impl EncryptedKKTRequest {
|
||||
pub fn to_bytes(mut self) -> Vec<u8> {
|
||||
self.body.extend(self.ephermeral_key.to_bytes());
|
||||
self.body
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EncryptedKKTResponse {
|
||||
body: Vec<u8>,
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
use crate::{
|
||||
ciphersuite::{HashFunction, KEM},
|
||||
error::KKTError,
|
||||
};
|
||||
|
||||
use classic_mceliece_rust::keypair_boxed;
|
||||
use libcrux_kem::{key_gen, Algorithm};
|
||||
|
||||
use libcrux_sha3;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
// (decapsulation_key, encapsulation_key)
|
||||
pub fn generate_keypair_libcrux<R>(
|
||||
rng: &mut R,
|
||||
kem: KEM,
|
||||
) -> Result<(libcrux_kem::PrivateKey, libcrux_kem::PublicKey), KKTError>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
match kem {
|
||||
KEM::MlKem768 => Ok(key_gen(Algorithm::MlKem768, rng)?),
|
||||
KEM::XWing => Ok(key_gen(Algorithm::XWingKemDraft06, rng)?),
|
||||
KEM::X25519 => Ok(key_gen(Algorithm::X25519, rng)?),
|
||||
_ => Err(KKTError::KEMError {
|
||||
info: "Key Generation Error: Unsupported Libcrux Algorithm",
|
||||
}),
|
||||
}
|
||||
}
|
||||
// (decapsulation_key, encapsulation_key)
|
||||
pub fn generate_keypair_mceliece<'a, R>(
|
||||
rng: &mut R,
|
||||
) -> (
|
||||
classic_mceliece_rust::SecretKey<'a>,
|
||||
classic_mceliece_rust::PublicKey<'a>,
|
||||
)
|
||||
where
|
||||
// this is annoying because mceliece lib uses rand 0.8.5...
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let (encapsulation_key, decapsulation_key) = keypair_boxed(rng);
|
||||
(decapsulation_key, encapsulation_key)
|
||||
}
|
||||
|
||||
pub fn hash_key_bytes(
|
||||
hash_function: &HashFunction,
|
||||
hash_length: usize,
|
||||
key_bytes: &[u8],
|
||||
) -> Vec<u8> {
|
||||
let mut hashed_key: Vec<u8> = vec![0u8; hash_length];
|
||||
match hash_function {
|
||||
HashFunction::Blake3 => {
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
hasher.update(key_bytes);
|
||||
hasher.finalize_xof().fill(&mut hashed_key);
|
||||
hasher.reset();
|
||||
}
|
||||
HashFunction::SHAKE256 => {
|
||||
libcrux_sha3::shake256_ema(&mut hashed_key, &key_bytes);
|
||||
}
|
||||
HashFunction::SHAKE128 => {
|
||||
libcrux_sha3::shake128_ema(&mut hashed_key, &key_bytes);
|
||||
}
|
||||
HashFunction::SHA256 => {
|
||||
libcrux_sha3::sha256_ema(&mut hashed_key, &key_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
hashed_key
|
||||
}
|
||||
|
||||
/// This does NOT run in constant time.
|
||||
// It's fine for KKT since we are comparing hashes.
|
||||
fn compare_hashes(a: &[u8], b: &[u8]) -> bool {
|
||||
a == b
|
||||
}
|
||||
|
||||
pub fn validate_encapsulation_key(
|
||||
hash_function: &HashFunction,
|
||||
hash_length: usize,
|
||||
encapsulation_key: &[u8],
|
||||
expected_hash_bytes: &[u8],
|
||||
) -> bool {
|
||||
compare_hashes(
|
||||
&hash_encapsulation_key(hash_function, hash_length, encapsulation_key),
|
||||
expected_hash_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn validate_key_bytes(
|
||||
hash_function: &HashFunction,
|
||||
hash_length: usize,
|
||||
key_bytes: &[u8],
|
||||
expected_hash_bytes: &[u8],
|
||||
) -> bool {
|
||||
compare_hashes(
|
||||
&hash_key_bytes(hash_function, hash_length, key_bytes),
|
||||
expected_hash_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn hash_encapsulation_key(
|
||||
hash_function: &HashFunction,
|
||||
hash_length: usize,
|
||||
encapsulation_key: &[u8],
|
||||
) -> Vec<u8> {
|
||||
hash_key_bytes(hash_function, hash_length, &encapsulation_key)
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod ciphersuite;
|
||||
pub mod context;
|
||||
// pub mod encryption;
|
||||
pub mod error;
|
||||
pub mod frame;
|
||||
pub mod key_utils;
|
||||
pub mod session;
|
||||
|
||||
// pub mod psq;
|
||||
|
||||
// This must be less than 4 bits
|
||||
pub const KKT_VERSION: u8 = 1;
|
||||
const _: () = assert!(KKT_VERSION < 1 << 4);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM},
|
||||
frame::KKTFrame,
|
||||
key_utils::{generate_keypair_libcrux, generate_keypair_mceliece, hash_encapsulation_key},
|
||||
session::{
|
||||
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
||||
responder_ingest_message, responder_process,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_kkt_psq_e2e_clear() {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// generate ed25519 keys
|
||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_initiator);
|
||||
let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0);
|
||||
|
||||
let mut secret_responder: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_responder);
|
||||
let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1);
|
||||
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||
for hash_function in [
|
||||
HashFunction::Blake3,
|
||||
HashFunction::SHA256,
|
||||
HashFunction::SHAKE128,
|
||||
HashFunction::SHAKE256,
|
||||
] {
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
kem,
|
||||
hash_function,
|
||||
crate::ciphersuite::SignatureScheme::Ed25519,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// generate kem public keys
|
||||
|
||||
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||
KEM::MlKem768 => (
|
||||
EncapsulationKey::MlKem768(
|
||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||
),
|
||||
EncapsulationKey::MlKem768(
|
||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||
),
|
||||
),
|
||||
KEM::XWing => (
|
||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||
),
|
||||
KEM::X25519 => (
|
||||
EncapsulationKey::X25519(
|
||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||
),
|
||||
EncapsulationKey::X25519(
|
||||
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||
),
|
||||
),
|
||||
KEM::McEliece => (
|
||||
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||
),
|
||||
};
|
||||
|
||||
let i_kem_key_bytes = initiator_kem_public_key.encode();
|
||||
|
||||
let r_kem_key_bytes = responder_kem_public_key.encode();
|
||||
|
||||
let i_dir_hash = hash_encapsulation_key(
|
||||
&ciphersuite.hash_function(),
|
||||
ciphersuite.hash_len(),
|
||||
&i_kem_key_bytes,
|
||||
);
|
||||
|
||||
let r_dir_hash = hash_encapsulation_key(
|
||||
&ciphersuite.hash_function(),
|
||||
ciphersuite.hash_len(),
|
||||
&r_kem_key_bytes,
|
||||
);
|
||||
|
||||
// Anonymous Initiator, OneWay
|
||||
{
|
||||
let (mut i_context, i_frame) =
|
||||
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
let (mut r_context, _) =
|
||||
responder_ingest_message(&r_context, None, None, &i_frame_r).unwrap();
|
||||
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
let obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
// Initiator, OneWay
|
||||
{
|
||||
let (mut i_context, i_frame) = initiator_process(
|
||||
&mut rng,
|
||||
crate::context::KKTMode::OneWay,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
None,
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(r_obtained_key.is_none());
|
||||
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
let i_obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
|
||||
// Initiator, Mutual
|
||||
{
|
||||
let (mut i_context, i_frame) = initiator_process(
|
||||
&mut rng,
|
||||
crate::context::KKTMode::Mutual,
|
||||
ciphersuite,
|
||||
initiator_ed25519_keypair.private_key(),
|
||||
Some(&initiator_kem_public_key),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let i_frame_bytes = i_frame.to_bytes();
|
||||
|
||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||
|
||||
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||
&r_context,
|
||||
Some(initiator_ed25519_keypair.public_key()),
|
||||
Some(&i_dir_hash),
|
||||
&i_frame_r,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
|
||||
|
||||
let r_frame = responder_process(
|
||||
&mut r_context,
|
||||
i_frame_r.session_id_ref(),
|
||||
responder_ed25519_keypair.private_key(),
|
||||
&responder_kem_public_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let r_bytes = r_frame.to_bytes();
|
||||
|
||||
let obtained_key = initiator_ingest_response(
|
||||
&mut i_context,
|
||||
responder_ed25519_keypair.public_key(),
|
||||
&r_dir_hash,
|
||||
&r_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
use nym_crypto::asymmetric::ed25519::{self, Signature};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::{
|
||||
ciphersuite::{Ciphersuite, EncapsulationKey},
|
||||
context::{KKTContext, KKTMode, KKTRole, KKTStatus},
|
||||
error::KKTError,
|
||||
frame::{KKTFrame, KKT_SESSION_ID_LEN},
|
||||
key_utils::validate_encapsulation_key,
|
||||
};
|
||||
|
||||
pub fn initiator_process<'a, R>(
|
||||
rng: &mut R,
|
||||
mode: KKTMode,
|
||||
ciphersuite: Ciphersuite,
|
||||
signing_key: &ed25519::PrivateKey,
|
||||
own_encapsulation_key: Option<&EncapsulationKey<'a>>,
|
||||
) -> Result<(KKTContext, KKTFrame), KKTError>
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let context = KKTContext::new(KKTRole::Initiator, mode, ciphersuite)?;
|
||||
|
||||
let context_bytes = context.encode()?;
|
||||
|
||||
let mut session_id = [0; KKT_SESSION_ID_LEN];
|
||||
// Generate Session ID
|
||||
rng.fill_bytes(&mut session_id);
|
||||
|
||||
let body: &[u8] = match mode {
|
||||
KKTMode::OneWay => &[],
|
||||
KKTMode::Mutual => match own_encapsulation_key {
|
||||
Some(encaps_key) => &encaps_key.encode(),
|
||||
|
||||
// Missing key
|
||||
None => {
|
||||
return Err(KKTError::FunctionInputError {
|
||||
info: "KEM Key Not Provided",
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut bytes_to_sign =
|
||||
Vec::with_capacity(context.full_message_len() - context.signature_len());
|
||||
bytes_to_sign.extend_from_slice(&context_bytes);
|
||||
bytes_to_sign.extend_from_slice(&body);
|
||||
bytes_to_sign.extend_from_slice(&session_id);
|
||||
|
||||
let signature = signing_key.sign(bytes_to_sign).to_bytes();
|
||||
|
||||
Ok((
|
||||
context,
|
||||
KKTFrame::new(&context_bytes, &body, &session_id, &signature),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn anonymous_initiator_process<R>(
|
||||
rng: &mut R,
|
||||
ciphersuite: Ciphersuite,
|
||||
) -> Result<(KKTContext, KKTFrame), KKTError>
|
||||
where
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let context = KKTContext::new(KKTRole::AnonymousInitiator, KKTMode::OneWay, ciphersuite)?;
|
||||
let context_bytes = context.encode()?;
|
||||
|
||||
let mut session_id = [0u8; KKT_SESSION_ID_LEN];
|
||||
rng.fill_bytes(&mut session_id);
|
||||
|
||||
Ok((
|
||||
context,
|
||||
KKTFrame::new(&context_bytes, &[], &session_id, &[]),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn initiator_ingest_response<'a>(
|
||||
own_context: &mut KKTContext,
|
||||
remote_verification_key: &ed25519::PublicKey,
|
||||
expected_hash: &[u8],
|
||||
message_bytes: &[u8],
|
||||
) -> Result<EncapsulationKey<'a>, KKTError> {
|
||||
// sizes have to be correct
|
||||
let (frame, remote_context) = KKTFrame::from_bytes(message_bytes)?;
|
||||
|
||||
check_compatibility(&own_context, &remote_context)?;
|
||||
match remote_context.status() {
|
||||
KKTStatus::Ok => {
|
||||
let mut bytes_to_verify: Vec<u8> = Vec::with_capacity(
|
||||
remote_context.full_message_len() - remote_context.signature_len(),
|
||||
);
|
||||
bytes_to_verify.extend_from_slice(&remote_context.encode()?);
|
||||
bytes_to_verify.extend_from_slice(frame.body_ref());
|
||||
bytes_to_verify.extend_from_slice(frame.session_id_ref());
|
||||
|
||||
match Signature::from_bytes(frame.signature_ref()) {
|
||||
Ok(sig) => match remote_verification_key.verify(bytes_to_verify, &sig) {
|
||||
Ok(()) => {
|
||||
let received_encapsulation_key = EncapsulationKey::decode(
|
||||
own_context.ciphersuite().kem(),
|
||||
frame.body_ref(),
|
||||
)?;
|
||||
|
||||
match validate_encapsulation_key(
|
||||
&own_context.ciphersuite().hash_function(),
|
||||
own_context.ciphersuite().hash_len(),
|
||||
&frame.body_ref(),
|
||||
expected_hash,
|
||||
) {
|
||||
true => Ok(received_encapsulation_key),
|
||||
|
||||
// The key does not match the hash obtained from the directory
|
||||
false => Err(KKTError::KEMError { info: "Hash of received encapsulation key does not match the value stored on the directory." }),
|
||||
}
|
||||
}
|
||||
Err(_) => Err(KKTError::SigVerifError),
|
||||
},
|
||||
Err(_) => Err(KKTError::SigConstructorError),
|
||||
}
|
||||
}
|
||||
_ => Err(KKTError::ResponderFlaggedError {
|
||||
status: remote_context.status(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// todo: figure out how to handle errors using status codes
|
||||
|
||||
pub fn responder_ingest_message<'a>(
|
||||
remote_context: &KKTContext,
|
||||
remote_verification_key: Option<&ed25519::PublicKey>,
|
||||
expected_hash: Option<&[u8]>,
|
||||
remote_frame: &KKTFrame,
|
||||
) -> Result<(KKTContext, Option<EncapsulationKey<'a>>), KKTError> {
|
||||
let own_context = remote_context.derive_responder_header()?;
|
||||
|
||||
match remote_context.role() {
|
||||
KKTRole::AnonymousInitiator => Ok((own_context, None)),
|
||||
|
||||
KKTRole::Initiator => {
|
||||
match remote_verification_key {
|
||||
Some(remote_verif_key) => {
|
||||
let mut bytes_to_verify: Vec<u8> = Vec::with_capacity(
|
||||
own_context.full_message_len() - own_context.signature_len(),
|
||||
);
|
||||
bytes_to_verify.extend_from_slice(remote_frame.context_ref());
|
||||
bytes_to_verify.extend_from_slice(remote_frame.body_ref());
|
||||
bytes_to_verify.extend_from_slice(remote_frame.session_id_ref());
|
||||
|
||||
match Signature::from_bytes(remote_frame.signature_ref()) {
|
||||
Ok(sig) => match remote_verif_key.verify(bytes_to_verify, &sig) {
|
||||
Ok(()) => {
|
||||
// using own_context here because maybe for whatever reason we want to ignore the remote kem key
|
||||
match own_context.mode() {
|
||||
KKTMode::OneWay => return Ok((own_context, None)),
|
||||
KKTMode::Mutual => {
|
||||
match expected_hash {
|
||||
Some(expected_hash) =>{
|
||||
let received_encapsulation_key =
|
||||
EncapsulationKey::decode(own_context.ciphersuite().kem(), remote_frame.body_ref())?;
|
||||
if
|
||||
validate_encapsulation_key(
|
||||
&own_context.ciphersuite().hash_function(),
|
||||
own_context.ciphersuite().hash_len(),
|
||||
&remote_frame.body_ref(),
|
||||
expected_hash,
|
||||
){
|
||||
Ok((own_context, Some(received_encapsulation_key)))
|
||||
}
|
||||
// The key does not match the hash obtained from the directory
|
||||
else {
|
||||
Err(KKTError::KEMError { info: "Hash of received encapsulation key does not match the value stored on the directory." })
|
||||
}
|
||||
}
|
||||
None => Err(KKTError::FunctionInputError { info: "Expected hash of the remote encapsulation key is not provided." }),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => Err(KKTError::SigVerifError),
|
||||
},
|
||||
Err(_) => Err(KKTError::SigConstructorError),
|
||||
}
|
||||
}
|
||||
None => Err(KKTError::FunctionInputError {
|
||||
info: "Remote Signature Verification Key Not Provided",
|
||||
}),
|
||||
}
|
||||
}
|
||||
KKTRole::Responder => Err(KKTError::IncompatibilityError {
|
||||
info: "Responder received a request from another responder.",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn responder_process<'a>(
|
||||
own_context: &mut KKTContext,
|
||||
session_id: &[u8],
|
||||
signing_key: &ed25519::PrivateKey,
|
||||
encapsulation_key: &EncapsulationKey<'a>,
|
||||
) -> Result<KKTFrame, KKTError> {
|
||||
let body = encapsulation_key.encode();
|
||||
|
||||
let context_bytes = own_context.encode()?;
|
||||
|
||||
let mut bytes_to_sign =
|
||||
Vec::with_capacity(own_context.full_message_len() - own_context.signature_len());
|
||||
bytes_to_sign.extend_from_slice(&own_context.encode()?);
|
||||
bytes_to_sign.extend_from_slice(&body);
|
||||
bytes_to_sign.extend_from_slice(&session_id);
|
||||
|
||||
let signature = signing_key.sign(bytes_to_sign).to_bytes();
|
||||
|
||||
Ok(KKTFrame::new(
|
||||
&context_bytes,
|
||||
&body,
|
||||
&session_id,
|
||||
&signature,
|
||||
))
|
||||
}
|
||||
|
||||
fn check_compatibility(
|
||||
_own_context: &KKTContext,
|
||||
_remote_context: &KKTContext,
|
||||
) -> Result<(), KKTError> {
|
||||
// todo: check ciphersuite/context compatibility
|
||||
Ok(())
|
||||
}
|
||||
@@ -22,12 +22,6 @@ tokio-util = { workspace = true, features = ["codec"] }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-noise-keys = { path = "keys" }
|
||||
|
||||
nympsq = {path = "../nympsq"}
|
||||
libcrux-ed25519 = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = ["test-utils"] }
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
rand = "0.9.2"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
|
||||
@@ -9,7 +9,6 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(from = "u8", into = "u8")]
|
||||
pub enum NoiseVersion {
|
||||
V1,
|
||||
V2,
|
||||
Unknown(u8), //Implies a newer version we don't know
|
||||
}
|
||||
|
||||
@@ -17,7 +16,6 @@ impl From<u8> for NoiseVersion {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => NoiseVersion::V1,
|
||||
2 => NoiseVersion::V2,
|
||||
other => NoiseVersion::Unknown(other),
|
||||
}
|
||||
}
|
||||
@@ -27,7 +25,6 @@ impl From<NoiseVersion> for u8 {
|
||||
fn from(version: NoiseVersion) -> Self {
|
||||
match version {
|
||||
NoiseVersion::V1 => 1,
|
||||
NoiseVersion::V2 => 2,
|
||||
NoiseVersion::Unknown(other) => other,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_noise_keys::NoiseVersion;
|
||||
use nympsq::error::PSQError;
|
||||
use snow::Error;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
@@ -71,12 +70,6 @@ pub enum NoiseError {
|
||||
|
||||
#[error("Handshake timeout")]
|
||||
HandshakeTimeout(#[from] tokio::time::error::Elapsed),
|
||||
|
||||
#[error("Missing Field")]
|
||||
MissingField { info: &'static str },
|
||||
|
||||
#[error("PSQ Error")]
|
||||
PSQError(PSQError),
|
||||
}
|
||||
|
||||
impl NoiseError {
|
||||
@@ -88,18 +81,6 @@ impl NoiseError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PSQError> for NoiseError {
|
||||
fn from(err: PSQError) -> Self {
|
||||
NoiseError::PSQError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libcrux_kem::Error> for NoiseError {
|
||||
fn from(err: libcrux_kem::Error) -> Self {
|
||||
NoiseError::PSQError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for NoiseError {
|
||||
fn from(err: Error) -> Self {
|
||||
match err {
|
||||
|
||||
+28
-136
@@ -1,11 +1,9 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_noise_keys::NoiseVersion;
|
||||
use nympsq::psq::CONTEXT_LEN;
|
||||
|
||||
use snow::{error::Prerequisite, Error};
|
||||
use snow::error::Prerequisite;
|
||||
use snow::Error;
|
||||
use tokio::net::TcpStream;
|
||||
use tracing::{error, warn};
|
||||
|
||||
@@ -14,138 +12,48 @@ pub mod connection;
|
||||
pub mod error;
|
||||
pub mod stream;
|
||||
|
||||
use crate::{
|
||||
config::NoiseConfig, connection::Connection, error::NoiseError, stream::NoiseStreamBuilder,
|
||||
};
|
||||
use crate::config::NoiseConfig;
|
||||
use crate::connection::Connection;
|
||||
use crate::error::NoiseError;
|
||||
use crate::stream::NoiseStreamBuilder;
|
||||
|
||||
const NOISE_PSK_PREFIX: &[u8] = b"NYMTECH_NOISE_dQw4w9WgXcQ";
|
||||
|
||||
pub(crate) const NOISE_PSQ_DEFAULT_CONTEXT: &[u8; CONTEXT_LEN] = b"Exsl88AD2ccS99kk";
|
||||
pub(crate) const NOISE_PSQ_DEFAULT_DURATION_SECS: u64 = 1000;
|
||||
|
||||
pub const LATEST_NOISE_VERSION: NoiseVersion = NoiseVersion::V2;
|
||||
pub const LATEST_NOISE_VERSION: NoiseVersion = NoiseVersion::V1;
|
||||
|
||||
// TODO: this should be behind some trait because presumably, depending on the version,
|
||||
// other arguments would be needed
|
||||
mod psk_gen {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{error::NoiseError, stream::Psk, NOISE_PSK_PREFIX};
|
||||
use libcrux_ed25519::VerificationKey;
|
||||
use libcrux_kem::{PrivateKey, PublicKey};
|
||||
use libcrux_psq::impls::{MlKem768, XWingKemDraft06, X25519};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
|
||||
use nympsq::{
|
||||
error::PSQError,
|
||||
psq::{PSQInitiator, PSQResponder, CONTEXT_LEN, PSK_HANDLE_LEN},
|
||||
};
|
||||
|
||||
use crate::error::NoiseError;
|
||||
use crate::stream::Psk;
|
||||
use crate::NOISE_PSK_PREFIX;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_noise_keys::NoiseVersion;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub fn generate_psk_v1(responder_pub_key: &[u8]) -> [u8; 32] {
|
||||
pub(crate) fn generate_psk(
|
||||
responder_pub_key: x25519::PublicKey,
|
||||
version: NoiseVersion,
|
||||
) -> Result<Psk, NoiseError> {
|
||||
match version {
|
||||
NoiseVersion::V1 => Ok(generate_psk_v1(responder_pub_key)),
|
||||
NoiseVersion::Unknown(noise_version) => {
|
||||
Err(NoiseError::PskGenerationFailure { noise_version })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_psk_v1(responder_pub_key: x25519::PublicKey) -> [u8; 32] {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(NOISE_PSK_PREFIX);
|
||||
hasher.update(responder_pub_key);
|
||||
hasher.update(responder_pub_key.to_bytes());
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
pub(crate) fn psq_initiate_x25519(
|
||||
initiator: &mut PSQInitiator<X25519>,
|
||||
responder_pub_key: impl AsRef<[u8]>,
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psq_ttl: Duration,
|
||||
) -> Result<Vec<u8>, NoiseError> {
|
||||
let pub_key =
|
||||
PublicKey::decode(libcrux_kem::Algorithm::X25519, responder_pub_key.as_ref())?;
|
||||
let message =
|
||||
initiator.compute_initiator_message(&mut rand::rng(), &pub_key, context, psq_ttl)?;
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub(crate) fn psq_respond_x25519(
|
||||
responder_private_key: impl AsRef<[u8]>,
|
||||
responder_public_key: impl AsRef<[u8]>,
|
||||
initiator_verification_key: &ed25519::PublicKey,
|
||||
initiator_message: &mut [u8],
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psq_ttl: Duration,
|
||||
psk_handle: &[u8; PSK_HANDLE_LEN],
|
||||
) -> Result<(Psk, Vec<u8>), PSQError> {
|
||||
let kem_private_key = PrivateKey::decode(
|
||||
libcrux_kem::Algorithm::X25519,
|
||||
responder_private_key.as_ref(),
|
||||
)?;
|
||||
let kem_public_key = PublicKey::decode(
|
||||
libcrux_kem::Algorithm::X25519,
|
||||
responder_public_key.as_ref(),
|
||||
)?;
|
||||
|
||||
let responder: PSQResponder<X25519> = PSQResponder::init(&kem_private_key, &kem_public_key);
|
||||
|
||||
let res = responder.compute_responder_message(
|
||||
&VerificationKey::from_bytes(initiator_verification_key.to_bytes()),
|
||||
initiator_message,
|
||||
context,
|
||||
psq_ttl,
|
||||
psk_handle,
|
||||
)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub(crate) fn psq_initiate_xwing(
|
||||
initiator: &mut PSQInitiator<XWingKemDraft06>,
|
||||
responder_pub_key: impl AsRef<[u8]>,
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psq_ttl: Duration,
|
||||
) -> Result<Vec<u8>, NoiseError> {
|
||||
let pub_key = PublicKey::decode(
|
||||
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||
responder_pub_key.as_ref(),
|
||||
)?;
|
||||
let message =
|
||||
initiator.compute_initiator_message(&mut rand::rng(), &pub_key, context, psq_ttl)?;
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub(crate) fn psq_respond_xwing(
|
||||
responder_private_key: impl AsRef<[u8]>,
|
||||
responder_public_key: impl AsRef<[u8]>,
|
||||
initiator_verification_key: &ed25519::PublicKey,
|
||||
initiator_message: &mut [u8],
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psq_ttl: Duration,
|
||||
psk_handle: &[u8; PSK_HANDLE_LEN],
|
||||
) -> Result<(Psk, Vec<u8>), PSQError> {
|
||||
let kem_private_key = PrivateKey::decode(
|
||||
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||
responder_private_key.as_ref(),
|
||||
)?;
|
||||
let kem_public_key = PublicKey::decode(
|
||||
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||
responder_public_key.as_ref(),
|
||||
)?;
|
||||
|
||||
let responder: PSQResponder<XWingKemDraft06> =
|
||||
PSQResponder::init(&kem_private_key, &kem_public_key);
|
||||
|
||||
let res = responder.compute_responder_message(
|
||||
&VerificationKey::from_bytes(initiator_verification_key.to_bytes()),
|
||||
initiator_message,
|
||||
context,
|
||||
psq_ttl,
|
||||
psk_handle,
|
||||
)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upgrade_noise_initiator(
|
||||
conn: TcpStream,
|
||||
config: &NoiseConfig,
|
||||
initiator_identity_keypair: Option<ed25519::KeyPair>,
|
||||
) -> Result<Connection<TcpStream>, NoiseError> {
|
||||
if config.unsafe_disabled {
|
||||
warn!("Noise is disabled in the config. Not attempting any handshake");
|
||||
@@ -165,7 +73,6 @@ pub async fn upgrade_noise_initiator(
|
||||
|
||||
let handshake_version = match key.supported_version {
|
||||
NoiseVersion::V1 => NoiseVersion::V1,
|
||||
NoiseVersion::V2 => NoiseVersion::V2,
|
||||
|
||||
// We're talking to a more recent node, but we can't adapt. Let's try to do our best and if it fails, it fails.
|
||||
// If that node sees we're older, it will try to adapt too.
|
||||
@@ -175,29 +82,14 @@ pub async fn upgrade_noise_initiator(
|
||||
}
|
||||
};
|
||||
|
||||
let (signing_key, verification_key): (Option<[u8; 32]>, Option<[u8; 32]>) =
|
||||
match initiator_identity_keypair {
|
||||
Some(keypair) => (
|
||||
Some(keypair.private_key().to_bytes()),
|
||||
Some(keypair.public_key().to_bytes()),
|
||||
),
|
||||
None => (None, None),
|
||||
};
|
||||
NoiseStreamBuilder::new(conn)
|
||||
.perform_initiator_handshake(
|
||||
config,
|
||||
handshake_version,
|
||||
key.x25519_pubkey,
|
||||
signing_key,
|
||||
verification_key,
|
||||
)
|
||||
.perform_initiator_handshake(config, handshake_version, key.x25519_pubkey)
|
||||
.await
|
||||
.map(|stream| Connection::Noise(Box::new(stream)))
|
||||
}
|
||||
pub async fn upgrade_noise_responder(
|
||||
conn: TcpStream,
|
||||
config: &NoiseConfig,
|
||||
initiator_verification_key: Option<ed25519::PublicKey>,
|
||||
) -> Result<Connection<TcpStream>, NoiseError> {
|
||||
if config.unsafe_disabled {
|
||||
warn!("Noise is disabled in the config. Not attempting any handshake");
|
||||
@@ -220,7 +112,7 @@ pub async fn upgrade_noise_responder(
|
||||
};
|
||||
|
||||
NoiseStreamBuilder::new(conn)
|
||||
.perform_responder_handshake(config, initiator_verification_key)
|
||||
.perform_responder_handshake(config)
|
||||
.await
|
||||
.map(|stream| Connection::Noise(Box::new(stream)))
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
config::{NoiseConfig, NoisePattern},
|
||||
error::NoiseError,
|
||||
psk_gen::{generate_psk_v1, psq_initiate_x25519, psq_respond_x25519},
|
||||
stream::{codec::NymNoiseCodec, framing::NymNoiseFrame},
|
||||
NOISE_PSQ_DEFAULT_CONTEXT, NOISE_PSQ_DEFAULT_DURATION_SECS,
|
||||
};
|
||||
|
||||
use crate::config::{NoiseConfig, NoisePattern};
|
||||
use crate::error::NoiseError;
|
||||
use crate::psk_gen::generate_psk;
|
||||
use crate::stream::codec::NymNoiseCodec;
|
||||
use crate::stream::framing::NymNoiseFrame;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{Sink, SinkExt, Stream, StreamExt};
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_noise_keys::NoiseVersion;
|
||||
use nympsq::psq::{PSQInitiator, PSK_HANDLE_LEN};
|
||||
use snow::{Builder, HandshakeState, TransportState};
|
||||
use std::{cmp::min, io, pin::Pin, task::ready, task::Poll, time::Duration};
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::Poll;
|
||||
use std::{cmp::min, task::ready};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
@@ -23,7 +22,6 @@ mod codec;
|
||||
mod framing;
|
||||
|
||||
const TAGLEN: usize = 16;
|
||||
// This needs to be increased when using psq
|
||||
const HANDSHAKE_MAX_LEN: usize = 1024; // using this constant to limit the handshake's buffer size
|
||||
|
||||
pub(crate) type Psk = [u8; 32];
|
||||
@@ -47,65 +45,19 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
pattern: NoisePattern,
|
||||
local_private_key: impl AsRef<[u8]>,
|
||||
remote_pub_key: impl AsRef<[u8]>,
|
||||
local_signing_key: Option<impl AsRef<[u8]>>,
|
||||
local_verification_key: Option<impl AsRef<[u8]>>,
|
||||
psk: Psk,
|
||||
version: NoiseVersion,
|
||||
) -> Result<NoiseStream<C>, NoiseError>
|
||||
where
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let mut handshake = Builder::new(pattern.as_noise_params())
|
||||
let handshake = Builder::new(pattern.as_noise_params())
|
||||
.local_private_key(local_private_key.as_ref())
|
||||
.remote_public_key(remote_pub_key.as_ref())
|
||||
.psk(pattern.psk_position(), &psk)
|
||||
.build_initiator()?;
|
||||
|
||||
let payload: Vec<u8> = match version {
|
||||
NoiseVersion::V1 => {
|
||||
handshake.set_psk(
|
||||
pattern.psk_position() as usize,
|
||||
&generate_psk_v1(remote_pub_key.as_ref()),
|
||||
)?;
|
||||
vec![]
|
||||
}
|
||||
NoiseVersion::V2 => match (local_signing_key, local_verification_key) {
|
||||
(Some(signing_key), Some(verification_key)) => {
|
||||
let mut psq_initiator = PSQInitiator::init(signing_key, verification_key);
|
||||
let payload = psq_initiate_x25519(
|
||||
&mut psq_initiator,
|
||||
&remote_pub_key,
|
||||
NOISE_PSQ_DEFAULT_CONTEXT,
|
||||
Duration::from_secs(NOISE_PSQ_DEFAULT_DURATION_SECS),
|
||||
)?;
|
||||
handshake.set_psk(
|
||||
pattern.psk_position() as usize,
|
||||
//
|
||||
&psq_initiator.get_psk().unwrap(),
|
||||
)?;
|
||||
payload
|
||||
}
|
||||
(None, None) => {
|
||||
return Err(NoiseError::MissingField {
|
||||
info: "Missing Local Signing Key and Local Verification Key",
|
||||
})
|
||||
}
|
||||
(None, _) => {
|
||||
return Err(NoiseError::MissingField {
|
||||
info: "Local Signing Key",
|
||||
})
|
||||
}
|
||||
(_, None) => {
|
||||
return Err(NoiseError::MissingField {
|
||||
info: "Local Verification Key",
|
||||
})
|
||||
}
|
||||
},
|
||||
NoiseVersion::Unknown(version) => {
|
||||
return Err(NoiseError::UnknownVersion { encoded: version })
|
||||
}
|
||||
};
|
||||
|
||||
self.perform_handshake(handshake, payload, version, pattern)
|
||||
.await
|
||||
self.perform_handshake(handshake, version, pattern).await
|
||||
}
|
||||
|
||||
pub(crate) async fn perform_initiator_handshake(
|
||||
@@ -113,12 +65,12 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
config: &NoiseConfig,
|
||||
version: NoiseVersion,
|
||||
remote_pub_key: x25519::PublicKey,
|
||||
local_signing_key: Option<impl AsRef<[u8]>>,
|
||||
local_verification_key: Option<impl AsRef<[u8]>>,
|
||||
) -> Result<NoiseStream<C>, NoiseError>
|
||||
where
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let psk = generate_psk(remote_pub_key, version)?;
|
||||
|
||||
let timeout = config.timeout;
|
||||
tokio::time::timeout(
|
||||
timeout,
|
||||
@@ -126,8 +78,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
config.pattern,
|
||||
config.local_key.private_key(),
|
||||
remote_pub_key,
|
||||
local_signing_key,
|
||||
local_verification_key,
|
||||
psk,
|
||||
version,
|
||||
),
|
||||
)
|
||||
@@ -138,8 +89,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
mut self,
|
||||
noise_pattern: NoisePattern,
|
||||
local_private_key: impl AsRef<[u8]>,
|
||||
local_public_key: impl AsRef<[u8]>,
|
||||
initiator_verification_key: Option<ed25519::PublicKey>,
|
||||
local_pub_key: x25519::PublicKey,
|
||||
) -> Result<NoiseStream<C>, NoiseError>
|
||||
where
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
@@ -167,58 +117,26 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
});
|
||||
}
|
||||
|
||||
// 2. generate psk and handshake state
|
||||
let psk = generate_psk(local_pub_key, initial_frame.header.version)?;
|
||||
|
||||
let mut handshake = Builder::new(pattern.as_noise_params())
|
||||
.local_private_key(local_private_key.as_ref())
|
||||
.psk(pattern.psk_position(), &psk)
|
||||
.build_responder()?;
|
||||
|
||||
// update handshake state with initial frame
|
||||
let mut buf = BytesMut::zeroed(HANDSHAKE_MAX_LEN);
|
||||
let len = handshake.read_message(&initial_frame.data, &mut buf)?;
|
||||
buf.truncate(len);
|
||||
|
||||
let payload: Vec<u8> = match initial_frame.version() {
|
||||
NoiseVersion::V1 => {
|
||||
handshake.set_psk(
|
||||
pattern.psk_position() as usize,
|
||||
&generate_psk_v1(local_public_key.as_ref()),
|
||||
)?;
|
||||
vec![]
|
||||
}
|
||||
NoiseVersion::V2 => match initiator_verification_key {
|
||||
Some(verif_key) => {
|
||||
let (psk, message) = psq_respond_x25519(
|
||||
&local_private_key,
|
||||
&local_public_key,
|
||||
&verif_key,
|
||||
&mut buf,
|
||||
NOISE_PSQ_DEFAULT_CONTEXT,
|
||||
Duration::from_secs(NOISE_PSQ_DEFAULT_DURATION_SECS),
|
||||
&[1; PSK_HANDLE_LEN],
|
||||
)?;
|
||||
handshake.set_psk(pattern.psk_position() as usize, psk.as_slice())?;
|
||||
|
||||
message
|
||||
}
|
||||
None => {
|
||||
return Err(NoiseError::MissingField {
|
||||
info: "Initiator Verification Key",
|
||||
})
|
||||
}
|
||||
},
|
||||
NoiseVersion::Unknown(version) => {
|
||||
return Err(NoiseError::UnknownVersion { encoded: version })
|
||||
}
|
||||
};
|
||||
handshake.read_message(&initial_frame.data, &mut buf)?;
|
||||
|
||||
// 3. run handshake to completion
|
||||
self.perform_handshake(handshake, payload, initial_frame.version(), pattern)
|
||||
self.perform_handshake(handshake, initial_frame.version(), pattern)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn perform_responder_handshake(
|
||||
self,
|
||||
config: &NoiseConfig,
|
||||
initiator_verification_key: Option<ed25519::PublicKey>,
|
||||
) -> Result<NoiseStream<C>, NoiseError>
|
||||
where
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
@@ -229,8 +147,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
self.perform_responder_handshake_inner(
|
||||
config.pattern,
|
||||
config.local_key.private_key(),
|
||||
config.local_key.public_key(),
|
||||
initiator_verification_key,
|
||||
*config.local_key.public_key(),
|
||||
),
|
||||
)
|
||||
.await?
|
||||
@@ -239,7 +156,6 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
async fn send_handshake_msg(
|
||||
&mut self,
|
||||
handshake: &mut HandshakeState,
|
||||
payload: impl AsRef<[u8]>,
|
||||
version: NoiseVersion,
|
||||
pattern: NoisePattern,
|
||||
) -> Result<(), NoiseError>
|
||||
@@ -247,7 +163,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let mut buf = BytesMut::zeroed(HANDSHAKE_MAX_LEN); // we're in the handshake, we can afford a smaller buffer
|
||||
let len = handshake.write_message(payload.as_ref(), &mut buf)?;
|
||||
let len = handshake.write_message(&[], &mut buf)?;
|
||||
buf.truncate(len);
|
||||
|
||||
let frame = NymNoiseFrame::new_handshake_frame(buf.freeze(), version, pattern)?;
|
||||
@@ -260,7 +176,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
handshake: &mut HandshakeState,
|
||||
version: NoiseVersion,
|
||||
pattern: NoisePattern,
|
||||
) -> Result<Bytes, NoiseError>
|
||||
) -> Result<(), NoiseError>
|
||||
where
|
||||
C: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
@@ -283,12 +199,9 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
});
|
||||
}
|
||||
|
||||
// The buffer below is the payload...
|
||||
// we're in the handshake, we can afford a smaller buffer
|
||||
|
||||
let mut buf = BytesMut::zeroed(HANDSHAKE_MAX_LEN);
|
||||
let mut buf = BytesMut::zeroed(HANDSHAKE_MAX_LEN); // we're in the handshake, we can afford a smaller buffer
|
||||
handshake.read_message(&frame.data, &mut buf)?;
|
||||
Ok(buf.into())
|
||||
Ok(())
|
||||
}
|
||||
Some(Err(err)) => Err(err),
|
||||
None => Err(NoiseError::HandshakeError),
|
||||
@@ -298,7 +211,6 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
async fn perform_handshake(
|
||||
mut self,
|
||||
mut handshake_state: HandshakeState,
|
||||
payload: impl AsRef<[u8]>,
|
||||
version: NoiseVersion,
|
||||
pattern: NoisePattern,
|
||||
) -> Result<NoiseStream<C>, NoiseError>
|
||||
@@ -307,7 +219,7 @@ impl<C> NoiseStreamBuilder<C> {
|
||||
{
|
||||
while !handshake_state.is_handshake_finished() {
|
||||
if handshake_state.is_my_turn() {
|
||||
self.send_handshake_msg(&mut handshake_state, &payload, version, pattern)
|
||||
self.send_handshake_msg(&mut handshake_state, version, pattern)
|
||||
.await?;
|
||||
} else {
|
||||
self.recv_handshake_msg(&mut handshake_state, version, pattern)
|
||||
@@ -607,26 +519,24 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn noise_handshake_v1() -> anyhow::Result<()> {
|
||||
async fn noise_handshake() -> anyhow::Result<()> {
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
|
||||
let initiator_keys = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
let responder_keys = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
|
||||
let no_key: Option<[u8; 0]> = None;
|
||||
|
||||
let (initiator_stream, responder_stream) = mock_streams();
|
||||
|
||||
let psk = generate_psk(*responder_keys.public_key(), NoiseVersion::V1)?;
|
||||
let pattern = NoisePattern::default();
|
||||
|
||||
let stream_initiator = NoiseStreamBuilder::new(initiator_stream)
|
||||
.perform_initiator_handshake_inner(
|
||||
pattern,
|
||||
initiator_keys.private_key().to_bytes(),
|
||||
*responder_keys.public_key(),
|
||||
no_key,
|
||||
no_key,
|
||||
responder_keys.public_key().to_bytes(),
|
||||
psk,
|
||||
NoiseVersion::V1,
|
||||
);
|
||||
|
||||
@@ -635,75 +545,6 @@ mod tests {
|
||||
pattern,
|
||||
responder_keys.private_key().to_bytes(),
|
||||
*responder_keys.public_key(),
|
||||
None,
|
||||
);
|
||||
|
||||
let initiator_fut =
|
||||
tokio::spawn(
|
||||
async move { timeout(Duration::from_millis(200), stream_initiator).await },
|
||||
);
|
||||
let responder_fut =
|
||||
tokio::spawn(
|
||||
async move { timeout(Duration::from_millis(200), stream_responder).await },
|
||||
);
|
||||
|
||||
let (initiator, responder) = join!(initiator_fut, responder_fut);
|
||||
|
||||
let mut initiator = initiator???;
|
||||
let mut responder = responder???;
|
||||
|
||||
let msg = b"hello there";
|
||||
// if noise was successful we should be able to write a proper message across
|
||||
timeout(Duration::from_millis(200), initiator.write_all(msg)).await??;
|
||||
|
||||
initiator.inner_stream.flush().await?;
|
||||
|
||||
let inner_buf = initiator.inner_stream.get_ref().unchecked_tx_data();
|
||||
|
||||
let mut buf = [0u8; 11];
|
||||
timeout(Duration::from_millis(200), responder.read(&mut buf)).await??;
|
||||
|
||||
assert_eq!(&buf[..], msg);
|
||||
|
||||
// the inner content is different from the actual msg since it was encrypted
|
||||
assert_ne!(inner_buf, buf);
|
||||
assert_ne!(inner_buf.len(), msg.len());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
async fn noise_handshake_v2() -> anyhow::Result<()> {
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
|
||||
let initiator_keys = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
let initator_identity_keys = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
let responder_keys = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||
|
||||
let init_verif_key = initator_identity_keys.public_key().clone();
|
||||
|
||||
let (initiator_stream, responder_stream) = mock_streams();
|
||||
|
||||
let pattern = NoisePattern::default();
|
||||
|
||||
let stream_initiator = NoiseStreamBuilder::new(initiator_stream)
|
||||
.perform_initiator_handshake_inner(
|
||||
pattern,
|
||||
initiator_keys.private_key().to_bytes(),
|
||||
responder_keys.public_key().to_bytes(),
|
||||
Some(initator_identity_keys.private_key().to_bytes()),
|
||||
Some(initator_identity_keys.public_key().to_bytes()),
|
||||
NoiseVersion::V2,
|
||||
);
|
||||
|
||||
let stream_responder = NoiseStreamBuilder::new(responder_stream)
|
||||
.perform_responder_handshake_inner(
|
||||
pattern,
|
||||
responder_keys.private_key().to_bytes(),
|
||||
*responder_keys.public_key(),
|
||||
Some(init_verif_key),
|
||||
);
|
||||
|
||||
let initiator_fut =
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
[package]
|
||||
name = "nympsq"
|
||||
version = "0.1.0"
|
||||
authors = ["Georgio Nicolas <georgio@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"]}
|
||||
|
||||
libcrux-traits = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = ["test-utils"] }
|
||||
libcrux-ed25519 = { git = "https://github.com/cryspen/libcrux" }
|
||||
|
||||
rand = "0.9.2"
|
||||
tls_codec = { version = "0.4.2", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = {workspace = true}
|
||||
|
||||
[[bench]]
|
||||
name = "benches"
|
||||
harness = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use rand::prelude::*;
|
||||
|
||||
pub fn gen_ed25519_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate Ed25519 Keypair", |b| {
|
||||
b.iter(|| {
|
||||
let mut s: [u8; 32] = [0u8; 32];
|
||||
rand::rng().fill_bytes(&mut s);
|
||||
ed25519::KeyPair::from_secret(s, 0)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn gen_mlkem768_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate MlKem768 Keypair", |b| {
|
||||
b.iter(|| {
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rand::rng()).unwrap()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn gen_x25519_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate DHKem Keypair", |b| {
|
||||
b.iter(|| libcrux_kem::key_gen(libcrux_kem::Algorithm::X25519, &mut rand::rng()).unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
pub fn gen_xwing_keypair(c: &mut Criterion) {
|
||||
c.bench_function("Generate XWingKem Keypair", |b| {
|
||||
b.iter(|| {
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::XWingKemDraft06, &mut rand::rng()).unwrap()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
gen_ed25519_keypair,
|
||||
gen_mlkem768_keypair,
|
||||
gen_x25519_keypair,
|
||||
gen_xwing_keypair
|
||||
);
|
||||
criterion_main!(benches);
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PSQError {
|
||||
#[error("encountered a decryption error")]
|
||||
DecryptionError,
|
||||
|
||||
#[error("encountered a KEM error")]
|
||||
KEMError,
|
||||
|
||||
#[error("encountered a PSQ error")]
|
||||
PSQError,
|
||||
|
||||
#[error("encountered a Serialization/Deserialization error")]
|
||||
SerializationError,
|
||||
|
||||
#[error("encountered an IO error: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
|
||||
#[error("Incorrect state")]
|
||||
IncorrectStateError,
|
||||
|
||||
#[error("Handshake did not complete")]
|
||||
HandshakeError,
|
||||
|
||||
#[error("Unknown PSQ version")]
|
||||
UnknownVersion,
|
||||
}
|
||||
|
||||
impl From<libcrux_kem::Error> for PSQError {
|
||||
fn from(err: libcrux_kem::Error) -> Self {
|
||||
match err {
|
||||
// Error::Decrypt => PSQError::DecryptionError,
|
||||
_err => PSQError::KEMError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libcrux_psq::Error> for PSQError {
|
||||
fn from(err: libcrux_psq::Error) -> Self {
|
||||
match err {
|
||||
// Error::Decrypt => PSQError::DecryptionError,
|
||||
_err => PSQError::PSQError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tls_codec::Error> for PSQError {
|
||||
fn from(err: tls_codec::Error) -> Self {
|
||||
match err {
|
||||
_err => PSQError::SerializationError,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod error;
|
||||
pub mod psq;
|
||||
@@ -1,270 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{io::Cursor, marker::PhantomData, time::Duration};
|
||||
|
||||
use libcrux_ed25519::VerificationKey;
|
||||
use libcrux_psq::{
|
||||
cred::Ed25519,
|
||||
psk_registration::{Initiator, InitiatorMsg, Responder, ResponderMsg},
|
||||
traits::PSQ,
|
||||
};
|
||||
|
||||
use tls_codec::{Deserialize, Serialize};
|
||||
|
||||
use libcrux_traits::kem::KEM;
|
||||
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crate::error::PSQError;
|
||||
|
||||
pub const PSK_LENGTH: usize = 32;
|
||||
pub const CONTEXT_LEN: usize = 16;
|
||||
pub const PSK_HANDLE_LEN: usize = 16;
|
||||
|
||||
pub struct PSQInitiator<T: PSQ> {
|
||||
signing_key: [u8; 32],
|
||||
verification_key: VerificationKey,
|
||||
state: Option<Initiator>,
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: PSQ> PSQInitiator<T> {
|
||||
pub fn init(signing_key: impl AsRef<[u8]>, verification_key: impl AsRef<[u8]>) -> Self {
|
||||
let mut sig_key: [u8; 32] = [0u8; 32];
|
||||
sig_key.clone_from_slice(signing_key.as_ref());
|
||||
|
||||
let mut verif_key: [u8; 32] = [0u8; 32];
|
||||
verif_key.clone_from_slice(verification_key.as_ref());
|
||||
|
||||
Self {
|
||||
signing_key: sig_key,
|
||||
verification_key: VerificationKey::from_bytes(verif_key),
|
||||
state: None,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_initiator_message<R>(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
responder_kem_public_key: &<T::InnerKEM as KEM>::EncapsulationKey,
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psk_ttl: Duration,
|
||||
) -> Result<Vec<u8>, PSQError>
|
||||
where
|
||||
<T as PSQ>::InnerKEM: KEM,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let (state, message) = Initiator::send_initial_message::<Ed25519, T>(
|
||||
context,
|
||||
psk_ttl,
|
||||
responder_kem_public_key,
|
||||
&self.signing_key,
|
||||
&self.verification_key,
|
||||
rng,
|
||||
)?;
|
||||
self.state = Some(state);
|
||||
|
||||
Ok(message.tls_serialize_detached()?)
|
||||
}
|
||||
|
||||
// The initator generates the PSK themselves while computing the first message.
|
||||
// It's possible to get_psk() this after sending the first message.
|
||||
// The key will only be valid if we're sure that the responder has received it.
|
||||
//
|
||||
// If we use noise right after PSQ, we can just extract the PSK and expect the responder
|
||||
// to just send the first Noise message instead of responding to with a PSQ message.
|
||||
// In other words, instead of using the responder's PSQ message for key confirmation,
|
||||
// we could use a Noise handshake for key confirmation.
|
||||
|
||||
pub fn get_psk(&self) -> Option<[u8; 32]> {
|
||||
match &self.state {
|
||||
Some(initiator_state) => Some(*initiator_state.unregistered_psk()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalize(&self, responder_message: &[u8]) -> Result<[u8; PSK_LENGTH], PSQError> {
|
||||
match &self.state {
|
||||
Some(state) => match ResponderMsg::tls_deserialize_exact(responder_message) {
|
||||
Ok(deserialized_responder_message) => {
|
||||
match state.complete_handshake(&deserialized_responder_message) {
|
||||
Ok(psk) => Ok(psk.psk),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
},
|
||||
|
||||
None => Err(PSQError::IncorrectStateError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PSQResponder<'a, T: PSQ>
|
||||
where
|
||||
<T as PSQ>::InnerKEM: KEM,
|
||||
{
|
||||
kem_private_key: &'a <T::InnerKEM as KEM>::DecapsulationKey,
|
||||
kem_public_key: &'a <T::InnerKEM as KEM>::EncapsulationKey,
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
impl<'a, T: PSQ> PSQResponder<'a, T>
|
||||
where
|
||||
<T as PSQ>::InnerKEM: KEM,
|
||||
{
|
||||
pub fn init(
|
||||
kem_private_key: &'a <T::InnerKEM as KEM>::DecapsulationKey,
|
||||
kem_public_key: &'a <T::InnerKEM as KEM>::EncapsulationKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
kem_private_key,
|
||||
kem_public_key,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_responder_message(
|
||||
&self,
|
||||
initiator_verification_key: &VerificationKey,
|
||||
initiator_message: &mut [u8],
|
||||
context: &[u8; CONTEXT_LEN],
|
||||
psk_ttl: Duration,
|
||||
psk_handle: &[u8; PSK_HANDLE_LEN],
|
||||
) -> Result<([u8; PSK_LENGTH], Vec<u8>), PSQError> {
|
||||
let mut cursor = Cursor::new(initiator_message);
|
||||
let deserialized_initiator_message = InitiatorMsg::tls_deserialize(&mut cursor)?;
|
||||
|
||||
// the problem is here the deserialized message is the same on the initiator's side
|
||||
let (registered_psk, responder_msg) = Responder::send::<Ed25519, T>(
|
||||
psk_handle,
|
||||
psk_ttl,
|
||||
context,
|
||||
self.kem_public_key,
|
||||
self.kem_private_key,
|
||||
initiator_verification_key,
|
||||
&deserialized_initiator_message,
|
||||
)?;
|
||||
|
||||
let responder_bytes = responder_msg.tls_serialize_detached()?;
|
||||
Ok((registered_psk.psk, responder_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::time::Duration;
|
||||
|
||||
use libcrux_ed25519::VerificationKey;
|
||||
use libcrux_psq::impls::{MlKem768, XWingKemDraft06, X25519};
|
||||
use libcrux_psq::traits::PSQ;
|
||||
use libcrux_traits::kem::KEM;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::psq::{CONTEXT_LEN, PSK_HANDLE_LEN};
|
||||
|
||||
use super::{PSQInitiator, PSQResponder};
|
||||
|
||||
fn test_helper<T: PSQ, R>(
|
||||
rng: &mut R,
|
||||
responder_kem_private_key: <T::InnerKEM as KEM>::DecapsulationKey,
|
||||
responder_kem_public_key: <T::InnerKEM as KEM>::EncapsulationKey,
|
||||
) where
|
||||
R: CryptoRng + rand::RngCore,
|
||||
{
|
||||
// set PSQ TTL
|
||||
let psk_ttl: Duration = Duration::from_secs(3600);
|
||||
|
||||
// generate random context string
|
||||
let mut context: [u8; CONTEXT_LEN] = [0u8; CONTEXT_LEN];
|
||||
rng.fill_bytes(&mut context);
|
||||
|
||||
// generate random psk handle
|
||||
let mut psk_handle: [u8; PSK_HANDLE_LEN] = [0u8; PSK_HANDLE_LEN];
|
||||
rng.fill_bytes(&mut psk_handle);
|
||||
|
||||
// generate ed25519 keys
|
||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||
rng.fill_bytes(&mut secret_initiator);
|
||||
let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0);
|
||||
|
||||
let mut initiator: PSQInitiator<T> = PSQInitiator::init(
|
||||
initiator_ed25519_keypair.private_key().to_bytes(),
|
||||
initiator_ed25519_keypair.public_key().to_bytes(),
|
||||
);
|
||||
let responder: PSQResponder<T> =
|
||||
PSQResponder::init(&responder_kem_private_key, &responder_kem_public_key);
|
||||
|
||||
let mut initiator_msg = initiator
|
||||
.compute_initiator_message(rng, &responder_kem_public_key, &context, psk_ttl)
|
||||
.unwrap();
|
||||
|
||||
let pre_response_psk = initiator.get_psk().unwrap();
|
||||
|
||||
let initiator_public_key =
|
||||
VerificationKey::from_bytes(initiator_ed25519_keypair.public_key().to_bytes());
|
||||
|
||||
let (responder_psk, responder_msg) = responder
|
||||
.compute_responder_message(
|
||||
&initiator_public_key,
|
||||
&mut initiator_msg,
|
||||
&context,
|
||||
psk_ttl,
|
||||
&psk_handle,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let initiator_psk = initiator.finalize(&responder_msg).unwrap();
|
||||
|
||||
assert_eq!(initiator_psk, pre_response_psk);
|
||||
assert_eq!(initiator_psk, responder_psk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_psq_e2e_mlkem() {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// generate mlkem keypair
|
||||
let (responder_kem_private_key, responder_kem_public_key) =
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rng).unwrap();
|
||||
|
||||
test_helper::<MlKem768, _>(
|
||||
&mut rng,
|
||||
responder_kem_private_key,
|
||||
responder_kem_public_key,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_psq_e2e_xwing() {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// generate xwing keypair
|
||||
let (responder_kem_private_key, responder_kem_public_key) =
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::XWingKemDraft06, &mut rand::rng())
|
||||
.unwrap();
|
||||
|
||||
test_helper::<XWingKemDraft06, _>(
|
||||
&mut rng,
|
||||
responder_kem_private_key,
|
||||
responder_kem_public_key,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_psq_e2e_dhkem() {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// generate dhkem keypair
|
||||
let (responder_kem_private_key, responder_kem_public_key) =
|
||||
libcrux_kem::key_gen(libcrux_kem::Algorithm::X25519, &mut rand::rng()).unwrap();
|
||||
|
||||
test_helper::<X25519, _>(
|
||||
&mut rng,
|
||||
responder_kem_private_key,
|
||||
responder_kem_public_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
Thursday, June 12th 2025, 11:03:30 UTC
|
||||
Friday, June 6th 2025, 09:33:10 UTC
|
||||
|
||||
@@ -47,53 +47,6 @@ This page displays a full list of all the changes during our release cycle from
|
||||
|
||||
<VarInfo />
|
||||
|
||||
## `v2025.11-cheddar`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.11-cheddar)
|
||||
- [`nym-node`](nodes/nym-node.mdx) version `1.13.0`
|
||||
|
||||
```sh
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-06-10T09:41:31.291089877Z
|
||||
Build Version: 1.13.0
|
||||
Commit SHA: ef220882d4bcf0a8a703ea62f9273e017c9ebb51
|
||||
Commit Date: 2025-06-10T11:39:20.000000000+02:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.86.0
|
||||
rustc Channel: stable
|
||||
cargo Profile: release
|
||||
```
|
||||
### Operators Updates & Tools
|
||||
|
||||
- **Nym Improvement Proposals**: NIPs are being introduced, allowing us to propose changes to the Nym network and ecosystem, and decentralise governance
|
||||
- **NIP1** will outline the governance process: Its draft will be published soon for discussion
|
||||
- **NIP2** will be about reducing the stake saturation point variable, which would make reward distribution across the network more equitable
|
||||
- **Join the Operator AMA next Tuesday** for an in-depth overview of what this all means (feat Nym Chief Scientist Claudia Diaz)
|
||||
- Keep an eye on our channels for information about how to participate in the upcoming voting (no need for forum registration, we are building something new!)
|
||||
|
||||
### Features
|
||||
|
||||
- [Track wireguard credential retries](https://github.com/nymtech/nym/pull/5783)
|
||||
|
||||
- [Swap a decode into a `fromrow` to please future `postgres` feature](https://github.com/nymtech/nym/pull/5785): This PR change a sqlx derive from `Decode` to `FromRow` because a future PR will bring the `postgres` feature and it doesn't like that `Decode` there
|
||||
|
||||
- [Fix contains ticketbook function that always returned true](https://github.com/nymtech/nym/pull/5787)
|
||||
|
||||
- [QoL: `RequestPath` trait for `http-api-client`](https://github.com/nymtech/nym/pull/5788): Those `PathSegments` in `ApiClient` weren't very user-friendly. Instead we've defined a `RequestPath` trait that allows you to also pass a good old `&str` or `String` and internally it does exactly the same sanitization as before. `PathSegments` are also fully supported to not break any existing compatibility.
|
||||
|
||||
- [Nym Statistics API](https://github.com/nymtech/nym/pull/5800):
|
||||
- Types for `nym-vpn-client`: It introduces `VpnClientStatsReport` which will be used by nym vpn clients to report statistics. Those types are in the monorepo because they're used by the `num-statistics-API`
|
||||
- Nym Statistics API: Basically a `REST API` with a `postgres` backend. In this first iteration, it only accepts `VpnClientStatsReport` and stores them in its database. It optionally connects to the Nym API to confirm reports are coming from the network and attach country code to them if it's the case (exit node country code).
|
||||
|
||||
- [Resolve 1.87 clippy warnings](https://github.com/nymtech/nym/pull/5802)
|
||||
|
||||
- [Adjusted wallet storybook mocks to fix the build](https://github.com/nymtech/nym/pull/5804)
|
||||
|
||||
- [Set cached storage counters to 0](https://github.com/nymtech/nym/pull/5812)
|
||||
|
||||
- [No autoremoval of peers](https://github.com/nymtech/nym/pull/5831)
|
||||
|
||||
## `v2025.10-brie`
|
||||
|
||||
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.10-brie)
|
||||
|
||||
@@ -18,12 +18,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
|
||||
## Current version
|
||||
|
||||
```sh
|
||||
ym-node
|
||||
nym-node
|
||||
Binary Name: nym-node
|
||||
Build Timestamp: 2025-06-10T09:41:31.291089877Z
|
||||
Build Version: 1.13.0
|
||||
Commit SHA: ef220882d4bcf0a8a703ea62f9273e017c9ebb51
|
||||
Commit Date: 2025-06-10T11:39:20.000000000+02:00
|
||||
Build Timestamp: 2025-05-27T10:13:18.511075453Z
|
||||
Build Version: 1.12.0
|
||||
Commit SHA: 1c6db86259d08d80e8bcfbc4fcc71ccb147fcfd0
|
||||
Commit Date: 2025-05-27T12:11:13.000000000+02:00
|
||||
Commit Branch: HEAD
|
||||
rustc Version: 1.86.0
|
||||
rustc Channel: stable
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-api"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.60"
|
||||
version = "1.1.61"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
rust-version.workspace = true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node-status-api"
|
||||
version = "3.1.0"
|
||||
version = "3.0.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -28,7 +28,7 @@ moka = { workspace = true, features = ["future"] }
|
||||
# Nym API: revert after Cheddar is out
|
||||
nym-contracts-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
nym-mixnet-contract-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar", features = ["openapi"]}
|
||||
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
nym-node-status-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
nym-crypto = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
nym-http-api-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
|
||||
|
||||
@@ -14,7 +14,6 @@ pub(crate) mod metrics;
|
||||
pub(crate) mod mixnodes;
|
||||
pub(crate) mod nym_nodes;
|
||||
pub(crate) mod services;
|
||||
pub(crate) mod status;
|
||||
pub(crate) mod summary;
|
||||
pub(crate) mod testruns;
|
||||
|
||||
@@ -40,8 +39,7 @@ impl RouterBuilder {
|
||||
.nest("/mixnodes", mixnodes::routes())
|
||||
.nest("/services", services::routes())
|
||||
.nest("/summary", summary::routes())
|
||||
.nest("/metrics", metrics::routes())
|
||||
.nest("/status", status::routes()),
|
||||
.nest("/metrics", metrics::routes()),
|
||||
)
|
||||
.nest(
|
||||
"/explorer/v3",
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use axum::{extract::State, Json, Router};
|
||||
use nym_validator_client::models::BinaryBuildInformationOwned;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::http::{
|
||||
error::HttpResult,
|
||||
state::{AppState, HealthInfo},
|
||||
};
|
||||
|
||||
pub(crate) fn routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/build_information", axum::routing::get(build_information))
|
||||
.route("/health", axum::routing::get(health))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Status",
|
||||
get,
|
||||
path = "/build_information",
|
||||
context_path = "/v2/status",
|
||||
responses(
|
||||
(status = 200, body = BinaryBuildInformationOwned)
|
||||
)
|
||||
)]
|
||||
#[instrument(level = tracing::Level::INFO, skip_all)]
|
||||
async fn build_information(
|
||||
State(state): State<AppState>,
|
||||
) -> HttpResult<Json<BinaryBuildInformationOwned>> {
|
||||
let build_info = state.build_information().to_owned();
|
||||
|
||||
Ok(Json(build_info))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "Status",
|
||||
get,
|
||||
path = "/health",
|
||||
context_path = "/v2/status",
|
||||
responses(
|
||||
(status = 200, body = HealthInfo)
|
||||
)
|
||||
)]
|
||||
#[instrument(level = tracing::Level::INFO, skip_all)]
|
||||
async fn health(State(state): State<AppState>) -> HttpResult<Json<HealthInfo>> {
|
||||
Ok(Json(state.health()))
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
use cosmwasm_std::Decimal;
|
||||
use itertools::Itertools;
|
||||
use moka::{future::Cache, Entry};
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_contracts_common::NaiveFloat;
|
||||
use nym_crypto::asymmetric::ed25519::PublicKey;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_validator_client::nym_api::SkimmedNode;
|
||||
use semver::Version;
|
||||
use serde::Serialize;
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use time::UtcDateTime;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, instrument, warn};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use super::models::SessionStats;
|
||||
use crate::{
|
||||
db::{queries, DbPool},
|
||||
http::models::{
|
||||
@@ -23,7 +18,7 @@ use crate::{
|
||||
monitor::{DelegationsCache, NodeGeoCache},
|
||||
};
|
||||
|
||||
pub(crate) use nym_validator_client::models::BinaryBuildInformationOwned;
|
||||
use super::models::SessionStats;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct AppState {
|
||||
@@ -33,7 +28,6 @@ pub(crate) struct AppState {
|
||||
agent_max_count: i64,
|
||||
node_geocache: NodeGeoCache,
|
||||
node_delegations: Arc<RwLock<DelegationsCache>>,
|
||||
bin_info: BinaryInfo,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -52,7 +46,6 @@ impl AppState {
|
||||
agent_max_count,
|
||||
node_geocache,
|
||||
node_delegations,
|
||||
bin_info: BinaryInfo::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,15 +78,6 @@ impl AppState {
|
||||
.await
|
||||
.delegations_owned(node_id)
|
||||
}
|
||||
|
||||
pub(crate) fn health(&self) -> HealthInfo {
|
||||
let uptime = (UtcDateTime::now() - self.bin_info.startup_time).whole_seconds();
|
||||
HealthInfo { uptime }
|
||||
}
|
||||
|
||||
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
|
||||
&self.bin_info.build_info
|
||||
}
|
||||
}
|
||||
|
||||
static GATEWAYS_LIST_KEY: &str = "gateways";
|
||||
@@ -647,23 +631,3 @@ async fn aggregate_node_info_from_db(
|
||||
|
||||
Ok(parsed_nym_nodes)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BinaryInfo {
|
||||
startup_time: UtcDateTime,
|
||||
build_info: BinaryBuildInformationOwned,
|
||||
}
|
||||
|
||||
impl BinaryInfo {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
startup_time: UtcDateTime::now(),
|
||||
build_info: bin_info_owned!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub(crate) struct HealthInfo {
|
||||
uptime: i64,
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-node"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
@@ -464,17 +464,16 @@ impl ConnectionHandler {
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn handle_connection(&mut self, socket: TcpStream) {
|
||||
let noise_stream =
|
||||
match upgrade_noise_responder(socket, &self.shared.noise_config, None).await {
|
||||
Ok(noise_stream) => noise_stream,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to perform Noise handshake with {:?} - {err}",
|
||||
self.remote_address
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let noise_stream = match upgrade_noise_responder(socket, &self.shared.noise_config).await {
|
||||
Ok(noise_stream) => noise_stream,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to perform Noise handshake with {:?} - {err}",
|
||||
self.remote_address
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
debug!(
|
||||
"Noise responder handshake completed for {:?}",
|
||||
self.remote_address
|
||||
|
||||
@@ -55,7 +55,5 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
|
||||
},
|
||||
explorer_api: parse_optional_str(EXPLORER_API),
|
||||
nym_vpn_api_url: None,
|
||||
nym_vpn_api_urls: None,
|
||||
nym_api_urls: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ impl fmt::Display for OptionalValidators {
|
||||
.as_ref()
|
||||
.map(|validators| format!(",\nsandbox: [\n{}\n]", validators.iter().format("\n")))
|
||||
.unwrap_or_default();
|
||||
write!(f, "{s1}{s2}")
|
||||
write!(f, "{s1}{s2}{s3}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[package]
|
||||
name = "nym-network-requester"
|
||||
license = "GPL-3.0"
|
||||
version = "1.1.58"
|
||||
version = "1.1.59"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version = "1.70"
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
[package]
|
||||
name = "sqlx-pool-guard"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
tracing.workspace = true
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
features = ["runtime-tokio-rustls", "sqlite"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
workspace = true
|
||||
features = ["rt-multi-thread", "macros", "time", "fs"]
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
||||
proc_pidinfo.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.61", features = [
|
||||
"Win32",
|
||||
"Win32_System",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Threading",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Wdk_System_SystemInformation",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{io, path::Path};
|
||||
|
||||
use proc_pidinfo::{
|
||||
ProcFDInfo, ProcFDType, VnodeFdInfoWithPath, proc_pidfdinfo_self, proc_pidinfo_list_self,
|
||||
};
|
||||
|
||||
/// Check if there are no open file descriptors for the given files.
|
||||
///
|
||||
/// Uses `proc_pidinfo` (`sys/proc_info.h`)
|
||||
/// See: http://blog.palominolabs.com/2012/06/19/getting-the-files-being-used-by-a-process-on-mac-os-x/
|
||||
pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
|
||||
let fd_list = proc_pidinfo_list_self::<ProcFDInfo>()?;
|
||||
|
||||
for fd in fd_list
|
||||
.iter()
|
||||
.filter(|s| s.fd_type() == Ok(ProcFDType::VNODE))
|
||||
{
|
||||
let Some(vnode) = proc_pidfdinfo_self::<VnodeFdInfoWithPath>(fd.proc_fd)
|
||||
.inspect_err(|e| {
|
||||
tracing::warn!("proc_pidfdinfo_self::<VnodeFdInfoWithPath>() failure: {e}");
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(true) = vnode
|
||||
.path()
|
||||
.map(|vnode_path| file_paths.contains(&vnode_path))
|
||||
.inspect_err(|e| {
|
||||
tracing::warn!("vnode.path() failure: {e:?}");
|
||||
})
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{
|
||||
io,
|
||||
ops::{Deref, DerefMut},
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "windows.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[path = "apple.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[path = "linux.rs"]
|
||||
mod imp;
|
||||
|
||||
/// Max number of retry attempts
|
||||
const CHECK_FILES_CLOSED_MAX_ATTEMPTS: u8 = 20;
|
||||
|
||||
/// Delay between file checks
|
||||
const CHECK_FILES_CLOSED_RETRY_DELAY: Duration = Duration::from_millis(100);
|
||||
|
||||
/// `sqlx::SqlitePool` wrapper providing a workaround for the [known bug](https://github.com/launchbadge/sqlx/issues/3217).
|
||||
/// In principle after requesting to close the sqlite pool, the wrapper monitors open file descriptor and polls periodically until all database files are closed.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SqlitePoolGuard {
|
||||
/// Path to sqlite database file.
|
||||
database_path: PathBuf,
|
||||
|
||||
/// Inner connection pool.
|
||||
connection_pool: sqlx::SqlitePool,
|
||||
}
|
||||
|
||||
impl SqlitePoolGuard {
|
||||
/// Create new instance providing path to database and connection pool
|
||||
pub fn new(database_path: PathBuf, connection_pool: sqlx::SqlitePool) -> Self {
|
||||
Self {
|
||||
database_path,
|
||||
connection_pool,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns database path
|
||||
pub fn database_path(&self) -> &Path {
|
||||
&self.database_path
|
||||
}
|
||||
|
||||
/// Close udnerlying sqlite pool and wait for files to be closed before returning.
|
||||
pub async fn close(&self) {
|
||||
// Avoid waiting for db files once the pool is marked closed to ensure that we don't wait on some other sqlite pool to close the database.
|
||||
if !self.connection_pool.is_closed() {
|
||||
tracing::info!("Closing sqlite pool: {}", self.database_path.display());
|
||||
self.close_pool_inner().await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
async fn close_pool_inner(&self) -> std::io::Result<()> {
|
||||
self.connection_pool.close().await;
|
||||
|
||||
self.wait_for_db_files_close().await.inspect_err(|e| {
|
||||
tracing::error!("Failed to wait for file to close: {e}");
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns all database files, including shm and wal files.
|
||||
fn all_database_files(&self) -> Vec<PathBuf> {
|
||||
let mut database_files = vec![];
|
||||
let canonical_path = self
|
||||
.database_path
|
||||
.canonicalize()
|
||||
.inspect_err(|e| {
|
||||
tracing::error!(
|
||||
"Failed to canonicalize path: {}. Cause: {e}",
|
||||
self.database_path.display()
|
||||
);
|
||||
})
|
||||
.unwrap_or(self.database_path.clone());
|
||||
|
||||
if let Some(ext) = canonical_path.extension() {
|
||||
for added_ext in ["-shm", "-wal"] {
|
||||
let mut new_ext = ext.to_owned();
|
||||
new_ext.push(added_ext);
|
||||
database_files.push(canonical_path.with_extension(new_ext));
|
||||
}
|
||||
}
|
||||
database_files.push(canonical_path);
|
||||
database_files
|
||||
}
|
||||
|
||||
/// Wait for database files to be closed before returning.
|
||||
async fn wait_for_db_files_close(&self) -> std::io::Result<()> {
|
||||
let database_files = self.all_database_files();
|
||||
let paths: Vec<&Path> = database_files.iter().map(PathBuf::as_path).collect();
|
||||
|
||||
for _ in 0..CHECK_FILES_CLOSED_MAX_ATTEMPTS {
|
||||
match imp::check_files_closed(&paths)
|
||||
.await
|
||||
.inspect_err(|e| tracing::error!("imp::check_files_closed() failure: {e}"))
|
||||
{
|
||||
Ok(false) | Err(_) => tokio::time::sleep(CHECK_FILES_CLOSED_RETRY_DELAY).await,
|
||||
Ok(true) => return Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::TimedOut,
|
||||
"timed out waiting for sqlite files to be closed",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SqlitePoolGuard {
|
||||
type Target = sqlx::SqlitePool;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.connection_pool
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SqlitePoolGuard {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.connection_pool
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sqlx::{
|
||||
ConnectOptions, Executor,
|
||||
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_wait_close() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::TRACE)
|
||||
.init();
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let database_path = temp_dir.path().join("storage.db");
|
||||
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||
.synchronous(SqliteSynchronous::Normal)
|
||||
.auto_vacuum(SqliteAutoVacuum::Incremental)
|
||||
.filename(database_path.clone())
|
||||
.create_if_missing(true)
|
||||
.disable_statement_logging();
|
||||
let connection_pool = sqlx::SqlitePool::connect_with(opts).await.unwrap();
|
||||
|
||||
connection_pool
|
||||
.execute("create table test (col int)")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let guard = SqlitePoolGuard::new(database_path.clone(), connection_pool);
|
||||
assert!(
|
||||
guard
|
||||
.wait_for_db_files_close()
|
||||
.await
|
||||
.err()
|
||||
.is_some_and(|e| e.kind() == io::ErrorKind::TimedOut)
|
||||
);
|
||||
|
||||
assert!(guard.close_pool_inner().await.is_ok());
|
||||
tokio::fs::remove_file(database_path).await.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{io, path::Path};
|
||||
|
||||
static PROC_SELF_FD_DIR: &str = "/proc/self/fd/";
|
||||
|
||||
/// Check if there are no open file descriptors for the given files.
|
||||
///
|
||||
/// Linux, Android: uses `/proc/self/fd/` to list open file descriptors
|
||||
/// See: https://stackoverflow.com/a/59797198/351305
|
||||
pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
|
||||
let mut dir = tokio::fs::read_dir(PROC_SELF_FD_DIR).await?;
|
||||
|
||||
while let Ok(Some(entry)) = dir.next_entry().await {
|
||||
if entry
|
||||
.file_type()
|
||||
.await
|
||||
.inspect_err(|e| tracing::warn!("entry.file_type() failure: {e}"))
|
||||
.is_ok_and(|entry_type| entry_type.is_symlink())
|
||||
{
|
||||
match tokio::fs::read_link(entry.path()).await {
|
||||
Ok(resolved_path) => {
|
||||
if file_paths.contains(&resolved_path.as_ref()) {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to read symlink: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::{
|
||||
ffi::{OsString, c_uchar, c_ulong, c_ushort, c_void},
|
||||
io,
|
||||
os::windows::ffi::OsStringExt,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use windows::{
|
||||
Wdk::System::SystemInformation::{NtQuerySystemInformation, SYSTEM_INFORMATION_CLASS},
|
||||
Win32::{
|
||||
Foundation::{HANDLE, MAX_PATH, NTSTATUS, STATUS_INFO_LENGTH_MISMATCH},
|
||||
Storage::FileSystem::{
|
||||
FILE_NAME_NORMALIZED, FILE_TYPE_DISK, GetFileType, GetFinalPathNameByHandleW,
|
||||
},
|
||||
System::{
|
||||
Memory::{
|
||||
GetProcessHeap, HEAP_FLAGS, HEAP_ZERO_MEMORY, HeapAlloc, HeapFree, HeapReAlloc,
|
||||
},
|
||||
Threading::GetCurrentProcessId,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/// Private information class used to retrieve open file handles
|
||||
const SYSTEM_HANDLE_INFORMATION_CLASS: SYSTEM_INFORMATION_CLASS = SYSTEM_INFORMATION_CLASS(0x10);
|
||||
|
||||
/// Initial buffer size holding the handle info
|
||||
/// The number is based on what I observe on a pretty standard Windows 11
|
||||
const SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE: usize = 2_500_000;
|
||||
|
||||
/// Check if there are no open handles to the given files.
|
||||
///
|
||||
/// Uses undocumented NT API to obtain open handles on the system.
|
||||
/// See: https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/get-all-open-handles-and-kernel-object-address-from-userland
|
||||
pub async fn check_files_closed(file_paths: &[&Path]) -> io::Result<bool> {
|
||||
let current_pid = unsafe { GetCurrentProcessId() };
|
||||
|
||||
// Allocate info struct on heap with some initial value
|
||||
let mut reserved_memory = SYSTEM_HANDLE_INFORMATION_INITIAL_SIZE;
|
||||
let mut handle_table_info = HeapGuard::<SystemHandleInformation>::new(reserved_memory)?;
|
||||
|
||||
// Request system handle information
|
||||
let mut status: NTSTATUS = NTSTATUS::default();
|
||||
let mut return_len = reserved_memory as u32;
|
||||
for _ in 0..2 {
|
||||
status = unsafe {
|
||||
NtQuerySystemInformation(
|
||||
SYSTEM_HANDLE_INFORMATION_CLASS,
|
||||
handle_table_info.as_mut_ptr() as _,
|
||||
return_len,
|
||||
&mut return_len,
|
||||
)
|
||||
};
|
||||
|
||||
// Buffer is too small, resize memory and retry again.
|
||||
if status == STATUS_INFO_LENGTH_MISMATCH {
|
||||
tracing::trace!("Buffer is too small ({reserved_memory}), resizing to {return_len}");
|
||||
reserved_memory = return_len as usize;
|
||||
handle_table_info.reallocate(reserved_memory)?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
status.ok()?;
|
||||
|
||||
// Convert returned data into slice
|
||||
let num_handles = unsafe { (*handle_table_info.inner).number_of_handles };
|
||||
let proc_entries = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
(*handle_table_info.as_mut_ptr()).handles.as_ptr(),
|
||||
num_handles as usize,
|
||||
)
|
||||
};
|
||||
|
||||
// Iterate over open file handle entries
|
||||
for entry in proc_entries {
|
||||
if entry.unique_process_id == current_pid {
|
||||
let file_handle = HANDLE(entry.handle_value as _);
|
||||
|
||||
// Filter everything except disk files
|
||||
if unsafe { GetFileType(file_handle) } == FILE_TYPE_DISK {
|
||||
// Obtain canonical path for file handle
|
||||
let mut file_handle_path = vec![0u16; MAX_PATH as usize];
|
||||
let num_chars_without_nul = unsafe {
|
||||
GetFinalPathNameByHandleW(
|
||||
file_handle,
|
||||
&mut file_handle_path,
|
||||
FILE_NAME_NORMALIZED,
|
||||
) as usize
|
||||
};
|
||||
|
||||
if num_chars_without_nul > 0 {
|
||||
let path_str = OsString::from_wide(&file_handle_path[0..num_chars_without_nul]);
|
||||
let file_handle_pathbuf = PathBuf::from(path_str);
|
||||
if file_paths.contains(&file_handle_pathbuf.as_path()) {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct SystemHandleInformation {
|
||||
pub number_of_handles: c_ulong,
|
||||
pub handles: [SystemHandleTableEntryInfo; 1],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct SystemHandleTableEntryInfo {
|
||||
pub unique_process_id: c_ulong,
|
||||
pub object_type_index: c_uchar,
|
||||
pub handle_attributes: c_uchar,
|
||||
pub handle_value: c_ushort,
|
||||
pub object: *mut c_void,
|
||||
pub granted_access: c_ulong,
|
||||
}
|
||||
|
||||
/// Managed heap memory
|
||||
struct HeapGuard<T> {
|
||||
inner: *mut T,
|
||||
process_heap: HANDLE,
|
||||
}
|
||||
|
||||
impl<T> HeapGuard<T> {
|
||||
/// Allocate new memory using `HealAlloc`
|
||||
fn new(length: usize) -> io::Result<Self> {
|
||||
let process_heap = unsafe { GetProcessHeap()? };
|
||||
let inner: *mut T = unsafe { HeapAlloc(process_heap, HEAP_ZERO_MEMORY, length) as _ };
|
||||
|
||||
if inner.is_null() {
|
||||
Err(io::Error::other("Failed to allocate memory"))
|
||||
} else {
|
||||
Ok(Self {
|
||||
inner,
|
||||
process_heap,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Reallocate existing chunk of memory
|
||||
///
|
||||
/// On success: the internal memory pointer is replaced.
|
||||
/// On failure: the internal memory pointer remains the same and still valid.
|
||||
fn reallocate(&mut self, new_length: usize) -> io::Result<()> {
|
||||
let new_ptr: *mut T = unsafe {
|
||||
HeapReAlloc(
|
||||
self.process_heap,
|
||||
HEAP_ZERO_MEMORY,
|
||||
Some(self.inner as _),
|
||||
new_length,
|
||||
) as _
|
||||
};
|
||||
|
||||
if new_ptr.is_null() {
|
||||
Err(io::Error::other("Failed to reallocate memory"))
|
||||
} else {
|
||||
self.inner = new_ptr;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn as_mut_ptr(&self) -> *mut T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for HeapGuard<T> {
|
||||
fn drop(&mut self) {
|
||||
#[allow(clippy::expect_used)]
|
||||
unsafe {
|
||||
HeapFree(
|
||||
self.process_heap,
|
||||
HEAP_FLAGS(0),
|
||||
Some(self.inner as *mut c_void),
|
||||
)
|
||||
}
|
||||
.expect("HeapFree failure");
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,6 @@ impl<'a> From<&'a LoadedNetwork> for nym_config::defaults::NymNetworkDetails {
|
||||
contracts,
|
||||
explorer_api: None,
|
||||
nym_vpn_api_url: None,
|
||||
nym_vpn_api_urls: None,
|
||||
nym_api_urls: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ impl InitCtx {
|
||||
contracts: Default::default(),
|
||||
explorer_api: None,
|
||||
nym_vpn_api_url: None,
|
||||
nym_vpn_api_urls: None,
|
||||
nym_api_urls: None,
|
||||
};
|
||||
Ok(Config::try_from_nym_network_details(&network_details)?)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.57"
|
||||
version = "1.1.58"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nymvisor"
|
||||
version = "0.1.22"
|
||||
version = "0.1.23"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user