Coverage Report

Created: 2024-05-20 06:38

/rust/registry/src/index.crates.io-6f17d22bba15001f/openssl-0.10.62/src/pkcs7.rs
Line
Count
Source (jump to first uncovered line)
1
use bitflags::bitflags;
2
use foreign_types::{ForeignType, ForeignTypeRef};
3
use libc::c_int;
4
use std::mem;
5
use std::ptr;
6
7
use crate::bio::{MemBio, MemBioSlice};
8
use crate::error::ErrorStack;
9
use crate::pkey::{HasPrivate, PKeyRef};
10
use crate::stack::{Stack, StackRef};
11
use crate::symm::Cipher;
12
use crate::x509::store::X509StoreRef;
13
use crate::x509::{X509Ref, X509};
14
use crate::{cvt, cvt_p};
15
use openssl_macros::corresponds;
16
17
foreign_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
30
0
bitflags! {
31
0
    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32
0
    #[repr(transparent)]
33
0
    pub struct Pkcs7Flags: c_int {
34
0
        const TEXT = ffi::PKCS7_TEXT;
35
0
        const NOCERTS = ffi::PKCS7_NOCERTS;
36
0
        const NOSIGS = ffi::PKCS7_NOSIGS;
37
0
        const NOCHAIN = ffi::PKCS7_NOCHAIN;
38
0
        const NOINTERN = ffi::PKCS7_NOINTERN;
39
0
        const NOVERIFY = ffi::PKCS7_NOVERIFY;
40
0
        const DETACHED = ffi::PKCS7_DETACHED;
41
0
        const BINARY = ffi::PKCS7_BINARY;
42
0
        const NOATTR = ffi::PKCS7_NOATTR;
43
0
        const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
44
0
        const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
45
0
        const CRLFEOL = ffi::PKCS7_CRLFEOL;
46
0
        const STREAM = ffi::PKCS7_STREAM;
47
0
        const NOCRL = ffi::PKCS7_NOCRL;
48
0
        const PARTIAL = ffi::PKCS7_PARTIAL;
49
0
        const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
50
0
        #[cfg(not(any(ossl101, ossl102, libressl)))]
51
0
        const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT;
52
0
    }
53
0
}
54
55
impl Pkcs7 {
56
0
    from_pem! {
57
0
        /// Deserializes a PEM-encoded PKCS#7 signature
58
0
        ///
59
0
        /// The input should have a header of `-----BEGIN PKCS7-----`.
60
0
        #[corresponds(PEM_read_bio_PKCS7)]
61
0
        from_pem,
62
0
        Pkcs7,
63
0
        ffi::PEM_read_bio_PKCS7
64
0
    }
65
66
0
    from_der! {
67
0
        /// Deserializes a DER-encoded PKCS#7 signature
68
0
        #[corresponds(d2i_PKCS7)]
69
0
        from_der,
70
0
        Pkcs7,
71
0
        ffi::d2i_PKCS7
72
0
    }
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
0
    pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack> {
80
0
        ffi::init();
81
82
0
        let input_bio = MemBioSlice::new(input)?;
83
0
        let mut bcont_bio = ptr::null_mut();
84
        unsafe {
85
0
            let pkcs7 =
86
0
                cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?;
87
0
            let out = if !bcont_bio.is_null() {
88
0
                let bcont_bio = MemBio::from_ptr(bcont_bio);
89
0
                Some(bcont_bio.get_buf().to_vec())
90
            } else {
91
0
                None
92
            };
93
0
            Ok((pkcs7, out))
94
        }
95
0
    }
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
0
    pub fn encrypt(
104
0
        certs: &StackRef<X509>,
105
0
        input: &[u8],
106
0
        cipher: Cipher,
107
0
        flags: Pkcs7Flags,
108
0
    ) -> Result<Pkcs7, ErrorStack> {
109
0
        let input_bio = MemBioSlice::new(input)?;
110
111
        unsafe {
112
0
            cvt_p(ffi::PKCS7_encrypt(
113
0
                certs.as_ptr(),
114
0
                input_bio.as_ptr(),
115
0
                cipher.as_ptr(),
116
0
                flags.bits(),
117
0
            ))
118
0
            .map(Pkcs7)
119
        }
120
0
    }
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
0
    pub fn sign<PT>(
130
0
        signcert: &X509Ref,
131
0
        pkey: &PKeyRef<PT>,
132
0
        certs: &StackRef<X509>,
133
0
        input: &[u8],
134
0
        flags: Pkcs7Flags,
135
0
    ) -> Result<Pkcs7, ErrorStack>
136
0
    where
137
0
        PT: HasPrivate,
138
0
    {
139
0
        let input_bio = MemBioSlice::new(input)?;
140
        unsafe {
141
0
            cvt_p(ffi::PKCS7_sign(
142
0
                signcert.as_ptr(),
143
0
                pkey.as_ptr(),
144
0
                certs.as_ptr(),
145
0
                input_bio.as_ptr(),
146
0
                flags.bits(),
147
0
            ))
148
0
            .map(Pkcs7)
149
        }
150
0
    }
151
}
152
153
impl Pkcs7Ref {
154
    /// Converts PKCS#7 structure to S/MIME format
155
    #[corresponds(SMIME_write_PKCS7)]
156
0
    pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack> {
157
0
        let input_bio = MemBioSlice::new(input)?;
158
0
        let output = MemBio::new()?;
159
        unsafe {
160
0
            cvt(ffi::SMIME_write_PKCS7(
161
0
                output.as_ptr(),
162
0
                self.as_ptr(),
163
0
                input_bio.as_ptr(),
164
0
                flags.bits(),
165
0
            ))
166
0
            .map(|_| output.get_buf().to_owned())
167
        }
168
0
    }
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
0
    pub fn decrypt<PT>(
194
0
        &self,
195
0
        pkey: &PKeyRef<PT>,
196
0
        cert: &X509Ref,
197
0
        flags: Pkcs7Flags,
198
0
    ) -> Result<Vec<u8>, ErrorStack>
199
0
    where
200
0
        PT: HasPrivate,
201
0
    {
202
0
        let output = MemBio::new()?;
203
204
        unsafe {
205
0
            cvt(ffi::PKCS7_decrypt(
206
0
                self.as_ptr(),
207
0
                pkey.as_ptr(),
208
0
                cert.as_ptr(),
209
0
                output.as_ptr(),
210
0
                flags.bits(),
211
0
            ))
212
0
            .map(|_| output.get_buf().to_owned())
213
        }
214
0
    }
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
0
    pub fn verify(
224
0
        &self,
225
0
        certs: &StackRef<X509>,
226
0
        store: &X509StoreRef,
227
0
        indata: Option<&[u8]>,
228
0
        out: Option<&mut Vec<u8>>,
229
0
        flags: Pkcs7Flags,
230
0
    ) -> Result<(), ErrorStack> {
231
0
        let out_bio = MemBio::new()?;
232
233
0
        let indata_bio = match indata {
234
0
            Some(data) => Some(MemBioSlice::new(data)?),
235
0
            None => None,
236
        };
237
0
        let indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr());
238
0
239
0
        unsafe {
240
0
            cvt(ffi::PKCS7_verify(
241
0
                self.as_ptr(),
242
0
                certs.as_ptr(),
243
0
                store.as_ptr(),
244
0
                indata_bio_ptr,
245
0
                out_bio.as_ptr(),
246
0
                flags.bits(),
247
0
            ))
248
0
            .map(|_| ())?
249
        }
250
251
0
        if let Some(data) = out {
252
0
            data.clear();
253
0
            data.extend_from_slice(out_bio.get_buf());
254
0
        }
255
256
0
        Ok(())
257
0
    }
258
259
    /// Retrieve the signer's certificates from the PKCS#7 structure without verifying them.
260
    #[corresponds(PKCS7_get0_signers)]
261
0
    pub fn signers(
262
0
        &self,
263
0
        certs: &StackRef<X509>,
264
0
        flags: Pkcs7Flags,
265
0
    ) -> Result<Stack<X509>, ErrorStack> {
266
        unsafe {
267
0
            let ptr = cvt_p(ffi::PKCS7_get0_signers(
268
0
                self.as_ptr(),
269
0
                certs.as_ptr(),
270
0
                flags.bits(),
271
0
            ))?;
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
0
            let stack = Stack::<X509>::from_ptr(ptr);
277
0
            for cert in &stack {
278
0
                mem::forget(cert.to_owned());
279
0
            }
280
281
0
            Ok(stack)
282
        }
283
0
    }
284
}
285
286
#[cfg(test)]
287
mod 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
}