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

callback_signer.rs (4924B)


      1 // Copyright 2024 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 #![deny(missing_docs)]
     15 
     16 //! The `callback_signer` module provides a way to obtain a [`Signer`] or [`AsyncSigner`]
     17 //! using a callback and public signing certificates.
     18 
     19 use crate::{
     20     error::{Error, Result},
     21     AsyncSigner, Signer, SigningAlg,
     22 };
     23 
     24 /// Defines a callback function interface for a [`CallbackSigner`].
     25 ///
     26 /// The callback should return a signature for the given data.
     27 /// The callback should return an error if the data cannot be signed.
     28 pub type CallbackFunc = dyn Fn(*const (), &[u8]) -> std::result::Result<Vec<u8>, Error>;
     29 
     30 /// Defines a signer that uses a callback to sign data.
     31 ///
     32 /// The private key should only be known by the callback.
     33 pub struct CallbackSigner {
     34     /// An opaque context for the signer, used to store any necessary state.
     35     pub context: *const (),
     36 
     37     /// The callback to use to sign data.
     38     pub callback: Box<CallbackFunc>,
     39 
     40     /// The signing algorithm to use.
     41     pub alg: SigningAlg,
     42 
     43     /// The public certificates to use in PEM format.
     44     pub certs: Vec<u8>,
     45 
     46     /// A max size to reserve for the signature.
     47     pub reserve_size: usize,
     48 
     49     /// The optional URL of a Time Stamping Authority.
     50     pub tsa_url: Option<String>,
     51 }
     52 
     53 unsafe impl Send for CallbackSigner {}
     54 
     55 unsafe impl Sync for CallbackSigner {}
     56 
     57 impl CallbackSigner {
     58     /// Create a new callback signer.
     59     pub fn new<F, T>(callback: F, alg: SigningAlg, certs: T) -> Self
     60     where
     61         F: Fn(*const (), &[u8]) -> std::result::Result<Vec<u8>, Error> + 'static,
     62         T: Into<Vec<u8>>,
     63     {
     64         let certs = certs.into();
     65         let reserve_size = 10000 + certs.len();
     66         Self {
     67             context: std::ptr::null(),
     68             callback: Box::new(callback),
     69             alg,
     70             certs,
     71             reserve_size,
     72             ..Default::default()
     73         }
     74     }
     75 
     76     /// Set a time stamping authority URL to call when signing.
     77     pub fn set_tsa_url<S: Into<String>>(mut self, url: S) -> Self {
     78         self.tsa_url = Some(url.into());
     79         self
     80     }
     81 
     82     /// Set a context value for the signer.
     83     ///
     84     /// This can be used to store any necessary state for the callback.
     85     /// Safety: The context must be valid for the lifetime of the signer.
     86     /// There is no Rust memory management for the context since it may also come from FFI.    
     87     pub const fn set_context(mut self, context: *const ()) -> Self {
     88         self.context = context;
     89         self
     90     }
     91 }
     92 
     93 // This default is only intended for struct completion, do not use on its own.
     94 impl Default for CallbackSigner {
     95     fn default() -> Self {
     96         Self {
     97             context: std::ptr::null(),
     98             callback: Box::new(|_, _| Err(Error::UnsupportedType)),
     99             alg: SigningAlg::Es256,
    100             certs: Vec::new(),
    101             reserve_size: 10000,
    102             tsa_url: None,
    103         }
    104     }
    105 }
    106 
    107 impl Signer for CallbackSigner {
    108     fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
    109         (self.callback)(self.context, data)
    110     }
    111 
    112     fn alg(&self) -> SigningAlg {
    113         self.alg
    114     }
    115 
    116     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    117         let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?;
    118         Ok(pems.into_iter().map(|p| p.into_contents()).collect())
    119     }
    120 
    121     fn reserve_size(&self) -> usize {
    122         self.reserve_size
    123     }
    124 
    125     fn time_authority_url(&self) -> Option<String> {
    126         self.tsa_url.clone()
    127     }
    128 }
    129 
    130 use async_trait::async_trait;
    131 
    132 #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
    133 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
    134 // I'm not sure if this is useful since the callback is still synchronous.
    135 impl AsyncSigner for CallbackSigner {
    136     async fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>> {
    137         (self.callback)(self.context, &data)
    138     }
    139 
    140     fn alg(&self) -> SigningAlg {
    141         self.alg
    142     }
    143 
    144     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    145         let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?;
    146         Ok(pems.into_iter().map(|p| p.into_contents()).collect())
    147     }
    148 
    149     fn reserve_size(&self) -> usize {
    150         self.reserve_size
    151     }
    152 
    153     fn time_authority_url(&self) -> Option<String> {
    154         self.tsa_url.clone()
    155     }
    156 
    157     #[cfg(target_arch = "wasm32")]
    158     async fn send_timestamp_request(&self, _message: &[u8]) -> Option<Result<Vec<u8>>> {
    159         None
    160     }
    161 }