1use std::path::Path;
12
13pub struct RsaKeyParts {
19 pub e: Vec<u8>,
20 pub p: Vec<u8>,
21 pub q: Vec<u8>,
22 pub u: Vec<u8>,
24 pub dp: Vec<u8>,
26 pub dq: Vec<u8>,
28 pub n: Vec<u8>,
29}
30
31impl Drop for RsaKeyParts {
36 fn drop(&mut self) {
37 use zeroize::Zeroize;
38 for buf in [
39 &mut self.e,
40 &mut self.p,
41 &mut self.q,
42 &mut self.u,
43 &mut self.dp,
44 &mut self.dq,
45 &mut self.n,
46 ] {
47 buf.zeroize();
48 }
49 }
50}
51
52#[derive(Debug)]
54pub enum RsaKeyError {
55 Io(std::io::Error),
57 Parse(String),
59 WrongSize(usize),
61 Crypto(String),
63 MissingComponent(&'static str),
65}
66
67impl std::fmt::Display for RsaKeyError {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 RsaKeyError::Io(e) => write!(f, "cannot read key file: {e}"),
71 RsaKeyError::Parse(e) => write!(f, "could not parse RSA private key: {e}"),
72 RsaKeyError::WrongSize(bits) => write!(
73 f,
74 "key is RSA-{bits}, but the card slot is RSA-2048; \
75 import only supports 2048-bit keys"
76 ),
77 RsaKeyError::Crypto(e) => write!(f, "RSA operation failed: {e}"),
78 RsaKeyError::MissingComponent(c) => write!(f, "RSA key missing precomputed {c}"),
79 }
80 }
81}
82
83impl std::error::Error for RsaKeyError {}
84
85impl From<std::io::Error> for RsaKeyError {
86 fn from(e: std::io::Error) -> Self {
87 RsaKeyError::Io(e)
88 }
89}
90
91pub fn generate_2048() -> Result<RsaKeyParts, RsaKeyError> {
96 let mut rng = rand::thread_rng();
97 let key =
98 rsa::RsaPrivateKey::new(&mut rng, 2048).map_err(|e| RsaKeyError::Crypto(e.to_string()))?;
99 parts_from_key(key)
100}
101
102pub fn load_from_file(path: &Path) -> Result<RsaKeyParts, RsaKeyError> {
109 let bytes = zeroize::Zeroizing::new(std::fs::read(path)?);
112 parts_from_encoded(&bytes)
113}
114
115fn parts_from_encoded(bytes: &[u8]) -> Result<RsaKeyParts, RsaKeyError> {
119 use rsa::pkcs1::DecodeRsaPrivateKey;
120 use rsa::pkcs8::DecodePrivateKey;
121
122 let key = if bytes.starts_with(b"-----BEGIN") {
123 let text = std::str::from_utf8(bytes)
124 .map_err(|_| RsaKeyError::Parse("key file is not valid PEM/UTF-8".into()))?;
125 rsa::RsaPrivateKey::from_pkcs8_pem(text)
127 .or_else(|_| rsa::RsaPrivateKey::from_pkcs1_pem(text))
128 .map_err(|e| RsaKeyError::Parse(e.to_string()))?
129 } else {
130 rsa::RsaPrivateKey::from_pkcs8_der(bytes)
132 .or_else(|_| rsa::RsaPrivateKey::from_pkcs1_der(bytes))
133 .map_err(|e| RsaKeyError::Parse(e.to_string()))?
134 };
135 parts_from_key(key)
136}
137
138fn parts_from_key(mut key: rsa::RsaPrivateKey) -> Result<RsaKeyParts, RsaKeyError> {
141 use rsa::traits::{PrivateKeyParts, PublicKeyParts};
142
143 let bits = key.n().bits();
144 if bits != 2048 {
145 return Err(RsaKeyError::WrongSize(bits));
146 }
147 key.precompute()
150 .map_err(|e| RsaKeyError::Crypto(e.to_string()))?;
151
152 let primes = key.primes();
153 if primes.len() != 2 {
154 return Err(RsaKeyError::Crypto("expected a 2-prime RSA key".into()));
155 }
156 let dp = key
157 .dp()
158 .ok_or(RsaKeyError::MissingComponent("dp"))?
159 .to_bytes_be();
160 let dq = key
161 .dq()
162 .ok_or(RsaKeyError::MissingComponent("dq"))?
163 .to_bytes_be();
164 let u = key
166 .qinv()
167 .ok_or(RsaKeyError::MissingComponent("qinv"))?
168 .to_bytes_be()
169 .1;
170 Ok(RsaKeyParts {
171 e: key.e().to_bytes_be(),
172 n: key.n().to_bytes_be(),
173 p: primes[0].to_bytes_be(),
174 q: primes[1].to_bytes_be(),
175 u,
176 dp,
177 dq,
178 })
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use rsa::pkcs8::EncodePrivateKey;
185
186 #[test]
187 fn generate_2048_has_expected_shapes() {
188 let k = generate_2048().expect("keygen");
189 assert_eq!(k.n.len(), 256, "modulus should be 256 bytes");
192 assert_eq!(k.p.len(), 128, "p should be 128 bytes");
193 assert_eq!(k.q.len(), 128, "q should be 128 bytes");
194 assert_eq!(k.e, vec![0x01, 0x00, 0x01]);
196 assert!(!k.u.is_empty() && !k.dp.is_empty() && !k.dq.is_empty());
198 assert!(k.dp.len() <= 128 && k.dq.len() <= 128);
199 }
200
201 #[test]
202 fn load_round_trips_through_der() {
203 let mut rng = rand::thread_rng();
207 let key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("keygen");
208 let der = key.to_pkcs8_der().expect("encode der");
209 let parsed = parts_from_encoded(der.as_bytes()).expect("parse der");
210 use rsa::traits::PublicKeyParts;
211 assert_eq!(parsed.n, key.n().to_bytes_be());
212 assert_eq!(parsed.e, key.e().to_bytes_be());
213 }
214
215 #[test]
216 fn rejects_non_2048() {
217 let mut rng = rand::thread_rng();
219 let key = rsa::RsaPrivateKey::new(&mut rng, 1024).expect("keygen");
220 let der = key.to_pkcs8_der().expect("encode der");
221 match parts_from_encoded(der.as_bytes()) {
224 Err(RsaKeyError::WrongSize(1024)) => {}
225 Err(e) => panic!("expected WrongSize(1024), got error: {e}"),
226 Ok(_) => panic!("expected WrongSize(1024), but parsing succeeded"),
227 }
228 }
229
230 #[test]
231 fn rejects_garbage() {
232 assert!(matches!(
233 parts_from_encoded(&[0xDE, 0xAD, 0xBE, 0xEF]),
234 Err(RsaKeyError::Parse(_))
235 ));
236 }
237}