From 8a7bcf90975d0ce93d6c70b002ed9b4525c21e61 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 12 Aug 2021 06:25:07 +0200 Subject: [PATCH] `no_std` support (#225) * No std implementation * Run tests with std * Adding wasm32-unknown-unknown target Co-authored-by: Kevin Lewi --- .github/workflows/main.yml | 53 ++++++++++++++--------- Cargo.toml | 8 ++-- src/envelope.rs | 4 +- src/errors.rs | 18 +++++--- src/group/expand.rs | 5 ++- src/group/mod.rs | 4 +- src/group/p256.rs | 6 +-- src/group/ristretto.rs | 2 +- src/impls.rs | 32 +++++++------- src/key_exchange/traits.rs | 1 + src/key_exchange/tripledh.rs | 4 +- src/keypair.rs | 74 +++++++++++++++++--------------- src/lib.rs | 3 ++ src/messages.rs | 3 +- src/opaque.rs | 4 +- src/oprf.rs | 4 +- src/serialization/mod.rs | 31 ++++++------- src/serialization/tests.rs | 8 +++- src/slow_hash.rs | 3 +- src/tests/full_test.rs | 30 +++++++------ src/tests/mock_rng.rs | 3 +- src/tests/opaque_test_vectors.rs | 5 ++- src/tests/voprf_test_vectors.rs | 2 + 23 files changed, 179 insertions(+), 128 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f843217..678e957 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,6 +41,12 @@ jobs: command: test args: --no-default-features --features ${{ matrix.backend_feature }} + - name: Run cargo test with std + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features std --features ${{ matrix.backend_feature }} + cross-test: name: Test on ${{ matrix.target }} (using cross) runs-on: ubuntu-latest @@ -61,10 +67,10 @@ jobs: # Note: just use `cross` as you would `cargo`, but always # pass the `--target=${{ matrix.target }}` arg. (Yes, really). - run: cross test --verbose --target=${{ matrix.target }} --no-default-features --features ${{ matrix.backend_feature }} + - run: cross test --verbose --target=${{ matrix.target }} --no-default-features --features std --features ${{ matrix.backend_feature }} - - slow-hash-test: - name: Test on ${{ matrix.target }} with slow hash + feature-test: + name: Test on ${{ matrix.target }} with ${{ matrix.frontend_feature }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -73,26 +79,14 @@ jobs: - u64_backend - u32_backend - p256,u64_backend + frontend_feature: + - slow-hash + - serialize steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 - - run: cargo test --verbose --features slow-hash --no-default-features --features ${{ matrix.backend_feature }} - - serde-test: - name: Test on ${{ matrix.target }} with serde support - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend - steps: - - uses: actions/checkout@v2 - - uses: hecrj/setup-rust-action@v1 - - run: cargo test --verbose --features serialize --no-default-features --features ${{ matrix.backend_feature }} - + - run: cargo test --verbose --features ${{ matrix.frontend_feature }} --no-default-features --features ${{ matrix.backend_feature }} + - run: cargo test --verbose --features ${{ matrix.frontend_feature }},std --no-default-features --features ${{ matrix.backend_feature }} simple-login-test: runs-on: ubuntu-latest @@ -142,6 +136,25 @@ jobs: - name: Run expect (which then runs cargo run) run: expect -f scripts/digital_locker.exp + build-no-std: + name: Build with no-std on ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + # for wasm + - wasm32-unknown-unknown + backend_feature: + - u64_backend + - u32_backend + - p256,u64_backend + steps: + - uses: actions/checkout@v2 + - uses: hecrj/setup-rust-action@v1 + - run: rustup target add ${{ matrix.target }} + - run: cargo build --verbose --target=${{ matrix.target }} --no-default-features --features ${{ matrix.backend_feature }} + benches: name: cargo bench compilation runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 6648185..47dfe2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "opaque-ke" version = "2.0.0-pre.1" repository = "https://github.com/novifinancial/opaque-ke" keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"] +categories = ["no-std"] description = "An implementation of the OPAQUE password-authenticated key exchange protocol" authors = ["Kevin Lewi ", "François Garillot "] license = "MIT" @@ -16,17 +17,19 @@ p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"] bench = [] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] +std = ["curve25519-dalek/std"] serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde"] [dependencies] argon2 = { version = "0.2", optional = true } base64 = { version = "0.13", optional = true } constant_time_eq = "0.1" -curve25519-dalek = { version = "3", default-features = false, features = ["std"] } +curve25519-dalek = { version = "3", default-features = false } digest = "0.9" displaydoc = "0.2" generic-array = "0.14" generic-bytes = { version = "0.1" } +getrandom = { version = "0.2", features = ["js"] } hkdf = "0.11" hmac = "0.11" num-bigint = { version = "0.4", optional = true } @@ -34,14 +37,13 @@ num-integer = { version = "0.1", optional = true } num-traits = { version = "0.2", optional = true } once_cell = { version = "1", optional = true } p256_ = { package = "p256", version = "0.9", optional = true } -rand = "0.8" +rand = { version = "0.8", default-features = false } serde = { version = "1", features = ["derive"], optional = true } subtle = { version = "2.3", default-features = false } thiserror = "1" zeroize = { version = "1", features = ["zeroize_derive"] } [dev-dependencies] -anyhow = "1" base64 = "0.13" bincode = "1" chacha20poly1305 = "0.8" diff --git a/src/envelope.rs b/src/envelope.rs index 0f776f6..b8e4211 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -11,13 +11,15 @@ use crate::{ keypair::{KeyPair, PrivateKey, PublicKey}, opaque::{bytestrings_from_identifiers, Identifiers}, }; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use generic_bytes::SizedBytes; use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand::{CryptoRng, RngCore}; -use std::convert::TryFrom; use zeroize::Zeroize; // Constant string used as salt for HKDF computation diff --git a/src/errors.rs b/src/errors.rs index 339b8e0..9fbc2aa 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,9 +4,10 @@ // LICENSE file in the root directory of this source tree. //! A list of error types which are produced during an execution of the protocol -use std::convert::Infallible; +use core::convert::Infallible; +use core::fmt::Debug; +#[cfg(feature = "std")] use std::error::Error; -use std::fmt::Debug; use displaydoc::Display; @@ -61,7 +62,7 @@ pub enum InternalPakeError { } impl Debug for InternalPakeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Custom(custom) => f.debug_tuple("InvalidByteSequence").field(custom).finish(), Self::InvalidByteSequence => f.debug_tuple("InvalidByteSequence").finish(), @@ -98,6 +99,7 @@ impl Debug for InternalPakeError { } } +#[cfg(feature = "std")] impl Error for InternalPakeError {} impl InternalPakeError { @@ -157,7 +159,7 @@ pub enum PakeError { } impl Debug for PakeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::CryptoError(internal_pake_error) => f .debug_tuple("CryptoError") @@ -177,6 +179,7 @@ impl Debug for PakeError { } } +#[cfg(feature = "std")] impl Error for PakeError {} // This is meant to express future(ly) non-trivial ways of converting the @@ -230,7 +233,7 @@ pub enum ProtocolError { } impl Debug for ProtocolError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::VerificationError(pake_error) => f .debug_tuple("VerificationError") @@ -247,6 +250,7 @@ impl Debug for ProtocolError { } } +#[cfg(feature = "std")] impl Error for ProtocolError {} // This is meant to express future(ly) non-trivial ways of converting the @@ -268,8 +272,8 @@ impl From> for ProtocolError { // See https://github.com/rust-lang/rust/issues/64715 and remove this when // merged, and https://github.com/dtolnay/thiserror/issues/62 for why this // comes up in our doc tests. -impl From<::std::convert::Infallible> for ProtocolError { - fn from(_: ::std::convert::Infallible) -> Self { +impl From<::core::convert::Infallible> for ProtocolError { + fn from(_: ::core::convert::Infallible) -> Self { unreachable!() } } diff --git a/src/group/expand.rs b/src/group/expand.rs index 9616784..c67ad36 100644 --- a/src/group/expand.rs +++ b/src/group/expand.rs @@ -6,6 +6,7 @@ use crate::errors::{InternalPakeError, ProtocolError}; use crate::hash::Hash; use crate::serialization::i2osp; +use alloc::vec::Vec; use digest::{BlockInput, Digest}; use generic_array::typenum::Unsigned; @@ -42,7 +43,7 @@ pub fn expand_message_xmd( let l_i_b_str = i2osp(len_in_bytes, 2)?; let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat(); - let mut b: Vec> = vec![H::digest(&msg_prime).to_vec()]; // b[0] + let mut b: Vec> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0] let mut h = H::new(); h.update(&b[0]); @@ -76,7 +77,7 @@ mod tests { #[test] fn test_expand_message_xmd() { // Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - let test_vectors: Vec = vec![ + let test_vectors: alloc::vec::Vec = alloc::vec![ Params { msg: "", len_in_bytes: 0x20, diff --git a/src/group/mod.rs b/src/group/mod.rs index 41e3901..54fea77 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -14,9 +14,9 @@ mod x25519; use crate::errors::{InternalPakeError, ProtocolError}; use crate::hash::Hash; +use core::ops::Mul; use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; -use std::ops::Mul; use zeroize::Zeroize; /// A prime-order subgroup of a base field (EC, prime-order field ...). This @@ -34,7 +34,7 @@ pub trait Group: Copy + Sized + for<'a> Mul<&'a ::Scalar, Output /// Generates the contextString parameter as defined in /// - fn get_context_string(mode: u8) -> Result, ProtocolError> { + fn get_context_string(mode: u8) -> Result, ProtocolError> { use crate::serialization::i2osp; Ok([i2osp(mode as usize, 1)?, i2osp(Self::SUITE_ID, 2)?].concat()) diff --git a/src/group/p256.rs b/src/group/p256.rs index 4cf89dc..a3abe0f 100644 --- a/src/group/p256.rs +++ b/src/group/p256.rs @@ -11,6 +11,8 @@ use super::Group; use crate::errors::{InternalPakeError, ProtocolError}; use crate::hash::Hash; +use core::ops::{Add, Div, Mul, Neg, Sub}; +use core::str::FromStr; use generic_array::typenum::{U32, U33}; use generic_array::{ArrayLength, GenericArray}; use num_bigint::{BigInt, Sign}; @@ -24,8 +26,6 @@ use p256_::elliptic_curve::subtle::ConstantTimeEq; use p256_::elliptic_curve::Field; use p256_::{AffinePoint, EncodedPoint, ProjectivePoint}; use rand::{CryptoRng, RngCore}; -use std::ops::{Add, Div, Mul, Neg, Sub}; -use std::str::FromStr; // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 // `p: 2^256 - 2^224 + 2^192 + 2^96 - 1` @@ -411,7 +411,7 @@ mod tests { #[test] fn map_to_curve_simple_swu() { // Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1 - let test_vectors: Vec = vec![ + let test_vectors = alloc::vec![ Params { msg: "", px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index adc2f3e..34b8486 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -6,6 +6,7 @@ use super::Group; use crate::errors::{InternalPakeError, ProtocolError}; use crate::hash::Hash; +use core::convert::TryInto; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -14,7 +15,6 @@ use curve25519_dalek::{ }; use generic_array::{typenum::U32, GenericArray}; use rand::{CryptoRng, RngCore}; -use std::convert::TryInto; use subtle::ConstantTimeEq; /// The implementation of such a subgroup for Ristretto diff --git a/src/impls.rs b/src/impls.rs index 675174f..e9b842f 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -5,10 +5,10 @@ macro_rules! impl_debug_eq_hash_for { (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? std::fmt::Debug for $name$(<$($gen),+>)? - $(where $($type: std::fmt::Debug,)+)? + impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? + $(where $($type: core::fmt::Debug,)+)? { - 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("$name") .field("$field1", &self.$field1) $(.field("$field2", &self.$field2))* @@ -29,20 +29,20 @@ macro_rules! impl_debug_eq_hash_for { } } - impl$(<$($gen$(: $bound)?),+>)? std::hash::Hash for $name$(<$($gen),+>)? - $(where $($type: std::hash::Hash,)+)? + impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? + $(where $($type: core::hash::Hash,)+)? { - fn hash(&self, state: &mut H) { - std::hash::Hash::hash(&self.$field1, state); - $(std::hash::Hash::hash(&self.$field2, state);)* + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&self.$field1, state); + $(core::hash::Hash::hash(&self.$field2, state);)* } } }; (tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? std::fmt::Debug for $name$(<$($gen),+>)? - $(where $($type: std::fmt::Debug,)+)? + impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? + $(where $($type: core::fmt::Debug,)+)? { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_tuple("$name") .field(&self.$field1) $(.field(&self.$field2))* @@ -63,12 +63,12 @@ macro_rules! impl_debug_eq_hash_for { } } - impl$(<$($gen$(: $bound)?),+>)? std::hash::Hash for $name$(<$($gen),+>)? - $(where $($type: std::hash::Hash,)+)? + impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? + $(where $($type: core::hash::Hash,)+)? { - fn hash(&self, state: &mut H) { - std::hash::Hash::hash(&self.$field1, state); - $(std::hash::Hash::hash(&self.$field2, state);)* + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&self.$field1, state); + $(core::hash::Hash::hash(&self.$field2, state);)* } } }; diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index 2285f65..ff94ef4 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -10,6 +10,7 @@ use crate::{ hash::Hash, keypair::{PrivateKey, PublicKey, SecretKey}, }; +use alloc::vec::Vec; use rand::{CryptoRng, RngCore}; use zeroize::Zeroize; diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index aa29294..e0225a4 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -18,6 +18,9 @@ use crate::{ keypair::{KeyPair, PrivateKey, PublicKey, SecretKey, SizedBytesExt}, serialization::serialize, }; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; use digest::{Digest, FixedOutput}; use generic_array::{ typenum::{Unsigned, U32}, @@ -27,7 +30,6 @@ use generic_bytes::SizedBytes; use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand::{CryptoRng, RngCore}; -use std::convert::TryFrom; use zeroize::Zeroize; pub(crate) type NonceLen = U32; diff --git a/src/keypair.rs b/src/keypair.rs index f81410b..f298e1e 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -9,17 +9,19 @@ use crate::errors::{InternalPakeError, ProtocolError}; use crate::group::Group; +use alloc::borrow::ToOwned; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::ops::Deref; #[cfg(test)] use generic_array::typenum::Unsigned; use generic_array::{ArrayLength, GenericArray}; use generic_bytes::{SizedBytes, TryFromSizedBytesError}; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use proptest::prelude::*; -#[cfg(test)] +#[cfg(all(test, feature = "std"))] use rand::{rngs::StdRng, SeedableRng}; use rand::{CryptoRng, RngCore}; -use std::fmt::Debug; -use std::ops::Deref; use zeroize::Zeroize; /// Convenience extension trait of SizedBytes @@ -57,7 +59,7 @@ impl> Clone for KeyPair { } impl + Debug> Debug for KeyPair { - 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("KeyPair") .field("pk", &self.pk) .field("sk", &self.sk) @@ -73,8 +75,8 @@ impl + PartialEq> PartialEq for KeyPair { impl + Eq> Eq for KeyPair {} -impl + std::hash::Hash> std::hash::Hash for KeyPair { - fn hash(&self, state: &mut H) { +impl + core::hash::Hash> core::hash::Hash for KeyPair { + fn hash(&self, state: &mut H) { self.pk.hash(state); self.sk.hash(state); } @@ -139,14 +141,14 @@ impl KeyPair { #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - vec![ + alloc::vec![ (self.pk.as_ptr(), G::ElemLen::to_usize()), (self.sk.as_ptr(), G::ScalarLen::to_usize()), ] } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] impl KeyPair { /// Test-only strategy returning a proptest Strategy based on /// generate_random @@ -179,7 +181,7 @@ impl> Clone for Key { } impl> Debug for Key { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_tuple("Key").field(&self.0).finish() } } @@ -192,8 +194,8 @@ impl> PartialEq for Key { } } -impl> std::hash::Hash for Key { - fn hash(&self, state: &mut H) { +impl> core::hash::Hash for Key { + fn hash(&self, state: &mut H) { self.0.hash(state); } } @@ -297,7 +299,7 @@ pub trait SecretKey: Clone + Sized + Zeroize { } impl SecretKey for PrivateKey { - type Error = std::convert::Infallible; + type Error = core::convert::Infallible; fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalPakeError> { let pk_data = GenericArray::::from_slice(&pk.0[..]); @@ -373,19 +375,20 @@ impl SizedBytes for PublicKey { mod tests { use super::*; use crate::errors::*; + use core::slice::from_raw_parts; use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::typenum::Unsigned; use rand::rngs::OsRng; - use std::slice::from_raw_parts; #[test] fn test_zeroize_key() -> Result<(), ProtocolError> { let key_len = ::ElemLen::to_usize(); - let mut key = - Key::<::ElemLen>(GenericArray::clone_from_slice(&vec![ + let mut key = Key::<::ElemLen>(GenericArray::clone_from_slice( + &alloc::vec![ 1u8; key_len - ])); + ], + )); let ptr = key.as_ptr(); key.zeroize(); @@ -412,6 +415,7 @@ mod tests { Ok(()) } + #[cfg(feature = "std")] proptest! { #[test] fn test_ristretto_check(kp in KeyPair::::uniform_keypair_strategy()) { @@ -448,7 +452,7 @@ mod tests { } #[test] - fn remote_key() -> anyhow::Result<()> { + fn remote_key() { use crate::{ CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters, @@ -473,7 +477,7 @@ mod tests { struct RemoteKey(PrivateKey); impl SecretKey for RemoteKey { - type Error = std::convert::Infallible; + type Error = core::convert::Infallible; fn diffie_hellman( &self, @@ -502,27 +506,29 @@ mod tests { let sk = RistrettoPoint::random_nonzero_scalar(&mut OsRng); let sk_bytes = RistrettoPoint::scalar_as_bytes(sk); let sk = RemoteKey(PrivateKey::from_arr(&sk_bytes).unwrap()); - let keypair = KeyPair::from_private_key(sk)?; + let keypair = KeyPair::from_private_key(sk).unwrap(); let server_setup = ServerSetup::::new_with_key(&mut OsRng, keypair); let ClientRegistrationStartResult { message, state: client, - } = ClientRegistration::::start(&mut OsRng, PASSWORD.as_bytes())?; + } = ClientRegistration::::start(&mut OsRng, PASSWORD.as_bytes()).unwrap(); let ServerRegistrationStartResult { message, .. } = - ServerRegistration::start(&server_setup, message, &[])?; - let ClientRegistrationFinishResult { message, .. } = client.finish( - &mut OsRng, - message, - ClientRegistrationFinishParameters::Default, - )?; + ServerRegistration::start(&server_setup, message, &[]).unwrap(); + let ClientRegistrationFinishResult { message, .. } = client + .finish( + &mut OsRng, + message, + ClientRegistrationFinishParameters::Default, + ) + .unwrap(); let file = ServerRegistration::finish(message); let ClientLoginStartResult { message, state: client, - } = ClientLogin::::start(&mut OsRng, PASSWORD.as_bytes())?; + } = ClientLogin::::start(&mut OsRng, PASSWORD.as_bytes()).unwrap(); let ServerLoginStartResult { message, state: server, @@ -534,11 +540,11 @@ mod tests { message, &[], ServerLoginStartParameters::default(), - )?; - let ClientLoginFinishResult { message, .. } = - client.finish(message, ClientLoginFinishParameters::Default)?; - server.finish(message)?; - - Ok(()) + ) + .unwrap(); + let ClientLoginFinishResult { message, .. } = client + .finish(message, ClientLoginFinishParameters::Default) + .unwrap(); + server.finish(message).unwrap(); } } diff --git a/src/lib.rs b/src/lib.rs index 8c81542..99ae69d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -824,6 +824,7 @@ #![cfg_attr(not(feature = "bench"), deny(missing_docs))] #![deny(unsafe_code)] +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(any(feature = "u64_backend", feature = "u32_backend",)))] compile_error!( @@ -831,6 +832,8 @@ compile_error!( please enable one of: u64_backend, u32_backend" ); +extern crate alloc; + // Error types pub mod errors; diff --git a/src/messages.rs b/src/messages.rs index 00b7deb..382195e 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -17,6 +17,7 @@ use crate::{ keypair::{KeyPair, PublicKey, SecretKey, SizedBytesExt}, opaque::ServerSetup, }; +use alloc::vec::Vec; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use generic_bytes::SizedBytes; @@ -197,7 +198,7 @@ impl RegistrationUpload { rng: &mut R, server_setup: &ServerSetup, ) -> Self { - let mut masking_key = vec![0u8; ::OutputSize::to_usize()]; + let mut masking_key = alloc::vec![0u8; ::OutputSize::to_usize()]; rng.fill_bytes(&mut masking_key); Self { diff --git a/src/opaque.rs b/src/opaque.rs index fd4c538..b3475a6 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -19,12 +19,14 @@ use crate::{ CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, }; +use alloc::vec; +use alloc::vec::Vec; +use core::marker::PhantomData; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use generic_bytes::SizedBytes; use hkdf::Hkdf; use rand::{CryptoRng, RngCore}; -use std::marker::PhantomData; use zeroize::Zeroize; const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad"; diff --git a/src/oprf.rs b/src/oprf.rs index d0fe3e7..e21fb26 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -11,7 +11,7 @@ use rand::{CryptoRng, RngCore}; /// Used to store the OPRF input and blinding factor #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] pub struct Token { - pub(crate) data: Vec, + pub(crate) data: alloc::vec::Vec, pub(crate) blind: Grp::Scalar, } @@ -154,7 +154,7 @@ mod tests { #[test] fn oprf_inversion_unsalted() { let mut rng = OsRng; - let mut input = vec![0u8; 64]; + let mut input = alloc::vec![0u8; 64]; rng.fill_bytes(&mut input); let (token, alpha) = blind::<_, RistrettoPoint, sha2::Sha512>(&input, &mut rng).unwrap(); let res = diff --git a/src/serialization/mod.rs b/src/serialization/mod.rs index ed65a83..9ff0df6 100644 --- a/src/serialization/mod.rs +++ b/src/serialization/mod.rs @@ -4,10 +4,11 @@ // LICENSE file in the root directory of this source tree. use crate::errors::PakeError; +use alloc::vec::Vec; // Corresponds to the I2OSP() function from RFC8017 -pub(crate) fn i2osp(input: usize, length: usize) -> Result, PakeError> { - let sizeof_usize = std::mem::size_of::(); +pub(crate) fn i2osp(input: usize, length: usize) -> Result, PakeError> { + let sizeof_usize = core::mem::size_of::(); // Check if input >= 256^length if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 { @@ -18,7 +19,7 @@ pub(crate) fn i2osp(input: usize, length: usize) -> Result, PakeError> { return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec()); } - let mut output = vec![0u8; length]; + let mut output = alloc::vec![0u8; length]; output.splice( length - sizeof_usize..length, input.to_be_bytes().iter().cloned(), @@ -28,12 +29,12 @@ pub(crate) fn i2osp(input: usize, length: usize) -> Result, PakeError> { // Corresponds to the OS2IP() function from RFC8017 pub(crate) fn os2ip(input: &[u8]) -> Result { - if input.len() > std::mem::size_of::() { + if input.len() > core::mem::size_of::() { return Err(PakeError::SerializationError); } - let mut output_array = [0u8; std::mem::size_of::()]; - output_array[std::mem::size_of::() - input.len()..].copy_from_slice(input); + let mut output_array = [0u8; core::mem::size_of::()]; + output_array[core::mem::size_of::() - input.len()..].copy_from_slice(input); Ok(usize::from_be_bytes(output_array)) } @@ -45,7 +46,7 @@ pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result, PakeE // Tokenizes an input of the format I2OSP(len(input), max_bytes) || input, outputting // (input, remainder) pub(crate) fn tokenize(input: &[u8], size_bytes: usize) -> Result<(Vec, Vec), PakeError> { - if size_bytes > std::mem::size_of::() || input.len() < size_bytes { + if size_bytes > core::mem::size_of::() || input.len() < size_bytes { return Err(PakeError::SerializationError); } @@ -89,17 +90,17 @@ macro_rules! impl_serialize_and_deserialize_for { .map_err(serde::de::Error::custom) } else { struct ByteVisitor { - marker: std::marker::PhantomData, + marker: core::marker::PhantomData, } impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor { type Value = $t; fn expecting( &self, - formatter: &mut std::fmt::Formatter, - ) -> std::fmt::Result { - formatter.write_str(std::concat!( + formatter: &mut core::fmt::Formatter, + ) -> core::fmt::Result { + formatter.write_str(core::concat!( "the byte representation of a ", - std::stringify!($t) + core::stringify!($t) )) } @@ -110,16 +111,16 @@ macro_rules! impl_serialize_and_deserialize_for { $t::::deserialize(value).map_err(|_| { serde::de::Error::invalid_value( serde::de::Unexpected::Bytes(value), - &std::concat!( + &core::concat!( "invalid byte sequence for ", - std::stringify!($t) + core::stringify!($t) ), ) }) } } deserializer.deserialize_bytes(ByteVisitor:: { - marker: std::marker::PhantomData, + marker: core::marker::PhantomData, }) } } diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index 468fd01..eb50553 100644 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -16,6 +16,10 @@ use crate::{ serialization::{i2osp, os2ip, serialize}, *, }; +#[cfg(test)] +use alloc::vec; +#[cfg(test)] +use alloc::vec::Vec; use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; use generic_array::typenum::Unsigned; @@ -351,8 +355,8 @@ fn ke3_message_roundtrip() { proptest! { #[test] -fn test_i2osp_os2ip(bytes in vec(any::(), 0..std::mem::size_of::())) { - assert_eq!(i2osp(os2ip(&bytes)?, bytes.len())?, bytes); +fn test_i2osp_os2ip(bytes in vec(any::(), 0..core::mem::size_of::())) { + assert_eq!(i2osp(os2ip(&bytes).unwrap(), bytes.len()).unwrap(), bytes); } #[test] diff --git a/src/slow_hash.rs b/src/slow_hash.rs index 5596406..bb666f2 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -6,6 +6,7 @@ //! Trait specifying a slow hashing function use crate::{errors::InternalPakeError, hash::Hash}; +use alloc::vec::Vec; use digest::Digest; #[cfg(feature = "slow-hash")] use generic_array::typenum::Unsigned; @@ -36,7 +37,7 @@ impl SlowHash for argon2::Argon2<'_> { input: GenericArray::OutputSize>, ) -> Result, InternalPakeError> { let params = argon2::Argon2::default(); - let mut output = vec![0u8; ::OutputSize::to_usize()]; + let mut output = alloc::vec![0u8; ::OutputSize::to_usize()]; params .hash_password_into( argon2::Algorithm::Argon2id, diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs index 7480e8f..ad17387 100644 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -6,22 +6,17 @@ #![allow(unsafe_code)] use crate::{ - ciphersuite::CipherSuite, - errors::*, - group::Group, - key_exchange::tripledh::{NonceLen, TripleDH}, - keypair::KeyPair, - opaque::*, - slow_hash::NoOpHash, - tests::mock_rng::CycleRng, - *, + ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, opaque::*, + slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, }; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::slice::from_raw_parts; use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; -use generic_array::typenum::Unsigned; use generic_bytes::SizedBytes; -use rand::{rngs::OsRng, RngCore}; +use rand::rngs::OsRng; use serde_json::Value; -use std::slice::from_raw_parts; use zeroize::Zeroize; // Tests @@ -154,8 +149,9 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters { } } -fn stringify_test_vectors(p: &TestVectorParameters) -> String { - let mut s = String::new(); +#[cfg(feature = "std")] +fn stringify_test_vectors(p: &TestVectorParameters) -> alloc::string::String { + let mut s = alloc::string::String::new(); s.push_str("{\n"); s.push_str(format!("\"client_s_pk\": \"{}\",\n", hex::encode(&p.client_s_pk)).as_str()); s.push_str(format!("\"client_s_sk\": \"{}\",\n", hex::encode(&p.client_s_sk)).as_str()); @@ -277,7 +273,12 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String { s } +#[cfg(feature = "std")] fn generate_parameters() -> TestVectorParameters { + use crate::{group::Group, key_exchange::tripledh::NonceLen, keypair::KeyPair}; + use generic_array::typenum::Unsigned; + use rand::RngCore; + let mut rng = OsRng; // Inputs @@ -447,6 +448,7 @@ fn generate_parameters() -> TestVectorParameters { } } +#[cfg(feature = "std")] #[test] fn generate_test_vectors() { let parameters = generate_parameters::(); diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs index e34157d..1d10828 100644 --- a/src/tests/mock_rng.rs +++ b/src/tests/mock_rng.rs @@ -3,8 +3,9 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +use alloc::vec::Vec; +use core::cmp::min; use rand::{CryptoRng, Error, RngCore}; -use std::cmp::min; /// A simple implementation of `RngCore` for testing purposes. /// diff --git a/src/tests/opaque_test_vectors.rs b/src/tests/opaque_test_vectors.rs index 9be3e16..42011f0 100644 --- a/src/tests/opaque_test_vectors.rs +++ b/src/tests/opaque_test_vectors.rs @@ -7,6 +7,9 @@ use crate::{ ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, keypair::PrivateKey, opaque::*, slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, }; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::{format, vec}; use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::typenum::Unsigned; use generic_bytes::SizedBytes; @@ -711,7 +714,7 @@ macro_rules! rfc_to_params { }; } -fn rfc_to_json(input: &str) -> String { +fn rfc_to_json(input: &str) -> alloc::string::String { let mut json = vec![]; for line in input.lines() { // If line contains colon, then diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 2885654..2745046 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -7,6 +7,8 @@ use crate::group::Group; use crate::hash::Hash; use crate::tests::mock_rng::CycleRng; use crate::{errors::*, oprf}; +use alloc::string::ToString; +use alloc::vec::Vec; use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::GenericArray; use serde_json::Value;