]> git.proxmox.com Git - rustc.git/blame - vendor/openssl/src/pkcs7.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / openssl / src / pkcs7.rs
CommitLineData
0a29b90c
FG
1use bitflags::bitflags;
2use foreign_types::{ForeignType, ForeignTypeRef};
3use libc::c_int;
4use std::mem;
5use std::ptr;
6
7use crate::bio::{MemBio, MemBioSlice};
8use crate::error::ErrorStack;
9use crate::pkey::{HasPrivate, PKeyRef};
10use crate::stack::{Stack, StackRef};
11use crate::symm::Cipher;
12use crate::x509::store::X509StoreRef;
13use crate::x509::{X509Ref, X509};
14use crate::{cvt, cvt_p};
15use openssl_macros::corresponds;
16
17foreign_type_and_impl_send_sync! {
18 type CType = ffi::PKCS7;
19 fn drop = ffi::PKCS7_free;
20
21 /// A PKCS#7 structure.
22 ///
23 /// Contains signed and/or encrypted data.
24 pub struct Pkcs7;
25
26 /// Reference to `Pkcs7`
27 pub struct Pkcs7Ref;
28}
29
30bitflags! {
781aab86
FG
31 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32 #[repr(transparent)]
0a29b90c
FG
33 pub struct Pkcs7Flags: c_int {
34 const TEXT = ffi::PKCS7_TEXT;
35 const NOCERTS = ffi::PKCS7_NOCERTS;
36 const NOSIGS = ffi::PKCS7_NOSIGS;
37 const NOCHAIN = ffi::PKCS7_NOCHAIN;
38 const NOINTERN = ffi::PKCS7_NOINTERN;
39 const NOVERIFY = ffi::PKCS7_NOVERIFY;
40 const DETACHED = ffi::PKCS7_DETACHED;
41 const BINARY = ffi::PKCS7_BINARY;
42 const NOATTR = ffi::PKCS7_NOATTR;
43 const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
44 const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
45 const CRLFEOL = ffi::PKCS7_CRLFEOL;
46 const STREAM = ffi::PKCS7_STREAM;
47 const NOCRL = ffi::PKCS7_NOCRL;
48 const PARTIAL = ffi::PKCS7_PARTIAL;
49 const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
50 #[cfg(not(any(ossl101, ossl102, libressl)))]
51 const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT;
52 }
53}
54
55impl Pkcs7 {
56 from_pem! {
57 /// Deserializes a PEM-encoded PKCS#7 signature
58 ///
59 /// The input should have a header of `-----BEGIN PKCS7-----`.
60 #[corresponds(PEM_read_bio_PKCS7)]
61 from_pem,
62 Pkcs7,
63 ffi::PEM_read_bio_PKCS7
64 }
65
66 from_der! {
67 /// Deserializes a DER-encoded PKCS#7 signature
68 #[corresponds(d2i_PKCS7)]
69 from_der,
70 Pkcs7,
71 ffi::d2i_PKCS7
72 }
73
74 /// Parses a message in S/MIME format.
75 ///
76 /// Returns the loaded signature, along with the cleartext message (if
77 /// available).
78 #[corresponds(SMIME_read_PKCS7)]
79 pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack> {
80 ffi::init();
81
82 let input_bio = MemBioSlice::new(input)?;
83 let mut bcont_bio = ptr::null_mut();
84 unsafe {
85 let pkcs7 =
86 cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?;
87 let out = if !bcont_bio.is_null() {
88 let bcont_bio = MemBio::from_ptr(bcont_bio);
89 Some(bcont_bio.get_buf().to_vec())
90 } else {
91 None
92 };
93 Ok((pkcs7, out))
94 }
95 }
96
97 /// Creates and returns a PKCS#7 `envelopedData` structure.
98 ///
99 /// `certs` is a list of recipient certificates. `input` is the content to be
100 /// encrypted. `cipher` is the symmetric cipher to use. `flags` is an optional
101 /// set of flags.
102 #[corresponds(PKCS7_encrypt)]
103 pub fn encrypt(
104 certs: &StackRef<X509>,
105 input: &[u8],
106 cipher: Cipher,
107 flags: Pkcs7Flags,
108 ) -> Result<Pkcs7, ErrorStack> {
109 let input_bio = MemBioSlice::new(input)?;
110
111 unsafe {
112 cvt_p(ffi::PKCS7_encrypt(
113 certs.as_ptr(),
114 input_bio.as_ptr(),
115 cipher.as_ptr(),
781aab86 116 flags.bits(),
0a29b90c
FG
117 ))
118 .map(Pkcs7)
119 }
120 }
121
122 /// Creates and returns a PKCS#7 `signedData` structure.
123 ///
124 /// `signcert` is the certificate to sign with, `pkey` is the corresponding
125 /// private key. `certs` is an optional additional set of certificates to
126 /// include in the PKCS#7 structure (for example any intermediate CAs in the
127 /// chain).
128 #[corresponds(PKCS7_sign)]
129 pub fn sign<PT>(
130 signcert: &X509Ref,
131 pkey: &PKeyRef<PT>,
132 certs: &StackRef<X509>,
133 input: &[u8],
134 flags: Pkcs7Flags,
135 ) -> Result<Pkcs7, ErrorStack>
136 where
137 PT: HasPrivate,
138 {
139 let input_bio = MemBioSlice::new(input)?;
140 unsafe {
141 cvt_p(ffi::PKCS7_sign(
142 signcert.as_ptr(),
143 pkey.as_ptr(),
144 certs.as_ptr(),
145 input_bio.as_ptr(),
781aab86 146 flags.bits(),
0a29b90c
FG
147 ))
148 .map(Pkcs7)
149 }
150 }
151}
152
153impl Pkcs7Ref {
154 /// Converts PKCS#7 structure to S/MIME format
155 #[corresponds(SMIME_write_PKCS7)]
156 pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack> {
157 let input_bio = MemBioSlice::new(input)?;
158 let output = MemBio::new()?;
159 unsafe {
160 cvt(ffi::SMIME_write_PKCS7(
161 output.as_ptr(),
162 self.as_ptr(),
163 input_bio.as_ptr(),
781aab86 164 flags.bits(),
0a29b90c
FG
165 ))
166 .map(|_| output.get_buf().to_owned())
167 }
168 }
169
170 to_pem! {
171 /// Serializes the data into a PEM-encoded PKCS#7 structure.
172 ///
173 /// The output will have a header of `-----BEGIN PKCS7-----`.
174 #[corresponds(PEM_write_bio_PKCS7)]
175 to_pem,
176 ffi::PEM_write_bio_PKCS7
177 }
178
179 to_der! {
180 /// Serializes the data into a DER-encoded PKCS#7 structure.
181 #[corresponds(i2d_PKCS7)]
182 to_der,
183 ffi::i2d_PKCS7
184 }
185
186 /// Decrypts data using the provided private key.
187 ///
188 /// `pkey` is the recipient's private key, and `cert` is the recipient's
189 /// certificate.
190 ///
191 /// Returns the decrypted message.
192 #[corresponds(PKCS7_decrypt)]
193 pub fn decrypt<PT>(
194 &self,
195 pkey: &PKeyRef<PT>,
196 cert: &X509Ref,
197 flags: Pkcs7Flags,
198 ) -> Result<Vec<u8>, ErrorStack>
199 where
200 PT: HasPrivate,
201 {
202 let output = MemBio::new()?;
203
204 unsafe {
205 cvt(ffi::PKCS7_decrypt(
206 self.as_ptr(),
207 pkey.as_ptr(),
208 cert.as_ptr(),
209 output.as_ptr(),
781aab86 210 flags.bits(),
0a29b90c
FG
211 ))
212 .map(|_| output.get_buf().to_owned())
213 }
214 }
215
216 /// Verifies the PKCS#7 `signedData` structure contained by `&self`.
217 ///
218 /// `certs` is a set of certificates in which to search for the signer's
219 /// certificate. `store` is a trusted certificate store (used for chain
220 /// verification). `indata` is the signed data if the content is not present
221 /// in `&self`. The content is written to `out` if it is not `None`.
222 #[corresponds(PKCS7_verify)]
223 pub fn verify(
224 &self,
225 certs: &StackRef<X509>,
226 store: &X509StoreRef,
227 indata: Option<&[u8]>,
228 out: Option<&mut Vec<u8>>,
229 flags: Pkcs7Flags,
230 ) -> Result<(), ErrorStack> {
231 let out_bio = MemBio::new()?;
232
233 let indata_bio = match indata {
234 Some(data) => Some(MemBioSlice::new(data)?),
235 None => None,
236 };
237 let indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr());
238
239 unsafe {
240 cvt(ffi::PKCS7_verify(
241 self.as_ptr(),
242 certs.as_ptr(),
243 store.as_ptr(),
244 indata_bio_ptr,
245 out_bio.as_ptr(),
781aab86 246 flags.bits(),
0a29b90c
FG
247 ))
248 .map(|_| ())?
249 }
250
251 if let Some(data) = out {
252 data.clear();
253 data.extend_from_slice(out_bio.get_buf());
254 }
255
256 Ok(())
257 }
258
259 /// Retrieve the signer's certificates from the PKCS#7 structure without verifying them.
260 #[corresponds(PKCS7_get0_signers)]
261 pub fn signers(
262 &self,
263 certs: &StackRef<X509>,
264 flags: Pkcs7Flags,
265 ) -> Result<Stack<X509>, ErrorStack> {
266 unsafe {
267 let ptr = cvt_p(ffi::PKCS7_get0_signers(
268 self.as_ptr(),
269 certs.as_ptr(),
781aab86 270 flags.bits(),
0a29b90c
FG
271 ))?;
272
273 // The returned stack is owned by the caller, but the certs inside are not! Our stack interface can't deal
274 // with that, so instead we just manually bump the refcount of the certs so that the whole stack is properly
275 // owned.
276 let stack = Stack::<X509>::from_ptr(ptr);
277 for cert in &stack {
278 mem::forget(cert.to_owned());
279 }
280
281 Ok(stack)
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use crate::hash::MessageDigest;
289 use crate::pkcs7::{Pkcs7, Pkcs7Flags};
290 use crate::pkey::PKey;
291 use crate::stack::Stack;
292 use crate::symm::Cipher;
293 use crate::x509::store::X509StoreBuilder;
294 use crate::x509::X509;
295
296 #[test]
297 fn encrypt_decrypt_test() {
298 let cert = include_bytes!("../test/certs.pem");
299 let cert = X509::from_pem(cert).unwrap();
300 let mut certs = Stack::new().unwrap();
301 certs.push(cert.clone()).unwrap();
302 let message: String = String::from("foo");
303 let cipher = Cipher::des_ede3_cbc();
304 let flags = Pkcs7Flags::STREAM;
305 let pkey = include_bytes!("../test/key.pem");
306 let pkey = PKey::private_key_from_pem(pkey).unwrap();
307
308 let pkcs7 =
309 Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed");
310
311 let encrypted = pkcs7
312 .to_smime(message.as_bytes(), flags)
313 .expect("should succeed");
314
315 let (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed");
316
317 let decoded = pkcs7_decoded
318 .decrypt(&pkey, &cert, Pkcs7Flags::empty())
319 .expect("should succeed");
320
321 assert_eq!(decoded, message.into_bytes());
322 }
323
324 #[test]
325 fn sign_verify_test_detached() {
326 let cert = include_bytes!("../test/cert.pem");
327 let cert = X509::from_pem(cert).unwrap();
328 let certs = Stack::new().unwrap();
329 let message = "foo";
330 let flags = Pkcs7Flags::STREAM | Pkcs7Flags::DETACHED;
331 let pkey = include_bytes!("../test/key.pem");
332 let pkey = PKey::private_key_from_pem(pkey).unwrap();
333 let mut store_builder = X509StoreBuilder::new().expect("should succeed");
334
335 let root_ca = include_bytes!("../test/root-ca.pem");
336 let root_ca = X509::from_pem(root_ca).unwrap();
337 store_builder.add_cert(root_ca).expect("should succeed");
338
339 let store = store_builder.build();
340
341 let pkcs7 =
342 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
343
344 let signed = pkcs7
345 .to_smime(message.as_bytes(), flags)
346 .expect("should succeed");
347 println!("{:?}", String::from_utf8(signed.clone()).unwrap());
348 let (pkcs7_decoded, content) =
349 Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
350
351 let mut output = Vec::new();
352 pkcs7_decoded
353 .verify(
354 &certs,
355 &store,
356 Some(message.as_bytes()),
357 Some(&mut output),
358 flags,
359 )
360 .expect("should succeed");
361
362 assert_eq!(output, message.as_bytes());
363 assert_eq!(content.expect("should be non-empty"), message.as_bytes());
364 }
365
366 /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2
367 #[test]
368 #[cfg_attr(all(libressl360, not(libressl361)), ignore)]
369 fn sign_verify_test_normal() {
370 let cert = include_bytes!("../test/cert.pem");
371 let cert = X509::from_pem(cert).unwrap();
372 let certs = Stack::new().unwrap();
373 let message = "foo";
374 let flags = Pkcs7Flags::STREAM;
375 let pkey = include_bytes!("../test/key.pem");
376 let pkey = PKey::private_key_from_pem(pkey).unwrap();
377 let mut store_builder = X509StoreBuilder::new().expect("should succeed");
378
379 let root_ca = include_bytes!("../test/root-ca.pem");
380 let root_ca = X509::from_pem(root_ca).unwrap();
381 store_builder.add_cert(root_ca).expect("should succeed");
382
383 let store = store_builder.build();
384
385 let pkcs7 =
386 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
387
388 let signed = pkcs7
389 .to_smime(message.as_bytes(), flags)
390 .expect("should succeed");
391
392 let (pkcs7_decoded, content) =
393 Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
394
395 let mut output = Vec::new();
396 pkcs7_decoded
397 .verify(&certs, &store, None, Some(&mut output), flags)
398 .expect("should succeed");
399
400 assert_eq!(output, message.as_bytes());
401 assert!(content.is_none());
402 }
403
404 /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2
405 #[test]
406 #[cfg_attr(all(libressl360, not(libressl361)), ignore)]
407 fn signers() {
408 let cert = include_bytes!("../test/cert.pem");
409 let cert = X509::from_pem(cert).unwrap();
410 let cert_digest = cert.digest(MessageDigest::sha256()).unwrap();
411 let certs = Stack::new().unwrap();
412 let message = "foo";
413 let flags = Pkcs7Flags::STREAM;
414 let pkey = include_bytes!("../test/key.pem");
415 let pkey = PKey::private_key_from_pem(pkey).unwrap();
416 let mut store_builder = X509StoreBuilder::new().expect("should succeed");
417
418 let root_ca = include_bytes!("../test/root-ca.pem");
419 let root_ca = X509::from_pem(root_ca).unwrap();
420 store_builder.add_cert(root_ca).expect("should succeed");
421
422 let pkcs7 =
423 Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
424
425 let signed = pkcs7
426 .to_smime(message.as_bytes(), flags)
427 .expect("should succeed");
428
429 let (pkcs7_decoded, _) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
430
431 let empty_certs = Stack::new().unwrap();
432 let signer_certs = pkcs7_decoded
433 .signers(&empty_certs, flags)
434 .expect("should succeed");
435 assert_eq!(empty_certs.len(), 0);
436 assert_eq!(signer_certs.len(), 1);
437 let signer_digest = signer_certs[0].digest(MessageDigest::sha256()).unwrap();
438 assert_eq!(*cert_digest, *signer_digest);
439 }
440
441 #[test]
442 fn invalid_from_smime() {
443 let input = String::from("Invalid SMIME Message");
444 let result = Pkcs7::from_smime(input.as_bytes());
445
446 assert!(result.is_err());
447 }
448}