From 04885effdc33cbe2627e97c32e94ff7b63ad29b7 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Thu, 5 Mar 2026 15:24:11 +0100 Subject: [PATCH] feat: add graviola support --- .github/workflows/ci.yml | 3 +++ .gitlab-ci.yml | 1 + Cargo.lock | 29 +++++++++++++++++++++++++++++ watermelon-mini/Cargo.toml | 2 ++ watermelon-mini/src/lib.rs | 16 +++++++++++----- watermelon-net/Cargo.toml | 1 + watermelon-nkeys/Cargo.toml | 2 ++ watermelon-nkeys/src/seed.rs | 26 +++++++++++++++++++++++--- watermelon/Cargo.toml | 1 + 9 files changed, 73 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84bdf34..cbb5999 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,3 +88,6 @@ jobs: - name: Run tests (--no-default-features --features ring,getrandom) run: cargo test --no-default-features --features ring,getrandom + + - name: Run tests (--no-default-features --features graviola,getrandom) + run: cargo test --no-default-features --features graviola,getrandom diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bed1d15..2585bd1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,3 +43,4 @@ rust:test: - cargo test - cargo test --features websocket,portable-atomic - cargo test --no-default-features --features ring,getrandom + - cargo test --no-default-features --features graviola,getrandom diff --git a/Cargo.lock b/Cargo.lock index 8c6b96e..200d841 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,6 +413,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "graviola" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4387e0458389da24c6fe732531e65595c7c4a32b027f98f4789e512e28224465" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -906,6 +916,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-graviola" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323c712e50c59ceb2ba9ad4d79dcfd3e0046a082d61efa87fcdf8f59af04473c" +dependencies = [ + "graviola", + "rustls", +] + [[package]] name = "rustls-native-certs" version = "0.8.3" @@ -1062,6 +1082,12 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "shlex" version = "1.3.0" @@ -1250,6 +1276,7 @@ dependencies = [ "httparse", "rand", "ring", + "sha1_smol", "simdutf8", "tokio", "tokio-util", @@ -1407,6 +1434,7 @@ name = "watermelon-mini" version = "0.3.2" dependencies = [ "async-compression", + "rustls-graviola", "rustls-platform-verifier", "thiserror 2.0.18", "tokio", @@ -1441,6 +1469,7 @@ dependencies = [ "claims", "crc", "data-encoding", + "graviola", "ring", "thiserror 2.0.18", ] diff --git a/watermelon-mini/Cargo.toml b/watermelon-mini/Cargo.toml index 82d5901..3df34c0 100644 --- a/watermelon-mini/Cargo.toml +++ b/watermelon-mini/Cargo.toml @@ -15,6 +15,7 @@ features = ["websocket", "non-standard-zstd"] [dependencies] tokio = { version = "1.36", features = ["net"] } tokio-rustls = { version = "0.26", default-features = false } +rustls-graviola = { version = "0.3.4", default-features = false, optional = true } rustls-platform-verifier = "0.6" watermelon-net = { version = "0.2", path = "../watermelon-net", default-features = false } @@ -32,6 +33,7 @@ websocket = ["watermelon-net/websocket"] aws-lc-rs = ["tokio-rustls/aws-lc-rs", "watermelon-net/aws-lc-rs", "watermelon-nkeys/aws-lc-rs"] ring = ["tokio-rustls/ring", "watermelon-net/ring", "watermelon-nkeys/ring"] fips = ["tokio-rustls/fips", "watermelon-net/fips", "watermelon-nkeys/fips"] +graviola = ["dep:rustls-graviola", "watermelon-net/graviola", "watermelon-nkeys/graviola"] rand = ["watermelon-net/rand"] getrandom = ["watermelon-net/getrandom"] non-standard-zstd = ["watermelon-net/non-standard-zstd", "watermelon-proto/non-standard-zstd", "dep:async-compression", "async-compression/zstd"] diff --git a/watermelon-mini/src/lib.rs b/watermelon-mini/src/lib.rs index cd97948..d8f2ddf 100644 --- a/watermelon-mini/src/lib.rs +++ b/watermelon-mini/src/lib.rs @@ -6,7 +6,7 @@ use rustls_platform_verifier::Verifier; use tokio::net::TcpStream; use tokio_rustls::{ TlsConnector, - rustls::{self, ClientConfig, crypto::CryptoProvider, version::TLS13}, + rustls::{ClientConfig, crypto::CryptoProvider, version::TLS13}, }; use watermelon_net::Connection; use watermelon_proto::{ServerAddr, ServerInfo}; @@ -87,9 +87,15 @@ pub async fn easy_connect( fn crypto_provider() -> CryptoProvider { #[cfg(feature = "aws-lc-rs")] - return rustls::crypto::aws_lc_rs::default_provider(); + return tokio_rustls::rustls::crypto::aws_lc_rs::default_provider(); #[cfg(all(not(feature = "aws-lc-rs"), feature = "ring"))] - return rustls::crypto::ring::default_provider(); - #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))] - compile_error!("Please enable the `aws-lc-rs` or the `ring` feature") + return tokio_rustls::rustls::crypto::ring::default_provider(); + #[cfg(all( + not(feature = "aws-lc-rs"), + not(feature = "ring"), + feature = "graviola" + ))] + return rustls_graviola::default_provider(); + #[cfg(not(any(feature = "aws-lc-rs", feature = "ring", feature = "graviola")))] + compile_error!("Please enable the `aws-lc-rs`, the `ring` or the `graviola` feature") } diff --git a/watermelon-net/Cargo.toml b/watermelon-net/Cargo.toml index 416c05b..4f53186 100644 --- a/watermelon-net/Cargo.toml +++ b/watermelon-net/Cargo.toml @@ -37,6 +37,7 @@ websocket = ["dep:tokio-websockets", "dep:futures-sink", "dep:http"] ring = ["tokio-websockets?/ring"] aws-lc-rs = ["tokio-websockets?/aws_lc_rs"] fips = [] +graviola = ["tokio-websockets?/sha1_smol"] rand = ["tokio-websockets?/rand"] getrandom = ["tokio-websockets?/getrandom"] non-standard-zstd = ["watermelon-proto/non-standard-zstd"] diff --git a/watermelon-nkeys/Cargo.toml b/watermelon-nkeys/Cargo.toml index 709c774..aeecfa9 100644 --- a/watermelon-nkeys/Cargo.toml +++ b/watermelon-nkeys/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] aws-lc-rs = { version = "1.12.2", default-features = false, features = ["aws-lc-sys", "prebuilt-nasm"], optional = true } ring = { version = "0.17", optional = true } +graviola = { version = "0.3.4", optional = true } crc = "3.2.1" thiserror = "2" data-encoding = { version = "2.7.0", default-features = false } @@ -23,6 +24,7 @@ claims = "0.8" default = ["aws-lc-rs"] aws-lc-rs = ["dep:aws-lc-rs"] ring = ["dep:ring"] +graviola = ["dep:graviola"] fips = ["aws-lc-rs", "aws-lc-rs/fips"] [lints] diff --git a/watermelon-nkeys/src/seed.rs b/watermelon-nkeys/src/seed.rs index d690d85..8a3450a 100644 --- a/watermelon-nkeys/src/seed.rs +++ b/watermelon-nkeys/src/seed.rs @@ -3,14 +3,27 @@ use std::fmt::{self, Debug, Display}; #[cfg(feature = "aws-lc-rs")] use aws_lc_rs::signature::{Ed25519KeyPair, KeyPair as _, Signature as LlSignature}; use data_encoding::{BASE32_NOPAD, BASE64URL_NOPAD}; +#[cfg(all( + not(feature = "aws-lc-rs"), + not(feature = "ring"), + feature = "graviola" +))] +use graviola::signing::eddsa::Ed25519SigningKey as Ed25519KeyPair; #[cfg(all(not(feature = "aws-lc-rs"), feature = "ring"))] use ring::signature::{Ed25519KeyPair, KeyPair as _, Signature as LlSignature}; -#[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))] -compile_error!("Please enable the `aws-lc-rs` or the `ring` feature"); +#[cfg(not(any(feature = "aws-lc-rs", feature = "ring", feature = "graviola")))] +compile_error!("Please enable the `aws-lc-rs`, the `ring` or the `graviola` feature"); use crate::crc::Crc16; +#[cfg(all( + not(feature = "aws-lc-rs"), + not(feature = "ring"), + feature = "graviola" +))] +type LlSignature = [u8; 64]; + const SEED_PREFIX_BYTE: u8 = 18 << 3; /// A `NKey` private/public key pair. @@ -91,8 +104,11 @@ impl KeyPair { let kind = raw_seed[1]; + #[cfg(any(feature = "aws-lc-rs", feature = "ring"))] let key = Ed25519KeyPair::from_seed_unchecked(&raw_seed[2..]) .map_err(|_| KeyPairFromSeedError::DecodeError)?; + #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))] + let key = Ed25519KeyPair::from_bytes(raw_seed[2..].try_into().unwrap()).unwrap(); Ok(Self { kind, key }) } @@ -126,7 +142,11 @@ impl Display for PublicKey<'_> { let mut full_raw_seed = [0; 36]; full_raw_seed[0] = SEED_PREFIX_BYTE; full_raw_seed[1] = self.0.kind; - full_raw_seed[2..34].copy_from_slice(self.0.key.public_key().as_ref()); + #[cfg(any(feature = "aws-lc-rs", feature = "ring"))] + let public_key_bytes = self.0.key.public_key().as_ref(); + #[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))] + let public_key_bytes = &self.0.key.public_key().as_bytes(); + full_raw_seed[2..34].copy_from_slice(public_key_bytes); let crc = Crc16::compute(&full_raw_seed[..34]); full_raw_seed[34..36].copy_from_slice(&crc.to_raw_encoded()); Display::fmt(&BASE32_NOPAD.encode_display(&full_raw_seed), f) diff --git a/watermelon/Cargo.toml b/watermelon/Cargo.toml index dd10e84..28ddf31 100644 --- a/watermelon/Cargo.toml +++ b/watermelon/Cargo.toml @@ -50,6 +50,7 @@ websocket = ["watermelon-mini/websocket"] aws-lc-rs = ["watermelon-mini/aws-lc-rs", "watermelon-nkeys/aws-lc-rs"] ring = ["watermelon-mini/ring", "watermelon-nkeys/ring"] fips = ["watermelon-mini/fips", "watermelon-nkeys/fips"] +graviola = ["watermelon-mini/graviola", "watermelon-nkeys/graviola"] from-env = ["dep:envy"] rand = ["dep:rand", "watermelon-mini/rand", "watermelon-net/rand"] getrandom = ["dep:getrandom", "watermelon-mini/getrandom", "watermelon-net/getrandom"]