Skip to content

Commit

Permalink
feat(crypto): Introduce new (experimental) rust_native_crypto featu…
Browse files Browse the repository at this point in the history
…re (#850)

Move ed25519-dalek support from WASM (synchronous) to rust_native module.

Document which crypto implementation is used when.
  • Loading branch information
scouten-adobe authored Jan 15, 2025
1 parent 455051f commit e18a4ec
Show file tree
Hide file tree
Showing 18 changed files with 634 additions and 20 deletions.
6 changes: 6 additions & 0 deletions internal/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ rustdoc-args = ["--cfg", "docsrs"]

[features]
json_schema = ["dep:schemars"]
rust_native_crypto = ["dep:ed25519-dalek"]

[dependencies]
asn1-rs = "0.6.2"
Expand All @@ -43,6 +44,7 @@ c2pa-status-tracker = { path = "../status-tracker", version = "0.2.0" }
ciborium = "0.2.2"
const-hex = "1.14"
coset = "0.3.1"
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"
Expand Down Expand Up @@ -101,8 +103,12 @@ web-sys = { version = "0.3.58", features = [
getrandom = { version = "0.2.7", features = ["js"] }
js-sys = "0.3.58"

[dev-dependencies]
ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
actix = "0.13.1"
ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.31"
55 changes: 55 additions & 0 deletions internal/crypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,61 @@

This crate contains some of the internal cryptography implementation that is shared between the [c2pa crate](https://crates.io/crates/c2pa) and the [CAWG identity SDK crate](https://crates.io/crates/cawg-identity). It is not intended to be used directly in most cases.

## Crate features

This crate has two features, neither of which are enabled by default:

* `json_schema`: Used by c2pa-rs documentation code to generate JSON schema for types defined in this crate.
* `rust_native_crypto`: Where available, prefer Rust-native cryptography libraries for raw signature and validation implementations. (Experimental)

## Cryptographic library support

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

### Signing (synchronous or asynchronous)

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

(*) 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 | OpenSSL | `rsa` |
| `ps384` | OpenSSL | OpenSSL | `rsa` |
| `ps512` | OpenSSL | OpenSSL | `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 | OpenSSL | `rsa` |
| `ps384` | OpenSSL | OpenSSL | `rsa` |
| `ps512` | OpenSSL | OpenSSL | `rsa` |

(*) Applies to all supported platforms except WASM

### Contributions and feedback

We welcome contributions to this project. For information on contributing, providing feedback, and about ongoing work, see [Contributing](https://github.com/contentauth/c2pa-rs/blob/main/CONTRIBUTING.md). For additional information on nightly builds and testing, see [Contributing to the project](docs/project-contributions.md).
Expand Down
3 changes: 3 additions & 0 deletions internal/crypto/src/raw_signature/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub(crate) mod oids;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod openssl;

#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto", test))]
pub(crate) mod rust_native;

pub(crate) mod signer;
pub use signer::{
async_signer_from_cert_chain_and_private_key, signer_from_cert_chain_and_private_key,
Expand Down
4 changes: 2 additions & 2 deletions internal/crypto/src/raw_signature/openssl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
mod cert_chain;

mod ffi_mutex;
pub use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable};
pub(crate) use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable};

pub(crate) mod check_certificate_trust;
pub(crate) mod signers;
pub mod validators;
pub(crate) mod validators;
24 changes: 24 additions & 0 deletions internal/crypto/src/raw_signature/rust_native/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2025 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.

#![allow(unused)] // Not used on all platforms or all configs

//! Experimental support for synchronous raw signatures using Rust-native
//! crates. Intended mostly for the synchronous use cases on WASM, but may
//! eventually be used on all platforms.
//!
//! At the moment, does not offer support for all C2PA-supported cryptography
//! algorithms.
pub(crate) mod signers;
pub(crate) mod validators;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Adobe. All rights reserved.
// Copyright 2025 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),
Expand All @@ -11,6 +11,9 @@
// specific language governing permissions and limitations under
// each license.

//! This module binds Rust native logic for generating raw signatures to this
//! crate's [`RawSigner`] trait.
use crate::raw_signature::{RawSigner, RawSignerError, SigningAlg};

mod ed25519_signer;
Expand Down Expand Up @@ -39,7 +42,7 @@ pub(crate) fn signer_from_cert_chain_and_private_key(
)),

_ => Err(RawSignerError::InternalError(format!(
"unsupported algorithm: {alg}"
"unsupported signing algorithm {alg}"
))),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl RawSignatureValidator for Ed25519Validator {
data: &[u8],
public_key: &[u8],
) -> Result<(), RawSignatureValidationError> {
let (_, public_key) = SubjectPublicKeyInfo::from_der(&public_key)
let (_, public_key) = SubjectPublicKeyInfo::from_der(public_key)
.map_err(|_| RawSignatureValidationError::InvalidPublicKey)?;

let public_key = public_key
Expand All @@ -43,15 +43,15 @@ impl RawSignatureValidator for Ed25519Validator {
}

let mut public_key_slice: [u8; PUBLIC_KEY_LENGTH] = Default::default();
public_key_slice.copy_from_slice(&public_key);
public_key_slice.copy_from_slice(public_key);

let vk = VerifyingKey::from_bytes(&public_key_slice)
.map_err(|_| RawSignatureValidationError::InvalidPublicKey)?;

let ed_sig = Signature::from_slice(sig)
.map_err(|_| RawSignatureValidationError::InvalidSignature)?;

match vk.verify(&data, &ed_sig) {
match vk.verify(data, &ed_sig) {
Ok(_) => Ok(()),
Err(_) => Err(RawSignatureValidationError::SignatureMismatch),
}
Expand Down
30 changes: 30 additions & 0 deletions internal/crypto/src/raw_signature/rust_native/validators/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 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.

//! This module binds Rust native logic for generating raw signatures to this
//! crate's [`RawSignatureValidator`] trait.
use bcder::Oid;

use crate::raw_signature::{oids::*, RawSignatureValidator, SigningAlg};

mod ed25519_validator;
pub use ed25519_validator::Ed25519Validator;

/// Return a validator for the given signing algorithm.
pub fn validator_for_signing_alg(alg: SigningAlg) -> Option<Box<dyn RawSignatureValidator>> {
match alg {
SigningAlg::Ed25519 => Some(Box::new(Ed25519Validator {})),
_ => None,
}
}
16 changes: 10 additions & 6 deletions internal/crypto/src/raw_signature/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,23 @@ pub fn signer_from_cert_chain_and_private_key(
alg: SigningAlg,
time_stamp_service_url: Option<String>,
) -> Result<Box<dyn RawSigner + Send + Sync>, RawSignerError> {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto"))]
{
return crate::raw_signature::openssl::signers::signer_from_cert_chain_and_private_key(
match crate::raw_signature::rust_native::signers::signer_from_cert_chain_and_private_key(
cert_chain,
private_key,
alg,
time_stamp_service_url,
);
time_stamp_service_url.clone(),
) {
Ok(signer) => return Ok(signer),
Err(RawSignerError::InternalError(_)) => (),
Err(err) => return Err(err),
}
}

#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
#[cfg(not(target_arch = "wasm32"))]
{
return crate::raw_signature::webcrypto::signers::signer_from_cert_chain_and_private_key(
return crate::raw_signature::openssl::signers::signer_from_cert_chain_and_private_key(
cert_chain,
private_key,
alg,
Expand Down
9 changes: 9 additions & 0 deletions internal/crypto/src/raw_signature/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ pub trait AsyncRawSignatureValidator {
/// Which validators are available may vary depending on the platform and
/// which crate features were enabled.
pub fn validator_for_signing_alg(alg: SigningAlg) -> Option<Box<dyn RawSignatureValidator>> {
#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto"))]
{
if let Some(validator) =
crate::raw_signature::rust_native::validators::validator_for_signing_alg(alg)
{
return Some(validator);
}
}

#[cfg(not(target_arch = "wasm32"))]
if let Some(validator) =
crate::raw_signature::openssl::validators::validator_for_signing_alg(alg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl AsyncRawSignatureValidator for Ed25519Validator {
) -> Result<(), RawSignatureValidationError> {
// Sync and async cases are identical for Ed25519.

let sync_validator = crate::raw_signature::webcrypto::validators::Ed25519Validator {};
let sync_validator = crate::raw_signature::rust_native::validators::Ed25519Validator {};
sync_validator.validate(sig, data, public_key)
}
}
3 changes: 1 addition & 2 deletions internal/crypto/src/raw_signature/webcrypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ pub(crate) use async_validators::{
};

pub(crate) mod check_certificate_trust;
pub(crate) mod signers;
pub mod validators;
pub(crate) mod validators;

mod window_or_worker;
pub use window_or_worker::{WasmCryptoError, WindowOrWorker};
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ use crate::raw_signature::{oids::*, RawSignatureValidator, SigningAlg};
mod ecdsa_validator;
pub use ecdsa_validator::EcdsaValidator;

mod ed25519_validator;
pub use ed25519_validator::Ed25519Validator;

mod rsa_legacy_validator;
pub(crate) use rsa_legacy_validator::RsaLegacyValidator;

Expand All @@ -36,10 +33,10 @@ pub fn validator_for_signing_alg(alg: SigningAlg) -> Option<Box<dyn RawSignature
SigningAlg::Es256 => Some(Box::new(EcdsaValidator::Es256)),
SigningAlg::Es384 => Some(Box::new(EcdsaValidator::Es384)),
SigningAlg::Es512 => None, /* why is this unimplemented? */
SigningAlg::Ed25519 => Some(Box::new(Ed25519Validator {})),
SigningAlg::Ps256 => Some(Box::new(RsaValidator::Ps256)),
SigningAlg::Ps384 => Some(Box::new(RsaValidator::Ps384)),
SigningAlg::Ps512 => Some(Box::new(RsaValidator::Ps512)),
_ => None,
}
}

Expand Down
1 change: 1 addition & 0 deletions internal/crypto/src/tests/raw_signature/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@

mod async_signers;
mod async_validators;
mod rust_native;
mod signers;
mod validators;
15 changes: 15 additions & 0 deletions internal/crypto/src/tests/raw_signature/rust_native/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2025 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.

mod signers;
mod validators;
Loading

0 comments on commit e18a4ec

Please sign in to comment.