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

commit 2cb7fd87f7dd223bfcf5760a556abad78f4a93a0
parent 40fc8df617d99f7ffb76e61333856dcbafe1f882
Author: Eric Scouten <scouten@adobe.com>
Date:   Tue, 19 Jul 2022 09:09:27 -0700

(MINOR) Introduce a new `SigningAlg` enum (#76)


Diffstat:
Mmake_test_images/src/make_test_images.rs | 2++
Msdk/examples/client/client.rs | 4++--
Msdk/src/cose_sign.rs | 158++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msdk/src/cose_validator.rs | 185+++++++++++++++++++++++++++++++++++++------------------------------------------
Msdk/src/create_signer.rs | 65++++++++++++++++++++++-------------------------------------------
Msdk/src/lib.rs | 8++++++--
Msdk/src/manifest.rs | 5+++--
Msdk/src/manifest_store_report.rs | 2+-
Msdk/src/openssl/ec_signer.rs | 39++++++++++++++++++++-------------------
Msdk/src/openssl/ec_validator.rs | 48+++++++++++++++++++++++-------------------------
Msdk/src/openssl/ed_signer.rs | 22+++++++++++-----------
Msdk/src/openssl/ed_validator.rs | 20+++++++++-----------
Msdk/src/openssl/rsa_signer.rs | 60++++++++++++++++++++++++++++++------------------------------
Msdk/src/openssl/rsa_validator.rs | 124+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msdk/src/openssl/temp_signer.rs | 36++++++++++++++++++------------------
Msdk/src/openssl/temp_signer_async.rs | 26++++++++++++--------------
Msdk/src/signer.rs | 10+++++-----
Asdk/src/signing_alg.rs | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msdk/src/store.rs | 12++++++------
Msdk/src/time_stamp.rs | 28+++++++++++++++-------------
Msdk/src/utils/test.rs | 12+++++-------
Msdk/src/validator.rs | 42+++++++++++++++---------------------------
Msdk/src/wasm/webcrypto_validator.rs | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msdk/tests/integration.rs | 4++--
24 files changed, 650 insertions(+), 505 deletions(-)

diff --git a/make_test_images/src/make_test_images.rs b/make_test_images/src/make_test_images.rs @@ -15,6 +15,7 @@ use c2pa::{ assertions::{c2pa_action, Action, Actions, CreativeWork, SchemaDotOrgPerson}, create_signer, jumbf_io, Error, Ingredient, IngredientOptions, Manifest, ManifestStore, Signer, + SigningAlg, }; use anyhow::{Context, Result}; @@ -36,6 +37,7 @@ fn get_signer_with_alg(alg: &str) -> c2pa::Result<Box<dyn Signer>> { signcert_path.push(format!("../sdk/tests/fixtures/certs/{}.pub", alg)); let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pkey_path.push(format!("../sdk/tests/fixtures/certs/{}.pem", alg)); + let alg: SigningAlg = alg.parse().map_err(|_| c2pa::Error::UnsupportedType)?; create_signer::from_files(signcert_path, pkey_path, alg, None) } diff --git a/sdk/examples/client/client.rs b/sdk/examples/client/client.rs @@ -17,7 +17,7 @@ use anyhow::Result; use c2pa::{ assertions::{c2pa_action, labels, Action, Actions, CreativeWork, SchemaDotOrgPerson}, - create_signer, Ingredient, Manifest, ManifestStore, + create_signer, Ingredient, Manifest, ManifestStore, SigningAlg, }; use std::path::PathBuf; @@ -111,7 +111,7 @@ pub fn main() -> Result<()> { // sign and embed into the target file let signcert_path = "../sdk/tests/fixtures/certs.ps256.pem"; let pkey_path = "../sdk/tests/fixtures/certs.ps256.pub"; - let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; + let signer = create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None)?; manifest.embed(&source, &dest, &*signer)?; diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs @@ -25,79 +25,8 @@ use crate::{ cose_validator::verify_cose, status_tracker::OneShotStatusTracker, time_stamp::{cose_timestamp_countersign, make_cose_timestamp}, - Error, Result, Signer, + Error, Result, Signer, SigningAlg, }; -fn build_unprotected_header( - data: &[u8], - alg: &str, - certs: Vec<Vec<u8>>, - ta_url: Option<String>, - ocsp_val: Option<Vec<u8>>, -) -> Result<(Header, Header)> { - let alg_id = match alg { - "ps256" => HeaderBuilder::new() - .algorithm(iana::Algorithm::PS256) - .build(), - "ps384" => HeaderBuilder::new() - .algorithm(iana::Algorithm::PS384) - .build(), - "ps512" => HeaderBuilder::new() - .algorithm(iana::Algorithm::PS512) - .build(), - "es256" => HeaderBuilder::new() - .algorithm(iana::Algorithm::ES256) - .build(), - "es384" => HeaderBuilder::new() - .algorithm(iana::Algorithm::ES384) - .build(), - "es512" => HeaderBuilder::new() - .algorithm(iana::Algorithm::ES512) - .build(), - "ed25519" => HeaderBuilder::new() - .algorithm(iana::Algorithm::EdDSA) - .build(), - _ => return Err(Error::UnsupportedType), - }; - - let sc_der_array_or_bytes = match certs.len() { - 1 => Value::Bytes(certs[0].clone()), // single cert - _ => { - let mut sc_der_array: Vec<Value> = Vec::new(); - for cert in certs { - sc_der_array.push(Value::Bytes(cert)); - } - Value::Array(sc_der_array) // provide vec of certs when required - } - }; - - let mut unprotected = if let Some(url) = ta_url { - let cts = cose_timestamp_countersign(data, alg, &url)?; - let sigtst_vec = serde_cbor::to_vec(&make_cose_timestamp(&cts))?; - let sigtst_cbor = serde_cbor::from_slice(&sigtst_vec)?; - - HeaderBuilder::new() - .text_value("x5chain".to_string(), sc_der_array_or_bytes) - .text_value("sigTst".to_string(), sigtst_cbor) - } else { - HeaderBuilder::new().text_value("x5chain".to_string(), sc_der_array_or_bytes) - }; - - // set the ocsp responder response if available - if let Some(ocsp) = ocsp_val { - let mut ocsp_vec: Vec<Value> = Vec::new(); - let mut r_vals: Vec<(Value, Value)> = vec![]; - - ocsp_vec.push(Value::Bytes(ocsp)); - r_vals.push((Value::Text("ocspVals".to_string()), Value::Array(ocsp_vec))); - - unprotected = unprotected.text_value("rVals".to_string(), Value::Map(r_vals)); - } - - // build complete header - let unprotected_header = unprotected.build(); - - Ok((alg_id, unprotected_header)) -} /// Generate a COSE signature for a block of bytes which must be a valid C2PA /// claim structure. @@ -117,15 +46,15 @@ fn build_unprotected_header( /// 3. Verifies that the signature is valid COSE. Will respond with an error /// if unable to validate. pub fn sign_claim(claim_bytes: &[u8], signer: &dyn Signer, box_size: usize) -> Result<Vec<u8>> { - // must be a valid Claim + // Must be a valid claim. let label = "dummy_label"; let _claim = Claim::from_data(label, claim_bytes)?; - // generate and verify a CoseSign1 representation of the data + // Generate and verify a CoseSign1 representation of the data. cose_sign(signer, claim_bytes, box_size).and_then(|sig| { // Sanity check: Ensure that this signature is valid. - let mut cose_log = OneShotStatusTracker::new(); + match verify_cose(&sig, claim_bytes, b"", false, &mut cose_log) { Ok(_) => Ok(sig), Err(err) => Err(err), @@ -158,12 +87,12 @@ pub(crate) fn cose_sign(signer: &dyn Signer, data: &[u8], box_size: usize) -> Re string. */ - let alg = signer.alg().ok_or(Error::UnsupportedType)?; + let alg = signer.alg(); // build complete header let (alg_id, unprotected_header) = build_unprotected_header( data, - &alg, + alg, signer.certs()?, signer.time_authority_url(), signer.ocsp_val(), @@ -216,12 +145,12 @@ pub async fn cose_sign_async( string. */ - let alg = signer.alg().ok_or(Error::UnsupportedType)?; + let alg = signer.alg(); // build complete header let (alg_id, unprotected_header) = build_unprotected_header( data, - &alg, + alg, signer.certs()?, signer.time_authority_url(), signer.ocsp_val(), @@ -254,6 +183,77 @@ pub async fn cose_sign_async( Ok(c2pa_sig_data) } +fn build_unprotected_header( + data: &[u8], + alg: SigningAlg, + certs: Vec<Vec<u8>>, + ta_url: Option<String>, + ocsp_val: Option<Vec<u8>>, +) -> Result<(Header, Header)> { + let alg_id = match alg { + SigningAlg::Ps256 => HeaderBuilder::new() + .algorithm(iana::Algorithm::PS256) + .build(), + SigningAlg::Ps384 => HeaderBuilder::new() + .algorithm(iana::Algorithm::PS384) + .build(), + SigningAlg::Ps512 => HeaderBuilder::new() + .algorithm(iana::Algorithm::PS512) + .build(), + SigningAlg::Es256 => HeaderBuilder::new() + .algorithm(iana::Algorithm::ES256) + .build(), + SigningAlg::Es384 => HeaderBuilder::new() + .algorithm(iana::Algorithm::ES384) + .build(), + SigningAlg::Es512 => HeaderBuilder::new() + .algorithm(iana::Algorithm::ES512) + .build(), + SigningAlg::Ed25519 => HeaderBuilder::new() + .algorithm(iana::Algorithm::EdDSA) + .build(), + }; + + let sc_der_array_or_bytes = match certs.len() { + 1 => Value::Bytes(certs[0].clone()), // single cert + _ => { + let mut sc_der_array: Vec<Value> = Vec::new(); + for cert in certs { + sc_der_array.push(Value::Bytes(cert)); + } + Value::Array(sc_der_array) // provide vec of certs when required + } + }; + + let mut unprotected = if let Some(url) = ta_url { + let cts = cose_timestamp_countersign(data, alg, &url)?; + let sigtst_vec = serde_cbor::to_vec(&make_cose_timestamp(&cts))?; + let sigtst_cbor = serde_cbor::from_slice(&sigtst_vec)?; + + HeaderBuilder::new() + .text_value("x5chain".to_string(), sc_der_array_or_bytes) + .text_value("sigTst".to_string(), sigtst_cbor) + } else { + HeaderBuilder::new().text_value("x5chain".to_string(), sc_der_array_or_bytes) + }; + + // set the ocsp responder response if available + if let Some(ocsp) = ocsp_val { + let mut ocsp_vec: Vec<Value> = Vec::new(); + let mut r_vals: Vec<(Value, Value)> = vec![]; + + ocsp_vec.push(Value::Bytes(ocsp)); + r_vals.push((Value::Text("ocspVals".to_string()), Value::Array(ocsp_vec))); + + unprotected = unprotected.text_value("rVals".to_string(), Value::Map(r_vals)); + } + + // build complete header + let unprotected_header = unprotected.build(); + + Ok((alg_id, unprotected_header)) +} + const PAD: &str = "pad"; const PAD2: &str = "pad2"; const PAD_OFFSET: usize = 7; diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs @@ -11,30 +11,31 @@ // specific language governing permissions and limitations under // each license. -use crate::error::{Error, Result}; -use crate::status_tracker::{log_item, StatusTracker}; -use crate::time_stamp::gt_to_datetime; -use crate::validation_status; -#[cfg(not(target_arch = "wasm32"))] -use crate::validator::get_validator; -#[cfg(not(target_arch = "wasm32"))] -use crate::validator::CoseValidator; -use crate::validator::ValidationInfo; - -#[cfg(target_arch = "wasm32")] -use crate::wasm::webcrypto_validator::validate_async; +use std::str::FromStr; -use crate::asn1::rfc3161::TstInfo; use ciborium::value::Value; use conv::*; use coset::{sig_structure_data, Label, TaggedCborSerializable}; -use std::str::FromStr; +use x509_parser::{ + der_parser::ber::parse_ber_sequence, der_parser::oid, oid_registry::Oid, prelude::*, +}; + +use crate::{ + asn1::rfc3161::TstInfo, + error::{Error, Result}, + status_tracker::{log_item, StatusTracker}, + time_stamp::gt_to_datetime, + validation_status, + validator::ValidationInfo, + SigningAlg, +}; + +#[cfg(target_arch = "wasm32")] +use crate::wasm::webcrypto_validator::validate_async; -use x509_parser::der_parser::ber::parse_ber_sequence; -use x509_parser::der_parser::oid; -use x509_parser::oid_registry::Oid; -use x509_parser::prelude::*; +#[cfg(not(target_arch = "wasm32"))] +use crate::validator::{get_validator, CoseValidator}; const RSA_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .1); const EC_PUBLICKEY_OID: Oid<'static> = oid!(1.2.840 .10045 .2 .1); @@ -93,7 +94,7 @@ fn get_cose_sign1( } } fn check_cert( - _alg: &str, + _alg: SigningAlg, ca_der_bytes: &[u8], validation_log: &mut impl StatusTracker, _tst_info_opt: Option<&TstInfo>, @@ -492,50 +493,46 @@ fn check_cert( } } -pub(crate) fn get_validator_str(cs1: &coset::CoseSign1) -> Result<String> { +pub(crate) fn get_signing_alg(cs1: &coset::CoseSign1) -> Result<SigningAlg> { // find the supported handler for the algorithm - let validator_str = match cs1.protected.header.alg { + match cs1.protected.header.alg { Some(ref alg) => { - let alg_str = match alg { + match alg { coset::RegisteredLabelWithPrivate::PrivateUse(a) => match a { - -39 => "ps512", - -38 => "ps384", - -37 => "ps256", - -36 => "es512", - -35 => "es384", - -7 => "es256", + -39 => Ok(SigningAlg::Ps512), + -38 => Ok(SigningAlg::Ps384), + -37 => Ok(SigningAlg::Ps256), + -36 => Ok(SigningAlg::Es512), + -35 => Ok(SigningAlg::Es384), + -7 => Ok(SigningAlg::Es256), // todo: deprecated figure out lecacy support for RS signatures - -259 => "rs512", - -258 => "rs384", - -257 => "rs256", - - -8 => "ed25519", - _ => "unknown", + // -259 => "rs512", + // -258 => "rs384", + // -257 => "rs256", + -8 => Ok(SigningAlg::Ed25519), + _ => Err(Error::CoseSignatureAlgorithmNotSupported), }, coset::RegisteredLabelWithPrivate::Assigned(a) => match a { - coset::iana::Algorithm::PS512 => "ps512", - coset::iana::Algorithm::PS384 => "ps384", - coset::iana::Algorithm::PS256 => "ps256", - coset::iana::Algorithm::ES512 => "es512", - coset::iana::Algorithm::ES384 => "es384", - coset::iana::Algorithm::ES256 => "es256", + coset::iana::Algorithm::PS512 => Ok(SigningAlg::Ps512), + coset::iana::Algorithm::PS384 => Ok(SigningAlg::Ps384), + coset::iana::Algorithm::PS256 => Ok(SigningAlg::Ps256), + coset::iana::Algorithm::ES512 => Ok(SigningAlg::Es512), + coset::iana::Algorithm::ES384 => Ok(SigningAlg::Es384), + coset::iana::Algorithm::ES256 => Ok(SigningAlg::Es256), // todo: deprecated figure out lecacy support for RS signatures - coset::iana::Algorithm::RS512 => "rs512", - coset::iana::Algorithm::RS384 => "rs384", - coset::iana::Algorithm::RS256 => "rs256", - coset::iana::Algorithm::EdDSA => "ed25519", - _ => "unknown", + // coset::iana::Algorithm::RS512 => "rs512", + // coset::iana::Algorithm::RS384 => "rs384", + // coset::iana::Algorithm::RS256 => "rs256", + coset::iana::Algorithm::EdDSA => Ok(SigningAlg::Ed25519), + _ => Err(Error::CoseSignatureAlgorithmNotSupported), }, - coset::RegisteredLabelWithPrivate::Text(a) => a, - }; - - Some(alg_str.to_owned()) + coset::RegisteredLabelWithPrivate::Text(a) => a + .parse() + .map_err(|_| Error::CoseSignatureAlgorithmNotSupported), + } } - None => None, + None => Err(Error::CoseSignatureAlgorithmNotSupported), } - .ok_or(Error::CoseSignatureAlgorithmNotSupported)?; - - Ok(validator_str) } fn get_sign_cert(sign1: &coset::CoseSign1) -> Result<Vec<u8>> { @@ -638,9 +635,9 @@ fn get_timestamp_info(sign1: &coset::CoseSign1, data: &[u8]) -> Result<TstInfo> } }) { - let alg = get_validator_str(sign1)?; + let alg = get_signing_alg(sign1)?; let time_cbor = serde_cbor::to_vec(t)?; - let tst_infos = crate::time_stamp::cose_sigtst_to_tstinfos(&time_cbor, data, &alg)?; + let tst_infos = crate::time_stamp::cose_sigtst_to_tstinfos(&time_cbor, data, alg)?; // there should only be one but consider handling more in the future since it is technically ok if !tst_infos.is_empty() { @@ -674,8 +671,8 @@ pub async fn verify_cose_async( ) -> Result<ValidationInfo> { let mut sign1 = get_cose_sign1(&cose_bytes, &data, validation_log)?; - let validator_str = match get_validator_str(&sign1) { - Ok(s) => s, + let alg = match get_signing_alg(&sign1) { + Ok(a) => a, Err(_) => { let log_item = log_item!( "Cose_Sign1", @@ -701,15 +698,11 @@ pub async fn verify_cose_async( if !signature_only { // verify certs match get_timestamp_info(&sign1, &data) { - Ok(tst_info) => { - check_cert(&validator_str, &der_bytes, validation_log, Some(&tst_info))? - } + Ok(tst_info) => check_cert(alg, &der_bytes, validation_log, Some(&tst_info))?, Err(e) => { // log timestamp errors match e { - Error::NotFound => { - check_cert(&validator_str, &der_bytes, validation_log, None)? - } + Error::NotFound => check_cert(alg, &der_bytes, validation_log, None)?, Error::CoseTimeStampMismatch => { let log_item = log_item!( "Cose_Sign1", @@ -754,12 +747,10 @@ pub async fn verify_cose_async( sign1.payload.as_ref().unwrap_or(&vec![]), ); // get "to be signed" bytes - if let Ok(issuer) = - validate_with_cert_async(&validator_str, &sign1.signature, &tbs, &der_bytes).await - { + if let Ok(issuer) = validate_with_cert_async(alg, &sign1.signature, &tbs, &der_bytes).await { result.issuer_org = Some(issuer); result.validated = true; - result.alg = validator_str.to_owned(); + result.alg = Some(alg); // parse the temp time for now util we have TA result.date = get_signing_time(&sign1, &data, validation_log); @@ -775,7 +766,7 @@ pub fn get_signing_info( ) -> ValidationInfo { let mut date = None; let mut issuer_org = None; - let mut alg = "".to_string(); + let mut alg: Option<SigningAlg> = None; let _ = get_cose_sign1(cose_bytes, data, validation_log).and_then(|sign1| { // get the public key der @@ -784,8 +775,8 @@ pub fn get_signing_info( let _ = X509Certificate::from_der(&der_bytes).map(|(_rem, signcert)| { date = get_signing_time(&sign1, data, validation_log); issuer_org = extract_subject_from_cert(&signcert).ok(); - if let Ok(a) = get_validator_str(&sign1) { - alg = a; + if let Ok(a) = get_signing_alg(&sign1) { + alg = Some(a); } (_rem, signcert) @@ -817,8 +808,8 @@ pub fn verify_cose( ) -> Result<ValidationInfo> { let sign1 = get_cose_sign1(cose_bytes, data, validation_log)?; - let validator_str = match get_validator_str(&sign1) { - Ok(s) => s, + let alg = match get_signing_alg(&sign1) { + Ok(a) => a, Err(_) => { let log_item = log_item!( "Cose_Sign1", @@ -834,8 +825,7 @@ pub fn verify_cose( } }; - let validator = - get_validator(&validator_str).ok_or(Error::CoseSignatureAlgorithmNotSupported)?; + let validator = get_validator(alg); // build result structure let mut result = ValidationInfo::default(); @@ -849,11 +839,11 @@ pub fn verify_cose( if !signature_only { // verify certs match get_timestamp_info(&sign1, data) { - Ok(tst_info) => check_cert(&validator_str, der_bytes, validation_log, Some(&tst_info))?, + Ok(tst_info) => check_cert(alg, der_bytes, validation_log, Some(&tst_info))?, Err(e) => { // log timestamp errors match e { - Error::NotFound => check_cert(&validator_str, der_bytes, validation_log, None)?, + Error::NotFound => check_cert(alg, der_bytes, validation_log, None)?, Error::CoseTimeStampMismatch => { let log_item = log_item!( "Cose_Sign1", @@ -890,7 +880,7 @@ pub fn verify_cose( if let Ok(issuer) = validate_with_cert(validator, sig, verify_data, der_bytes) { result.issuer_org = Some(issuer); result.validated = true; - result.alg = validator_str.to_string(); + result.alg = Some(alg); // parse the temp time for now util we have TA result.date = get_signing_time(&sign1, data, validation_log); @@ -935,7 +925,7 @@ fn validate_with_cert( #[cfg(target_arch = "wasm32")] async fn validate_with_cert_async( - validator_str: &str, + signing_alg: SigningAlg, sig: &[u8], data: &[u8], der_bytes: &[u8], @@ -945,7 +935,7 @@ async fn validate_with_cert_async( let pk = signcert.public_key(); let pk_der = pk.raw; - if validate_async(validator_str, sig, data, pk_der).await? { + if validate_async(signing_alg, sig, data, pk_der).await? { Ok(extract_subject_from_cert(&signcert)?) } else { Err(Error::CoseSignature) @@ -954,7 +944,7 @@ async fn validate_with_cert_async( #[cfg(not(target_arch = "wasm32"))] async fn validate_with_cert_async( - validator_str: &str, + signing_alg: SigningAlg, sig: &[u8], data: &[u8], der_bytes: &[u8], @@ -965,8 +955,7 @@ async fn validate_with_cert_async( let pk = signcert.public_key(); let pk_der = pk.raw; - let validator = - get_validator(validator_str).ok_or(Error::CoseSignatureAlgorithmNotSupported)?; + let validator = get_validator(signing_alg); if validator.validate(sig, data, pk_der)? { Ok(extract_subject_from_cert(&signcert)?) @@ -980,11 +969,11 @@ async fn validate_with_cert_async( pub mod tests { #![allow(clippy::unwrap_used)] - use sha2::digest::generic_array::sequence::Shorten; + use super::*; - use crate::status_tracker::DetailedStatusTracker; + use sha2::digest::generic_array::sequence::Shorten; - use super::*; + use crate::{status_tracker::DetailedStatusTracker, SigningAlg}; #[test] #[cfg(feature = "file_io")] @@ -998,7 +987,7 @@ pub mod tests { if let Ok(signcert) = openssl::x509::X509::from_pem(&expired_cert) { let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert("ps256", &der_bytes, &mut validation_log, None).is_err()); + assert!(check_cert(SigningAlg::Ps256, &der_bytes, &mut validation_log, None).is_err()); assert!(!validation_log.get_log().is_empty()); @@ -1011,7 +1000,7 @@ pub mod tests { #[test] fn test_verify_cose_good() { - let validator = get_validator("ps256").unwrap(); + let validator = get_validator(SigningAlg::Ps256); let sig_bytes = include_bytes!("../tests/fixtures/sig_ps256.data"); let data_bytes = include_bytes!("../tests/fixtures/data_ps256.data"); @@ -1025,7 +1014,7 @@ pub mod tests { #[test] fn test_verify_ec_good() { // EC signatures - let mut validator = get_validator("es384").unwrap(); + let mut validator = get_validator(SigningAlg::Es384); let sig_es384_bytes = include_bytes!("../tests/fixtures/sig_es384.data"); let data_es384_bytes = include_bytes!("../tests/fixtures/data_es384.data"); @@ -1035,7 +1024,7 @@ pub mod tests { .validate(sig_es384_bytes, data_es384_bytes, key_es384_bytes) .unwrap()); - validator = get_validator("es512").unwrap(); + validator = get_validator(SigningAlg::Es512); let sig_es512_bytes = include_bytes!("../tests/fixtures/sig_es512.data"); let data_es512_bytes = include_bytes!("../tests/fixtures/data_es512.data"); @@ -1048,7 +1037,7 @@ pub mod tests { #[test] fn test_verify_cose_bad() { - let validator = get_validator("ps256").unwrap(); + let validator = get_validator(SigningAlg::Ps256); let sig_bytes = include_bytes!("../tests/fixtures/sig_ps256.data"); let data_bytes = include_bytes!("../tests/fixtures/data_ps256.data"); @@ -1074,36 +1063,36 @@ pub mod tests { let mut validation_log = DetailedStatusTracker::new(); - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es256", None); + let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); let es256_cert = std::fs::read(&cert_path).unwrap(); - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es384", None); + let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); let es384_cert = std::fs::read(&cert_path).unwrap(); - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es512", None); + let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let es512_cert = std::fs::read(&cert_path).unwrap(); - let (_, cert_path) = temp_signer::get_rsa_signer(&cert_dir, "ps256", None); + let (_, cert_path) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); let rsa_pss256_cert = std::fs::read(&cert_path).unwrap(); if let Ok(signcert) = openssl::x509::X509::from_pem(&es256_cert) { let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert("es256", &der_bytes, &mut validation_log, None).is_ok()); + assert!(check_cert(SigningAlg::Es256, &der_bytes, &mut validation_log, None).is_ok()); } if let Ok(signcert) = openssl::x509::X509::from_pem(&es384_cert) { let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert("es384", &der_bytes, &mut validation_log, None).is_ok()); + assert!(check_cert(SigningAlg::Es384, &der_bytes, &mut validation_log, None).is_ok()); } if let Ok(signcert) = openssl::x509::X509::from_pem(&es512_cert) { let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert("es512", &der_bytes, &mut validation_log, None).is_ok()); + assert!(check_cert(SigningAlg::Es512, &der_bytes, &mut validation_log, None).is_ok()); } if let Ok(signcert) = openssl::x509::X509::from_pem(&rsa_pss256_cert) { let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert("ps256", &der_bytes, &mut validation_log, None).is_ok()); + assert!(check_cert(SigningAlg::Ps256, &der_bytes, &mut validation_log, None).is_ok()); } } } diff --git a/sdk/src/create_signer.rs b/sdk/src/create_signer.rs @@ -19,10 +19,10 @@ use std::path::Path; use crate::{ - error::{Error, Result}, + error::Result, openssl::{EcSigner, EdSigner, RsaSigner}, signer::ConfigurableSigner, - Signer, + Signer, SigningAlg, }; /// Creates a [`Signer`] instance using signing certificate and private key @@ -35,36 +35,24 @@ use crate::{ /// /// * `signcert` - Signing certificate /// * `pkey` - Private key -/// * `alg` - Format for signing. Must be one of the supported -/// formats (`rs256`, `rs384`, `rs512`, `ps256`, `ps384`, `ps512`, -/// `es256`, `es384`, `es512`, or `ed25519`). +/// * `alg` - Format for signing /// * `tsa_url` - Optional URL for a timestamp authority pub fn from_keys( signcert: &[u8], pkey: &[u8], - alg: &str, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Box<dyn Signer>> { Ok(match alg { - "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, - )?), - "es256" | "es384" | "es512" => Box::new(EcSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new( + RsaSigner::from_signcert_and_pkey(signcert, pkey, alg, tsa_url)?, + ), + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( + EcSigner::from_signcert_and_pkey(signcert, pkey, alg, tsa_url)?, + ), + SigningAlg::Ed25519 => Box::new(EdSigner::from_signcert_and_pkey( + signcert, pkey, alg, tsa_url, )?), - "ed25519" => Box::new(EdSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, - )?), - _ => return Err(Error::BadParam(alg.to_owned())), }) } @@ -75,35 +63,26 @@ pub fn from_keys( /// /// * `signcert_path` - Path to the signing certificate file /// * `pkey_path` - Path to the private key file -/// * `alg` - Format for signing. Must be one of the supported -/// formats (`rs256`, `rs384`, `rs512`, `ps256`, `ps384`, `ps512`, -/// `es256`, `es384`, `es512`, or `ed25519`). +/// * `alg` - Format for signing /// * `tsa_url` - Optional URL for a timestamp authority pub fn from_files<P: AsRef<Path>>( signcert_path: P, pkey_path: P, - alg: &str, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Box<dyn Signer>> { Ok(match alg { - "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_files( - &signcert_path, - &pkey_path, - alg.to_owned(), - tsa_url, - )?), - "es256" | "es384" | "es512" => Box::new(EcSigner::from_files( - &signcert_path, - &pkey_path, - alg.to_owned(), - tsa_url, - )?), - "ed25519" => Box::new(EdSigner::from_files( + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new( + RsaSigner::from_files(&signcert_path, &pkey_path, alg, tsa_url)?, + ), + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( + EcSigner::from_files(&signcert_path, &pkey_path, alg, tsa_url)?, + ), + SigningAlg::Ed25519 => Box::new(EdSigner::from_files( &signcert_path, &pkey_path, - alg.to_owned(), + alg, tsa_url, )?), - _ => return Err(Error::BadParam(alg.to_owned())), }) } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs @@ -47,7 +47,8 @@ //! use c2pa::{ //! assertions::User, //! create_signer, -//! Manifest +//! Manifest, +//! SigningAlg, //! }; //! //! use std::path::PathBuf; @@ -64,7 +65,7 @@ //! // Create a ps256 signer using certs and key files //! let signcert_path = "tests/fixtures/certs/ps256.pub"; //! let pkey_path = "tests/fixtures/certs/ps256.pem"; -//! let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; +//! let signer = create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None)?; //! //! // embed a manifest using the signer //! manifest.embed(&source, &dest, &*signer)?; @@ -97,6 +98,9 @@ pub use manifest_store::ManifestStore; mod manifest_store_report; pub use manifest_store_report::ManifestStoreReport; +mod signing_alg; +pub use signing_alg::{SigningAlg, UnknownAlgorithmError}; + #[cfg(feature = "file_io")] pub(crate) mod ocsp_utils; #[cfg(feature = "file_io")] diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -568,7 +568,8 @@ impl Manifest { /// use c2pa::{ /// assertions::User, /// create_signer, - /// Manifest + /// Manifest, + /// SigningAlg, /// }; /// # fn main() -> Result<()> { /// let mut manifest = Manifest::new("my_app".to_owned()); @@ -580,7 +581,7 @@ impl Manifest { /// // Create a PS256 signer using certs and public key files. /// let signcert_path = "tests/fixtures/certs/ps256.pub"; /// let pkey_path = "tests/fixtures/certs/ps256.pem"; - /// let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; + /// let signer = create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None)?; /// /// // Embed a manifest using the signer. /// manifest.embed(&source, &dest, &*signer)?; diff --git a/sdk/src/manifest_store_report.rs b/sdk/src/manifest_store_report.rs @@ -150,7 +150,7 @@ impl ManifestReport { let signature = match claim.signature_info() { Some(info) => SignatureReport { - alg: info.alg, + alg: info.alg.map_or_else(String::new, |a| a.to_string()), issuer: info.issuer_org, time: info.date.map(|d| d.to_rfc3339()), }, diff --git a/sdk/src/openssl/ec_signer.rs b/sdk/src/openssl/ec_signer.rs @@ -16,7 +16,7 @@ use std::{fs, path::Path}; use crate::{ error::{wrap_io_err, wrap_openssl_err, Error, Result}, signer::ConfigurableSigner, - Signer, + Signer, SigningAlg, }; use openssl::hash::MessageDigest; use openssl::pkey::PKey; @@ -37,7 +37,7 @@ pub struct EcSigner { certs_size: usize, timestamp_size: usize, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, } @@ -45,7 +45,7 @@ impl ConfigurableSigner for EcSigner { fn from_files<P: AsRef<Path>>( signcert_path: P, pkey_path: P, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let signcert = fs::read(signcert_path).map_err(wrap_io_err)?; @@ -57,7 +57,7 @@ impl ConfigurableSigner for EcSigner { fn from_signcert_and_pkey( signcert: &[u8], pkey: &[u8], - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let certs_size = signcert.len(); @@ -86,21 +86,21 @@ impl Signer for EcSigner { fn sign(&self, data: &[u8]) -> Result<Vec<u8>> { let key = PKey::from_ec_key(self.pkey.clone()).map_err(wrap_openssl_err)?; - let mut signer = match self.alg.as_ref() { - "es256" => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?, - "es384" => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?, - "es512" => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?, + let mut signer = match self.alg { + SigningAlg::Es256 => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?, + SigningAlg::Es384 => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?, + SigningAlg::Es512 => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?, _ => return Err(Error::UnsupportedType), }; signer.update(data).map_err(wrap_openssl_err)?; let der_sig = signer.sign_to_vec().map_err(wrap_openssl_err)?; - der_to_p1363(&der_sig, &self.alg) + der_to_p1363(&der_sig, self.alg) } - fn alg(&self) -> Option<String> { - Some(self.alg.to_owned()) + fn alg(&self) -> SigningAlg { + self.alg } fn certs(&self) -> Result<Vec<Vec<u8>>> { @@ -145,7 +145,7 @@ fn parse_ec_sig(data: &[u8]) -> der_parser::error::BerResult<ECSigComps> { })(data) } -fn der_to_p1363(data: &[u8], alg: &str) -> Result<Vec<u8>> { +fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result<Vec<u8>> { // P1363 format: r | s let (_, p) = parse_ec_sig(data).map_err(|_err| Error::InvalidEcdsaSignature)?; @@ -154,9 +154,9 @@ fn der_to_p1363(data: &[u8], alg: &str) -> Result<Vec<u8>> { let mut s = extfmt::Hexlify(p.s).to_string(); let sig_len: usize = match alg { - "es256" => 64, - "es384" => 96, - "es512" => 132, + SigningAlg::Es256 => 64, + SigningAlg::Es384 => 96, + SigningAlg::Es512 => 132, _ => return Err(Error::UnsupportedType), }; @@ -208,12 +208,13 @@ mod tests { use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path}; + use crate::{openssl::temp_signer, utils::test::fixture_path, SigningAlg}; + #[test] fn es256_signer() { let cert_dir = fixture_path("certs"); - let (signer, _) = temp_signer::get_ec_signer(&cert_dir, "es256", None); + let (signer, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -228,7 +229,7 @@ mod tests { fn es384_signer() { let cert_dir = fixture_path("certs"); - let (signer, _) = temp_signer::get_ec_signer(&cert_dir, "es384", None); + let (signer, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -243,7 +244,7 @@ mod tests { fn es512_signer() { let cert_dir = fixture_path("certs"); - let (signer, _) = temp_signer::get_ec_signer(&cert_dir, "es512", None); + let (signer, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); diff --git a/sdk/src/openssl/ec_validator.rs b/sdk/src/openssl/ec_validator.rs @@ -11,20 +11,18 @@ // specific language governing permissions and limitations under // each license. -use crate::{validator::CoseValidator, Error, Result}; +use crate::{validator::CoseValidator, Error, Result, SigningAlg}; use openssl::ec::EcKey; use openssl::hash::MessageDigest; use openssl::pkey::PKey; pub struct EcValidator { - alg: String, + alg: SigningAlg, } impl EcValidator { - pub fn new(alg: &str) -> Self { - EcValidator { - alg: alg.to_owned(), - } + pub fn new(alg: SigningAlg) -> Self { + EcValidator { alg } } } @@ -33,19 +31,19 @@ impl CoseValidator for EcValidator { let public_key = EcKey::public_key_from_der(pkey).map_err(|_err| Error::CoseSignature)?; let key = PKey::from_ec_key(public_key).map_err(wrap_openssl_err)?; - let mut verifier = match self.alg.as_ref() { - "es256" => openssl::sign::Verifier::new(MessageDigest::sha256(), &key)?, - "es384" => openssl::sign::Verifier::new(MessageDigest::sha384(), &key)?, - "es512" => openssl::sign::Verifier::new(MessageDigest::sha512(), &key)?, + let mut verifier = match self.alg { + SigningAlg::Es256 => openssl::sign::Verifier::new(MessageDigest::sha256(), &key)?, + SigningAlg::Es384 => openssl::sign::Verifier::new(MessageDigest::sha384(), &key)?, + SigningAlg::Es512 => openssl::sign::Verifier::new(MessageDigest::sha512(), &key)?, _ => return Err(Error::UnsupportedType), }; // is this an expected P1363 sig size if sig.len() - != match self.alg.as_ref() { - "es256" => 64, - "es384" => 96, - "es512" => 132, + != match self.alg { + SigningAlg::Es256 => 64, + SigningAlg::Es384 => 96, + SigningAlg::Es512 => 132, _ => return Err(Error::UnsupportedType), } { @@ -79,13 +77,13 @@ mod tests { #![allow(clippy::unwrap_used)] use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path, Signer}; + use crate::{openssl::temp_signer, utils::test::fixture_path, Signer, SigningAlg}; #[test] fn sign_and_validate_es256() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es256", None); + let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -100,7 +98,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EcValidator::new("es256"); + let validator = EcValidator::new(SigningAlg::Es256); assert!(validator.validate(&signature, data, &pub_key).unwrap()); } @@ -108,7 +106,7 @@ mod tests { fn sign_and_validate_es384() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es384", None); + let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -123,7 +121,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EcValidator::new("es384"); + let validator = EcValidator::new(SigningAlg::Es384); assert!(validator.validate(&signature, data, &pub_key).unwrap()); } @@ -131,7 +129,7 @@ mod tests { fn sign_and_validate_es512() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es512", None); + let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -146,7 +144,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EcValidator::new("es512"); + let validator = EcValidator::new(SigningAlg::Es512); assert!(validator.validate(&signature, data, &pub_key).unwrap()); } @@ -154,7 +152,7 @@ mod tests { fn bad_sig_es256() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es256", None); + let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -166,7 +164,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EcValidator::new("es256"); + let validator = EcValidator::new(SigningAlg::Es256); let validated = validator.validate(&signature, data, &pub_key); assert!(validated.is_err()); } @@ -175,7 +173,7 @@ mod tests { fn bad_data_es256() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, "es256", None); + let (signer, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); let mut data = b"some sample content to sign".to_vec(); println!("data len = {}", data.len()); @@ -188,7 +186,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EcValidator::new("es256"); + let validator = EcValidator::new(SigningAlg::Es256); assert!(!validator.validate(&signature, &data, &pub_key).unwrap()); } } diff --git a/sdk/src/openssl/ed_signer.rs b/sdk/src/openssl/ed_signer.rs @@ -13,13 +13,13 @@ use std::{fs, path::Path}; -use crate::{signer::ConfigurableSigner, Error, Result, Signer}; - use openssl::{ pkey::{PKey, Private}, x509::X509, }; +use crate::{signer::ConfigurableSigner, Error, Result, Signer, SigningAlg}; + use super::check_chain_order; /// Implements `Signer` trait using OpenSSL's implementation of @@ -31,7 +31,7 @@ pub struct EdSigner { certs_size: usize, timestamp_size: usize, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, } @@ -39,7 +39,7 @@ impl ConfigurableSigner for EdSigner { fn from_files<P: AsRef<Path>>( signcert_path: P, pkey_path: P, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let signcert = fs::read(signcert_path).map_err(wrap_io_err)?; @@ -51,14 +51,14 @@ impl ConfigurableSigner for EdSigner { fn from_signcert_and_pkey( signcert: &[u8], pkey: &[u8], - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let certs_size = signcert.len(); let signcerts = X509::stack_from_pem(signcert).map_err(wrap_openssl_err)?; let pkey = PKey::private_key_from_pem(pkey).map_err(wrap_openssl_err)?; - if alg.to_lowercase() != "ed25519" { + if alg != SigningAlg::Ed25519 { return Err(Error::UnsupportedType); // only ed25519 is supported by C2PA } @@ -74,7 +74,7 @@ impl ConfigurableSigner for EdSigner { pkey, certs_size, timestamp_size: 10000, // todo: call out to TSA to get actual timestamp and use that size - alg: "ed25519".to_string(), + alg, tsa_url, }) } @@ -90,8 +90,8 @@ impl Signer for EdSigner { Ok(signed_data) } - fn alg(&self) -> Option<String> { - Some(self.alg.to_owned()) + fn alg(&self) -> SigningAlg { + self.alg } fn certs(&self) -> Result<Vec<Vec<u8>>> { @@ -127,13 +127,13 @@ mod tests { #![allow(clippy::unwrap_used)] use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path}; + use crate::{openssl::temp_signer, utils::test::fixture_path, SigningAlg}; #[test] fn ed25519_signer() { let cert_dir = fixture_path("certs"); - let (signer, _) = temp_signer::get_ed_signer(&cert_dir, "ed25519", None); + let (signer, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); diff --git a/sdk/src/openssl/ed_validator.rs b/sdk/src/openssl/ed_validator.rs @@ -13,17 +13,15 @@ use openssl::pkey::PKey; -use crate::{validator::CoseValidator, Error, Result}; +use crate::{validator::CoseValidator, Error, Result, SigningAlg}; pub struct EdValidator { - _alg: String, + _alg: SigningAlg, } impl EdValidator { - pub fn new(alg: &str) -> Self { - EdValidator { - _alg: alg.to_owned(), - } + pub fn new(alg: SigningAlg) -> Self { + EdValidator { _alg: alg } } } @@ -46,13 +44,13 @@ mod tests { use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path, Signer}; + use crate::{openssl::temp_signer, utils::test::fixture_path, Signer, SigningAlg}; #[test] fn sign_and_validate() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ed_signer(&cert_dir, "ed25519", None); + let (signer, cert_path) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let data = b"some sample content to sign"; println!("data len = {}", data.len()); @@ -66,7 +64,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EdValidator::new("ed25519"); + let validator = EdValidator::new(SigningAlg::Ed25519); assert!(validator.validate(&signature, data, &pub_key).unwrap()); } @@ -74,7 +72,7 @@ mod tests { fn bad_data() { let cert_dir = fixture_path("certs"); - let (signer, cert_path) = temp_signer::get_ed_signer(&cert_dir, "ed25519", None); + let (signer, cert_path) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let mut data = b"some sample content to sign".to_vec(); println!("data len = {}", data.len()); @@ -87,7 +85,7 @@ mod tests { let signcert = openssl::x509::X509::from_pem(&cert_bytes).unwrap(); let pub_key = signcert.public_key().unwrap().public_key_to_der().unwrap(); - let validator = EdValidator::new("es256"); + let validator = EdValidator::new(SigningAlg::Es256); // ^^ REVIEW with @mfisher: Is this correct? Shouldn't it be ed25519? assert!(!validator.validate(&signature, &data, &pub_key).unwrap()); diff --git a/sdk/src/openssl/rsa_signer.rs b/sdk/src/openssl/rsa_signer.rs @@ -14,7 +14,7 @@ use crate::{ ocsp_utils::{get_ocsp_response, OcspData}, signer::ConfigurableSigner, - Error, Result, Signer, + Error, Result, Signer, SigningAlg, }; use std::{cell::Cell, fs, path::Path}; @@ -38,7 +38,7 @@ pub struct RsaSigner { timestamp_size: usize, ocsp_size: Cell<usize>, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ocsp_rsp: Cell<OcspData>, } @@ -69,7 +69,7 @@ impl ConfigurableSigner for RsaSigner { fn from_files<P: AsRef<Path>>( signcert_path: P, pkey_path: P, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let signcert = fs::read(signcert_path).map_err(wrap_io_err)?; @@ -81,7 +81,7 @@ impl ConfigurableSigner for RsaSigner { fn from_signcert_and_pkey( signcert: &[u8], pkey: &[u8], - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self> { let signcerts = X509::stack_from_pem(signcert).map_err(wrap_openssl_err)?; @@ -115,8 +115,8 @@ impl ConfigurableSigner for RsaSigner { impl Signer for RsaSigner { fn sign(&self, data: &[u8]) -> Result<Vec<u8>> { - let mut signer = match self.alg.as_str() { - "ps256" => { + let mut signer = match self.alg { + SigningAlg::Ps256 => { let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey) .map_err(wrap_openssl_err)?; @@ -125,7 +125,7 @@ impl Signer for RsaSigner { signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } - "ps384" => { + SigningAlg::Ps384 => { let mut signer = openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey) .map_err(wrap_openssl_err)?; @@ -134,7 +134,7 @@ impl Signer for RsaSigner { signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } - "ps512" => { + SigningAlg::Ps512 => { let mut signer = openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey) .map_err(wrap_openssl_err)?; @@ -143,12 +143,12 @@ impl Signer for RsaSigner { signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } - "rs256" => openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey) - .map_err(wrap_openssl_err)?, - "rs384" => openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey) - .map_err(wrap_openssl_err)?, - "rs512" => openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey) - .map_err(wrap_openssl_err)?, + // "rs256" => openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey) + // .map_err(wrap_openssl_err)?, + // "rs384" => openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey) + // .map_err(wrap_openssl_err)?, + // "rs512" => openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey) + // .map_err(wrap_openssl_err)?, _ => return Err(Error::UnsupportedType), }; @@ -174,8 +174,8 @@ impl Signer for RsaSigner { Ok(certs) } - fn alg(&self) -> Option<String> { - Some(self.alg.to_owned()) + fn alg(&self) -> SigningAlg { + self.alg } fn time_authority_url(&self) -> Option<String> { @@ -214,7 +214,7 @@ mod tests { use crate::{ utils::test::{fixture_path, temp_signer}, - Signer, + Signer, SigningAlg, }; #[test] @@ -233,7 +233,7 @@ mod tests { let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data"); let signer = - RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, "ps256".to_string(), None) + RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, SigningAlg::Ps256, None) .unwrap(); let data = b"some sample content to sign"; @@ -243,19 +243,19 @@ mod tests { assert!(signature.len() <= signer.reserve_size()); } - #[test] - fn sign_rs256() { - let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data"); - let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data"); + // #[test] + // fn sign_rs256() { + // let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data"); + // let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data"); - let signer = - RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, "rs256".to_string(), None) - .unwrap(); + // let signer = + // RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, "rs256".to_string(), None) + // .unwrap(); - let data = b"some sample content to sign"; + // let data = b"some sample content to sign"; - let signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - assert!(signature.len() <= signer.reserve_size()); - } + // let signature = signer.sign(data).unwrap(); + // println!("signature len = {}", signature.len()); + // assert!(signature.len() <= signer.reserve_size()); + // } } diff --git a/sdk/src/openssl/rsa_validator.rs b/sdk/src/openssl/rsa_validator.rs @@ -11,18 +11,16 @@ // specific language governing permissions and limitations under // each license. -use crate::{validator::CoseValidator, Error, Result}; +use crate::{validator::CoseValidator, Error, Result, SigningAlg}; use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; pub struct RsaValidator { - alg: String, + alg: SigningAlg, } impl RsaValidator { - pub fn new(alg: &str) -> Self { - RsaValidator { - alg: alg.to_owned(), - } + pub fn new(alg: SigningAlg) -> Self { + RsaValidator { alg } } } @@ -31,28 +29,28 @@ impl CoseValidator for RsaValidator { let rsa = Rsa::public_key_from_der(pkey)?; let pkey = PKey::from_rsa(rsa)?; - let mut verifier = match self.alg.as_str() { - "ps256" => { + let mut verifier = match self.alg { + SigningAlg::Ps256 => { let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &pkey)?; verifier.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding verifier.set_rsa_mgf1_md(MessageDigest::sha256())?; verifier } - "ps384" => { + SigningAlg::Ps384 => { let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha384(), &pkey)?; verifier.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding verifier.set_rsa_mgf1_md(MessageDigest::sha384())?; verifier } - "ps512" => { + SigningAlg::Ps512 => { let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha512(), &pkey)?; verifier.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding verifier.set_rsa_mgf1_md(MessageDigest::sha512())?; verifier } - "rs256" => openssl::sign::Verifier::new(MessageDigest::sha256(), &pkey)?, - "rs384" => openssl::sign::Verifier::new(MessageDigest::sha384(), &pkey)?, - "rs512" => openssl::sign::Verifier::new(MessageDigest::sha512(), &pkey)?, + // "rs256" => openssl::sign::Verifier::new(MessageDigest::sha256(), &pkey)?, + // "rs384" => openssl::sign::Verifier::new(MessageDigest::sha384(), &pkey)?, + // "rs512" => openssl::sign::Verifier::new(MessageDigest::sha512(), &pkey)?, _ => return Err(Error::UnsupportedType), }; @@ -68,7 +66,7 @@ mod tests { #![allow(clippy::unwrap_used)] use super::*; - use crate::{signer::ConfigurableSigner, Signer}; + use crate::{signer::ConfigurableSigner, Signer, SigningAlg}; #[test] fn verify_rsa_signatures() { @@ -80,88 +78,88 @@ mod tests { let data = b"some sample content to sign"; - println!("Test RS256"); - let mut signer = crate::openssl::RsaSigner::from_signcert_and_pkey( - cert_bytes, - key_bytes, - "rs256".to_string(), - None, - ) - .unwrap(); - - let mut signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - let mut validator = RsaValidator::new("rs256"); - assert!(validator.validate(&signature, data, &pkey).unwrap()); - - println!("Test RS384"); - signer = crate::openssl::RsaSigner::from_signcert_and_pkey( - cert_bytes, - key_bytes, - "rs384".to_string(), - None, - ) - .unwrap(); - - signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - validator = RsaValidator::new("rs384"); - assert!(validator.validate(&signature, data, &pkey).unwrap()); - - println!("Test RS512"); - signer = crate::openssl::RsaSigner::from_signcert_and_pkey( - cert_bytes, - key_bytes, - "rs512".to_string(), - None, - ) - .unwrap(); - - signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - validator = RsaValidator::new("rs512"); - assert!(validator.validate(&signature, data, &pkey).unwrap()); + // println!("Test RS256"); + // let mut signer = crate::openssl::RsaSigner::from_signcert_and_pkey( + // cert_bytes, + // key_bytes, + // "rs256".to_string(), + // None, + // ) + // .unwrap(); + + // let mut signature = signer.sign(data).unwrap(); + // println!("signature len = {}", signature.len()); + // let mut validator = RsaValidator::new("rs256"); + // assert!(validator.validate(&signature, data, &pkey).unwrap()); + + // println!("Test RS384"); + // signer = crate::openssl::RsaSigner::from_signcert_and_pkey( + // cert_bytes, + // key_bytes, + // "rs384".to_string(), + // None, + // ) + // .unwrap(); + + // signature = signer.sign(data).unwrap(); + // println!("signature len = {}", signature.len()); + // validator = RsaValidator::new("rs384"); + // assert!(validator.validate(&signature, data, &pkey).unwrap()); + + // println!("Test RS512"); + // signer = crate::openssl::RsaSigner::from_signcert_and_pkey( + // cert_bytes, + // key_bytes, + // "rs512".to_string(), + // None, + // ) + // .unwrap(); + + // signature = signer.sign(data).unwrap(); + // println!("signature len = {}", signature.len()); + // validator = RsaValidator::new("rs512"); + // assert!(validator.validate(&signature, data, &pkey).unwrap()); println!("Test PS256"); - signer = crate::openssl::RsaSigner::from_signcert_and_pkey( + let mut signer = crate::openssl::RsaSigner::from_signcert_and_pkey( cert_bytes, key_bytes, - "ps256".to_string(), + SigningAlg::Ps256, None, ) .unwrap(); - signature = signer.sign(data).unwrap(); + let mut signature = signer.sign(data).unwrap(); println!("signature len = {}", signature.len()); - validator = RsaValidator::new("ps256"); + let mut validator = RsaValidator::new(SigningAlg::Ps256); assert!(validator.validate(&signature, data, &pkey).unwrap()); println!("Test PS384"); signer = crate::openssl::RsaSigner::from_signcert_and_pkey( cert_bytes, key_bytes, - "ps384".to_string(), + SigningAlg::Ps384, None, ) .unwrap(); signature = signer.sign(data).unwrap(); println!("signature len = {}", signature.len()); - validator = RsaValidator::new("ps384"); + validator = RsaValidator::new(SigningAlg::Ps384); assert!(validator.validate(&signature, data, &pkey).unwrap()); println!("Test PS512"); signer = crate::openssl::RsaSigner::from_signcert_and_pkey( cert_bytes, key_bytes, - "ps512".to_string(), + SigningAlg::Ps512, None, ) .unwrap(); signature = signer.sign(data).unwrap(); println!("signature len = {}", signature.len()); - validator = RsaValidator::new("ps512"); + validator = RsaValidator::new(SigningAlg::Ps512); assert!(validator.validate(&signature, data, &pkey).unwrap()); } } diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs @@ -36,6 +36,7 @@ use std::path::{Path, PathBuf}; use crate::{ openssl::{EcSigner, EdSigner, RsaSigner}, signer::ConfigurableSigner, + SigningAlg, }; /// Create an OpenSSL ES256 signer that can be used for testing purposes. @@ -44,7 +45,7 @@ use crate::{ /// /// * `path` - A directory (which must already exist) to receive the temporary /// private key / certificate pair. -/// * `alg` - A format for signing. Must be one of (`es256`, `es384`, or `es512`). +/// * `alg` - A format for signing. Must be one of the `SigningAlg::Es*` variants. /// * `tsa_url` - Optional URL for a timestamp authority. /// /// # Returns @@ -58,26 +59,26 @@ use crate::{ /// Can panic if unable to invoke OpenSSL executable properly. pub fn get_ec_signer<P: AsRef<Path>>( path: P, - alg: &str, + alg: SigningAlg, tsa_url: Option<String>, ) -> (EcSigner, PathBuf) { match alg { - "es256" | "es384" | "es512" => (), + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => (), _ => { panic!("Unknown EC signer alg {:#?}", alg); } } let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg); + sign_cert_path.push(alg.to_string()); sign_cert_path.set_extension("pub"); let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg); + pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); ( - EcSigner::from_files(&sign_cert_path, &pem_key_path, alg.to_string(), tsa_url).unwrap(), + EcSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } @@ -102,23 +103,23 @@ pub fn get_ec_signer<P: AsRef<Path>>( /// Can panic if unable to invoke OpenSSL executable properly. pub fn get_ed_signer<P: AsRef<Path>>( path: P, - alg: &str, + alg: SigningAlg, tsa_url: Option<String>, ) -> (EdSigner, PathBuf) { - if alg != "ed25519" { + if alg != SigningAlg::Ed25519 { panic!("Unknown ED signer alg {:#?}", alg); } let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg); + sign_cert_path.push(alg.to_string()); sign_cert_path.set_extension("pub"); let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg); + pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); ( - EdSigner::from_files(&sign_cert_path, &pem_key_path, alg.to_string(), tsa_url).unwrap(), + EdSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } @@ -129,8 +130,7 @@ pub fn get_ed_signer<P: AsRef<Path>>( /// /// * `path` - A directory (which must already exist) to receive the temporary /// private key / certificate pair. -/// * `alg` - A format for signing. Must be one of (`rs256`, `rs384`, `rs512`, -/// `ps256`, `ps384`, or `ps512`). +/// * `alg` - A format for signing. Must be one of the `SignerAlg::Ps*` options. /// * `tsa_url` - Optional URL for a timestamp authority. /// /// # Returns @@ -144,22 +144,22 @@ pub fn get_ed_signer<P: AsRef<Path>>( /// Can panic if unable to invoke OpenSSL executable properly. pub fn get_rsa_signer<P: AsRef<Path>>( path: P, - alg: &str, + alg: SigningAlg, tsa_url: Option<String>, ) -> (RsaSigner, PathBuf) { match alg { - "ps256" | "ps384" | "ps512" => (), + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => (), _ => { panic!("Unknown RSA signer alg {:#?}", alg); } } let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg); + sign_cert_path.push(alg.to_string()); sign_cert_path.set_extension("pub"); let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg); + pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); if !sign_cert_path.exists() || !pem_key_path.exists() { @@ -171,7 +171,7 @@ pub fn get_rsa_signer<P: AsRef<Path>>( } ( - RsaSigner::from_files(&sign_cert_path, &pem_key_path, alg.to_string(), tsa_url).unwrap(), + RsaSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs @@ -19,33 +19,31 @@ //! the asynchronous signing of claims. //! This module should be used only for testing purposes. +use crate::SigningAlg; + #[cfg(feature = "async_signer")] -fn get_local_signer(alg: &str) -> Box<dyn crate::Signer> { +fn get_local_signer(alg: SigningAlg) -> Box<dyn crate::Signer> { let cert_dir = crate::utils::test::fixture_path("certs"); match alg { - "ps256" | "ps384" | "ps512" => { + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { let (s, _k) = super::temp_signer::get_rsa_signer(&cert_dir, alg, None); Box::new(s) } - "es256" | "es384" | "es512" => { + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => { let (s, _k) = super::temp_signer::get_ec_signer(&cert_dir, alg, None); Box::new(s) } - "ed25519" => { + SigningAlg::Ed25519 => { let (s, _k) = super::temp_signer::get_ed_signer(&cert_dir, alg, None); Box::new(s) } - _ => { - let (s, _k) = super::temp_signer::get_rsa_signer(&cert_dir, "ps256", None); - Box::new(s) - } } } #[cfg(feature = "async_signer")] pub struct AsyncSignerAdapter { - alg: String, + alg: SigningAlg, certs: Vec<Vec<u8>>, reserve_size: usize, tsa_url: Option<String>, @@ -54,8 +52,8 @@ pub struct AsyncSignerAdapter { #[cfg(feature = "async_signer")] impl AsyncSignerAdapter { - pub fn new(alg: String) -> Self { - let signer = get_local_signer(&alg); + pub fn new(alg: SigningAlg) -> Self { + let signer = get_local_signer(alg); AsyncSignerAdapter { alg, @@ -72,12 +70,12 @@ impl AsyncSignerAdapter { #[async_trait::async_trait] impl crate::AsyncSigner for AsyncSignerAdapter { async fn sign(&self, data: Vec<u8>) -> crate::error::Result<Vec<u8>> { - let signer = get_local_signer(&self.alg); + let signer = get_local_signer(self.alg); signer.sign(&data) } - fn alg(&self) -> Option<String> { - Some(self.alg.clone()) + fn alg(&self) -> SigningAlg { + self.alg } fn certs(&self) -> crate::Result<Vec<Vec<u8>>> { diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs @@ -11,7 +11,7 @@ // specific language governing permissions and limitations under // each license. -use crate::Result; +use crate::{Result, SigningAlg}; /// The `Signer` trait generates a cryptographic signature over a byte array. /// @@ -21,7 +21,7 @@ pub trait Signer { fn sign(&self, data: &[u8]) -> Result<Vec<u8>>; /// Returns the algorithm of the Signer. - fn alg(&self) -> Option<String>; + fn alg(&self) -> SigningAlg; /// Returns the certificates as a Vec containing a Vec of DER bytes for each certificate. fn certs(&self) -> Result<Vec<Vec<u8>>>; @@ -51,7 +51,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { fn from_files<P: AsRef<std::path::Path>>( signcert_path: P, pkey_path: P, - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self>; @@ -59,7 +59,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { fn from_signcert_and_pkey( signcert: &[u8], pkey: &[u8], - alg: String, + alg: SigningAlg, tsa_url: Option<String>, ) -> Result<Self>; } @@ -79,7 +79,7 @@ pub trait AsyncSigner: Sync { async fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>>; /// Returns the algorithm of the Signer. - fn alg(&self) -> Option<String>; + fn alg(&self) -> SigningAlg; /// Returns the certificates as a Vec containing a Vec of DER bytes for each certificate. fn certs(&self) -> Result<Vec<Vec<u8>>>; diff --git a/sdk/src/signing_alg.rs b/sdk/src/signing_alg.rs @@ -0,0 +1,139 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +#![deny(missing_docs)] + +use std::{fmt, str::FromStr}; + +/// Describes the digital signature algorithms allowed by the C2PA spec. +/// +/// Per <https://c2pa.org/specifications/specifications/1.0/specs/C2PA_Specification.html#_digital_signatures>: +/// +/// > All digital signatures that are stored in a C2PA Manifest shall +/// > be generated using one of the digital signature algorithms and +/// > key types listed as described in this section. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SigningAlg { + /// ECDSA with SHA-256 + Es256, + + /// ECDSA with SHA-384 + Es384, + + /// ECDSA with SHA-512 + Es512, + + /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 + Ps256, + + /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 + Ps384, + + /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 + Ps512, + + /// Edwards-Curve DSA (Ed25519 instance only) + Ed25519, +} + +impl FromStr for SigningAlg { + type Err = UnknownAlgorithmError; + + fn from_str(alg: &str) -> Result<Self, Self::Err> { + match alg { + "es256" => Ok(Self::Es256), + "es384" => Ok(Self::Es384), + "es512" => Ok(Self::Es512), + "ps256" => Ok(Self::Ps256), + "ps384" => Ok(Self::Ps384), + "ps512" => Ok(Self::Ps512), + "ed25519" => Ok(Self::Ed25519), + _ => Err(UnknownAlgorithmError(alg.to_owned())), + } + } +} + +impl fmt::Display for SigningAlg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!( + f, + "{}", + match self { + Self::Es256 => "es256", + Self::Es384 => "es384", + Self::Es512 => "es512", + Self::Ps256 => "ps256", + Self::Ps384 => "ps384", + Self::Ps512 => "ps512", + Self::Ed25519 => "ed25519", + } + ) + } +} + +#[derive(Debug, PartialEq)] +/// This error is thrown when converting from a string to [`SigningAlg`] +/// if the algorithm string is unrecognized. +/// +/// The string must be one of "es256", "es384", "es512", "ps256", "ps384", +/// "ps512", or "ed25519". +pub struct UnknownAlgorithmError(String); + +impl fmt::Display for UnknownAlgorithmError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "UnknownAlgorithmError({})", self.0) + } +} + +impl std::error::Error for UnknownAlgorithmError {} + +#[cfg(test)] +mod tests { + #![allow(clippy::expect_used)] + #![allow(clippy::unwrap_used)] + + use super::*; + + #[test] + fn alg_from_str() { + assert_eq!("es256".parse(), Ok(SigningAlg::Es256)); + assert_eq!("es384".parse(), Ok(SigningAlg::Es384)); + assert_eq!("es512".parse(), Ok(SigningAlg::Es512)); + assert_eq!("ps256".parse(), Ok(SigningAlg::Ps256)); + assert_eq!("ps384".parse(), Ok(SigningAlg::Ps384)); + assert_eq!("ps512".parse(), Ok(SigningAlg::Ps512)); + assert_eq!("ed25519".parse(), Ok(SigningAlg::Ed25519)); + + let r: Result<SigningAlg, UnknownAlgorithmError> = "bogus".parse(); + assert_eq!(r, Err(UnknownAlgorithmError("bogus".to_string()))); + } + + #[test] + fn signing_alg_impl_display() { + assert_eq!(format!("{}", SigningAlg::Es256), "es256"); + assert_eq!(format!("{}", SigningAlg::Es384), "es384"); + assert_eq!(format!("{}", SigningAlg::Es512), "es512"); + assert_eq!(format!("{}", SigningAlg::Ps256), "ps256"); + assert_eq!(format!("{}", SigningAlg::Ps384), "ps384"); + assert_eq!(format!("{}", SigningAlg::Ps512), "ps512"); + assert_eq!(format!("{}", SigningAlg::Ed25519), "ed25519"); + } + + #[test] + fn err_impl_display() { + assert_eq!( + format!("{}", UnknownAlgorithmError("bogus".to_owned())), + "UnknownAlgorithmError(bogus)" + ); + } +} diff --git a/sdk/src/store.rs b/sdk/src/store.rs @@ -1928,6 +1928,7 @@ pub mod tests { create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, temp_signer, }, }, + SigningAlg, }; fn create_editing_claim(claim: &mut Claim) -> Result<&mut Claim> { @@ -2074,8 +2075,8 @@ pub mod tests { Ok(b"not a valid signature".to_vec()) } - fn alg(&self) -> Option<String> { - None + fn alg(&self) -> SigningAlg { + SigningAlg::Ps256 } fn certs(&self) -> Result<Vec<Vec<u8>>> { @@ -2114,7 +2115,7 @@ pub mod tests { #[test] #[cfg(feature = "file_io")] fn test_sign_with_expired_cert() { - use crate::{openssl::RsaSigner, signer::ConfigurableSigner}; + use crate::{openssl::RsaSigner, signer::ConfigurableSigner, SigningAlg}; // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -2128,7 +2129,7 @@ pub mod tests { let signcert_path = fixture_path("rsa-pss256_key-expired.pub"); let pkey_path = fixture_path("rsa-pss256-expired.pem"); let signer = - RsaSigner::from_files(signcert_path, pkey_path, "ps256".to_string(), None).unwrap(); + RsaSigner::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None).unwrap(); store.commit_claim(claim).unwrap(); @@ -2176,8 +2177,7 @@ pub mod tests { #[cfg(feature = "async_signer")] #[actix::test] async fn test_jumbf_generation_async() { - let signer = - crate::openssl::temp_signer_async::AsyncSignerAdapter::new("ps256".to_string()); + let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); diff --git a/sdk/src/time_stamp.rs b/sdk/src/time_stamp.rs @@ -22,6 +22,7 @@ use crate::error::{Error, Result}; use crate::hash_utils::vec_compare; use crate::asn1::rfc3161::{TimeStampResp, TstInfo, OID_CONTENT_TYPE_TST_INFO}; +use crate::SigningAlg; use bcder::decode::Constructed; use x509_certificate::DigestAlgorithm::{self}; @@ -29,32 +30,29 @@ use x509_certificate::DigestAlgorithm::{self}; use coset::{iana, sig_structure_data, HeaderBuilder, ProtectedHeader}; #[allow(dead_code)] -pub(crate) fn cose_countersign_data(data: &[u8], alg: &str) -> Vec<u8> { +pub(crate) fn cose_countersign_data(data: &[u8], alg: SigningAlg) -> Vec<u8> { let alg_id = match alg { - "ps256" => HeaderBuilder::new() + SigningAlg::Ps256 => HeaderBuilder::new() .algorithm(iana::Algorithm::PS256) .build(), - "ps384" => HeaderBuilder::new() + SigningAlg::Ps384 => HeaderBuilder::new() .algorithm(iana::Algorithm::PS384) .build(), - "ps512" => HeaderBuilder::new() + SigningAlg::Ps512 => HeaderBuilder::new() .algorithm(iana::Algorithm::PS512) .build(), - "es256" => HeaderBuilder::new() + SigningAlg::Es256 => HeaderBuilder::new() .algorithm(iana::Algorithm::ES256) .build(), - "es384" => HeaderBuilder::new() + SigningAlg::Es384 => HeaderBuilder::new() .algorithm(iana::Algorithm::ES384) .build(), - "es512" => HeaderBuilder::new() + SigningAlg::Es512 => HeaderBuilder::new() .algorithm(iana::Algorithm::ES512) .build(), - "ed25519" => HeaderBuilder::new() + SigningAlg::Ed25519 => HeaderBuilder::new() .algorithm(iana::Algorithm::EdDSA) .build(), - _ => HeaderBuilder::new() - .algorithm(iana::Algorithm::PS256) - .build(), }; let p_header = ProtectedHeader { @@ -74,7 +72,11 @@ pub(crate) fn cose_countersign_data(data: &[u8], alg: &str) -> Vec<u8> { } #[allow(dead_code)] -pub(crate) fn cose_timestamp_countersign(data: &[u8], alg: &str, tsa_url: &str) -> Result<Vec<u8>> { +pub(crate) fn cose_timestamp_countersign( + data: &[u8], + alg: SigningAlg, + tsa_url: &str, +) -> Result<Vec<u8>> { // create countersignature with TimeStampReq parameters // payload: data // context "CounterSigner" @@ -91,7 +93,7 @@ pub(crate) fn cose_timestamp_countersign(data: &[u8], alg: &str, tsa_url: &str) pub(crate) fn cose_sigtst_to_tstinfos( sigtst_cbor: &[u8], data: &[u8], - alg: &str, + alg: SigningAlg, ) -> Result<Vec<TstInfo>> { let tst_container: TstContainer = serde_cbor::from_slice(sigtst_cbor).map_err(|_err| Error::CoseTimeStampGeneration)?; diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs @@ -26,6 +26,7 @@ use crate::{ create_signer, openssl::RsaSigner, signer::{ConfigurableSigner, Signer}, + SigningAlg, }; use std::path::PathBuf; @@ -205,15 +206,12 @@ pub fn temp_signer() -> RsaSigner { pem_key_path.push("ps256"); pem_key_path.set_extension("pem"); - RsaSigner::from_files(&sign_cert_path, &pem_key_path, "ps256".to_string(), None) + RsaSigner::from_files(&sign_cert_path, &pem_key_path, SigningAlg::Ps256, None) .expect("get_temp_signer") } /// Create a [`Signer`] instance for a specific algorithm that can be used for testing purposes. /// -/// # Parameters: -/// alg: The algorithm to use -/// /// # Returns /// /// Returns a boxed [`Signer`] instance. @@ -223,15 +221,15 @@ pub fn temp_signer() -> RsaSigner { /// Can panic if the certs cannot be read. (This function should only /// be used as part of testing infrastructure.) #[cfg(feature = "file_io")] -pub fn temp_signer_with_alg(alg: &str) -> Box<dyn Signer> { +pub fn temp_signer_with_alg(alg: SigningAlg) -> Box<dyn Signer> { #![allow(clippy::expect_used)] // sign and embed into the target file let mut sign_cert_path = fixture_path("certs"); - sign_cert_path.push(alg); + sign_cert_path.push(alg.to_string()); sign_cert_path.set_extension("pub"); let mut pem_key_path = fixture_path("certs"); - pem_key_path.push(alg); + pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); create_signer::from_files(sign_cert_path.clone(), pem_key_path, alg, None) diff --git a/sdk/src/validator.rs b/sdk/src/validator.rs @@ -13,29 +13,18 @@ #[cfg(feature = "file_io")] use crate::openssl::{EcValidator, EdValidator, RsaValidator}; -use crate::Result; +use crate::{Result, SigningAlg}; use chrono::{DateTime, Utc}; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ValidationInfo { - pub alg: String, // validation algorithm + pub alg: Option<SigningAlg>, // validation algorithm pub date: Option<DateTime<Utc>>, pub issuer_org: Option<String>, pub validated: bool, // claim signature is valid } -impl Default for ValidationInfo { - fn default() -> Self { - ValidationInfo { - alg: "".to_owned(), - date: None, - issuer_org: None, - validated: false, - } - } -} - /// Trait to support validating a signature against the provided data pub(crate) trait CoseValidator { /// validate signature "sig" for given "data using provided public key" @@ -64,19 +53,18 @@ impl CoseValidator for DummyValidator { /// return validator for supported C2PA algorthms #[cfg(feature = "file_io")] -pub(crate) fn get_validator(alg: &str) -> Option<Box<dyn CoseValidator>> { - match alg.to_lowercase().as_str() { - "es256" => Some(Box::new(EcValidator::new("es256"))), - "es384" => Some(Box::new(EcValidator::new("es384"))), - "es512" => Some(Box::new(EcValidator::new("es512"))), - "ps256" => Some(Box::new(RsaValidator::new("ps256"))), - "ps384" => Some(Box::new(RsaValidator::new("ps384"))), - "ps512" => Some(Box::new(RsaValidator::new("ps512"))), - "rs256" => Some(Box::new(RsaValidator::new("rs256"))), - "rs384" => Some(Box::new(RsaValidator::new("rs384"))), - "rs512" => Some(Box::new(RsaValidator::new("rs512"))), - "ed25519" => Some(Box::new(EdValidator::new("ed25519"))), - _ => None, +pub(crate) fn get_validator(alg: SigningAlg) -> Box<dyn CoseValidator> { + match alg { + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => { + Box::new(EcValidator::new(alg)) + } + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { + Box::new(RsaValidator::new(alg)) + } + // "rs256" => Some(Box::new(RsaValidator::new("rs256"))), + // "rs384" => Some(Box::new(RsaValidator::new("rs384"))), + // "rs512" => Some(Box::new(RsaValidator::new("rs512"))), + SigningAlg::Ed25519 => Box::new(EdValidator::new(alg)), } } diff --git a/sdk/src/wasm/webcrypto_validator.rs b/sdk/src/wasm/webcrypto_validator.rs @@ -11,18 +11,21 @@ // specific language governing permissions and limitations under // each license. -use crate::utils::hash_utils::hash_by_alg; -use crate::wasm::context::WindowOrWorker; -use crate::{Error, Result}; +use std::convert::TryFrom; + use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array}; use rsa::{BigUint, PaddingScheme, PublicKey, RsaPublicKey}; use sha2::{Sha256, Sha384, Sha512}; use spki::SubjectPublicKeyInfo; -use std::convert::TryFrom; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use web_sys::{CryptoKey, SubtleCrypto}; use x509_parser::der_parser::ber::{parse_ber_sequence, BerObject}; + +use crate::{ + utils::hash_utils::hash_by_alg, wasm::context::WindowOrWorker, Error, Result, SigningAlg, +}; + pub struct EcKeyImportParams { name: String, named_curve: String, @@ -204,14 +207,11 @@ async fn async_validate( } } -pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> Result<bool> { - web_sys::console::debug_2( - &"Validating with algorithm".into(), - &String::from(alg).into(), - ); +pub async fn validate_async(alg: SigningAlg, sig: &[u8], data: &[u8], pkey: &[u8]) -> Result<bool> { + web_sys::console::debug_2(&"Validating with algorithm".into(), &alg.to_string().into()); match alg { - "ps256" => { + SigningAlg::Ps256 => { async_validate( "RSA-PSS".to_string(), "SHA-256".to_string(), @@ -222,7 +222,7 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "ps384" => { + SigningAlg::Ps384 => { async_validate( "RSA-PSS".to_string(), "SHA-384".to_string(), @@ -233,7 +233,7 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "ps512" => { + SigningAlg::Ps512 => { async_validate( "RSA-PSS".to_string(), "SHA-512".to_string(), @@ -244,7 +244,40 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "es256" => { + // "rs256" => { + // async_validate( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-256".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + // "rs384" => { + // async_validate( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-384".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + // "rs512" => { + // async_validate( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-512".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + SigningAlg::Es256 => { async_validate( "ECDSA".to_string(), "SHA-256".to_string(), @@ -255,7 +288,7 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "es384" => { + SigningAlg::Es384 => { async_validate( "ECDSA".to_string(), "SHA-384".to_string(), @@ -266,7 +299,7 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "es512" => { + SigningAlg::Es512 => { async_validate( "ECDSA".to_string(), "SHA-512".to_string(), @@ -277,6 +310,7 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } + // TODO: Can we cover Ed25519? _ => return Err(Error::UnsupportedType), } } @@ -287,6 +321,8 @@ pub mod tests { use super::*; + use crate::SigningAlg; + #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -302,7 +338,7 @@ pub mod tests { let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); - let validated = validate_async("ps256", sig_bytes, data_bytes, key_bytes) + let validated = validate_async(SigningAlg::Ps256, sig_bytes, data_bytes, key_bytes) .await .unwrap(); @@ -318,10 +354,14 @@ pub mod tests { let data_es384_bytes = include_bytes!("../../tests/fixtures/data_es384.data"); let key_es384_bytes = include_bytes!("../../tests/fixtures/key_es384.data"); - let mut validated = - validate_async("es384", sig_es384_bytes, data_es384_bytes, key_es384_bytes) - .await - .unwrap(); + let mut validated = validate_async( + SigningAlg::Es384, + sig_es384_bytes, + data_es384_bytes, + key_es384_bytes, + ) + .await + .unwrap(); assert_eq!(validated, true); @@ -329,9 +369,14 @@ pub mod tests { let data_es512_bytes = include_bytes!("../../tests/fixtures/data_es512.data"); let key_es512_bytes = include_bytes!("../../tests/fixtures/key_es512.data"); - validated = validate_async("es512", sig_es512_bytes, data_es512_bytes, key_es512_bytes) - .await - .unwrap(); + validated = validate_async( + SigningAlg::Es512, + sig_es512_bytes, + data_es512_bytes, + key_es512_bytes, + ) + .await + .unwrap(); assert_eq!(validated, true); @@ -339,9 +384,14 @@ pub mod tests { let data_es256_bytes = include_bytes!("../../tests/fixtures/data_es256.data"); let key_es256_bytes = include_bytes!("../../tests/fixtures/key_es256.data"); - let validated = validate_async("es256", sig_es256_bytes, data_es256_bytes, key_es256_bytes) - .await - .unwrap(); + let validated = validate_async( + SigningAlg::Es256, + sig_es256_bytes, + data_es256_bytes, + key_es256_bytes, + ) + .await + .unwrap(); assert_eq!(validated, true); } @@ -361,7 +411,7 @@ pub mod tests { bad_bytes[2] = b'p'; bad_bytes[3] = b'a'; - let validated = validate_async("ps256", sig_bytes, &bad_bytes, key_bytes) + let validated = validate_async(SigningAlg::Ps256, sig_bytes, &bad_bytes, key_bytes) .await .unwrap(); diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs @@ -18,7 +18,7 @@ mod integration_1 { use c2pa::{ assertions::{c2pa_action, Action, Actions}, - create_signer, Ingredient, Manifest, ManifestStore, Result, Signer, + create_signer, Ingredient, Manifest, ManifestStore, Result, Signer, SigningAlg, }; use std::path::PathBuf; use tempfile::tempdir; @@ -31,7 +31,7 @@ mod integration_1 { signcert_path.push("tests/fixtures/certs/ps256.pub"); let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pkey_path.push("tests/fixtures/certs/ps256.pem"); - create_signer::from_files(signcert_path, pkey_path, "ps256", None) + create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None) .expect("get_signer_from_files") }