Skip to content

Commit a52b6fb

Browse files
committed
Feature to build with(out) cryptographic primitives
1 parent 30489d7 commit a52b6fb

File tree

6 files changed

+115
-29
lines changed

6 files changed

+115
-29
lines changed

rcgen/Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ x509-parser = { workspace = true, features = ["verify"], optional = true }
3131
zeroize = { version = "1.2", optional = true }
3232

3333
[features]
34-
default = ["pem", "ring"]
35-
aws_lc_rs = ["dep:aws-lc-rs"]
36-
ring = ["dep:ring"]
34+
default = ["crypto", "pem", "ring"]
35+
crypto = []
36+
aws_lc_rs = ["crypto", "dep:aws-lc-rs"]
37+
ring = ["crypto", "dep:ring"]
3738

3839

3940
[package.metadata.docs.rs]

rcgen/src/error.rs

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

4851
impl fmt::Display for Error {
@@ -91,6 +94,8 @@ impl fmt::Display for Error {
9194
f,
9295
"CRL issuer must specify no key usage, or key usage including cRLSign"
9396
)?,
97+
#[cfg(not(feature = "crypto"))]
98+
MissingSerialNumber => write!(f, "A serial number must be specified")?,
9499
};
95100
Ok(())
96101
}

rcgen/src/key_pair.rs

+51-11
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
}
@@ -59,6 +70,7 @@ impl KeyPair {
5970
/// Parses the key pair from the DER format
6071
///
6172
/// Equivalent to using the [`TryFrom`] implementation.
73+
#[cfg(feature = "crypto")]
6274
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
6375
Ok(der.try_into()?)
6476
}
@@ -69,7 +81,7 @@ impl KeyPair {
6981
}
7082

7183
/// Parses the key pair from the ASCII PEM format
72-
#[cfg(feature = "pem")]
84+
#[cfg(all(feature = "pem", feature = "crypto"))]
7385
pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
7486
let private_key = pem::parse(pem_str)._err()?;
7587
let private_key_der: &[_] = private_key.contents();
@@ -89,7 +101,7 @@ impl KeyPair {
89101
/// using the specified [`SignatureAlgorithm`]
90102
///
91103
/// Same as [from_pem_and_sign_algo](Self::from_pem_and_sign_algo).
92-
#[cfg(feature = "pem")]
104+
#[cfg(all(feature = "pem", feature = "crypto"))]
93105
pub fn from_pem_and_sign_algo(
94106
pem_str: &str,
95107
alg: &'static SignatureAlgorithm,
@@ -108,6 +120,7 @@ impl KeyPair {
108120
/// key pair. However, sometimes multiple signature algorithms fit for the
109121
/// same der key. In that instance, you can use this function to precisely
110122
/// specify the `SignatureAlgorithm`.
123+
#[cfg(feature = "crypto")]
111124
pub fn from_der_and_sign_algo(
112125
pkcs8: &[u8],
113126
alg: &'static SignatureAlgorithm,
@@ -152,6 +165,7 @@ impl KeyPair {
152165
})
153166
}
154167

168+
#[cfg(feature = "crypto")]
155169
pub(crate) fn from_raw(
156170
pkcs8: &[u8],
157171
) -> Result<(KeyPairKind, &'static SignatureAlgorithm), Error> {
@@ -178,6 +192,7 @@ impl KeyPair {
178192
}
179193

180194
/// Generate a new random key pair for the specified signature algorithm
195+
#[cfg(feature = "crypto")]
181196
pub fn generate(alg: &'static SignatureAlgorithm) -> Result<Self, Error> {
182197
let rng = &SystemRandom::new();
183198

@@ -227,10 +242,22 @@ impl KeyPair {
227242
return Err(Error::CertificateKeyPairMismatch)
228243
},
229244
Some(kp) => Ok(kp),
230-
None => KeyPair::generate(sig_alg),
245+
None => {
246+
if cfg!(not(feature = "crypto")) {
247+
return Err(Error::KeyGenerationUnavailable);
248+
} else {
249+
KeyPair::generate(sig_alg)
250+
}
251+
},
231252
}
232253
}
233254

255+
/// Generate a new random key pair for the specified signature algorithm
256+
#[cfg(not(feature = "crypto"))]
257+
pub fn generate(_alg: &'static SignatureAlgorithm) -> Result<Self, Error> {
258+
Err(Error::KeyGenerationUnavailable)
259+
}
260+
234261
/// Get the raw public key of this key pair
235262
///
236263
/// The key is in raw format, as how [`ring::signature::KeyPair::public_key`]
@@ -253,17 +280,20 @@ impl KeyPair {
253280

254281
pub(crate) fn sign(&self, msg: &[u8], writer: DERWriter) -> Result<(), Error> {
255282
match &self.kind {
283+
#[cfg(feature = "crypto")]
256284
KeyPairKind::Ec(kp) => {
257285
let system_random = SystemRandom::new();
258286
let signature = kp.sign(&system_random, msg)._err()?;
259287
let sig = &signature.as_ref();
260288
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
261289
},
290+
#[cfg(feature = "crypto")]
262291
KeyPairKind::Ed(kp) => {
263292
let signature = kp.sign(msg);
264293
let sig = &signature.as_ref();
265294
writer.write_bitvec_bytes(&sig, &sig.len() * 8);
266295
},
296+
#[cfg(feature = "crypto")]
267297
KeyPairKind::Rsa(kp, padding_alg) => {
268298
let system_random = SystemRandom::new();
269299
let mut signature = vec![0; rsa_key_pair_public_modulus_len(kp)];
@@ -303,6 +333,7 @@ impl KeyPair {
303333
///
304334
/// Panics if called on a remote key pair.
305335
pub fn serialize_der(&self) -> Vec<u8> {
336+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
306337
if let KeyPairKind::Remote(_) = self.kind {
307338
panic!("Serializing a remote key pair is not supported")
308339
}
@@ -315,6 +346,7 @@ impl KeyPair {
315346
///
316347
/// Panics if called on a remote key pair.
317348
pub fn serialized_der(&self) -> &[u8] {
349+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
318350
if let KeyPairKind::Remote(_) = self.kind {
319351
panic!("Serializing a remote key pair is not supported")
320352
}
@@ -324,6 +356,7 @@ impl KeyPair {
324356

325357
/// Access the remote key pair if it is a remote one
326358
pub fn as_remote(&self) -> Option<&(dyn RemoteKeyPair + Send + Sync)> {
359+
#[cfg_attr(not(feature = "crypto"), allow(irrefutable_let_patterns))]
327360
if let KeyPairKind::Remote(remote) = &self.kind {
328361
Some(remote.as_ref())
329362
} else {
@@ -340,6 +373,7 @@ impl KeyPair {
340373
}
341374
}
342375

376+
#[cfg(feature = "crypto")]
343377
impl TryFrom<&[u8]> for KeyPair {
344378
type Error = Error;
345379

@@ -353,6 +387,7 @@ impl TryFrom<&[u8]> for KeyPair {
353387
}
354388
}
355389

390+
#[cfg(feature = "crypto")]
356391
impl TryFrom<Vec<u8>> for KeyPair {
357392
type Error = Error;
358393

@@ -372,8 +407,11 @@ impl PublicKeyData for KeyPair {
372407
}
373408
fn raw_bytes(&self) -> &[u8] {
374409
match &self.kind {
410+
#[cfg(feature = "crypto")]
375411
KeyPairKind::Ec(kp) => kp.public_key().as_ref(),
412+
#[cfg(feature = "crypto")]
376413
KeyPairKind::Ed(kp) => kp.public_key().as_ref(),
414+
#[cfg(feature = "crypto")]
377415
KeyPairKind::Rsa(kp, _) => kp.public_key().as_ref(),
378416
KeyPairKind::Remote(kp) => kp.public_key(),
379417
}
@@ -395,12 +433,14 @@ pub trait RemoteKeyPair {
395433
fn algorithm(&self) -> &'static SignatureAlgorithm;
396434
}
397435

436+
#[cfg(feature = "crypto")]
398437
impl<T> ExternalError<T> for Result<T, ring_error::KeyRejected> {
399438
fn _err(self) -> Result<T, Error> {
400439
self.map_err(|e| Error::RingKeyRejected(e.to_string()))
401440
}
402441
}
403442

443+
#[cfg(feature = "crypto")]
404444
impl<T> ExternalError<T> for Result<T, ring_error::Unspecified> {
405445
fn _err(self) -> Result<T, Error> {
406446
self.map_err(|_| Error::RingUnspecified)

rcgen/src/lib.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ println!("{}", key_pair.serialize_pem());
3535

3636
#[cfg(feature = "pem")]
3737
use pem::Pem;
38+
#[cfg(feature = "crypto")]
3839
use ring_like::digest;
3940
use std::collections::HashMap;
4041
use std::convert::TryFrom;
@@ -552,6 +553,7 @@ pub struct CertificateParams {
552553

553554
impl Default for CertificateParams {
554555
fn default() -> Self {
556+
use crate::sign_algo::algo::*;
555557
// not_before and not_after set to reasonably long dates
556558
let not_before = date_time_ymd(1975, 01, 01);
557559
let not_after = date_time_ymd(4096, 01, 01);
@@ -572,7 +574,10 @@ impl Default for CertificateParams {
572574
custom_extensions: Vec::new(),
573575
key_pair: None,
574576
use_authority_key_identifier_extension: false,
577+
#[cfg(feature = "crypto")]
575578
key_identifier_method: KeyIdMethod::Sha256,
579+
#[cfg(not(feature = "crypto"))]
580+
key_identifier_method: KeyIdMethod::PreSpecified(Vec::new()),
576581
}
577582
}
578583
}
@@ -965,11 +970,18 @@ impl CertificateParams {
965970
if let Some(ref serial) = self.serial_number {
966971
writer.next().write_bigint_bytes(serial.as_ref(), true);
967972
} else {
968-
let hash = digest::digest(&digest::SHA256, pub_key.raw_bytes());
969-
// RFC 5280 specifies at most 20 bytes for a serial number
970-
let mut sl = hash.as_ref()[0..20].to_vec();
971-
sl[0] = sl[0] & 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes
972-
writer.next().write_bigint_bytes(&sl, true);
973+
#[cfg(feature = "crypto")]
974+
{
975+
let hash = digest::digest(&digest::SHA256, pub_key.raw_bytes());
976+
// RFC 5280 specifies at most 20 bytes for a serial number
977+
let mut sl = hash.as_ref()[0..20].to_vec();
978+
sl[0] = sl[0] & 0x7f; // MSB must be 0 to ensure encoding bignum in 20 bytes
979+
writer.next().write_bigint_bytes(&sl, true);
980+
}
981+
#[cfg(not(feature = "crypto"))]
982+
if self.serial_number.is_none() {
983+
return Err(Error::MissingSerialNumber);
984+
}
973985
};
974986
// Write signature algorithm
975987
sig_alg.write_alg_ident(writer.next());
@@ -1172,7 +1184,6 @@ impl CertificateParams {
11721184
Ok(())
11731185
})
11741186
}
1175-
11761187
fn serialize_der_with_signer<K: PublicKeyData>(
11771188
&self,
11781189
pub_key: &K,
@@ -1370,10 +1381,13 @@ impl CustomExtension {
13701381
#[non_exhaustive]
13711382
pub enum KeyIdMethod {
13721383
/// RFC 7093 method 1 - a truncated SHA256 digest.
1384+
#[cfg(feature = "crypto")]
13731385
Sha256,
13741386
/// RFC 7093 method 2 - a truncated SHA384 digest.
1387+
#[cfg(feature = "crypto")]
13751388
Sha384,
13761389
/// RFC 7093 method 3 - a truncated SHA512 digest.
1390+
#[cfg(feature = "crypto")]
13771391
Sha512,
13781392
/// Pre-specified identifier. The exact given value is used as the key identifier.
13791393
PreSpecified(Vec<u8>),
@@ -1389,15 +1403,21 @@ impl KeyIdMethod {
13891403
/// X.509v3 extensions.
13901404
pub(crate) fn derive(&self, subject_public_key_info: impl AsRef<[u8]>) -> Vec<u8> {
13911405
let digest_method = match &self {
1406+
#[cfg(feature = "crypto")]
13921407
Self::Sha256 => &digest::SHA256,
1408+
#[cfg(feature = "crypto")]
13931409
Self::Sha384 => &digest::SHA384,
1410+
#[cfg(feature = "crypto")]
13941411
Self::Sha512 => &digest::SHA512,
13951412
Self::PreSpecified(b) => {
13961413
return b.to_vec();
13971414
},
13981415
};
1399-
let digest = digest::digest(digest_method, subject_public_key_info.as_ref());
1400-
digest.as_ref()[0..20].to_vec()
1416+
#[cfg(feature = "crypto")]
1417+
{
1418+
let digest = digest::digest(digest_method, subject_public_key_info.as_ref());
1419+
digest.as_ref()[0..20].to_vec()
1420+
}
14011421
}
14021422
}
14031423

rcgen/src/ring_like.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
#[cfg(feature = "ring")]
1+
#[cfg(all(feature = "crypto", feature = "ring"))]
22
pub(crate) use ring::*;
33

4-
#[cfg(all(not(feature = "ring"), feature = "aws_lc_rs"))]
4+
#[cfg(all(feature = "crypto", not(feature = "ring"), feature = "aws_lc_rs"))]
55
pub(crate) use aws_lc_rs::*;
66

7+
#[cfg(feature = "crypto")]
78
use crate::error::ExternalError;
9+
#[cfg(feature = "crypto")]
810
use crate::Error;
911

12+
#[cfg(feature = "crypto")]
1013
pub(crate) fn ecdsa_from_pkcs8(
1114
alg: &'static signature::EcdsaSigningAlgorithm,
1215
pkcs8: &[u8],
@@ -23,6 +26,7 @@ pub(crate) fn ecdsa_from_pkcs8(
2326
}
2427
}
2528

29+
#[cfg(feature = "crypto")]
2630
pub(crate) fn rsa_key_pair_public_modulus_len(kp: &signature::RsaKeyPair) -> usize {
2731
#[cfg(feature = "ring")]
2832
{
@@ -35,5 +39,5 @@ pub(crate) fn rsa_key_pair_public_modulus_len(kp: &signature::RsaKeyPair) -> usi
3539
}
3640
}
3741

38-
#[cfg(not(any(feature = "ring", feature = "aws_lc_rs")))]
39-
compile_error!("At least one of the 'ring' or 'aws_lc_rs' features must be activated");
42+
#[cfg(all(feature = "crypto", not(any(feature = "ring", feature = "aws_lc_rs"))))]
43+
compile_error!("At least one of the 'ring' or 'aws_lc_rs' features must be activated when the 'crypto' feature is enabled");

0 commit comments

Comments
 (0)