c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

rsa_signer.rs (9768B)


      1 // Copyright 2022 Adobe. All rights reserved.
      2 // This file is licensed to you under the Apache License,
      3 // Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
      4 // or the MIT license (http://opensource.org/licenses/MIT),
      5 // at your option.
      6 
      7 // Unless required by applicable law or agreed to in writing,
      8 // this software is distributed on an "AS IS" BASIS, WITHOUT
      9 // WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
     10 // implied. See the LICENSE-MIT and LICENSE-APACHE files for the
     11 // specific language governing permissions and limitations under
     12 // each license.
     13 
     14 use std::cell::Cell;
     15 
     16 //use extfmt::Hexlify;
     17 use openssl::{
     18     hash::MessageDigest,
     19     pkey::{PKey, Private},
     20     rsa::{Rsa, RsaPrivateKeyBuilder},
     21     x509::X509,
     22 };
     23 
     24 use super::check_chain_order;
     25 use crate::{ocsp_utils::OcspData, signer::ConfigurableSigner, Error, Result, Signer, SigningAlg};
     26 
     27 /// Implements `Signer` trait using OpenSSL's implementation of
     28 /// SHA256 + RSA encryption.
     29 pub struct RsaSigner {
     30     signcerts: Vec<X509>,
     31     pkey: PKey<Private>,
     32 
     33     certs_size: usize,
     34     timestamp_size: usize,
     35     ocsp_size: Cell<usize>,
     36 
     37     alg: SigningAlg,
     38     tsa_url: Option<String>,
     39     ocsp_rsp: Cell<OcspData>,
     40 }
     41 
     42 impl RsaSigner {
     43     // Sample of OCSP stapling while signing. This code is only for demo purposes and not for
     44     // production use since there is no caching in the SDK and fetching is expensive. This is behind the
     45     // feature flag 'psxxx_ocsp_stapling_experimental'
     46     fn update_ocsp(&self) {
     47         // do we need an update
     48         let now = chrono::offset::Utc::now();
     49 
     50         // is it time for an OCSP update
     51         let ocsp_data = self.ocsp_rsp.take();
     52         let next_update = ocsp_data.next_update;
     53         self.ocsp_rsp.set(ocsp_data);
     54         if now > next_update {
     55             #[cfg(feature = "psxxx_ocsp_stapling_experimental")]
     56             {
     57                 if let Ok(certs) = self.certs() {
     58                     if let Some(ocsp_rsp) = crate::ocsp_utils::fetch_ocsp_response(&certs) {
     59                         self.ocsp_size.set(ocsp_rsp.len());
     60                         let mut validation_log =
     61                             crate::status_tracker::DetailedStatusTracker::default();
     62                         if let Ok(ocsp_data) = crate::ocsp_utils::check_ocsp_response(
     63                             &ocsp_rsp,
     64                             None,
     65                             &mut validation_log,
     66                         ) {
     67                             self.ocsp_rsp.set(ocsp_data);
     68                         }
     69                     }
     70                 }
     71             }
     72         }
     73     }
     74 }
     75 
     76 impl ConfigurableSigner for RsaSigner {
     77     fn from_signcert_and_pkey(
     78         signcert: &[u8],
     79         pkey: &[u8],
     80         alg: SigningAlg,
     81         tsa_url: Option<String>,
     82     ) -> Result<Self> {
     83         let signcerts = X509::stack_from_pem(signcert).map_err(wrap_openssl_err)?;
     84         let rsa = Rsa::private_key_from_pem(pkey).map_err(wrap_openssl_err)?;
     85 
     86         // rebuild RSA keys to eliminate incompatible values
     87         let n = rsa.n().to_owned().map_err(wrap_openssl_err)?;
     88         let e = rsa.e().to_owned().map_err(wrap_openssl_err)?;
     89         let d = rsa.d().to_owned().map_err(wrap_openssl_err)?;
     90         let po = rsa.p();
     91         let qo = rsa.q();
     92         let dmp1o = rsa.dmp1();
     93         let dmq1o = rsa.dmq1();
     94         let iqmpo = rsa.iqmp();
     95         let mut builder = RsaPrivateKeyBuilder::new(n, e, d).map_err(wrap_openssl_err)?;
     96 
     97         if let Some(p) = po {
     98             if let Some(q) = qo {
     99                 builder = builder
    100                     .set_factors(p.to_owned()?, q.to_owned()?)
    101                     .map_err(wrap_openssl_err)?;
    102             }
    103         }
    104 
    105         if let Some(dmp1) = dmp1o {
    106             if let Some(dmq1) = dmq1o {
    107                 if let Some(iqmp) = iqmpo {
    108                     builder = builder
    109                         .set_crt_params(dmp1.to_owned()?, dmq1.to_owned()?, iqmp.to_owned()?)
    110                         .map_err(wrap_openssl_err)?;
    111                 }
    112             }
    113         }
    114 
    115         let new_rsa = builder.build();
    116 
    117         let pkey = PKey::from_rsa(new_rsa).map_err(wrap_openssl_err)?;
    118 
    119         // make sure cert chains are in order
    120         if !check_chain_order(&signcerts) {
    121             return Err(Error::BadParam(
    122                 "certificate chain is not in correct order".to_string(),
    123             ));
    124         }
    125 
    126         let signer = RsaSigner {
    127             signcerts,
    128             pkey,
    129             certs_size: signcert.len(),
    130             timestamp_size: 10000, /* todo: call out to TSA to get actual timestamp and use that size */
    131             ocsp_size: Cell::new(0),
    132             alg,
    133             tsa_url,
    134             ocsp_rsp: Cell::new(OcspData::new()),
    135         };
    136 
    137         // get OCSP if possible
    138         signer.update_ocsp();
    139 
    140         Ok(signer)
    141     }
    142 }
    143 
    144 impl Signer for RsaSigner {
    145     fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
    146         let mut signer = match self.alg {
    147             SigningAlg::Ps256 => {
    148                 let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey)
    149                     .map_err(wrap_openssl_err)?;
    150 
    151                 signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding
    152                 signer.set_rsa_mgf1_md(MessageDigest::sha256())?;
    153                 signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
    154                 signer
    155             }
    156             SigningAlg::Ps384 => {
    157                 let mut signer = openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey)
    158                     .map_err(wrap_openssl_err)?;
    159 
    160                 signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding
    161                 signer.set_rsa_mgf1_md(MessageDigest::sha384())?;
    162                 signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
    163                 signer
    164             }
    165             SigningAlg::Ps512 => {
    166                 let mut signer = openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey)
    167                     .map_err(wrap_openssl_err)?;
    168 
    169                 signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding
    170                 signer.set_rsa_mgf1_md(MessageDigest::sha512())?;
    171                 signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
    172                 signer
    173             }
    174             // "rs256" => openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey)
    175             //     .map_err(wrap_openssl_err)?,
    176             // "rs384" => openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey)
    177             //     .map_err(wrap_openssl_err)?,
    178             // "rs512" => openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey)
    179             //     .map_err(wrap_openssl_err)?,
    180             _ => return Err(Error::UnsupportedType),
    181         };
    182 
    183         let signed_data = signer.sign_oneshot_to_vec(data)?;
    184 
    185         // println!("sig: {}", Hexlify(&signed_data));
    186 
    187         Ok(signed_data)
    188     }
    189 
    190     fn reserve_size(&self) -> usize {
    191         1024 + self.certs_size + self.timestamp_size + self.ocsp_size.get() // the Cose_Sign1 contains complete certs, timestamps and ocsp so account for size
    192     }
    193 
    194     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    195         let mut certs: Vec<Vec<u8>> = Vec::new();
    196 
    197         for c in &self.signcerts {
    198             let cert = c.to_der().map_err(wrap_openssl_err)?;
    199             certs.push(cert);
    200         }
    201 
    202         Ok(certs)
    203     }
    204 
    205     fn alg(&self) -> SigningAlg {
    206         self.alg
    207     }
    208 
    209     fn time_authority_url(&self) -> Option<String> {
    210         self.tsa_url.clone()
    211     }
    212 
    213     fn ocsp_val(&self) -> Option<Vec<u8>> {
    214         // update OCSP if needed
    215         self.update_ocsp();
    216 
    217         let ocsp_data = self.ocsp_rsp.take();
    218         let ocsp_rsp = ocsp_data.ocsp_der.clone();
    219         self.ocsp_rsp.set(ocsp_data);
    220         if !ocsp_rsp.is_empty() {
    221             Some(ocsp_rsp)
    222         } else {
    223             None
    224         }
    225     }
    226 }
    227 
    228 const fn wrap_openssl_err(err: openssl::error::ErrorStack) -> Error {
    229     Error::OpenSslError(err)
    230 }
    231 
    232 #[allow(unused_imports)]
    233 #[allow(clippy::unwrap_used)]
    234 #[cfg(test)]
    235 mod tests {
    236 
    237     use super::*;
    238     use crate::{
    239         utils::test::{fixture_path, temp_signer},
    240         Signer, SigningAlg,
    241     };
    242 
    243     #[test]
    244     fn signer_from_files() {
    245         let signer = temp_signer();
    246         let data = b"some sample content to sign";
    247 
    248         let signature = signer.sign(data).unwrap();
    249         println!("signature len = {}", signature.len());
    250         assert!(signature.len() <= signer.reserve_size());
    251     }
    252 
    253     #[test]
    254     fn sign_ps256() {
    255         let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data");
    256         let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data");
    257 
    258         let signer =
    259             RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, SigningAlg::Ps256, None)
    260                 .unwrap();
    261 
    262         let data = b"some sample content to sign";
    263 
    264         let signature = signer.sign(data).unwrap();
    265         println!("signature len = {}", signature.len());
    266         assert!(signature.len() <= signer.reserve_size());
    267     }
    268 
    269     // #[test]
    270     // fn sign_rs256() {
    271     //     let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data");
    272     //     let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data");
    273 
    274     //     let signer =
    275     //         RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, "rs256".to_string(), None)
    276     //             .unwrap();
    277 
    278     //     let data = b"some sample content to sign";
    279 
    280     //     let signature = signer.sign(data).unwrap();
    281     //     println!("signature len = {}", signature.len());
    282     //     assert!(signature.len() <= signer.reserve_size());
    283     // }
    284 }