Skip to content

Commit 4a49584

Browse files
authored
Support compiling without cryptography primitives (#208)
This makes all of the crypto libraries optional. My use case requires simply generating the binary ASN.1 structures and all cryptography is handled externally with HSMs. This change removes unnecessary code and build complexity (for example, I'm targeting WASM, and I need to take extra steps to build the dependencies I don't need). Note to the maintainer: this is a clean (and ideally better) rework of a previous PR, #207. Because I deleted the older and messier branch, the previous PR couldn't be updated, so I closed it.
1 parent 30179f6 commit 4a49584

File tree

12 files changed

+160
-42
lines changed

12 files changed

+160
-42
lines changed

.github/workflows/ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
matrix:
3434
features:
3535
- --all-features
36+
- --no-default-features
3637
- --no-default-features --features ring
3738
- --no-default-features --features aws_lc_rs
3839
- --no-default-features --features aws_lc_rs,pem
@@ -137,6 +138,8 @@ jobs:
137138
run: cargo test --verbose --features x509-parser --all-targets
138139
- name: Run the tests with aws_lc_rs backend enabled
139140
run: cargo test --verbose --no-default-features --features aws_lc_rs,pem --all-targets
141+
- name: Run the tests with no features enabled
142+
run: cargo test --verbose --no-default-features --all-targets
140143

141144
build:
142145
strategy:

rcgen/Cargo.toml

+8-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ required-features = ["pem"]
2121
name = "sign-leaf-with-ca"
2222
required-features = ["pem", "x509-parser"]
2323

24+
[[example]]
25+
name = "simple"
26+
required-features = ["crypto"]
27+
2428
[dependencies]
2529
aws-lc-rs = { version = "1.0.0", optional = true }
2630
yasna = { version = "0.5.2", features = ["time", "std"] }
@@ -31,9 +35,10 @@ x509-parser = { workspace = true, features = ["verify"], optional = true }
3135
zeroize = { version = "1.2", optional = true }
3236

3337
[features]
34-
default = ["pem", "ring"]
35-
aws_lc_rs = ["dep:aws-lc-rs"]
36-
ring = ["dep:ring"]
38+
default = ["crypto", "pem", "ring"]
39+
crypto = []
40+
aws_lc_rs = ["crypto", "dep:aws-lc-rs"]
41+
ring = ["crypto", "dep:ring"]
3742

3843

3944
[package.metadata.docs.rs]

rcgen/src/crl.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,26 @@ use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber};
2121
/// extern crate rcgen;
2222
/// use rcgen::*;
2323
///
24+
/// #[cfg(not(feature = "crypto"))]
25+
/// struct MyKeyPair { public_key: Vec<u8> }
26+
/// #[cfg(not(feature = "crypto"))]
27+
/// impl RemoteKeyPair for MyKeyPair {
28+
/// fn public_key(&self) -> &[u8] { &self.public_key }
29+
/// fn sign(&self, _: &[u8]) -> Result<Vec<u8>, rcgen::Error> { Ok(vec![]) }
30+
/// fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 }
31+
/// }
2432
/// # fn main () {
2533
/// // Generate a CRL issuer.
26-
/// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]);
34+
/// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]).unwrap();
35+
/// issuer_params.serial_number = Some(SerialNumber::from(9999));
2736
/// issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
2837
/// issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign];
38+
/// #[cfg(feature = "crypto")]
2939
/// let key_pair = KeyPair::generate().unwrap();
40+
/// #[cfg(not(feature = "crypto"))]
41+
/// let remote_key_pair = MyKeyPair { public_key: vec![] };
42+
/// #[cfg(not(feature = "crypto"))]
43+
/// let key_pair = KeyPair::from_remote(Box::new(remote_key_pair)).unwrap();
3044
/// let issuer = Certificate::generate_self_signed(issuer_params, &key_pair).unwrap();
3145
/// // Describe a revoked certificate.
3246
/// let revoked_cert = RevokedCertParams{
@@ -42,7 +56,10 @@ use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber};
4256
/// crl_number: SerialNumber::from(1234),
4357
/// issuing_distribution_point: None,
4458
/// revoked_certs: vec![revoked_cert],
59+
/// #[cfg(feature = "crypto")]
4560
/// key_identifier_method: KeyIdMethod::Sha256,
61+
/// #[cfg(not(feature = "crypto"))]
62+
/// key_identifier_method: KeyIdMethod::PreSpecified(vec![]),
4663
/// };
4764
/// let crl = CertificateRevocationList::from_params(crl).unwrap();
4865
///# }

rcgen/src/error.rs

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub enum Error {
4242
InvalidCrlNextUpdate,
4343
/// CRL issuer specifies Key Usages that don't include cRLSign.
4444
IssuerNotCrlSigner,
45+
#[cfg(not(feature = "crypto"))]
46+
/// Missing serial number
47+
MissingSerialNumber,
4548
}
4649

4750
impl fmt::Display for Error {
@@ -86,6 +89,8 @@ impl fmt::Display for Error {
8689
f,
8790
"CRL issuer must specify no key usage, or key usage including cRLSign"
8891
)?,
92+
#[cfg(not(feature = "crypto"))]
93+
MissingSerialNumber => write!(f, "A serial number must be specified")?,
8994
};
9095
Ok(())
9196
}

rcgen/src/key_pair.rs

+46-12
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
11
#[cfg(feature = "pem")]
22
use pem::Pem;
3+
#[cfg(feature = "crypto")]
34
use std::convert::TryFrom;
45
use std::fmt;
56
use yasna::DERWriter;
67

8+
#[cfg(any(feature = "crypto", feature = "pem"))]
79
use crate::error::ExternalError;
8-
use crate::ring_like::error as ring_error;
9-
use crate::ring_like::rand::SystemRandom;
10-
use crate::ring_like::signature::{
11-
self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as RingKeyPair, RsaEncoding, RsaKeyPair,
10+
#[cfg(feature = "crypto")]
11+
use crate::ring_like::{
12+
error as ring_error,
13+
rand::SystemRandom,
14+
signature::{
15+
self, EcdsaKeyPair, Ed25519KeyPair, KeyPair as RingKeyPair, RsaEncoding, RsaKeyPair,
16+
},
17+
{ecdsa_from_pkcs8, rsa_key_pair_public_modulus_len},
1218
};
13-
use crate::ring_like::{ecdsa_from_pkcs8, rsa_key_pair_public_modulus_len};
14-
use crate::sign_algo::algo::*;
15-
use crate::sign_algo::SignAlgo;
19+
#[cfg(feature = "crypto")]
20+
use crate::sign_algo::{algo::*, SignAlgo};
1621
#[cfg(feature = "pem")]
1722
use crate::ENCODE_CONFIG;
18-
use crate::{Error, SignatureAlgorithm};
23+
use crate::{sign_algo::SignatureAlgorithm, Error};
1924

2025
/// A key pair variant
2126
#[allow(clippy::large_enum_variant)]
2227
pub(crate) enum KeyPairKind {
2328
/// A Ecdsa key pair
29+
#[cfg(feature = "crypto")]
2430
Ec(EcdsaKeyPair),
2531
/// A Ed25519 key pair
32+
#[cfg(feature = "crypto")]
2633
Ed(Ed25519KeyPair),
2734
/// A RSA key pair
35+
#[cfg(feature = "crypto")]
2836
Rsa(RsaKeyPair, &'static dyn RsaEncoding),
2937
/// A remote key pair
3038
Remote(Box<dyn RemoteKeyPair + Send + Sync>),
@@ -33,8 +41,11 @@ pub(crate) enum KeyPairKind {
3341
impl fmt::Debug for KeyPairKind {
3442
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3543
match self {
44+
#[cfg(feature = "crypto")]
3645
Self::Ec(key_pair) => write!(f, "{:?}", key_pair),
46+
#[cfg(feature = "crypto")]
3747
Self::Ed(key_pair) => write!(f, "{:?}", key_pair),
48+
#[cfg(feature = "crypto")]
3849
Self::Rsa(key_pair, _) => write!(f, "{:?}", key_pair),
3950
Self::Remote(_) => write!(f, "Box<dyn RemotePrivateKey>"),
4051
}
@@ -57,13 +68,15 @@ pub struct KeyPair {
5768

5869
impl KeyPair {
5970
/// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair
71+
#[cfg(feature = "crypto")]
6072
pub fn generate() -> Result<Self, Error> {
6173
Self::generate_for(&PKCS_ECDSA_P256_SHA256)
6274
}
6375

6476
/// Generate a new random key pair for the specified signature algorithm
6577
///
6678
/// If you're not sure which algorithm to use, [`PKCS_ECDSA_P256_SHA256`] is a good choice.
79+
#[cfg(feature = "crypto")]
6780
pub fn generate_for(alg: &'static SignatureAlgorithm) -> Result<Self, Error> {
6881
let rng = &SystemRandom::new();
6982

@@ -102,6 +115,7 @@ impl KeyPair {
102115
/// Parses the key pair from the DER format
103116
///
104117
/// Equivalent to using the [`TryFrom`] implementation.
118+
#[cfg(feature = "crypto")]
105119
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
106120
Ok(der.try_into()?)
107121
}
@@ -112,7 +126,7 @@ impl KeyPair {
112126
}
113127

114128
/// Parses the key pair from the ASCII PEM format
115-
#[cfg(feature = "pem")]
129+
#[cfg(all(feature = "pem", feature = "crypto"))]
116130
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
117131
let private_key = pem::parse(pem_str)._err()?;
118132
let private_key_der: &[_] = private_key.contents();
@@ -132,7 +146,7 @@ impl KeyPair {
132146
/// using the specified [`SignatureAlgorithm`]
133147
///
134148
/// Same as [from_pem_and_sign_algo](Self::from_pem_and_sign_algo).
135-
#[cfg(feature = "pem")]
149+
#[cfg(all(feature = "pem", feature = "crypto"))]
136150
pub fn from_pem_and_sign_algo(
137151
pem_str: &str,
138152
alg: &'static SignatureAlgorithm,
@@ -151,6 +165,7 @@ impl KeyPair {
151165
/// key pair. However, sometimes multiple signature algorithms fit for the
152166
/// same der key. In that instance, you can use this function to precisely
153167
/// specify the `SignatureAlgorithm`.
168+
#[cfg(feature = "crypto")]
154169
pub fn from_der_and_sign_algo(
155170
pkcs8: &[u8],
156171
alg: &'static SignatureAlgorithm,
@@ -195,6 +210,7 @@ impl KeyPair {
195210
})
196211
}
197212

213+
#[cfg(feature = "crypto")]
198214
pub(crate) fn from_raw(
199215
pkcs8: &[u8],
200216
) -> Result<(KeyPairKind, &'static SignatureAlgorithm), Error> {
@@ -242,17 +258,20 @@ impl KeyPair {
242258

243259
pub(crate) fn sign(&self, msg: &[u8], writer: DERWriter) -> Result<(), Error> {
244260
match &self.kind {
261+
#[cfg(feature = "crypto")]
245262
KeyPairKind::Ec(kp) => {
246263
let system_random = SystemRandom::new();
247264
let signature = kp.sign(&system_random, msg)._err()?;
248265
let sig = &signature.as_ref();
249266
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
250267
},
268+
#[cfg(feature = "crypto")]
251269
KeyPairKind::Ed(kp) => {
252270
let signature = kp.sign(msg);
253271
let sig = &signature.as_ref();
254272
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
255273
},
274+
#[cfg(feature = "crypto")]
256275
KeyPairKind::Rsa(kp, padding_alg) => {
257276
let system_random = SystemRandom::new();
258277
let mut signature = vec![0; rsa_key_pair_public_modulus_len(kp)];
@@ -292,6 +311,7 @@ impl KeyPair {
292311
///
293312
/// Panics if called on a remote key pair.
294313
pub fn serialize_der(&self) -> Vec<u8> {
314+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
295315
if let KeyPairKind::Remote(_) = self.kind {
296316
panic!("Serializing a remote key pair is not supported")
297317
}
@@ -304,6 +324,7 @@ impl KeyPair {
304324
///
305325
/// Panics if called on a remote key pair.
306326
pub fn serialized_der(&self) -> &[u8] {
327+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
307328
if let KeyPairKind::Remote(_) = self.kind {
308329
panic!("Serializing a remote key pair is not supported")
309330
}
@@ -313,6 +334,7 @@ impl KeyPair {
313334

314335
/// Access the remote key pair if it is a remote one
315336
pub fn as_remote(&self) -> Option<&(dyn RemoteKeyPair + Send + Sync)> {
337+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
316338
if let KeyPairKind::Remote(remote) = &self.kind {
317339
Some(remote.as_ref())
318340
} else {
@@ -329,6 +351,7 @@ impl KeyPair {
329351
}
330352
}
331353

354+
#[cfg(feature = "crypto")]
332355
impl TryFrom<&[u8]> for KeyPair {
333356
type Error = Error;
334357

@@ -342,6 +365,7 @@ impl TryFrom<&[u8]> for KeyPair {
342365
}
343366
}
344367

368+
#[cfg(feature = "crypto")]
345369
impl TryFrom<Vec<u8>> for KeyPair {
346370
type Error = Error;
347371

@@ -361,8 +385,11 @@ impl PublicKeyData for KeyPair {
361385
}
362386
fn raw_bytes(&self) -> &[u8] {
363387
match &self.kind {
388+
#[cfg(feature = "crypto")]
364389
KeyPairKind::Ec(kp) => kp.public_key().as_ref(),
390+
#[cfg(feature = "crypto")]
365391
KeyPairKind::Ed(kp) => kp.public_key().as_ref(),
392+
#[cfg(feature = "crypto")]
366393
KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(),
367394
KeyPairKind::Remote(kp) => kp.public_key(),
368395
}
@@ -384,12 +411,14 @@ pub trait RemoteKeyPair {
384411
fn algorithm(&self) -> &'static SignatureAlgorithm;
385412
}
386413

414+
#[cfg(feature = "crypto")]
387415
impl<T> ExternalError<T> for Result<T, ring_error::KeyRejected> {
388416
fn _err(self) -> Result<T, Error> {
389417
self.map_err(|e| Error::RingKeyRejected(e.to_string()))
390418
}
391419
}
392420

421+
#[cfg(feature = "crypto")]
393422
impl<T> ExternalError<T> for Result<T, ring_error::Unspecified> {
394423
fn _err(self) -> Result<T, Error> {
395424
self.map_err(|_| Error::RingUnspecified)
@@ -419,11 +448,16 @@ pub(crate) trait PublicKeyData {
419448

420449
#[cfg(test)]
421450
mod test {
451+
#[cfg(crypto)]
422452
use super::*;
423453

424-
use crate::ring_like::rand::SystemRandom;
425-
use crate::ring_like::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING};
454+
#[cfg(crypto)]
455+
use crate::ring_like::{
456+
rand::SystemRandom,
457+
signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING},
458+
};
426459

460+
#[cfg(crypto)]
427461
#[test]
428462
fn test_algorithm() {
429463
let rng = SystemRandom::new();

0 commit comments

Comments
 (0)