cdx_core/security/
es384.rs1use crate::error::invalid_manifest;
6use crate::{DocumentId, Result};
7
8use super::signature::{Signature, SignatureAlgorithm, SignatureVerification, SignerInfo};
9use super::signer::{Signer, Verifier};
10
11pub struct Es384Signer {
13 signing_key: p384::ecdsa::SigningKey,
14 signer_info: SignerInfo,
15}
16
17impl Es384Signer {
18 pub fn from_pem(pem: &str, signer_info: SignerInfo) -> Result<Self> {
24 use p384::pkcs8::DecodePrivateKey;
25
26 let signing_key = p384::ecdsa::SigningKey::from_pkcs8_pem(pem)
27 .map_err(|e| invalid_manifest(format!("Failed to parse P-384 private key PEM: {e}")))?;
28
29 Ok(Self {
30 signing_key,
31 signer_info,
32 })
33 }
34
35 pub fn generate(signer_info: SignerInfo) -> Result<(Self, String)> {
43 use p384::pkcs8::EncodePublicKey;
44
45 use p384::elliptic_curve::Generate;
46 let signing_key = p384::ecdsa::SigningKey::generate();
47 let verifying_key = signing_key.verifying_key();
48 let public_key_pem = verifying_key
49 .to_public_key_pem(p384::pkcs8::LineEnding::LF)
50 .map_err(|e| invalid_manifest(format!("Failed to encode P-384 public key: {e}")))?;
51
52 Ok((
53 Self {
54 signing_key,
55 signer_info,
56 },
57 public_key_pem,
58 ))
59 }
60
61 pub fn public_key_pem(&self) -> Result<String> {
67 use p384::pkcs8::EncodePublicKey;
68
69 self.signing_key
70 .verifying_key()
71 .to_public_key_pem(p384::pkcs8::LineEnding::LF)
72 .map_err(|e| invalid_manifest(format!("Failed to encode P-384 public key: {e}")))
73 }
74}
75
76impl Signer for Es384Signer {
77 fn algorithm(&self) -> SignatureAlgorithm {
78 SignatureAlgorithm::ES384
79 }
80
81 fn signer_info(&self) -> SignerInfo {
82 self.signer_info.clone()
83 }
84
85 fn sign(&self, document_id: &DocumentId) -> Result<Signature> {
86 use base64::Engine;
87 use ecdsa::signature::Signer as EcdsaSignerTrait;
88
89 if document_id.is_pending() {
90 return Err(crate::Error::InvalidManifest {
91 reason: "Cannot sign a pending document ID".to_string(),
92 });
93 }
94
95 let signature: p384::ecdsa::Signature = self.signing_key.sign(document_id.digest());
97
98 let value = base64::engine::general_purpose::STANDARD.encode(signature.to_bytes());
100
101 let sig_id = format!(
103 "sig-{}",
104 &crate::Hasher::hash(crate::HashAlgorithm::Sha256, value.as_bytes()).hex_digest()[..8]
105 );
106
107 Ok(Signature::new(
108 sig_id,
109 SignatureAlgorithm::ES384,
110 self.signer_info.clone(),
111 value,
112 ))
113 }
114}
115
116pub struct Es384Verifier {
118 verifying_key: p384::ecdsa::VerifyingKey,
119}
120
121impl Es384Verifier {
122 pub fn from_pem(pem: &str) -> Result<Self> {
128 use p384::pkcs8::DecodePublicKey;
129
130 let verifying_key = p384::ecdsa::VerifyingKey::from_public_key_pem(pem)
131 .map_err(|e| invalid_manifest(format!("Failed to parse P-384 public key PEM: {e}")))?;
132
133 Ok(Self { verifying_key })
134 }
135}
136
137impl Verifier for Es384Verifier {
138 fn verify(
139 &self,
140 document_id: &DocumentId,
141 signature: &Signature,
142 ) -> Result<SignatureVerification> {
143 use base64::Engine;
144 use ecdsa::signature::Verifier as EcdsaVerifierTrait;
145
146 if signature.algorithm != SignatureAlgorithm::ES384 {
147 return Ok(SignatureVerification::invalid(
148 &signature.id,
149 format!(
150 "Algorithm mismatch: expected ES384, got {}",
151 signature.algorithm
152 ),
153 ));
154 }
155
156 let sig_bytes = base64::engine::general_purpose::STANDARD
158 .decode(&signature.value)
159 .map_err(|e| invalid_manifest(format!("Failed to decode signature: {e}")))?;
160
161 let ecdsa_sig = p384::ecdsa::Signature::from_slice(&sig_bytes)
163 .map_err(|e| invalid_manifest(format!("Invalid ES384 signature format: {e}")))?;
164
165 match self.verifying_key.verify(document_id.digest(), &ecdsa_sig) {
167 Ok(()) => Ok(SignatureVerification::valid(&signature.id)),
168 Err(e) => Ok(SignatureVerification::invalid(
169 &signature.id,
170 format!("ES384 signature verification failed: {e}"),
171 )),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use crate::security::test_helpers;
180
181 fn generate_keypair() -> (Es384Signer, Es384Verifier) {
182 let signer_info = SignerInfo::new("Test ES384 Signer");
183 let (signer, public_key_pem) = Es384Signer::generate(signer_info).unwrap();
184 let verifier = Es384Verifier::from_pem(&public_key_pem).unwrap();
185 (signer, verifier)
186 }
187
188 #[test]
189 fn test_generate_and_sign() {
190 let signer_info = SignerInfo::new("Test ES384 Signer");
191 let (signer, public_key_pem) = Es384Signer::generate(signer_info).unwrap();
192
193 assert!(!public_key_pem.is_empty());
194 assert!(public_key_pem.contains("BEGIN PUBLIC KEY"));
195
196 test_helpers::assert_sign_produces_valid_signature(&signer, SignatureAlgorithm::ES384);
197 }
198
199 #[test]
200 fn test_sign_and_verify() {
201 let (signer, verifier) = generate_keypair();
202 test_helpers::assert_sign_verify_roundtrip(&signer, &verifier);
203 }
204
205 #[test]
206 fn test_verify_wrong_document() {
207 let (signer, verifier) = generate_keypair();
208 test_helpers::assert_verify_wrong_document_fails(&signer, &verifier);
209 }
210
211 #[test]
212 fn test_cannot_sign_pending_id() {
213 let (signer, _) = generate_keypair();
214 test_helpers::assert_cannot_sign_pending_id(&signer);
215 }
216
217 #[test]
218 fn test_algorithm_mismatch() {
219 let (signer, verifier) = generate_keypair();
220 test_helpers::assert_algorithm_mismatch_rejected(
221 &signer,
222 &verifier,
223 SignatureAlgorithm::ES256,
224 );
225 }
226}