Skip to content

Commit

Permalink
fix: Remove dependency on SubtleCrypto (#881)
Browse files Browse the repository at this point in the history
  • Loading branch information
mauricefisher64 authored Jan 31, 2025
1 parent 56bea2f commit 6236c66
Show file tree
Hide file tree
Showing 42 changed files with 754 additions and 891 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions internal/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,17 @@ const-hex = "1.14"
const-oid = { version = "0.9.6", optional = true }
coset = "0.3.8"
der = { version = "0.7.9", optional = true }
ecdsa = { version = "0.16.9", features = ["digest", "sha2"] }
ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"], optional = true }
getrandom = { version = "0.2.7", features = ["js"] }
hex = "0.4.3"
nom = "7.1.3"
num-bigint-dig = { version = "0.8.4", optional = true }
p256 = "0.13.2"
p384 = "0.13.0"
p521 = { version = "0.13.3", features = ["pkcs8", "digest", "ecdsa"] }
pkcs1 = { version = "0.7.5", optional = true }
pkcs8 = "0.10.2"
rand = "0.8.5"
rasn = "0.22.0"
rasn-ocsp = "0.22.0"
Expand Down Expand Up @@ -98,10 +103,7 @@ features = ["now", "wasmbind"]
async-trait = "0.1.77"
const-oid = "0.9.6"
der = "0.7.9"
ecdsa = "0.16.9"
ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"] }
p256 = "0.13.2"
p384 = "0.13.0"
num-bigint-dig = "0.8.4"
pkcs1 = "0.7.5"
rsa = { version = "0.9.7", features = ["pem", "sha2"] }
Expand Down
39 changes: 12 additions & 27 deletions internal/crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This crate has two features, neither of which are enabled by default:

`c2pa-crypto` will use different cryptography libraries depending on which platform and feature flags are used:

### Signing (synchronous or asynchronous)
### Signing

| C2PA `SigningAlg` | Default (*) | `feature = "rust_native_crypto"` (*) | WASM |
| --- | --- | --- | --- |
Expand All @@ -30,32 +30,17 @@ This crate has two features, neither of which are enabled by default:
(*) Applies to all supported platforms except WASM <br />
❌ = not supported

### Validation (synchronous)

| C2PA `SigningAlg` | Default (*) | `feature = "rust_native_crypto"` (*) | WASM |
| --- | --- | --- | --- |
| `es256` | OpenSSL | OpenSSL | `p256` |
| `es384` | OpenSSL | OpenSSL | `p384` |
| `es512` | OpenSSL | OpenSSL ||
| `ed25519` | OpenSSL | `ed25519-dalek` | `ed25519-dalek` |
| `ps256` | OpenSSL | `rsa` | `rsa` |
| `ps384` | OpenSSL | `rsa` | `rsa` |
| `ps512` | OpenSSL | `rsa` | `rsa` |

(*) Applies to all supported platforms except WASM <br />
❌ = not supported

### Validation (asynchronous)

| C2PA `SigningAlg` | Default (*) | `feature = "rust_native_crypto"` (*) | WASM |
| --- | --- | --- | --- |
| `es256` | OpenSSL | OpenSSL | WebCrypto |
| `es384` | OpenSSL | OpenSSL | WebCrypto |
| `es512` | OpenSSL | OpenSSL | WebCrypto |
| `ed25519` | OpenSSL | `ed25519-dalek` | `ed25519-dalek` |
| `ps256` | OpenSSL | `rsa` | `rsa` |
| `ps384` | OpenSSL | `rsa` | `rsa` |
| `ps512` | OpenSSL | `rsa` | `rsa` |
### Validation

| C2PA `SigningAlg` | Default (*) | `feature = "rust_native_crypto"` (*) or WASM |
| --- | --- | --- |
| `es256` | OpenSSL | `p256` |
| `es384` | OpenSSL | `p384` |
| `es512` | OpenSSL | `p521` |
| `ed25519` | OpenSSL | `ed25519-dalek` |
| `ps256` | OpenSSL | `rsa` |
| `ps384` | OpenSSL | `rsa` |
| `ps512` | OpenSSL | `rsa` |

(*) Applies to all supported platforms except WASM

Expand Down
18 changes: 2 additions & 16 deletions internal/crypto/src/cose/certificate_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ pub fn check_certificate_profile(
let mut aki_good = false;
let mut ski_good = false;
let mut key_usage_good = false;
let mut handled_all_critical = true;
let extended_key_usage_good = match tbscert
.extended_key_usage()
.map_err(|_| CertificateProfileError::InvalidCertificate)?
Expand Down Expand Up @@ -417,28 +416,15 @@ pub fn check_certificate_profile(
ParsedExtension::CRLNumber(_) => (),
ParsedExtension::ReasonCode(_) => (),
ParsedExtension::InvalidityDate(_) => (),

ParsedExtension::Unparsed => {
if e.critical {
// Unhandled critical extension.
handled_all_critical = false;
}
}

_ => {
if e.critical {
// Unhandled critical extension.
handled_all_critical = false;
}
}
_ => (),
}
}

// If cert is a CA must have valid SubjectKeyIdentifier.
ski_good = if tbscert.is_ca() { ski_good } else { true };

// Check all flags.
if aki_good && ski_good && key_usage_good && extended_key_usage_good && handled_all_critical {
if aki_good && ski_good && key_usage_good && extended_key_usage_good {
Ok(())
} else {
log_item!(
Expand Down
33 changes: 8 additions & 25 deletions internal/crypto/src/cose/certificate_trust_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,14 @@ impl CertificateTrustPolicy {
return Ok(());
}

if _async {
#[cfg(target_arch = "wasm32")]
{
return crate::raw_signature::webcrypto::check_certificate_trust::check_certificate_trust(
self,
chain_der,
end_entity_cert_der,
signing_time_epoch,
)
.await;
}
#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto", test))]
{
return crate::raw_signature::rust_native::check_certificate_trust::check_certificate_trust(
self,
chain_der,
end_entity_cert_der,
signing_time_epoch,
);
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -372,20 +369,6 @@ impl From<crate::raw_signature::openssl::OpenSslMutexUnavailable> for Certificat
}
}

#[cfg(target_arch = "wasm32")]
impl From<crate::raw_signature::webcrypto::WasmCryptoError> for CertificateTrustError {
fn from(err: crate::raw_signature::webcrypto::WasmCryptoError) -> Self {
match err {
crate::raw_signature::webcrypto::WasmCryptoError::UnknownContext => {
Self::InternalError("unknown WASM context".to_string())
}
crate::raw_signature::webcrypto::WasmCryptoError::NoCryptoAvailable => {
Self::InternalError("WASM crypto unavailable".to_string())
}
}
}
}

/// This error can occur when adding certificates to a
/// [`CertificateTrustPolicy`].
#[derive(Debug, Eq, PartialEq)]
Expand Down
30 changes: 26 additions & 4 deletions internal/crypto/src/cose/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ use coset::{
};
use serde_bytes::ByteBuf;

use super::cert_chain_from_sign1;
use crate::{
cose::{add_sigtst_header, add_sigtst_header_async, CoseError, TimeStampStorage},
p1363::{der_to_p1363, parse_ec_der_sig},
ec_utils::{der_to_p1363, ec_curve_from_public_key_der, parse_ec_der_sig},
raw_signature::{AsyncRawSigner, RawSigner, SigningAlg},
};

Expand Down Expand Up @@ -154,7 +155,17 @@ pub fn sign_v1(
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
if parse_ec_der_sig(&signature).is_ok() {
// Fix up DER signature to be in P1363 format.
der_to_p1363(&signature, alg)?
let certs = cert_chain_from_sign1(&sign1)?;

let signing_cert = certs.first().ok_or(CoseError::CborGenerationError(
"bad certificate chain".to_string(),
))?;

let curve = ec_curve_from_public_key_der(signing_cert).ok_or(
CoseError::CborGenerationError("incorrect EC signature format".to_string()),
)?;

der_to_p1363(&signature, curve.p1363_sig_len())?
} else {
signature
}
Expand All @@ -165,6 +176,7 @@ pub fn sign_v1(
// The payload is provided elsewhere, so we don't need to repeat it in the
// `Cose_Sign1` structure.
sign1.payload = None;

pad_cose_sig(&mut sign1, box_size)
}

Expand Down Expand Up @@ -217,7 +229,17 @@ pub fn sign_v2(
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
if parse_ec_der_sig(&signature).is_ok() {
// Fix up DER signature to be in P1363 format.
der_to_p1363(&signature, alg)?
let certs = cert_chain_from_sign1(&sign1)?;

let signing_cert = certs.first().ok_or(CoseError::CborGenerationError(
"bad certificate chain".to_string(),
))?;

let curve = ec_curve_from_public_key_der(signing_cert).ok_or(
CoseError::CborGenerationError("incorrect EC signature format".to_string()),
)?;

der_to_p1363(&signature, curve.p1363_sig_len())?
} else {
signature
}
Expand Down Expand Up @@ -348,7 +370,7 @@ fn pad_cose_sig(sign1: &mut CoseSign1, end_size: Option<usize>) -> Result<Vec<u8
return Ok(cur_vec);
}

// check for box too small and matched size
// Check for box too small and matched size.
if cur_size + PAD_OFFSET > end_size {
return Err(CoseError::BoxSizeTooSmall);
}
Expand Down
52 changes: 38 additions & 14 deletions internal/crypto/src/cose/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// specific language governing permissions and limitations under
// each license.

use std::io::Cursor;
use std::io::Write;

use asn1_rs::FromDer;
use async_generic::async_generic;
Expand All @@ -28,12 +28,13 @@ use x509_parser::prelude::X509Certificate;

use crate::{
asn1::rfc3161::TstInfo,
base64::encode,
cose::{
cert_chain_from_sign1, check_certificate_profile, parse_cose_sign1, signing_alg_from_sign1,
validate_cose_tst_info, validate_cose_tst_info_async, CertificateInfo,
CertificateTrustError, CertificateTrustPolicy, CoseError,
},
p1363::parse_ec_der_sig,
ec_utils::parse_ec_der_sig,
raw_signature::{validator_for_signing_alg, SigningAlg},
time_stamp::TimeStampError,
};
Expand Down Expand Up @@ -93,9 +94,13 @@ impl Verifier<'_> {
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
if parse_ec_der_sig(&sign1.signature).is_ok() {
// Should have been in P1363 format, not DER.
log_item!("Cose_Sign1", "unsupported signature format", "verify_cose")
.validation_status(SIGNING_CREDENTIAL_INVALID)
.failure_no_throw(validation_log, CoseError::InvalidEcdsaSignature);
log_item!(
"Cose_Sign1",
"unsupported signature format (EC signature should be in P1363 r|s format)",
"verify_cose"
)
.validation_status(SIGNING_CREDENTIAL_INVALID)
.failure_no_throw(validation_log, CoseError::InvalidEcdsaSignature);

// validation_log.log(log_item, CoseError::InvalidEcdsaSignature)?;
return Err(CoseError::InvalidEcdsaSignature);
Expand Down Expand Up @@ -313,18 +318,37 @@ impl Verifier<'_> {
}

fn dump_cert_chain(certs: &[Vec<u8>]) -> Result<Vec<u8>, CoseError> {
let mut out_buf: Vec<u8> = Vec::new();
let mut writer = Cursor::new(out_buf);
let mut writer = Vec::new();

let line_len = 64;
let cert_begin = "-----BEGIN CERTIFICATE-----";
let cert_end = "-----END CERTIFICATE-----";

for der_bytes in certs {
let c = x509_certificate::X509Certificate::from_der(der_bytes)
.map_err(|_e| CoseError::CborParsingError("invalid X509 certificate".to_string()))?;
let cert_base_str = encode(der_bytes);

// Break line into fixed-length lines.
let cert_lines = cert_base_str
.chars()
.collect::<Vec<char>>()
.chunks(line_len)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>();

writer
.write_fmt(format_args!("{}\n", cert_begin))
.map_err(|_e| CoseError::InternalError("could not write PEM".to_string()))?;

for l in cert_lines {
writer
.write_fmt(format_args!("{}\n", l))
.map_err(|_e| CoseError::InternalError("could not write PEM".to_string()))?;
}

c.write_pem(&mut writer).map_err(|_| {
CoseError::InternalError("I/O error constructing cert_chain dump".to_string())
})?;
writer
.write_fmt(format_args!("{}\n", cert_end))
.map_err(|_e| CoseError::InternalError("could not write PEM".to_string()))?;
}

out_buf = writer.into_inner();
Ok(out_buf)
Ok(writer)
}
Loading

0 comments on commit 6236c66

Please sign in to comment.