Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: nostd, core+alloc support #22

Merged
merged 8 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:

jobs:
build:
name: ${{ matrix.name }}
name: ${{ matrix.rust }} - ${{ matrix.target }}
runs-on: ubuntu-latest

# The build matrix does not yet support 'allow failures' at job level.
Expand All @@ -22,9 +22,19 @@ jobs:
- 1.36.0
- stable
- nightly
target:
- x86_64-unknown-linux-gnu
- thumbv7m-none-eabi
include:
- rust: nightly
target: x86_64-unknown-linux-gnu
args: --all-features
- target: thumbv7m-none-eabi
test-target: arm-unknown-linux-gnueabi
args: --no-default-features --features=alloc
exclude:
- rust: 1.36.0
target: thumbv7m-none-eabi

steps:
- name: Checkout
Expand All @@ -34,24 +44,28 @@ jobs:
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust || 'stable' }}
target: ${{ matrix.target }}
profile: minimal
override: true

- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --verbose ${{ matrix.args }}
args: --target ${{ matrix.target }} --verbose ${{ matrix.args }}

- name: Test
uses: actions-rs/cargo@v1
with:
use-cross: true
command: test
args: --verbose ${{ matrix.args }}
args: --target ${{ matrix.test-target || matrix.target }} --verbose ${{ matrix.args }}

- name: Bench
uses: actions-rs/cargo@v1
with:
use-cross: true
command: bench
args: --verbose --no-run ${{ matrix.args }}
args: --target ${{ matrix.test-target || matrix.target }} --verbose --no-run ${{ matrix.args }}
if: matrix.rust == 'nightly'
33 changes: 17 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@ categories = ["cryptography"]
readme = "README.md"

[dependencies]
num-bigint = { version = "0.6", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" }
num-traits = "0.2.6"
num-integer = "0.1.39"
num-iter = "0.1.37"
lazy_static = "1.3.0"
rand = "0.7.0"
byteorder = "1.3.1"
thiserror = "1.0.11"
subtle = "2.0.0"
simple_asn1 = "0.4"
num-bigint = { version = "0.6", features = ["i128", "u64_digit", "prime", "zeroize"], default-features = false, package = "num-bigint-dig" }
num-traits = { version= "0.2.9", default-features = false, features = ["libm"] }
num-integer = { version = "0.1.39", default-features = false }
num-iter = { version = "0.1.37", default-features = false }
lazy_static = { version = "1.3.0", features = ["spin_no_std"] }
rand = { version = "0.7.0", default-features = false }
byteorder = { version = "1.3.1", default-features = false }
subtle = { version = "2.0.0", default-features = false }
simple_asn1 = { version = "0.4", optional = true }
pem = { version = "0.8", optional = true }
digest = { version = "0.9.0", features = ["std"] }
sha2 = "0.9.0"
digest = { version = "0.9.0", default-features = false }

[dependencies.zeroize]
version = "1.1.0"
Expand All @@ -35,16 +33,17 @@ package = "serde"
optional = true
version = "1.0.89"
default-features = false
features = ["std", "derive"]
features = ["derive"]

[dev-dependencies]
base64 = "0.12.0"
hex = "0.4.0"
serde_test = "1.0.89"
rand_xorshift = "0.2.0"
pem = "0.8"
sha-1 = "0.9.0"
sha3 = "0.9.0"
sha-1 = { default-features = false, version = "0.9.0" }
sha2 = { default-features = false, version = "0.9.0" }
sha3 = { default-features = false, version = "0.9.0" }

[[bench]]
name = "key"
Expand All @@ -56,8 +55,10 @@ name = "key"
# debug = true

[features]
default = ["pem"]
default = ["std", "pem"]
nightly = ["subtle/nightly", "num-bigint/nightly"]
serde = ["num-bigint/serde", "serde_crate"]
serde1 = ["serde"] # deprecated
expose-internals = []
std = ["alloc", "simple_asn1", "digest/std", "rand/std"]
alloc = ["digest/alloc"]
5 changes: 4 additions & 1 deletion src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use digest::DynDigest;
use num_bigint::traits::ModInverse;
use num_bigint::{BigUint, RandPrime};
use num_traits::{FromPrimitive, One, Zero};
#[allow(unused_imports)]
use num_traits::Float;
use rand::Rng;
use alloc::vec;

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

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

while i < out.len() {
Expand Down
2 changes: 2 additions & 0 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use num_bigint::{BigUint, ToBigInt};
use num_traits::Zero;
use pem::{EncodeConfig, LineEnding};
use simple_asn1::{to_der, ASN1Block};
use std::prelude::v1::*;
use std::{vec, format};

const DEFAULT_ENCODING_CONFIG: EncodeConfig = EncodeConfig {
line_ending: LineEnding::LF,
Expand Down
49 changes: 29 additions & 20 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,51 @@
use thiserror::Error;
use alloc::string::String;

pub type Result<T> = ::std::result::Result<T, Error>;
pub type Result<T> = core::result::Result<T, Error>;

/// Error types
#[derive(Debug, Error)]
#[derive(Debug)]
pub enum Error {
#[error("invalid padding scheme")]
InvalidPaddingScheme,
#[error("decryption error")]
Decryption,
#[error("verification error")]
Verification,
#[error("message too long")]
MessageTooLong,
#[error("input must be hashed")]
InputNotHashed,
#[error("nprimes must be >= 2")]
NprimesTooSmall,
#[error("too few primes of given length to generate an RSA key")]
TooFewPrimes,
#[error("invalid prime value")]
InvalidPrime,
#[error("invalid modulus")]
InvalidModulus,
#[error("invalid exponent")]
InvalidExponent,
#[error("invalid coefficient")]
InvalidCoefficient,
#[error("public exponent too small")]
PublicExponentTooSmall,
#[error("public exponent too large")]
PublicExponentTooLarge,
#[error("parse error: {}", reason)]
ParseError { reason: String },
#[error("encoding error: {}", reason)]
EncodeError { reason: String },
#[error("internal error")]
Internal,
#[error("label too long")]
LabelTooLong,
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Error::InvalidPaddingScheme => write!(f, "invalid padding scheme"),
Error::Decryption => write!(f, "decryption error"),
Error::Verification => write!(f, "verification error"),
Error::MessageTooLong => write!(f, "message too long"),
Error::InputNotHashed => write!(f, "input must be hashed"),
Error::NprimesTooSmall => write!(f, "nprimes must be >= 2"),
Error::TooFewPrimes => write!(f, "too few primes of given length to generate an RSA key"),
Error::InvalidPrime => write!(f, "invalid prime value"),
Error::InvalidModulus => write!(f, "invalid modulus"),
Error::InvalidExponent => write!(f, "invalid exponent"),
Error::InvalidCoefficient => write!(f, "invalid coefficient"),
Error::PublicExponentTooSmall => write!(f, "public exponent too small"),
Error::PublicExponentTooLarge => write!(f, "public exponent too large"),
Error::ParseError { reason } => write!(f, "parse error: {}", reason),
Error::EncodeError { reason } => write!(f, "encoding error: {}", reason),
Error::Internal => write!(f, "internal error"),
Error::LabelTooLong => write!(f, "label too long"),
}
}
}
4 changes: 3 additions & 1 deletion src/internals.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use num_bigint::{BigInt, BigUint, IntoBigInt, IntoBigUint, ModInverse, RandBigInt, ToBigInt};
use num_traits::{One, Signed, Zero};
use rand::Rng;
use std::borrow::Cow;
use alloc::borrow::Cow;
use zeroize::Zeroize;
use alloc::vec::Vec;
use alloc::vec;

use crate::errors::{Error, Result};
use crate::key::{PublicKeyParts, RSAPrivateKey};
Expand Down
40 changes: 26 additions & 14 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use num_bigint::traits::ModInverse;
use num_bigint::Sign::Plus;
use num_bigint::{BigInt, BigUint};
use num_traits::{FromPrimitive, One};
use rand::{rngs::ThreadRng, Rng};
use rand::{rngs::StdRng, Rng};
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use std::ops::Deref;
use core::ops::Deref;
use zeroize::Zeroize;
use alloc::vec::Vec;

use crate::algorithms::{generate_multi_prime_key, generate_multi_prime_key_with_exp};
use crate::errors::{Error, Result};
Expand Down Expand Up @@ -247,6 +248,7 @@ impl RSAPublicKey {
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let public_key = RSAPublicKey::from_pkcs1(&der_bytes).expect("failed to parse key");
/// ```
#[cfg(feature = "std")]
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPublicKey> {
crate::parse::parse_public_key_pkcs1(der)
}
Expand Down Expand Up @@ -281,6 +283,7 @@ impl RSAPublicKey {
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let public_key = RSAPublicKey::from_pkcs8(&der_bytes).expect("failed to parse key");
/// ```
#[cfg(feature = "std")]
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPublicKey> {
crate::parse::parse_public_key_pkcs8(der)
}
Expand Down Expand Up @@ -405,6 +408,7 @@ impl RSAPrivateKey {
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
/// ```
#[cfg(feature = "std")]
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPrivateKey> {
crate::parse::parse_private_key_pkcs1(der)
}
Expand Down Expand Up @@ -445,6 +449,7 @@ impl RSAPrivateKey {
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let private_key = RSAPrivateKey::from_pkcs8(&der_bytes).expect("failed to parse key");
/// ```
#[cfg(feature = "std")]
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPrivateKey> {
crate::parse::parse_private_key_pkcs8(der)
}
Expand Down Expand Up @@ -554,10 +559,10 @@ impl RSAPrivateKey {
match padding {
// need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything
PaddingScheme::PKCS1v15Encrypt => {
pkcs1v15::decrypt::<ThreadRng, _>(None, self, ciphertext)
pkcs1v15::decrypt::<StdRng, _>(None, self, ciphertext)
}
PaddingScheme::OAEP { mut digest, label } => {
oaep::decrypt::<ThreadRng, _>(None, self, ciphertext, &mut *digest, label)
oaep::decrypt::<StdRng, _>(None, self, ciphertext, &mut *digest, label)
}
_ => Err(Error::InvalidPaddingScheme),
}
Expand All @@ -584,14 +589,15 @@ impl RSAPrivateKey {
/// Sign the given digest.
pub fn sign(&self, padding: PaddingScheme, digest_in: &[u8]) -> Result<Vec<u8>> {
match padding {
// need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything
PaddingScheme::PKCS1v15Sign { ref hash } => {
pkcs1v15::sign::<ThreadRng, _>(None, self, hash.as_ref(), digest_in)
pkcs1v15::sign::<StdRng, _>(None, self, hash.as_ref(), digest_in)
}
PaddingScheme::PSS {
mut salt_rng,
mut digest,
salt_len,
} => pss::sign::<_, ThreadRng, _>(
} => pss::sign::<_, StdRng, _>(
&mut *salt_rng,
None,
self,
Expand Down Expand Up @@ -652,9 +658,10 @@ mod tests {
use super::*;
use crate::internals;

use std::time::SystemTime;
use digest::{Digest, DynDigest};
use num_traits::{FromPrimitive, ToPrimitive};
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng};
use rand::{distributions::Alphanumeric, rngs::StdRng, SeedableRng};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_256, Sha3_384, Sha3_512};
Expand Down Expand Up @@ -687,10 +694,11 @@ mod tests {
let pub_key: RSAPublicKey = private_key.clone().into();
let m = BigUint::from_u64(42).expect("invalid 42");
let c = internals::encrypt(&pub_key, &m);
let m2 = internals::decrypt::<ThreadRng>(None, &private_key, &c)
let m2 = internals::decrypt::<StdRng>(None, &private_key, &c)
.expect("unable to decrypt without blinding");
assert_eq!(m, m2);
let mut rng = thread_rng();
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let mut rng = StdRng::seed_from_u64(seed.as_secs());
let m3 = internals::decrypt(Some(&mut rng), &private_key, &c)
.expect("unable to decrypt with blinding");
assert_eq!(m, m3);
Expand All @@ -700,7 +708,8 @@ mod tests {
($name:ident, $multi:expr, $size:expr) => {
#[test]
fn $name() {
let mut rng = thread_rng();
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let mut rng = StdRng::seed_from_u64(seed.as_secs());

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

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

let k = prk.size();

Expand All @@ -913,7 +924,7 @@ mod tests {
}
let has_label: bool = rng.gen();
let label: Option<String> = if has_label {
Some(rng.sample_iter(&Alphanumeric).take(30).collect())
Some(rng.clone().sample_iter(&Alphanumeric).take(30).collect())
} else {
None
};
Expand Down Expand Up @@ -949,7 +960,8 @@ mod tests {

#[test]
fn test_decrypt_oaep_invalid_hash() {
let mut rng = thread_rng();
let seed = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let mut rng = StdRng::seed_from_u64(seed.as_secs());
let priv_key = get_private_key();
let pub_key: RSAPublicKey = (&priv_key).into();
let ciphertext = pub_key
Expand Down
Loading