cdx_core/security/
rsa_pss.rs1use crate::error::invalid_manifest;
7use crate::{DocumentId, Result};
8
9use super::signature::{Signature, SignatureAlgorithm, SignatureVerification, SignerInfo};
10use super::signer::{Signer, Verifier};
11
12pub struct Ps256Signer {
14 signing_key: rsa::RsaPrivateKey,
15 signer_info: SignerInfo,
16}
17
18impl Ps256Signer {
19 pub fn from_pem(pem: &str, signer_info: SignerInfo) -> Result<Self> {
25 use rsa::pkcs8::DecodePrivateKey;
26
27 let signing_key = rsa::RsaPrivateKey::from_pkcs8_pem(pem)
28 .map_err(|e| invalid_manifest(format!("Failed to parse RSA private key PEM: {e}")))?;
29
30 Ok(Self {
31 signing_key,
32 signer_info,
33 })
34 }
35
36 pub fn generate(signer_info: SignerInfo, bits: usize) -> Result<(Self, String)> {
45 use rsa::pkcs8::EncodePublicKey;
46
47 let signing_key =
48 rsa::RsaPrivateKey::new(&mut rand_core::UnwrapErr(getrandom::SysRng), bits)
49 .map_err(|e| invalid_manifest(format!("Failed to generate RSA key: {e}")))?;
50
51 let public_key = signing_key.to_public_key();
52 let public_key_pem = public_key
53 .to_public_key_pem(rsa::pkcs8::LineEnding::LF)
54 .map_err(|e| invalid_manifest(format!("Failed to encode RSA public key: {e}")))?;
55
56 Ok((
57 Self {
58 signing_key,
59 signer_info,
60 },
61 public_key_pem,
62 ))
63 }
64
65 pub fn generate_2048(signer_info: SignerInfo) -> Result<(Self, String)> {
73 Self::generate(signer_info, 2048)
74 }
75
76 pub fn public_key_pem(&self) -> Result<String> {
82 use rsa::pkcs8::EncodePublicKey;
83
84 self.signing_key
85 .to_public_key()
86 .to_public_key_pem(rsa::pkcs8::LineEnding::LF)
87 .map_err(|e| invalid_manifest(format!("Failed to encode RSA public key: {e}")))
88 }
89}
90
91impl Signer for Ps256Signer {
92 fn algorithm(&self) -> SignatureAlgorithm {
93 SignatureAlgorithm::PS256
94 }
95
96 fn signer_info(&self) -> SignerInfo {
97 self.signer_info.clone()
98 }
99
100 fn sign(&self, document_id: &DocumentId) -> Result<Signature> {
101 use base64::Engine;
102 use rsa::signature::RandomizedSigner;
103 use rsa::signature::SignatureEncoding;
104
105 if document_id.is_pending() {
106 return Err(crate::Error::InvalidManifest {
107 reason: "Cannot sign a pending document ID".to_string(),
108 });
109 }
110
111 let signing_key = rsa::pss::SigningKey::<rsa::sha2::Sha256>::new(self.signing_key.clone());
113
114 let signature = signing_key.sign_with_rng(
116 &mut rand_core::UnwrapErr(getrandom::SysRng),
117 document_id.digest(),
118 );
119
120 let value = base64::engine::general_purpose::STANDARD.encode(signature.to_bytes());
122
123 let sig_id = format!(
125 "sig-{}",
126 &crate::Hasher::hash(crate::HashAlgorithm::Sha256, value.as_bytes()).hex_digest()[..8]
127 );
128
129 Ok(Signature::new(
130 sig_id,
131 SignatureAlgorithm::PS256,
132 self.signer_info.clone(),
133 value,
134 ))
135 }
136}
137
138pub struct Ps256Verifier {
140 verifying_key: rsa::RsaPublicKey,
141}
142
143impl Ps256Verifier {
144 pub fn from_pem(pem: &str) -> Result<Self> {
150 use rsa::pkcs8::DecodePublicKey;
151
152 let verifying_key = rsa::RsaPublicKey::from_public_key_pem(pem)
153 .map_err(|e| invalid_manifest(format!("Failed to parse RSA public key PEM: {e}")))?;
154
155 Ok(Self { verifying_key })
156 }
157}
158
159impl Verifier for Ps256Verifier {
160 fn verify(
161 &self,
162 document_id: &DocumentId,
163 signature: &Signature,
164 ) -> Result<SignatureVerification> {
165 use base64::Engine;
166 use rsa::pss::VerifyingKey;
167 use rsa::signature::Verifier as RsaVerifierTrait;
168
169 if signature.algorithm != SignatureAlgorithm::PS256 {
170 return Ok(SignatureVerification::invalid(
171 &signature.id,
172 format!(
173 "Algorithm mismatch: expected PS256, got {}",
174 signature.algorithm
175 ),
176 ));
177 }
178
179 let sig_bytes = base64::engine::general_purpose::STANDARD
181 .decode(&signature.value)
182 .map_err(|e| invalid_manifest(format!("Failed to decode signature: {e}")))?;
183
184 let verifying_key = VerifyingKey::<rsa::sha2::Sha256>::new(self.verifying_key.clone());
186
187 let rsa_sig = rsa::pss::Signature::try_from(sig_bytes.as_slice())
189 .map_err(|e| invalid_manifest(format!("Invalid PS256 signature format: {e}")))?;
190
191 match verifying_key.verify(document_id.digest(), &rsa_sig) {
193 Ok(()) => Ok(SignatureVerification::valid(&signature.id)),
194 Err(e) => Ok(SignatureVerification::invalid(
195 &signature.id,
196 format!("PS256 signature verification failed: {e}"),
197 )),
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::security::test_helpers;
206
207 fn generate_keypair() -> (Ps256Signer, Ps256Verifier) {
208 let signer_info = SignerInfo::new("Test PS256 Signer");
209 let (signer, public_key_pem) = Ps256Signer::generate_2048(signer_info).unwrap();
210 let verifier = Ps256Verifier::from_pem(&public_key_pem).unwrap();
211 (signer, verifier)
212 }
213
214 #[test]
215 fn test_generate_and_sign() {
216 let signer_info = SignerInfo::new("Test PS256 Signer");
217 let (signer, public_key_pem) = Ps256Signer::generate_2048(signer_info).unwrap();
218
219 assert!(!public_key_pem.is_empty());
220 assert!(public_key_pem.contains("BEGIN PUBLIC KEY"));
221
222 test_helpers::assert_sign_produces_valid_signature(&signer, SignatureAlgorithm::PS256);
223 }
224
225 #[test]
226 fn test_sign_and_verify() {
227 let (signer, verifier) = generate_keypair();
228 test_helpers::assert_sign_verify_roundtrip(&signer, &verifier);
229 }
230
231 #[test]
232 fn test_verify_wrong_document() {
233 let (signer, verifier) = generate_keypair();
234 test_helpers::assert_verify_wrong_document_fails(&signer, &verifier);
235 }
236
237 #[test]
238 fn test_cannot_sign_pending_id() {
239 let (signer, _) = generate_keypair();
240 test_helpers::assert_cannot_sign_pending_id(&signer);
241 }
242
243 #[test]
244 fn test_algorithm_mismatch() {
245 let (signer, verifier) = generate_keypair();
246 test_helpers::assert_algorithm_mismatch_rejected(
247 &signer,
248 &verifier,
249 SignatureAlgorithm::ES256,
250 );
251 }
252}