Skip to main content

app_store_server_library/crypto/
mod.rs

1//! Cryptographic backend abstraction.
2
3#[cfg(feature = "rust_crypto")]
4pub mod rust_crypto;
5
6#[cfg(feature = "aws_lc")]
7pub mod aws_lc;
8
9#[cfg(feature = "ring")]
10pub mod ring;
11
12pub mod jws;
13
14use std::fmt::Debug;
15use std::sync::{Arc, OnceLock};
16
17/// Simple PEM decoder - extracts base64 content between BEGIN/END markers
18#[cfg(any(feature = "aws_lc", feature = "ring"))]
19pub(crate) fn decode_pem(pem: &str) -> Result<Vec<u8>, &'static str> {
20    use base64::prelude::*;
21
22    let lines: Vec<&str> = pem.lines().collect();
23
24    // Find BEGIN and END markers
25    let start = lines
26        .iter()
27        .position(|l| l.starts_with("-----BEGIN"))
28        .ok_or("missing BEGIN marker")?;
29    let end = lines
30        .iter()
31        .position(|l| l.starts_with("-----END"))
32        .ok_or("missing END marker")?;
33
34    if end <= start + 1 {
35        return Err("no content between markers");
36    }
37
38    // Concatenate base64 lines
39    let b64: String = lines[start + 1..end].concat();
40
41    BASE64_STANDARD
42        .decode(&b64)
43        .map_err(|_| "invalid base64")
44}
45
46/// Converts a fixed-width `r‖s` ECDSA P-256 signature to `SEQUENCE { INTEGER r, INTEGER s }`.
47#[cfg(any(feature = "aws_lc", feature = "ring"))]
48pub(crate) fn ecdsa_raw_to_der(rs: &[u8; 64]) -> Result<Vec<u8>, CryptoError> {
49    use asn1_rs::{Integer, Sequence, ToDer};
50
51    let to_error = |e: asn1_rs::SerializeError| CryptoError::SigningError(e.to_string());
52
53    let r = Integer::from_const_array::<32>(rs[..32].try_into().expect("32 bytes"));
54    let s = Integer::from_const_array::<32>(rs[32..].try_into().expect("32 bytes"));
55
56    Sequence::from_iter_to_der([r, s].into_iter())
57        .map_err(to_error)?
58        .to_der_vec()
59        .map_err(to_error)
60}
61
62/// Errors raised by cryptographic primitives.
63#[derive(thiserror::Error, Debug)]
64pub enum CryptoError {
65    #[error("Key error: {0}")]
66    KeyError(String),
67
68    #[error("Signing error: {0}")]
69    SigningError(String),
70
71    #[error("Verification error: {0}")]
72    VerificationError(String),
73}
74
75/// ECDSA P-256 signing and verification.
76pub trait P256SigningSuite: Send + Sync + Debug {
77    /// `pem` is a PKCS#8 PEM private key, as Apple issues them (`.p8`).
78    fn private_key(&self, pem: &str) -> Result<Box<dyn P256PrivateKey>, CryptoError>;
79
80    /// `spki_der` is a DER-encoded SubjectPublicKeyInfo.
81    fn public_key(&self, spki_der: &[u8]) -> Result<Box<dyn P256PublicKey>, CryptoError>;
82}
83
84pub trait P256PrivateKey: Send + Sync + Debug {
85    /// Signs `message` with ECDSA P-256, returning signature.
86    ///
87    /// The implementation hashes `message` internally with SHA-256 — callers
88    /// pass the raw message, NOT a pre-computed digest.
89    fn signature(&self, message: &[u8]) -> Result<P256Signature, CryptoError>;
90}
91
92/// ECDSA P-256 signature: `(raw, der)`.
93pub type P256Signature = ([u8; 64], Vec<u8>);
94
95pub trait P256PublicKey: Send + Sync + Debug {
96    /// Returns `Ok(())` when `signature` (fixed-width `r‖s`, JWS/RFC 7515) is
97    /// valid over `message`.
98    ///
99    /// `message` is the raw message; the implementation hashes it internally.
100    fn is_valid_signature(&self, signature: &[u8; 64], message: &[u8]) -> Result<(), CryptoError>;
101}
102
103/// Controls the cryptography used by this library.
104///
105/// Individual fields can be overridden using struct-update syntax against a
106/// backend's `DEFAULT_PROVIDER`:
107///
108/// ```ignore
109/// CryptoProvider { sha256_hasher: &MyHasher, ..DEFAULT_PROVIDER }
110///     .install_default()
111///     .expect("provider already installed");
112/// ```
113#[derive(Debug, Clone)]
114pub struct CryptoProvider {
115    /// ECDSA P-256 signing and verification.
116    pub p256_signing: &'static dyn P256SigningSuite,
117}
118
119static PROCESS_DEFAULT: OnceLock<Arc<CryptoProvider>> = OnceLock::new();
120
121impl CryptoProvider {
122    /// Sets this `CryptoProvider` as the default for this process.
123    ///
124    /// After calling this, other callers can obtain a reference to the installed
125    /// default via [`CryptoProvider::get_default()`].
126    pub fn install_default(self) -> Result<(), Arc<Self>> {
127        PROCESS_DEFAULT.set(Arc::new(self))
128    }
129
130    /// Returns the default `CryptoProvider` for this process.
131    ///
132    /// This will be `None` if no default has been set yet.
133    pub fn get_default() -> Option<&'static Arc<Self>> {
134        PROCESS_DEFAULT.get()
135    }
136
137    /// The process default provider, installing the crate-feature default if
138    /// none has been set.
139    pub fn default_provider() -> &'static Arc<Self> {
140        PROCESS_DEFAULT.get_or_init(|| Arc::new(Self::from_crate_features()))
141    }
142
143    #[allow(unreachable_code)]
144    fn from_crate_features() -> Self {
145        #[cfg(feature = "rust_crypto")]
146        {
147            return rust_crypto::DEFAULT_PROVIDER;
148        }
149
150        #[cfg(feature = "aws_lc")]
151        {
152            return aws_lc::DEFAULT_PROVIDER;
153        }
154
155        #[cfg(feature = "ring")]
156        {
157            return ring::DEFAULT_PROVIDER;
158        }
159
160        panic!("No crypto backend. Enable 'rust_crypto', 'aws_lc' or 'ring' feature.");
161    }
162}
163
164#[cfg(all(test, any(feature = "aws_lc", feature = "ring")))]
165mod der_tests {
166    use super::ecdsa_raw_to_der;
167
168    /// The two cases DER's minimal-encoding rules turn on: a high bit that
169    /// needs a 0x00 pad, and leading zeros that must be stripped.
170    #[test]
171    fn der_integers_are_minimally_encoded_and_positive() {
172        let mut rs = [0u8; 64];
173        rs[0] = 0xFF; // r: high bit set, must be padded
174        rs[32 + 31] = 0x01; // s: 31 leading zeros, must be stripped to one byte
175
176        let der = ecdsa_raw_to_der(&rs).expect("encode");
177
178        // SEQUENCE { INTEGER 00 FF 00*31, INTEGER 01 }
179        assert_eq!(der[0], 0x30);
180        assert_eq!(der[1] as usize, der.len() - 2);
181        assert_eq!(der[2], 0x02);
182        assert_eq!(der[3], 33, "r must be padded to 33 bytes");
183        assert_eq!(der[4], 0x00, "pad byte keeps r positive");
184        assert_eq!(der[5], 0xFF);
185        assert_eq!(&der[der.len() - 3..], &[0x02, 0x01, 0x01], "s is one byte");
186    }
187
188    /// An all-zero component is the one case where stripping must stop short
189    /// of emptying the INTEGER.
190    #[test]
191    fn zero_component_encodes_as_a_single_zero_byte() {
192        let der = ecdsa_raw_to_der(&[0u8; 64]).expect("encode");
193
194        // SEQUENCE { INTEGER 00, INTEGER 00 }
195        assert_eq!(der, vec![0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00]);
196    }
197}