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

test.rs (19018B)


      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 thema
     11 // specific language governing permissions and limitations under
     12 // each license.
     13 
     14 #![allow(clippy::unwrap_used)]
     15 
     16 use std::path::PathBuf;
     17 #[cfg(feature = "file_io")]
     18 use std::{
     19     io::{Cursor, Read, Write},
     20     path::Path,
     21 };
     22 
     23 use tempfile::TempDir;
     24 
     25 use crate::{
     26     assertions::{labels, Action, Actions, Ingredient, ReviewRating, SchemaDotOrg, Thumbnail},
     27     claim::Claim,
     28     salt::DefaultSalt,
     29     store::Store,
     30     RemoteSigner, Result, Signer, SigningAlg,
     31 };
     32 #[cfg(feature = "file_io")]
     33 use crate::{
     34     asset_io::CAIReadWrite, create_signer, hash_utils::Hasher,
     35     jumbf_io::get_assetio_handler_from_path,
     36 };
     37 #[cfg(feature = "openssl_sign")]
     38 use crate::{
     39     openssl::{AsyncSignerAdapter, RsaSigner},
     40     signer::ConfigurableSigner,
     41 };
     42 
     43 pub const TEST_SMALL_JPEG: &str = "earth_apollo17.jpg";
     44 
     45 pub const TEST_WEBP: &str = "mars.webp";
     46 
     47 pub const TEST_VC: &str = r#"{
     48     "@context": [
     49     "https://www.w3.org/2018/credentials/v1",
     50     "http://schema.org"
     51     ],
     52     "type": [
     53     "VerifiableCredential",
     54     "NPPACredential"
     55     ],
     56     "issuer": "https://nppa.org/",
     57     "credentialSubject": {
     58         "id": "did:nppa:eb1bb9934d9896a374c384521410c7f14",
     59         "name": "Bob Ross",
     60         "memberOf": "https://nppa.org/"
     61     },
     62     "proof": {
     63         "type": "RsaSignature2018",
     64         "created": "2021-06-18T21:19:10Z",
     65         "proofPurpose": "assertionMethod",
     66         "verificationMethod":
     67         "did:nppa:eb1bb9934d9896a374c384521410c7f14#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
     68         "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19DJBMvvFAIC00nSGB6Tn0XKbbF9XrsaJZREWvR2aONYTQQxnyXirtXnlewJMBBn2h9hfcGZrvnC1b6PgWmukzFJ1IiH1dWgnDIS81BH-IxXnPkbuYDeySorc4QU9MJxdVkY5EL4HYbcIfwKj6X4LBQ2_ZHZIu1jdqLcRZqHcsDF5KKylKc1THn5VRWy5WhYg_gBnyWny8E6Qkrze53MR7OuAmmNJ1m1nN8SxDrG6a08L78J0-Fbas5OjAQz3c17GY8mVuDPOBIOVjMEghBlgl3nOi1ysxbRGhHLEK4s0KKbeRogZdgt1DkQxDFxxn41QWDw_mmMCjs9qxg0zcZzqEJw"
     69     }
     70 }"#;
     71 
     72 /// creates a claim for testing
     73 pub fn create_test_claim() -> Result<Claim> {
     74     let mut claim = Claim::new("adobe unit test", Some("adobe"));
     75 
     76     // add some data boxes
     77     let _db_uri = claim.add_databox("text/plain", "this is a test".as_bytes().to_vec(), None)?;
     78     let _db_uri_1 =
     79         claim.add_databox("text/plain", "this is more text".as_bytes().to_vec(), None)?;
     80 
     81     // add VC entry
     82     let _hu = claim.add_verifiable_credential(TEST_VC)?;
     83 
     84     // Add assertions.
     85     let actions = Actions::new()
     86         .add_action(
     87             Action::new("c2pa.cropped")
     88                 .set_parameter(
     89                     "name".to_owned(),
     90                     r#"{
     91                     "left": 0,
     92                     "right": 2000,
     93                     "top": 1000,
     94                     "bottom": 4000
     95                 }"#,
     96                 )
     97                 .unwrap(),
     98         )
     99         .add_action(
    100             Action::new("c2pa.filtered")
    101                 .set_parameter("name".to_owned(), "gaussian blur")?
    102                 .set_when("2015-06-26T16:43:23+0200"),
    103         );
    104     // add a binary thumbnail assertion  ('deadbeefadbeadbe')
    105     let some_binary_data: Vec<u8> = vec![
    106         0x0d, 0x0e, 0x0a, 0x0d, 0x0b, 0x0e, 0x0e, 0x0f, 0x0a, 0x0d, 0x0b, 0x0e, 0x0a, 0x0d, 0x0b,
    107         0x0e,
    108     ];
    109 
    110     // create a schema.org claim
    111     let cr = r#"{
    112         "@context": "https://schema.org",
    113         "@type": "ClaimReview",
    114         "claimReviewed": "The world is flat",
    115         "reviewRating": {
    116             "@type": "Rating",
    117             "ratingValue": "1",
    118             "bestRating": "5",
    119             "worstRating": "1",
    120             "alternateName": "False"
    121         }
    122     }"#;
    123     let claim_review = SchemaDotOrg::from_json_str(cr)?;
    124 
    125     let thumbnail_claim = Thumbnail::new(labels::JPEG_CLAIM_THUMBNAIL, some_binary_data.clone());
    126 
    127     let thumbnail_ingred = Thumbnail::new(labels::JPEG_INGREDIENT_THUMBNAIL, some_binary_data);
    128 
    129     claim.add_assertion(&actions)?;
    130     claim.add_assertion(&claim_review)?;
    131     claim.add_assertion(&thumbnail_claim)?;
    132 
    133     let thumb_uri = claim.add_assertion_with_salt(&thumbnail_ingred, &DefaultSalt::default())?;
    134 
    135     let review = ReviewRating::new(
    136         "a 3rd party plugin was used",
    137         Some("actions.unknownActionsPerformed".to_string()),
    138         1,
    139     );
    140 
    141     //let data_path = claim.add_ingredient_data("some data".as_bytes());
    142     let ingredient = Ingredient::new(
    143         "image 1.jpg",
    144         "image/jpeg",
    145         "xmp.iid:7b57930e-2f23-47fc-affe-0400d70b738d",
    146         Some("xmp.did:87d51599-286e-43b2-9478-88c79f49c347"),
    147     )
    148     .set_thumbnail(Some(&thumb_uri))
    149     //.set_manifest_data(&data_path)
    150     .add_review(review);
    151 
    152     let ingredient2 = Ingredient::new(
    153         "image 2.png",
    154         "image/png",
    155         "xmp.iid:7b57930e-2f23-47fc-affe-0400d70b738c",
    156         Some("xmp.did:87d51599-286e-43b2-9478-88c79f49c346"),
    157     )
    158     .set_thumbnail(Some(&thumb_uri));
    159 
    160     claim.add_assertion_with_salt(&ingredient, &DefaultSalt::default())?;
    161     claim.add_assertion_with_salt(&ingredient2, &DefaultSalt::default())?;
    162 
    163     Ok(claim)
    164 }
    165 
    166 /// Creates a store with an unsigned claim for testing
    167 pub fn create_test_store() -> Result<Store> {
    168     // Create claims store.
    169     let mut store = Store::new();
    170 
    171     let claim = create_test_claim()?;
    172     store.commit_claim(claim).unwrap();
    173     Ok(store)
    174 }
    175 
    176 /// returns a path to a file in the fixtures folder
    177 pub fn fixture_path(file_name: &str) -> PathBuf {
    178     let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    179     path.push("tests/fixtures");
    180     path.push(file_name);
    181     path
    182 }
    183 
    184 /// returns a path to a file in the temp_dir folder
    185 // note, you must pass TempDir from the caller's context
    186 pub fn temp_dir_path(temp_dir: &TempDir, file_name: &str) -> PathBuf {
    187     let mut path = PathBuf::from(temp_dir.path());
    188     path.push(file_name);
    189     path
    190 }
    191 
    192 // copies a fixture to a temp file and returns path to copy
    193 pub fn temp_fixture_path(temp_dir: &TempDir, file_name: &str) -> PathBuf {
    194     let fixture_src = fixture_path(file_name);
    195     let fixture_copy = temp_dir_path(temp_dir, file_name);
    196     std::fs::copy(fixture_src, &fixture_copy).unwrap();
    197     fixture_copy
    198 }
    199 
    200 /// Create a [`Signer`] instance that can be used for testing purposes.
    201 ///
    202 /// This is a suitable default for use when you need a [`Signer`], but
    203 /// don't care what the format is.
    204 ///
    205 /// # Returns
    206 ///
    207 /// Returns a boxed [`Signer`] instance.
    208 ///
    209 /// # Panics
    210 ///
    211 /// Can panic if the certs cannot be read. (This function should only
    212 /// be used as part of testing infrastructure.)
    213 #[cfg(feature = "file_io")]
    214 pub fn temp_signer_file() -> RsaSigner {
    215     #![allow(clippy::expect_used)]
    216     let mut sign_cert_path = fixture_path("certs");
    217     sign_cert_path.push("ps256");
    218     sign_cert_path.set_extension("pub");
    219 
    220     let mut pem_key_path = fixture_path("certs");
    221     pem_key_path.push("ps256");
    222     pem_key_path.set_extension("pem");
    223 
    224     RsaSigner::from_files(&sign_cert_path, &pem_key_path, SigningAlg::Ps256, None)
    225         .expect("get_temp_signer")
    226 }
    227 
    228 /// Utility to create a test file with a placeholder for a manifest
    229 #[cfg(feature = "file_io")]
    230 pub fn write_jpeg_placeholder_file(
    231     placeholder: &[u8],
    232     input: &Path,
    233     output_file: &mut dyn CAIReadWrite,
    234     mut hasher: Option<&mut Hasher>,
    235 ) -> Result<usize> {
    236     // get where we will put the data
    237     let mut f = std::fs::File::open(input).unwrap();
    238     let jpeg_io = get_assetio_handler_from_path(input).unwrap();
    239     let box_mapper = jpeg_io.asset_box_hash_ref().unwrap();
    240     let boxes = box_mapper.get_box_map(&mut f).unwrap();
    241     let sof = boxes.iter().find(|b| b.names[0] == "SOF0").unwrap();
    242 
    243     // build new asset with hole for new manifest
    244     let outbuf = Vec::new();
    245     let mut out_stream = Cursor::new(outbuf);
    246     let mut input_file = std::fs::File::open(input).unwrap();
    247 
    248     // write before
    249     let mut before = vec![0u8; sof.range_start];
    250     input_file.read_exact(before.as_mut_slice()).unwrap();
    251     if let Some(hasher) = hasher.as_deref_mut() {
    252         hasher.update(&before);
    253     }
    254     out_stream.write_all(&before).unwrap();
    255 
    256     // write placeholder
    257     out_stream.write_all(placeholder).unwrap();
    258 
    259     // write bytes after
    260     let mut after_buf = Vec::new();
    261     input_file.read_to_end(&mut after_buf).unwrap();
    262     if let Some(hasher) = hasher {
    263         hasher.update(&after_buf);
    264     }
    265     out_stream.write_all(&after_buf).unwrap();
    266 
    267     // save to output file
    268     output_file.write_all(&out_stream.into_inner()).unwrap();
    269 
    270     Ok(sof.range_start)
    271 }
    272 
    273 pub(crate) struct TestGoodSigner {}
    274 impl crate::Signer for TestGoodSigner {
    275     fn sign(&self, _data: &[u8]) -> Result<Vec<u8>> {
    276         Ok(b"not a valid signature".to_vec())
    277     }
    278 
    279     fn alg(&self) -> SigningAlg {
    280         SigningAlg::Ps256
    281     }
    282 
    283     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    284         Ok(Vec::new())
    285     }
    286 
    287     fn reserve_size(&self) -> usize {
    288         1024
    289     }
    290 
    291     fn send_timestamp_request(&self, _message: &[u8]) -> Option<crate::error::Result<Vec<u8>>> {
    292         Some(Ok(Vec::new()))
    293     }
    294 }
    295 
    296 pub(crate) struct AsyncTestGoodSigner {}
    297 
    298 #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
    299 #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
    300 impl crate::AsyncSigner for AsyncTestGoodSigner {
    301     async fn sign(&self, _data: Vec<u8>) -> Result<Vec<u8>> {
    302         Ok(b"not a valid signature".to_vec())
    303     }
    304 
    305     fn alg(&self) -> SigningAlg {
    306         SigningAlg::Ps256
    307     }
    308 
    309     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    310         Ok(Vec::new())
    311     }
    312 
    313     fn reserve_size(&self) -> usize {
    314         1024
    315     }
    316 
    317     async fn send_timestamp_request(
    318         &self,
    319         _message: &[u8],
    320     ) -> Option<crate::error::Result<Vec<u8>>> {
    321         Some(Ok(Vec::new()))
    322     }
    323 }
    324 
    325 /// Create a [`Signer`] instance that can be used for testing purposes using ps256 alg.
    326 ///
    327 /// # Returns
    328 ///
    329 /// Returns a boxed [`Signer`] instance.
    330 #[cfg(test)]
    331 pub(crate) fn temp_signer() -> Box<dyn Signer> {
    332     #[cfg(feature = "openssl_sign")]
    333     {
    334         #![allow(clippy::expect_used)]
    335         let sign_cert = include_bytes!("../../tests/fixtures/certs/ps256.pub").to_vec();
    336         let pem_key = include_bytes!("../../tests/fixtures/certs/ps256.pem").to_vec();
    337 
    338         let signer =
    339             RsaSigner::from_signcert_and_pkey(&sign_cert, &pem_key, SigningAlg::Ps256, None)
    340                 .expect("get_temp_signer");
    341 
    342         Box::new(signer)
    343     }
    344 
    345     // todo: the will be a RustTLS signer shortly
    346     #[cfg(not(feature = "openssl_sign"))]
    347     {
    348         Box::new(TestGoodSigner {})
    349     }
    350 }
    351 
    352 #[cfg(any(target_arch = "wasm32", feature = "openssl_sign"))]
    353 pub fn temp_async_signer() -> Box<dyn crate::signer::AsyncSigner> {
    354     #[cfg(feature = "openssl_sign")]
    355     {
    356         Box::new(AsyncSignerAdapter::new(SigningAlg::Es256))
    357     }
    358 
    359     #[cfg(target_arch = "wasm32")]
    360     {
    361         let sign_cert = include_str!("../../tests/fixtures/certs/es256.pub");
    362         let pem_key = include_str!("../../tests/fixtures/certs/es256.pem");
    363         let signer = WebCryptoSigner::new("es256", sign_cert, pem_key);
    364         Box::new(signer)
    365     }
    366 }
    367 
    368 /// Create a [`Signer`] instance for a specific algorithm that can be used for testing purposes.
    369 ///
    370 /// # Returns
    371 ///
    372 /// Returns a boxed [`Signer`] instance.
    373 ///
    374 /// # Panics
    375 ///
    376 /// Can panic if the certs cannot be read. (This function should only
    377 /// be used as part of testing infrastructure.)
    378 #[cfg(feature = "file_io")]
    379 pub fn temp_signer_with_alg(alg: SigningAlg) -> Box<dyn Signer> {
    380     #![allow(clippy::expect_used)]
    381     // sign and embed into the target file
    382     let mut sign_cert_path = fixture_path("certs");
    383     sign_cert_path.push(alg.to_string());
    384     sign_cert_path.set_extension("pub");
    385 
    386     let mut pem_key_path = fixture_path("certs");
    387     pem_key_path.push(alg.to_string());
    388     pem_key_path.set_extension("pem");
    389 
    390     create_signer::from_files(sign_cert_path.clone(), pem_key_path, alg, None)
    391         .expect("get_temp_signer_with_alg")
    392 }
    393 
    394 struct TempRemoteSigner {}
    395 
    396 #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
    397 #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
    398 impl crate::signer::RemoteSigner for TempRemoteSigner {
    399     async fn sign_remote(&self, claim_bytes: &[u8]) -> crate::error::Result<Vec<u8>> {
    400         #[cfg(feature = "openssl_sign")]
    401         {
    402             let signer =
    403                 crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256);
    404 
    405             // this would happen on some remote server
    406             crate::cose_sign::cose_sign_async(&signer, claim_bytes, self.reserve_size()).await
    407         }
    408         #[cfg(not(feature = "openssl_sign"))]
    409         {
    410             use std::io::{Seek, Write};
    411 
    412             let mut sign_bytes = std::io::Cursor::new(vec![0u8; self.reserve_size()]);
    413 
    414             sign_bytes.rewind()?;
    415             sign_bytes.write_all(claim_bytes)?;
    416 
    417             // fake sig
    418             Ok(sign_bytes.into_inner())
    419         }
    420     }
    421 
    422     fn reserve_size(&self) -> usize {
    423         10000
    424     }
    425 }
    426 
    427 #[cfg(target_arch = "wasm32")]
    428 struct WebCryptoSigner {
    429     signing_alg: SigningAlg,
    430     signing_alg_name: String,
    431     certs: Vec<Vec<u8>>,
    432     key: Vec<u8>,
    433 }
    434 
    435 #[cfg(target_arch = "wasm32")]
    436 impl WebCryptoSigner {
    437     pub fn new(alg: &str, cert: &str, key: &str) -> Self {
    438         static START_CERTIFICATE: &str = "-----BEGIN CERTIFICATE-----";
    439         static END_CERTIFICATE: &str = "-----END CERTIFICATE-----";
    440         static START_KEY: &str = "-----BEGIN PRIVATE KEY-----";
    441         static END_KEY: &str = "-----END PRIVATE KEY-----";
    442 
    443         let mut name = alg.to_owned().to_uppercase();
    444         name.insert(2, '-');
    445 
    446         let key = key
    447             .replace("\n", "")
    448             .replace(START_KEY, "")
    449             .replace(END_KEY, "");
    450         let key = crate::utils::base64::decode(&key).unwrap();
    451 
    452         let certs = cert
    453             .replace("\n", "")
    454             .replace(START_CERTIFICATE, "")
    455             .split(END_CERTIFICATE)
    456             .map(|x| crate::utils::base64::decode(x).unwrap())
    457             .collect();
    458 
    459         Self {
    460             signing_alg: alg.parse().unwrap(),
    461             signing_alg_name: name,
    462             certs,
    463             key,
    464         }
    465     }
    466 }
    467 
    468 #[cfg(target_arch = "wasm32")]
    469 #[async_trait::async_trait(?Send)]
    470 impl crate::signer::AsyncSigner for WebCryptoSigner {
    471     fn alg(&self) -> SigningAlg {
    472         self.signing_alg
    473     }
    474 
    475     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    476         Ok(self.certs.clone())
    477     }
    478 
    479     async fn sign(&self, claim_bytes: Vec<u8>) -> crate::error::Result<Vec<u8>> {
    480         use js_sys::{Array, Object, Reflect, Uint8Array};
    481         use wasm_bindgen_futures::JsFuture;
    482         use web_sys::CryptoKey;
    483 
    484         use crate::wasm::context::WindowOrWorker;
    485         let context = WindowOrWorker::new().unwrap();
    486         let crypto = context.subtle_crypto().unwrap();
    487 
    488         let mut data = claim_bytes.clone();
    489         let promise = crypto
    490             .digest_with_str_and_u8_array("SHA-256", &mut data)
    491             .unwrap();
    492         let result = JsFuture::from(promise).await.unwrap();
    493         let mut digest = Uint8Array::new(&result).to_vec();
    494 
    495         let key = Uint8Array::new_with_length(self.key.len() as u32);
    496         key.copy_from(&self.key);
    497         let usages = Array::new();
    498         usages.push(&"sign".into());
    499         let alg = Object::new();
    500         Reflect::set(&alg, &"name".into(), &"ECDSA".into()).unwrap();
    501         Reflect::set(&alg, &"namedCurve".into(), &"P-256".into()).unwrap();
    502 
    503         let promise = crypto
    504             .import_key_with_object("pkcs8", &key, &alg, true, &usages)
    505             .unwrap();
    506         let key: CryptoKey = JsFuture::from(promise).await.unwrap().into();
    507 
    508         let alg = Object::new();
    509         Reflect::set(&alg, &"name".into(), &"ECDSA".into()).unwrap();
    510         Reflect::set(&alg, &"hash".into(), &"SHA-256".into()).unwrap();
    511         let promise = crypto
    512             .sign_with_object_and_u8_array(&alg, &key, &mut digest)
    513             .unwrap();
    514         let result = JsFuture::from(promise).await.unwrap();
    515         Ok(Uint8Array::new(&result).to_vec())
    516     }
    517 
    518     fn reserve_size(&self) -> usize {
    519         10000
    520     }
    521 
    522     async fn send_timestamp_request(&self, _: &[u8]) -> Option<Result<Vec<u8>>> {
    523         None
    524     }
    525 }
    526 
    527 /// Create a [`RemoteSigner`] instance that can be used for testing purposes.
    528 ///
    529 /// # Returns
    530 ///
    531 /// Returns a boxed [`RemoteSigner`] instance.
    532 pub fn temp_remote_signer() -> Box<dyn RemoteSigner> {
    533     Box::new(TempRemoteSigner {})
    534 }
    535 
    536 /// Create an AsyncSigner that acts as a RemoteSigner
    537 struct TempAsyncRemoteSigner {
    538     signer: TempRemoteSigner,
    539 }
    540 
    541 #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
    542 #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
    543 impl crate::signer::AsyncSigner for TempAsyncRemoteSigner {
    544     // this will not be called but requires an implementation
    545     async fn sign(&self, claim_bytes: Vec<u8>) -> Result<Vec<u8>> {
    546         #[cfg(feature = "openssl_sign")]
    547         {
    548             let signer =
    549                 crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256);
    550 
    551             // this would happen on some remote server
    552             crate::cose_sign::cose_sign_async(&signer, &claim_bytes, self.reserve_size()).await
    553         }
    554         #[cfg(not(feature = "openssl_sign"))]
    555         {
    556             use std::io::{Seek, Write};
    557 
    558             let mut sign_bytes = std::io::Cursor::new(vec![0u8; self.reserve_size()]);
    559 
    560             sign_bytes.rewind()?;
    561             sign_bytes.write_all(&claim_bytes)?;
    562 
    563             // fake sig
    564             Ok(sign_bytes.into_inner())
    565         }
    566     }
    567 
    568     // signer will return a COSE structure
    569     fn direct_cose_handling(&self) -> bool {
    570         true
    571     }
    572 
    573     fn alg(&self) -> SigningAlg {
    574         SigningAlg::Ps256
    575     }
    576 
    577     fn certs(&self) -> Result<Vec<Vec<u8>>> {
    578         Ok(Vec::new())
    579     }
    580 
    581     fn reserve_size(&self) -> usize {
    582         10000
    583     }
    584 
    585     async fn send_timestamp_request(
    586         &self,
    587         _message: &[u8],
    588     ) -> Option<crate::error::Result<Vec<u8>>> {
    589         Some(Ok(Vec::new()))
    590     }
    591 }
    592 
    593 /// Create a [`AsyncSigner`] that does it's own COSE handling for testing.
    594 ///
    595 /// # Returns
    596 ///
    597 /// Returns a boxed [`RemoteSigner`] instance.
    598 pub fn temp_async_remote_signer() -> Box<dyn crate::signer::AsyncSigner> {
    599     Box::new(TempAsyncRemoteSigner {
    600         signer: TempRemoteSigner {},
    601     })
    602 }
    603 
    604 #[test]
    605 fn test_create_test_store() {
    606     #[allow(clippy::expect_used)]
    607     let store = create_test_store().expect("create test store");
    608 
    609     assert_eq!(store.claims().len(), 1);
    610 }