Skip to content

Commit

Permalink
no_std support
Browse files Browse the repository at this point in the history
Added basic no_std support by switching to `core` and `alloc`
libraries wherever possible. Restructured features in Cargo.toml
to allow for customized setups with or without the standard library.
  • Loading branch information
jmlepisto committed Aug 19, 2024
1 parent c6657a7 commit 4ab0a90
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 64 deletions.
61 changes: 43 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,45 @@ edition = "2021"
# This is slightly mumbo-jumboey, but in short:
# Features with a -resolver suffix simply enables the existence of a specific resolver,
# and -accelerated suffix means that this resolver will be the default used by the Builder.
#
# If default features are disabled and default-resolver is used, required crypto primitives
# must be enabled individually.
[features]
default = ["default-resolver"]
default-resolver = ["aes-gcm", "chacha20poly1305", "blake2", "sha2", "curve25519-dalek"]
default = ["default-resolver", "default-resolver-crypto", "std"]
default-resolver = []
default-resolver-crypto = ["use-aes-gcm", "use-chacha20poly1305", "use-blake2", "use-sha2", "use-curve25519-dalek"]
nightly = ["blake2/simd_opt", "subtle/nightly"]
ring-resolver = ["ring"]
ring-accelerated = ["ring-resolver", "default-resolver"]
libsodium-resolver = ["sodiumoxide", "byteorder"]
libsodium-accelerated = ["libsodium-resolver", "default-resolver"]
ring-resolver = ["ring", "std"]
ring-accelerated = ["ring-resolver", "default-resolver", "std"]
libsodium-resolver = ["sodiumoxide", "byteorder", "std"]
libsodium-accelerated = ["libsodium-resolver", "default-resolver", "std"]
vector-tests = []
hfs = []
pqclean_kyber1024 = ["pqcrypto-kyber", "pqcrypto-traits", "hfs", "default-resolver"]
xchachapoly = ["chacha20poly1305", "default-resolver"]
risky-raw-split = []

# Backwards-compatibility aliases
pqclean_kyber1024 = ["use-pqcrypto-kyber1024"]
xchachapoly = ["use-xchacha20poly1305"]

# Enable std features on dependencies if possible.
std = [
"rand_core/std",
"subtle/std",
"ring/std",
"blake2/std",
"sha2/std",
"byteorder/std",
]

# Crypto primitives for default-resolver.
use-curve25519-dalek = ["curve25519-dalek", "default-resolver"]
use-chacha20poly1305 = ["chacha20poly1305", "default-resolver"]
use-xchacha20poly1305 = ["chacha20poly1305", "default-resolver"]
use-blake2 = ["blake2", "default-resolver"]
use-sha2 = ["sha2", "default-resolver"]
use-aes-gcm = ["aes-gcm", "default-resolver"]
use-pqcrypto-kyber1024 = ["pqcrypto-kyber", "pqcrypto-traits", "hfs", "default-resolver"]

[[bench]]
name = "benches"
harness = false
Expand All @@ -37,24 +62,24 @@ harness = false
travis-ci = { repository = "mcginty/snow", branch = "master" }

[dependencies]
rand_core = { version = "0.6", features = ["std", "getrandom"] }
subtle = "2.4"
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
subtle = { version = "2.4", default-features = false }

# default crypto provider
aes-gcm = { version = "0.10", optional = true }
chacha20poly1305 = { version = "0.10", optional = true }
blake2 = { version = "0.10", optional = true }
sha2 = { version = "0.10", optional = true }
curve25519-dalek = { version = "4", optional = true }
aes-gcm = { version = "0.10", optional = true, default-features = false, features = ["aes"] }
chacha20poly1305 = { version = "0.10", optional = true, default-features = false }
blake2 = { version = "0.10", optional = true, default-features = false }
sha2 = { version = "0.10", optional = true, default-features = false }
curve25519-dalek = { version = "4", optional = true, default-features = false }

pqcrypto-kyber = { version = "0.8", optional = true }
pqcrypto-traits = { version = "0.3", optional = true }

# ring crypto provider
ring = { version = "0.17", optional = true, features = ["std"] }
ring = { version = "0.17", optional = true }
# libsodium crypto provider
sodiumoxide = { version = "0.2", optional = true }
byteorder = { version = "1.4", optional = true }
sodiumoxide = { version = "0.2", optional = true, default-features = false }
byteorder = { version = "1.4", optional = true, default-features = false }

[dev-dependencies]
criterion = "0.5"
Expand Down
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,69 @@ crypto implementations when available.
| BLAKE2s || |
| BLAKE2b || |

## `no_std` support and feature selection

Snow can be used in `no_std` environments if `alloc` is provided.

By default Snow uses the standard library, default cyprot resolver and a selected collection
of crypto primitives. To use Snow in `no_std` environments or make other kinds of customized
setups, use Snow with `default-features = false`. This way you will individually select
the components you wish to use. `default-resolver` is the only built-in resolver that
currently supports `no_std`.

To use a custom setup with `default-resolver`, enable your desired selection of cryptographic primitives:

| Primitive | Feature flag |
| :----------- | ---------------------: |
| **DH** |
| 25519 | `use-curve25519-dalek` |
| **Cipher** |
| AESGCM | `use-aes-gcm` |
| ChaChaPoly | `use-chacha20poly1305` |
| XChaChaPoly* | `use-xchacha20poly1305`|
| **Hash** |
| SHA256 | `use-sha2` |
| SHA512 | `use-sha2` |
| BLAKE2s | `use-blake2` |
| BLAKE2b | `use-blake2` |

\* *XChaChaPoly*, an extended nonce variant of *ChaChaPoly*, is not in the official specification of Noise!

### Example configurations

**25519 + AES-GCM + SHA** with standard library features.
```toml
default-features = false
features = [
"use-curve25519-dalek",
"use-aes-gcm",
"use-sha2",
"std",
]
```

**25519 + ChaChaPoly + BLAKE2** without standard library.
```toml
default-features = false
features = [
"use-curve25519-dalek",
"use-chacha20poly1305",
"use-blake2",
]
```

### `getrandom` support

Most crypto implementations supported by `default-resolver` will require
[`getrandom`](getrandom).

If your target platform is not directly supported
you might have to provide a custom implementation in your crate root.
Check out their [documentation](getrandom-custom) for details.

[getrandom]: https://crates.io/crates/getrandom
[getrandom-custom]: https://docs.rs/getrandom/0.2.15/getrandom/macro.register_custom_getrandom.html

## License

Licensed under either of:
Expand Down
9 changes: 6 additions & 3 deletions ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ COMMON_FEATURES="xchachapoly vector-tests"
set -x
cargo check --benches
cargo test $TARGET --no-default-features
cargo test $TARGET --features "$COMMON_FEATURES"
# Custom set of crypto without std
cargo test $TARGET --no-default-features --features "default-resolver use-curve25519-dalek use-blake2 use-chacha20poly1305"
# Custom set of crypto with std
cargo test $TARGET --no-default-features --features "default-resolver use-curve25519-dalek use-sha2 use-chacha20poly1305"
cargo test $TARGET --features "ring-resolver $COMMON_FEATURES"
cargo test $TARGET --features "ring-accelerated $COMMON_FEATURES"
if ! rustc -vV | grep 'host: .*windows' &> /dev/null; then
cargo test $TARGET --features "hfs pqclean_kyber1024 $COMMON_FEATURES"
cargo test $TARGET --features "ring-resolver hfs pqclean_kyber1024 $COMMON_FEATURES"
cargo test $TARGET --features "hfs use-pqcrypto-kyber1024 $COMMON_FEATURES"
cargo test $TARGET --features "ring-resolver hfs use-pqcrypto-kyber1024 $COMMON_FEATURES"
fi
cargo test $TARGET --features "libsodium-resolver $COMMON_FEATURES"
cargo test $TARGET --features "libsodium-accelerated $COMMON_FEATURES"
15 changes: 11 additions & 4 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::fmt::Debug;
use core::fmt::Debug;

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec, vec::Vec};

#[cfg(feature = "hfs")]
use crate::params::HandshakeModifier;
Expand Down Expand Up @@ -45,7 +48,7 @@ impl PartialEq for Keypair {
/// # use snow::Builder;
/// # let my_long_term_key = [0u8; 32];
/// # let their_pub_key = [0u8; 32];
/// # #[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))]
/// # #[cfg(any(feature = "default-resolver-crypto", feature = "ring-accelerated"))]
/// let noise = Builder::new("Noise_XX_25519_ChaChaPoly_BLAKE2s".parse()?)
/// .local_private_key(&my_long_term_key)?
/// .remote_public_key(&their_pub_key)?
Expand All @@ -65,7 +68,7 @@ pub struct Builder<'builder> {
}

impl<'builder> Debug for Builder<'builder> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Builder").field("params", &self.params.name).finish_non_exhaustive()
}
}
Expand Down Expand Up @@ -317,8 +320,12 @@ impl<'builder> Builder<'builder> {
}
}


#[cfg(test)]
#[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))]
#[cfg(all(
feature = "std",
any(feature = "default-resolver-crypto", feature = "ring-accelerated")
))]
mod tests {
use super::*;
type TestResult = Result<(), Box<dyn std::error::Error>>;
Expand Down
3 changes: 3 additions & 0 deletions src/cipherstate.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;

use crate::{
constants::{CIPHERKEYLEN, TAGLEN},
error::{Error, InitStage, StateProblem},
Expand Down
3 changes: 2 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! All error types used by Snow operations.

use std::fmt;
use core::fmt;

/// `snow` provides decently detailed errors, exposed as the [`Error`] enum,
/// to allow developers to react to errors in a more actionable way.
Expand Down Expand Up @@ -181,4 +181,5 @@ impl fmt::Display for Error {
}
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}
4 changes: 3 additions & 1 deletion src/handshakestate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use crate::{
types::{Dh, Hash, Random},
utils::Toggle,
};
use std::{
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
use core::{
convert::{TryFrom, TryInto},
fmt,
};
Expand Down
24 changes: 22 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! ```
//! # use snow::Error;
//! #
//! # #[cfg(any(feature = "default-resolver", feature = "ring-accelerated"))]
//! # #[cfg(any(feature = "default-resolver-crypto", feature = "ring-accelerated"))]
//! # fn try_main() -> Result<(), Error> {
//! static PATTERN: &'static str = "Noise_NN_25519_ChaChaPoly_BLAKE2s";
//!
Expand Down Expand Up @@ -43,7 +43,7 @@
//! # Ok(())
//! # }
//! #
//! # #[cfg(not(any(feature = "default-resolver", feature = "ring-accelerated")))]
//! # #[cfg(not(any(feature = "default-resolver-crypto", feature = "ring-accelerated")))]
//! # fn try_main() -> Result<(), ()> { Ok(()) }
//! #
//! # fn main() {
Expand All @@ -54,6 +54,26 @@
//! See `examples/simple.rs` for a more complete TCP client/server example with static keys.

#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(not(feature = "std"))]
extern crate alloc;

// Make sure the user is running a supported configuration.
#[cfg(feature = "default-resolver")]
#[cfg(any(
not(any(feature = "use-curve25519-dalek")),
not(any(
feature = "use-aes-gcm",
feature = "use-chacha20poly1305",
feature = "use-xchacha20poly1305"
)),
not(any(feature = "use-sha2", feature = "use-blake2"))
))]
compile_error!(
"Valid selection of crypto primitived must be enabled when using feature 'default-resolver'.
Enable at least one DH feature, one Cipher feature and one Hash feature. Check README.md for details."
);

macro_rules! copy_slices {
($inslice:expr, $outslice:expr) => {
Expand Down
11 changes: 7 additions & 4 deletions src/params/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
//! All structures related to Noise parameter definitions (cryptographic primitive choices, protocol
//! patterns/names)

#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned, string::String};

use crate::error::{Error, PatternProblem};
use std::str::FromStr;
use core::str::FromStr;
mod patterns;

pub use self::patterns::{
Expand Down Expand Up @@ -61,7 +64,7 @@ impl FromStr for DHChoice {
pub enum CipherChoice {
/// The ChaCha20Poly1305 AEAD.
ChaChaPoly,
#[cfg(feature = "xchachapoly")]
#[cfg(feature = "use-xchacha20poly1305")]
/// The XChaCha20Poly1305 AEAD, an extended nonce variant of ChaCha20Poly1305.
/// This variant is hidden behind a feature flag to highlight that it is not in the
/// official specification of the Noise Protocol.
Expand All @@ -77,7 +80,7 @@ impl FromStr for CipherChoice {
use self::CipherChoice::*;
match s {
"ChaChaPoly" => Ok(ChaChaPoly),
#[cfg(feature = "xchachapoly")]
#[cfg(feature = "use-xchacha20poly1305")]
"XChaChaPoly" => Ok(XChaChaPoly),
"AESGCM" => Ok(AESGCM),
_ => Err(PatternProblem::UnsupportedCipherType.into()),
Expand Down Expand Up @@ -257,7 +260,7 @@ impl FromStr for NoiseParams {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryFrom;
use core::convert::TryFrom;

#[test]
fn test_simple_handshake() {
Expand Down
5 changes: 4 additions & 1 deletion src/params/patterns.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#![allow(clippy::enum_glob_use)]

#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};

use crate::error::{Error, PatternProblem};
use std::{convert::TryFrom, str::FromStr};
use core::{convert::TryFrom, str::FromStr};

/// A small helper macro that behaves similar to the `vec![]` standard macro,
/// except it allocates a bit extra to avoid resizing.
Expand Down
Loading

0 comments on commit 4ab0a90

Please sign in to comment.