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

ec_signer.rs (7067B)


      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 openssl::{
     15     ec::EcKey,
     16     hash::MessageDigest,
     17     pkey::{PKey, Private},
     18     x509::X509,
     19 };
     20 use x509_parser::der_parser::{
     21     self,
     22     der::{parse_der_integer, parse_der_sequence_defined_g},
     23 };
     24 
     25 use super::check_chain_order;
     26 use crate::{
     27     error::{Error, Result},
     28     signer::ConfigurableSigner,
     29     Signer, SigningAlg,
     30 };
     31 
     32 /// Implements `Signer` trait using OpenSSL's implementation of
     33 /// ECDSA encryption.
     34 pub struct EcSigner {
     35     signcerts: Vec<X509>,
     36     pkey: EcKey<Private>,
     37 
     38     certs_size: usize,
     39     timestamp_size: usize,
     40 
     41     alg: SigningAlg,
     42     tsa_url: Option<String>,
     43 }
     44 
     45 impl ConfigurableSigner for EcSigner {
     46     fn from_signcert_and_pkey(
     47         signcert: &[u8],
     48         pkey: &[u8],
     49         alg: SigningAlg,
     50         tsa_url: Option<String>,
     51     ) -> Result<Self> {
     52         let certs_size = signcert.len();
     53         let pkey = EcKey::private_key_from_pem(pkey).map_err(Error::OpenSslError)?;
     54         let signcerts = X509::stack_from_pem(signcert).map_err(Error::OpenSslError)?;
     55 
     56         // make sure cert chains are in order
     57         if !check_chain_order(&signcerts) {
     58             return Err(Error::BadParam(
     59                 "certificate chain is not in correct order".to_string(),
     60             ));
     61         }
     62 
     63         Ok(EcSigner {
     64             signcerts,
     65             pkey,
     66             certs_size,
     67             timestamp_size: 10000, /* todo: call out to TSA to get actual timestamp and use that size */
     68             alg,
     69             tsa_url,
     70         })
     71     }
     72 }
     73 
     74 impl Signer for EcSigner {
     75     fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
     76         let key = PKey::from_ec_key(self.pkey.clone()).map_err(Error::OpenSslError)?;
     77 
     78         let mut signer = match self.alg {
     79             SigningAlg::Es256 => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?,
     80             SigningAlg::Es384 => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?,
     81             SigningAlg::Es512 => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?,
     82             _ => return Err(Error::UnsupportedType),
     83         };
     84 
     85         signer.update(data).map_err(Error::OpenSslError)?;
     86         let der_sig = signer.sign_to_vec().map_err(Error::OpenSslError)?;
     87 
     88         der_to_p1363(&der_sig, self.alg)
     89     }
     90 
     91     fn alg(&self) -> SigningAlg {
     92         self.alg
     93     }
     94 
     95     fn certs(&self) -> Result<Vec<Vec<u8>>> {
     96         let mut certs: Vec<Vec<u8>> = Vec::new();
     97 
     98         for c in &self.signcerts {
     99             let cert = c.to_der().map_err(Error::OpenSslError)?;
    100             certs.push(cert);
    101         }
    102 
    103         Ok(certs)
    104     }
    105 
    106     fn time_authority_url(&self) -> Option<String> {
    107         self.tsa_url.clone()
    108     }
    109 
    110     fn reserve_size(&self) -> usize {
    111         1024 + self.certs_size + self.timestamp_size // the Cose_Sign1 contains complete certs and timestamps so account for size
    112     }
    113 }
    114 
    115 // C2PA use P1363 format for EC signatures so we must
    116 // convert from ASN.1 DER to IEEE P1363 format to verify.
    117 struct ECSigComps<'a> {
    118     r: &'a [u8],
    119     s: &'a [u8],
    120 }
    121 
    122 fn parse_ec_sig(data: &[u8]) -> der_parser::error::BerResult<ECSigComps> {
    123     parse_der_sequence_defined_g(|content: &[u8], _| {
    124         let (rem1, r) = parse_der_integer(content)?;
    125         let (_rem2, s) = parse_der_integer(rem1)?;
    126 
    127         Ok((
    128             data,
    129             ECSigComps {
    130                 r: r.as_slice()?,
    131                 s: s.as_slice()?,
    132             },
    133         ))
    134     })(data)
    135 }
    136 
    137 fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result<Vec<u8>> {
    138     // P1363 format: r | s
    139 
    140     let (_, p) = parse_ec_sig(data).map_err(|_err| Error::InvalidEcdsaSignature)?;
    141 
    142     let mut r = extfmt::Hexlify(p.r).to_string();
    143     let mut s = extfmt::Hexlify(p.s).to_string();
    144 
    145     let sig_len: usize = match alg {
    146         SigningAlg::Es256 => 64,
    147         SigningAlg::Es384 => 96,
    148         SigningAlg::Es512 => 132,
    149         _ => return Err(Error::UnsupportedType),
    150     };
    151 
    152     // pad or truncate as needed
    153     let rp = if r.len() > sig_len {
    154         // truncate
    155         let offset = r.len() - sig_len;
    156         &r[offset..r.len()]
    157     } else {
    158         // pad
    159         while r.len() != sig_len {
    160             r.insert(0, '0');
    161         }
    162         r.as_ref()
    163     };
    164 
    165     let sp = if s.len() > sig_len {
    166         // truncate
    167         let offset = s.len() - sig_len;
    168         &s[offset..s.len()]
    169     } else {
    170         // pad
    171         while s.len() != sig_len {
    172             s.insert(0, '0');
    173         }
    174         s.as_ref()
    175     };
    176 
    177     if rp.len() != sig_len || rp.len() != sp.len() {
    178         return Err(Error::InvalidEcdsaSignature);
    179     }
    180 
    181     // merge r and s strings
    182     let mut new_sig = rp.to_string();
    183     new_sig.push_str(sp);
    184 
    185     // convert back from hex string to byte array
    186     (0..new_sig.len())
    187         .step_by(2)
    188         .map(|i| {
    189             u8::from_str_radix(&new_sig[i..i + 2], 16).map_err(|_err| Error::InvalidEcdsaSignature)
    190         })
    191         .collect()
    192 }
    193 
    194 #[cfg(test)]
    195 #[cfg(feature = "file_io")]
    196 mod tests {
    197     #![allow(clippy::unwrap_used)]
    198 
    199     use super::*;
    200     use crate::{openssl::temp_signer, utils::test::fixture_path};
    201 
    202     #[test]
    203     fn es256_signer() {
    204         let cert_dir = fixture_path("certs");
    205 
    206         let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es256, None);
    207 
    208         let data = b"some sample content to sign";
    209         println!("data len = {}", data.len());
    210 
    211         let signature = signer.sign(data).unwrap();
    212         println!("signature.len = {}", signature.len());
    213         assert!(signature.len() >= 64);
    214         assert!(signature.len() <= signer.reserve_size());
    215     }
    216 
    217     #[test]
    218     fn es384_signer() {
    219         let cert_dir = fixture_path("certs");
    220 
    221         let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es384, None);
    222 
    223         let data = b"some sample content to sign";
    224         println!("data len = {}", data.len());
    225 
    226         let signature = signer.sign(data).unwrap();
    227         println!("signature.len = {}", signature.len());
    228         assert!(signature.len() >= 64);
    229         assert!(signature.len() <= signer.reserve_size());
    230     }
    231 
    232     #[test]
    233     fn es512_signer() {
    234         let cert_dir = fixture_path("certs");
    235 
    236         let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es512, None);
    237 
    238         let data = b"some sample content to sign";
    239         println!("data len = {}", data.len());
    240 
    241         let signature = signer.sign(data).unwrap();
    242         println!("signature.len = {}", signature.len());
    243         assert!(signature.len() >= 64);
    244         assert!(signature.len() <= signer.reserve_size());
    245     }
    246 }