actpub_httpsig/key/
rsa.rs1use std::fmt;
11
12use aws_lc_rs::encoding::AsDer;
13use aws_lc_rs::rand::SystemRandom;
14use aws_lc_rs::rsa::{KeyPair as RsaKeyPair, KeySize};
15use aws_lc_rs::signature::{self, KeyPair as SignatureKeyPair, UnparsedPublicKey};
16
17use crate::error::Error;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[non_exhaustive]
23pub enum RsaBits {
24 Rsa2048,
26 Rsa4096,
28}
29
30impl RsaBits {
31 #[must_use]
33 pub const fn as_u32(self) -> u32 {
34 match self {
35 Self::Rsa2048 => 2048,
36 Self::Rsa4096 => 4096,
37 }
38 }
39
40 const fn as_key_size(self) -> KeySize {
41 match self {
42 Self::Rsa2048 => KeySize::Rsa2048,
43 Self::Rsa4096 => KeySize::Rsa4096,
44 }
45 }
46}
47
48pub struct RsaSigningKey {
55 inner: RsaKeyPair,
56 pkcs8_der: Vec<u8>,
57 public_spki_der: Vec<u8>,
58 bits: u32,
59}
60
61impl RsaSigningKey {
62 pub fn generate(bits: RsaBits) -> Result<Self, Error> {
68 let pair = RsaKeyPair::generate(bits.as_key_size())
69 .map_err(|_| Error::KeyGeneration("RSA generation failed"))?;
70 let pkcs8_der = pair
71 .as_der()
72 .map_err(|_| Error::KeyGeneration("RSA PKCS#8 v1 serialisation failed"))?
73 .as_ref()
74 .to_vec();
75 Self::build(pair, pkcs8_der, bits.as_u32())
76 }
77
78 pub fn from_pkcs8_der(der: &[u8]) -> Result<Self, Error> {
94 let pair =
95 RsaKeyPair::from_pkcs8(der).map_err(|e| Error::InvalidPkcs8(format!("RSA: {e}")))?;
96 let bits = u32::try_from(pair.public_modulus_len() * 8).unwrap_or(u32::MAX);
97 if !(2048..=8192).contains(&bits) || bits % 256 != 0 {
98 return Err(Error::UnsupportedRsaSize(bits));
99 }
100 Self::build(pair, der.to_vec(), bits)
101 }
102
103 fn build(pair: RsaKeyPair, pkcs8_der: Vec<u8>, bits: u32) -> Result<Self, Error> {
105 let spki = pair
109 .public_key()
110 .as_der()
111 .map_err(|_| Error::Crypto("RSA SPKI serialisation failed"))?
112 .as_ref()
113 .to_vec();
114 Ok(Self {
115 inner: pair,
116 pkcs8_der,
117 public_spki_der: spki,
118 bits,
119 })
120 }
121
122 #[must_use]
124 pub fn to_pkcs8_der(&self) -> &[u8] {
125 &self.pkcs8_der
126 }
127
128 #[must_use]
130 pub const fn bits(&self) -> u32 {
131 self.bits
132 }
133
134 #[must_use]
136 pub fn public_key(&self) -> RsaPublicKey {
137 RsaPublicKey {
138 spki_der: self.public_spki_der.clone(),
139 }
140 }
141
142 pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
149 let rng = SystemRandom::new();
150 let mut sig = vec![0u8; self.inner.public_modulus_len()];
151 self.inner
152 .sign(&signature::RSA_PKCS1_SHA256, &rng, message, &mut sig)
153 .map_err(|_| Error::Crypto("RSA PKCS#1 SHA-256 signing failed"))?;
154 Ok(sig)
155 }
156}
157
158impl fmt::Debug for RsaSigningKey {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 f.debug_struct("RsaSigningKey")
161 .field("bits", &self.bits)
162 .finish_non_exhaustive()
163 }
164}
165
166#[derive(Clone)]
168pub struct RsaPublicKey {
169 spki_der: Vec<u8>,
170}
171
172impl RsaPublicKey {
173 pub fn from_spki_der(der: &[u8]) -> Result<Self, Error> {
179 if der.is_empty() {
180 return Err(Error::InvalidPkcs8("empty SPKI DER".into()));
181 }
182 Ok(Self {
183 spki_der: der.to_vec(),
184 })
185 }
186
187 #[must_use]
189 pub fn as_spki_der(&self) -> &[u8] {
190 &self.spki_der
191 }
192
193 pub fn verify(&self, message: &[u8], signature_bytes: &[u8]) -> Result<(), Error> {
204 UnparsedPublicKey::new(
205 &signature::RSA_PKCS1_2048_8192_SHA256,
206 self.spki_der.as_slice(),
207 )
208 .verify(message, signature_bytes)
209 .map_err(|_| Error::VerificationFailed)
210 }
211}
212
213impl fmt::Debug for RsaPublicKey {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 f.debug_struct("RsaPublicKey")
216 .field("spki_bytes", &self.spki_der.len())
217 .finish_non_exhaustive()
218 }
219}
220
221impl PartialEq for RsaPublicKey {
222 fn eq(&self, other: &Self) -> bool {
223 self.spki_der == other.spki_der
224 }
225}
226
227impl Eq for RsaPublicKey {}
228
229#[cfg(test)]
230mod tests {
231 use pretty_assertions::assert_eq;
232
233 use super::*;
234
235 fn fresh_key() -> RsaSigningKey {
238 RsaSigningKey::generate(RsaBits::Rsa2048).expect("rng available")
239 }
240
241 #[test]
242 fn generate_then_sign_and_verify_roundtrips() {
243 let key = fresh_key();
244 let public = key.public_key();
245 let msg = b"ActivityPub inbox delivery";
246 let sig = key.sign(msg).expect("sign must succeed");
247 assert_eq!(sig.len(), 256);
249 public.verify(msg, &sig).expect("signature must verify");
250 }
251
252 #[test]
253 fn tampered_message_fails_verification() {
254 let key = fresh_key();
255 let public = key.public_key();
256 let sig = key.sign(b"original message").expect("sign");
257 let err = public
258 .verify(b"tampered message", &sig)
259 .expect_err("tampered message must not verify");
260 assert!(matches!(err, Error::VerificationFailed));
261 }
262
263 #[test]
264 fn pkcs8_roundtrip_preserves_key() {
265 let original = fresh_key();
266 let reloaded =
267 RsaSigningKey::from_pkcs8_der(original.to_pkcs8_der()).expect("reload must succeed");
268 assert_eq!(original.bits(), reloaded.bits());
269 let msg = b"cross-verify me";
271 let sig = reloaded.sign(msg).expect("sign");
272 original
273 .public_key()
274 .verify(msg, &sig)
275 .expect("reloaded key must produce signatures the original can verify");
276 }
277
278 #[test]
279 fn rsa_bits_reports_correct_width() {
280 let key = fresh_key();
281 assert_eq!(key.bits(), 2048);
282 }
283}