Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BoringSSL compatibiliy #703

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion export_schema/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ rust-version = "1.76.0"

[dependencies]
anyhow = "1.0.40"
c2pa = { path = "../sdk", features = ["json_schema", "unstable_api"] }
c2pa = { path = "../sdk", default-features = false, features = ["json_schema", "unstable_api"] }
schemars = "0.8.21"
serde_json = "1.0.117"
9 changes: 8 additions & 1 deletion internal/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ rustdoc-args = ["--cfg", "docsrs"]

[features]
json_schema = ["dep:schemars"]
openssl = ["dep:openssl"]
openssl = ["dep:openssl", "_anyssl"]
boringssl = ["dep:boring", "dep:pkcs8", "dep:pkcs1", "_anyssl"]

# Internal-only. Use the `openssl` feature to enable it.
_anyssl = []

[dependencies]
base64 = "0.22.1"
Expand All @@ -45,6 +49,9 @@ x509-certificate = "0.21.0"
x509-parser = "0.16.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
boring = { version = "4.13", optional = true }
pkcs8 = { version = "0.10.2", features = ["std"], optional = true }
pkcs1 = { version = "0.7.5", features = ["pem", "std"], optional = true }
openssl = { version = "0.10.61", features = ["vendored"], optional = true }
ureq = "2.4.0"
url = "2.5.3"
Expand Down
11 changes: 7 additions & 4 deletions internal/crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ pub(crate) mod internal;

pub mod ocsp;

#[cfg(all(feature = "openssl", not(target_arch = "wasm32")))]
pub mod openssl;

#[cfg(all(feature = "openssl", target_arch = "wasm32"))]
#[cfg(all(feature = "_anyssl", target_arch = "wasm32"))]
compile_error!("OpenSSL feature is not compatible with WASM platform");

#[cfg(all(feature = "boringssl", feature = "openssl"))]
compile_error!("BoringSSL and OpenSSL can't be both enabled at the same time");

#[cfg(feature = "_anyssl")]
pub mod openssl;

pub mod raw_signature;

mod signing_alg;
Expand Down
2 changes: 2 additions & 0 deletions internal/crypto/src/openssl/validators/ecdsa_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::{
bn::BigNum, ec::EcKey, ecdsa::EcdsaSig, hash::MessageDigest, pkey::PKey, sign::Verifier,
};
Expand Down
2 changes: 2 additions & 0 deletions internal/crypto/src/openssl/validators/ed25519_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::{pkey::PKey, sign::Verifier};

use crate::{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#![allow(missing_docs)] // REMOVE once this becomes `pub(crate)`

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa, sign::Verifier};

use crate::{
Expand Down
52 changes: 51 additions & 1 deletion internal/crypto/src/openssl/validators/rsa_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::{
hash::MessageDigest,
pkey::PKey,
Expand Down Expand Up @@ -45,7 +47,55 @@ impl RawSignatureValidator for RsaValidator {
public_key: &[u8],
) -> Result<(), RawSignatureValidationError> {
let _openssl = OpenSslMutex::acquire()?;
let rsa = Rsa::public_key_from_der(public_key)?;
let rsa = match Rsa::public_key_from_der(public_key) {
Ok(rsa) => rsa,
#[cfg(not(feature = "boringssl"))]
Err(err) => return Err(err.into()),
#[cfg(feature = "boringssl")]
Err(err) => {
use boring::bn::BigNum;
use pkcs8::der::asn1::BitStringRef;

// BoringSSL can't parse RSA-PSS parameters. This doesn't matter, because
// OpenSSL can't parse them either, and the C2PA SDK throws away
// "incompatible values" anyway.

// It's safe to ignore PSS parameters in signature verification:
// - the digest algorithm can't be changed, because the same algorithm is used
// for the message digest.
// - the mask parameter is always MGF1
// - salt len defaults to hash output len, and the salt is never used directly,
// only hashed.
//
// They're checked in this implementation anyway.

let pk = pkcs8::SubjectPublicKeyInfo::<pkcs1::RsaPssParams<'_>, BitStringRef<'_>>::try_from(public_key)
.ok()
.filter(|spki| {
// OID for RSASSA-PSS ASN.1
spki.algorithm.oid
== pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10")
})
.and_then(|spki| {
let pss = spki.algorithm.parameters?;
let required_salt_len = match self {
Self::Ps256 => 32,
Self::Ps384 => 48,
Self::Ps512 => 64,
};
// OID for MGF1 ASN.1
if pss.salt_len != required_salt_len || pss.mask_gen.oid != pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8") {
return None;
}
pkcs1::RsaPublicKey::try_from(spki.subject_public_key.raw_bytes()).ok()
})
.ok_or(err)?;

let n = BigNum::from_slice(pk.modulus.as_bytes())?;
let e = BigNum::from_slice(pk.public_exponent.as_bytes())?;
Rsa::from_public_components(n, e)?
}
};

// Rebuild RSA keys to eliminate incompatible values.
let n = rsa.n().to_owned()?;
Expand Down
13 changes: 8 additions & 5 deletions internal/crypto/src/raw_signature/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// each license.

use bcder::Oid;
#[cfg(feature = "boringssl")]
use boring as openssl;
use thiserror::Error;

use super::oids::*;
Expand Down Expand Up @@ -40,7 +42,7 @@ pub trait RawSignatureValidator {
/// 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(feature = "openssl")]
#[cfg(feature = "_anyssl")]
if let Some(validator) = crate::openssl::validators::validator_for_signing_alg(alg) {
return Some(validator);
}
Expand All @@ -50,6 +52,7 @@ pub fn validator_for_signing_alg(alg: SigningAlg) -> Option<Box<dyn RawSignature
return Some(validator);
}

let _ = alg; // mark as used if none are enabled
None
}

Expand All @@ -72,7 +75,7 @@ pub fn validator_for_sig_and_hash_algs(
{
// TO REVIEW: Do we need any of the RSA-PSS algorithms for this use case?

#[cfg(feature = "openssl")]
#[cfg(feature = "_anyssl")]
if let Some(validator) =
crate::openssl::validators::validator_for_sig_and_hash_algs(sig_alg, hash_alg)
{
Expand Down Expand Up @@ -117,12 +120,12 @@ pub enum RawSignatureValidationError {
///
/// NOTE: We do not directly capture the OpenSSL error itself because it
/// lacks an Eq implementation. Instead we capture the error description.
#[cfg(feature = "openssl")]
#[cfg(feature = "_anyssl")]
#[error("an error was reported by OpenSSL native code: {0}")]
OpenSslError(String),

/// The OpenSSL native code mutex could not be acquired.
#[cfg(feature = "openssl")]
#[cfg(feature = "_anyssl")]
#[error(transparent)]
OpenSslMutexUnavailable(#[from] crate::openssl::OpenSslMutexUnavailable),

Expand All @@ -144,7 +147,7 @@ pub enum RawSignatureValidationError {
InternalError(&'static str),
}

#[cfg(feature = "openssl")]
#[cfg(feature = "_anyssl")]
impl From<openssl::error::ErrorStack> for RawSignatureValidationError {
fn from(err: openssl::error::ErrorStack) -> Self {
Self::OpenSslError(err.to_string())
Expand Down
2 changes: 1 addition & 1 deletion internal/crypto/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod hash;
mod internal;
mod ocsp;

#[cfg(all(feature = "openssl", not(target_arch = "wasm32")))]
#[cfg(all(feature = "_anyssl", not(target_arch = "wasm32")))]
mod openssl;

mod raw_signature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::x509::X509;

use crate::{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;

use crate::{
openssl::validators::Ed25519Validator,
raw_signature::{RawSignatureValidationError, RawSignatureValidator},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::x509::X509;

use crate::{
Expand Down
2 changes: 2 additions & 0 deletions internal/crypto/src/tests/openssl/validators/rsa_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(feature = "boringssl")]
use boring as openssl;
use openssl::x509::X509;

use crate::{
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 @@ -11,4 +11,5 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(any(target_arch = "wasm32", feature = "_anyssl"))]
mod validator;
15 changes: 9 additions & 6 deletions make_test_images/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.76.0"

[[bin]]
name = "make_test_images"
required-features = ["default"]

[dependencies]
anyhow = "1.0.40"
c2pa = { path = "../sdk", default-features = false, features = [
"file_io",
"openssl_sign",
"unstable_api",
"file_io",
] }
c2pa = { path = "../sdk", default-features = false, features = ["unstable_api"] }
env_logger = "0.11"
log = "0.4.8"
image = { version = "0.25.2", default-features = false, features = [
Expand All @@ -26,3 +25,7 @@ regex = "1.5.6"
serde = "1.0.197"
serde_json = { version = "1.0.117", features = ["preserve_order"] }
tempfile = "3.10.1"

[features]
# prevents these features from being always enabled in the workspace
default = ["c2pa/openssl_sign", "c2pa/file_io"]
18 changes: 14 additions & 4 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ rustdoc-args = ["--cfg", "docsrs"]
default = ["v1_api"]
add_thumbnails = ["image"]
psxxx_ocsp_stapling_experimental = []
file_io = ["openssl_sign"]
file_io = ["boringssl_sign"]
serialize_thumbnails = []
no_interleaved_io = ["file_io"]
fetch_remote_manifests = []
openssl = ["dep:openssl", "c2pa-crypto/openssl"]
openssl_sign = ["openssl", "c2pa-crypto/openssl"]
openssl = ["dep:openssl", "c2pa-crypto/openssl", "_anyssl"]
openssl_sign = ["openssl", "c2pa-crypto/openssl", "_anyssl_sign"]
boringssl = ["dep:boring", "dep:pkcs8", "dep:pkcs1", "c2pa-crypto/boringssl", "_anyssl"]
boringssl_sign = ["boringssl", "c2pa-crypto/boringssl", "_anyssl_sign"]
json_schema = ["dep:schemars", "c2pa-crypto/json_schema"]
pdf = ["dep:lopdf"]
v1_api = []
Expand All @@ -44,6 +46,11 @@ unstable_api = []
# It enables some low-overhead timing features used in our development cycle.
diagnostics = []

# Internal-only. Use the `openssl` feature to enable it.
_anyssl = []
# Internal-only. Use the `openssl_sign` feature to enable it.
_anyssl_sign = []

[[example]]
name = "client"
required-features = ["file_io"]
Expand Down Expand Up @@ -137,6 +144,9 @@ image = { version = "0.24.7", default-features = false, features = [
"png",
], optional = true }
instant = "0.1.12"
boring = { version = "4.13", optional = true }
pkcs8 = { version = "0.10.2", features = ["std"], optional = true }
pkcs1 = { version = "0.7.5", features = ["pem", "std"], optional = true }
openssl = { version = "0.10.61", features = ["vendored"], optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand All @@ -163,7 +173,7 @@ web-sys = { version = "0.3.58", features = [
[dev-dependencies]
anyhow = "1.0.40"
mockall = "0.11.2"
c2pa = { path = ".", features = [
c2pa = { path = ".", default-features = false, features = [
"unstable_api",
] } # allow integration tests to use the new API
glob = "0.3.1"
Expand Down
4 changes: 2 additions & 2 deletions sdk/examples/client/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use anyhow::Result;
// This example is not designed to work with a wasm build
// so we provide this shell to avoid testing errors

#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "_anyssl")]
mod client;
fn main() -> Result<()> {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "_anyssl")]
client::main()?;
Ok(())
}
Loading