Skip to content

Commit e815294

Browse files
authored
feat: nostd, core+alloc support
* No-std support * Fix tests * Cleanly error out when building without the alloc feature * Run no-std tests on arm-linux-gnu target * Fix nostd tests * Attempt 2 at fixing nostd tests * Fix warnings when running tests in nostd mode * fixup! No-std support
1 parent 4274e83 commit e815294

14 files changed

+171
-70
lines changed

.github/workflows/ci.yml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ env:
1111

1212
jobs:
1313
build:
14-
name: ${{ matrix.name }}
14+
name: ${{ matrix.rust }} - ${{ matrix.target }}
1515
runs-on: ubuntu-latest
1616

1717
# The build matrix does not yet support 'allow failures' at job level.
@@ -22,9 +22,19 @@ jobs:
2222
- 1.36.0
2323
- stable
2424
- nightly
25+
target:
26+
- x86_64-unknown-linux-gnu
27+
- thumbv7m-none-eabi
2528
include:
2629
- rust: nightly
30+
target: x86_64-unknown-linux-gnu
2731
args: --all-features
32+
- target: thumbv7m-none-eabi
33+
test-target: arm-unknown-linux-gnueabi
34+
args: --no-default-features --features=alloc
35+
exclude:
36+
- rust: 1.36.0
37+
target: thumbv7m-none-eabi
2838

2939
steps:
3040
- name: Checkout
@@ -34,24 +44,28 @@ jobs:
3444
uses: actions-rs/toolchain@v1
3545
with:
3646
toolchain: ${{ matrix.rust || 'stable' }}
47+
target: ${{ matrix.target }}
3748
profile: minimal
3849
override: true
3950

4051
- name: Build
4152
uses: actions-rs/cargo@v1
4253
with:
54+
use-cross: true
4355
command: build
44-
args: --verbose ${{ matrix.args }}
56+
args: --target ${{ matrix.target }} --verbose ${{ matrix.args }}
4557

4658
- name: Test
4759
uses: actions-rs/cargo@v1
4860
with:
61+
use-cross: true
4962
command: test
50-
args: --verbose ${{ matrix.args }}
63+
args: --target ${{ matrix.test-target || matrix.target }} --verbose ${{ matrix.args }}
5164

5265
- name: Bench
5366
uses: actions-rs/cargo@v1
5467
with:
68+
use-cross: true
5569
command: bench
56-
args: --verbose --no-run ${{ matrix.args }}
70+
args: --target ${{ matrix.test-target || matrix.target }} --verbose --no-run ${{ matrix.args }}
5771
if: matrix.rust == 'nightly'

Cargo.toml

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@ categories = ["cryptography"]
1212
readme = "README.md"
1313

1414
[dependencies]
15-
num-bigint = { version = "0.6", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" }
16-
num-traits = "0.2.6"
17-
num-integer = "0.1.39"
18-
num-iter = "0.1.37"
19-
lazy_static = "1.3.0"
20-
rand = "0.7.0"
21-
byteorder = "1.3.1"
22-
thiserror = "1.0.11"
23-
subtle = "2.0.0"
24-
simple_asn1 = "0.4"
15+
num-bigint = { version = "0.6", features = ["i128", "u64_digit", "prime", "zeroize"], default-features = false, package = "num-bigint-dig" }
16+
num-traits = { version= "0.2.9", default-features = false, features = ["libm"] }
17+
num-integer = { version = "0.1.39", default-features = false }
18+
num-iter = { version = "0.1.37", default-features = false }
19+
lazy_static = { version = "1.3.0", features = ["spin_no_std"] }
20+
rand = { version = "0.7.0", default-features = false }
21+
byteorder = { version = "1.3.1", default-features = false }
22+
subtle = { version = "2.0.0", default-features = false }
23+
simple_asn1 = { version = "0.4", optional = true }
2524
pem = { version = "0.8", optional = true }
26-
digest = { version = "0.9.0", features = ["std"] }
27-
sha2 = "0.9.0"
25+
digest = { version = "0.9.0", default-features = false }
2826

2927
[dependencies.zeroize]
3028
version = "1.1.0"
@@ -35,16 +33,17 @@ package = "serde"
3533
optional = true
3634
version = "1.0.89"
3735
default-features = false
38-
features = ["std", "derive"]
36+
features = ["derive"]
3937

4038
[dev-dependencies]
4139
base64 = "0.12.0"
4240
hex = "0.4.0"
4341
serde_test = "1.0.89"
4442
rand_xorshift = "0.2.0"
4543
pem = "0.8"
46-
sha-1 = "0.9.0"
47-
sha3 = "0.9.0"
44+
sha-1 = { default-features = false, version = "0.9.0" }
45+
sha2 = { default-features = false, version = "0.9.0" }
46+
sha3 = { default-features = false, version = "0.9.0" }
4847

4948
[[bench]]
5049
name = "key"
@@ -56,8 +55,10 @@ name = "key"
5655
# debug = true
5756

5857
[features]
59-
default = ["pem"]
58+
default = ["std", "pem"]
6059
nightly = ["subtle/nightly", "num-bigint/nightly"]
6160
serde = ["num-bigint/serde", "serde_crate"]
6261
serde1 = ["serde"] # deprecated
6362
expose-internals = []
63+
std = ["alloc", "simple_asn1", "digest/std", "rand/std"]
64+
alloc = ["digest/alloc"]

src/algorithms.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use digest::DynDigest;
22
use num_bigint::traits::ModInverse;
33
use num_bigint::{BigUint, RandPrime};
44
use num_traits::{FromPrimitive, One, Zero};
5+
#[allow(unused_imports)]
6+
use num_traits::Float;
57
use rand::Rng;
8+
use alloc::vec;
69

710
use crate::errors::{Error, Result};
811
use crate::key::RSAPrivateKey;
@@ -143,7 +146,7 @@ pub fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) {
143146
let mut counter = [0u8; 4];
144147
let mut i = 0;
145148

146-
const MAX_LEN: u64 = std::u32::MAX as u64 + 1;
149+
const MAX_LEN: u64 = core::u32::MAX as u64 + 1;
147150
assert!(out.len() as u64 <= MAX_LEN);
148151

149152
while i < out.len() {

src/encode.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use num_bigint::{BigUint, ToBigInt};
88
use num_traits::Zero;
99
use pem::{EncodeConfig, LineEnding};
1010
use simple_asn1::{to_der, ASN1Block};
11+
use std::prelude::v1::*;
12+
use std::{vec, format};
1113

1214
const DEFAULT_ENCODING_CONFIG: EncodeConfig = EncodeConfig {
1315
line_ending: LineEnding::LF,

src/errors.rs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,51 @@
1-
use thiserror::Error;
1+
use alloc::string::String;
22

3-
pub type Result<T> = ::std::result::Result<T, Error>;
3+
pub type Result<T> = core::result::Result<T, Error>;
44

55
/// Error types
6-
#[derive(Debug, Error)]
6+
#[derive(Debug)]
77
pub enum Error {
8-
#[error("invalid padding scheme")]
98
InvalidPaddingScheme,
10-
#[error("decryption error")]
119
Decryption,
12-
#[error("verification error")]
1310
Verification,
14-
#[error("message too long")]
1511
MessageTooLong,
16-
#[error("input must be hashed")]
1712
InputNotHashed,
18-
#[error("nprimes must be >= 2")]
1913
NprimesTooSmall,
20-
#[error("too few primes of given length to generate an RSA key")]
2114
TooFewPrimes,
22-
#[error("invalid prime value")]
2315
InvalidPrime,
24-
#[error("invalid modulus")]
2516
InvalidModulus,
26-
#[error("invalid exponent")]
2717
InvalidExponent,
28-
#[error("invalid coefficient")]
2918
InvalidCoefficient,
30-
#[error("public exponent too small")]
3119
PublicExponentTooSmall,
32-
#[error("public exponent too large")]
3320
PublicExponentTooLarge,
34-
#[error("parse error: {}", reason)]
3521
ParseError { reason: String },
36-
#[error("encoding error: {}", reason)]
3722
EncodeError { reason: String },
38-
#[error("internal error")]
3923
Internal,
40-
#[error("label too long")]
4124
LabelTooLong,
4225
}
26+
27+
#[cfg(feature = "std")]
28+
impl std::error::Error for Error {}
29+
impl core::fmt::Display for Error {
30+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
31+
match self {
32+
Error::InvalidPaddingScheme => write!(f, "invalid padding scheme"),
33+
Error::Decryption => write!(f, "decryption error"),
34+
Error::Verification => write!(f, "verification error"),
35+
Error::MessageTooLong => write!(f, "message too long"),
36+
Error::InputNotHashed => write!(f, "input must be hashed"),
37+
Error::NprimesTooSmall => write!(f, "nprimes must be >= 2"),
38+
Error::TooFewPrimes => write!(f, "too few primes of given length to generate an RSA key"),
39+
Error::InvalidPrime => write!(f, "invalid prime value"),
40+
Error::InvalidModulus => write!(f, "invalid modulus"),
41+
Error::InvalidExponent => write!(f, "invalid exponent"),
42+
Error::InvalidCoefficient => write!(f, "invalid coefficient"),
43+
Error::PublicExponentTooSmall => write!(f, "public exponent too small"),
44+
Error::PublicExponentTooLarge => write!(f, "public exponent too large"),
45+
Error::ParseError { reason } => write!(f, "parse error: {}", reason),
46+
Error::EncodeError { reason } => write!(f, "encoding error: {}", reason),
47+
Error::Internal => write!(f, "internal error"),
48+
Error::LabelTooLong => write!(f, "label too long"),
49+
}
50+
}
51+
}

src/internals.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use num_bigint::{BigInt, BigUint, IntoBigInt, IntoBigUint, ModInverse, RandBigInt, ToBigInt};
22
use num_traits::{One, Signed, Zero};
33
use rand::Rng;
4-
use std::borrow::Cow;
4+
use alloc::borrow::Cow;
55
use zeroize::Zeroize;
6+
use alloc::vec::Vec;
7+
use alloc::vec;
68

79
use crate::errors::{Error, Result};
810
use crate::key::{PublicKeyParts, RSAPrivateKey};

src/key.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use num_bigint::traits::ModInverse;
22
use num_bigint::Sign::Plus;
33
use num_bigint::{BigInt, BigUint};
44
use num_traits::{FromPrimitive, One};
5-
use rand::{rngs::ThreadRng, Rng};
5+
use rand::{rngs::StdRng, Rng};
66
#[cfg(feature = "serde")]
77
use serde_crate::{Deserialize, Serialize};
8-
use std::ops::Deref;
8+
use core::ops::Deref;
99
use zeroize::Zeroize;
10+
use alloc::vec::Vec;
1011

1112
use crate::algorithms::{generate_multi_prime_key, generate_multi_prime_key_with_exp};
1213
use crate::errors::{Error, Result};
@@ -247,6 +248,7 @@ impl RSAPublicKey {
247248
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
248249
/// let public_key = RSAPublicKey::from_pkcs1(&der_bytes).expect("failed to parse key");
249250
/// ```
251+
#[cfg(feature = "std")]
250252
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPublicKey> {
251253
crate::parse::parse_public_key_pkcs1(der)
252254
}
@@ -281,6 +283,7 @@ impl RSAPublicKey {
281283
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
282284
/// let public_key = RSAPublicKey::from_pkcs8(&der_bytes).expect("failed to parse key");
283285
/// ```
286+
#[cfg(feature = "std")]
284287
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPublicKey> {
285288
crate::parse::parse_public_key_pkcs8(der)
286289
}
@@ -405,6 +408,7 @@ impl RSAPrivateKey {
405408
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
406409
/// let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
407410
/// ```
411+
#[cfg(feature = "std")]
408412
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPrivateKey> {
409413
crate::parse::parse_private_key_pkcs1(der)
410414
}
@@ -445,6 +449,7 @@ impl RSAPrivateKey {
445449
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
446450
/// let private_key = RSAPrivateKey::from_pkcs8(&der_bytes).expect("failed to parse key");
447451
/// ```
452+
#[cfg(feature = "std")]
448453
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPrivateKey> {
449454
crate::parse::parse_private_key_pkcs8(der)
450455
}
@@ -554,10 +559,10 @@ impl RSAPrivateKey {
554559
match padding {
555560
// need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything
556561
PaddingScheme::PKCS1v15Encrypt => {
557-
pkcs1v15::decrypt::<ThreadRng, _>(None, self, ciphertext)
562+
pkcs1v15::decrypt::<StdRng, _>(None, self, ciphertext)
558563
}
559564
PaddingScheme::OAEP { mut digest, label } => {
560-
oaep::decrypt::<ThreadRng, _>(None, self, ciphertext, &mut *digest, label)
565+
oaep::decrypt::<StdRng, _>(None, self, ciphertext, &mut *digest, label)
561566
}
562567
_ => Err(Error::InvalidPaddingScheme),
563568
}
@@ -584,14 +589,15 @@ impl RSAPrivateKey {
584589
/// Sign the given digest.
585590
pub fn sign(&self, padding: PaddingScheme, digest_in: &[u8]) -> Result<Vec<u8>> {
586591
match padding {
592+
// need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything
587593
PaddingScheme::PKCS1v15Sign { ref hash } => {
588-
pkcs1v15::sign::<ThreadRng, _>(None, self, hash.as_ref(), digest_in)
594+
pkcs1v15::sign::<StdRng, _>(None, self, hash.as_ref(), digest_in)
589595
}
590596
PaddingScheme::PSS {
591597
mut salt_rng,
592598
mut digest,
593599
salt_len,
594-
} => pss::sign::<_, ThreadRng, _>(
600+
} => pss::sign::<_, StdRng, _>(
595601
&mut *salt_rng,
596602
None,
597603
self,
@@ -652,9 +658,10 @@ mod tests {
652658
use super::*;
653659
use crate::internals;
654660

661+
use std::time::SystemTime;
655662
use digest::{Digest, DynDigest};
656663
use num_traits::{FromPrimitive, ToPrimitive};
657-
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng};
664+
use rand::{distributions::Alphanumeric, rngs::StdRng, SeedableRng};
658665
use sha1::Sha1;
659666
use sha2::{Sha224, Sha256, Sha384, Sha512};
660667
use sha3::{Sha3_256, Sha3_384, Sha3_512};
@@ -687,10 +694,11 @@ mod tests {
687694
let pub_key: RSAPublicKey = private_key.clone().into();
688695
let m = BigUint::from_u64(42).expect("invalid 42");
689696
let c = internals::encrypt(&pub_key, &m);
690-
let m2 = internals::decrypt::<ThreadRng>(None, &private_key, &c)
697+
let m2 = internals::decrypt::<StdRng>(None, &private_key, &c)
691698
.expect("unable to decrypt without blinding");
692699
assert_eq!(m, m2);
693-
let mut rng = thread_rng();
700+
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
701+
let mut rng = StdRng::seed_from_u64(seed.as_secs());
694702
let m3 = internals::decrypt(Some(&mut rng), &private_key, &c)
695703
.expect("unable to decrypt with blinding");
696704
assert_eq!(m, m3);
@@ -700,7 +708,8 @@ mod tests {
700708
($name:ident, $multi:expr, $size:expr) => {
701709
#[test]
702710
fn $name() {
703-
let mut rng = thread_rng();
711+
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
712+
let mut rng = StdRng::seed_from_u64(seed.as_secs());
704713

705714
for _ in 0..10 {
706715
let private_key = if $multi == 2 {
@@ -730,7 +739,8 @@ mod tests {
730739
#[test]
731740
fn test_impossible_keys() {
732741
// make sure not infinite loops are hit here.
733-
let mut rng = thread_rng();
742+
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
743+
let mut rng = StdRng::seed_from_u64(seed.as_secs());
734744
for i in 0..32 {
735745
let _ = RSAPrivateKey::new(&mut rng, i).is_err();
736746
let _ = generate_multi_prime_key(&mut rng, 3, i);
@@ -902,7 +912,8 @@ mod tests {
902912
}
903913

904914
fn do_test_encrypt_decrypt_oaep<D: 'static + Digest + DynDigest>(prk: &RSAPrivateKey) {
905-
let mut rng = thread_rng();
915+
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
916+
let mut rng = StdRng::seed_from_u64(seed.as_secs());
906917

907918
let k = prk.size();
908919

@@ -913,7 +924,7 @@ mod tests {
913924
}
914925
let has_label: bool = rng.gen();
915926
let label: Option<String> = if has_label {
916-
Some(rng.sample_iter(&Alphanumeric).take(30).collect())
927+
Some(rng.clone().sample_iter(&Alphanumeric).take(30).collect())
917928
} else {
918929
None
919930
};
@@ -949,7 +960,8 @@ mod tests {
949960

950961
#[test]
951962
fn test_decrypt_oaep_invalid_hash() {
952-
let mut rng = thread_rng();
963+
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
964+
let mut rng = StdRng::seed_from_u64(seed.as_secs());
953965
let priv_key = get_private_key();
954966
let pub_key: RSAPublicKey = (&priv_key).into();
955967
let ciphertext = pub_key

0 commit comments

Comments
 (0)