From d9b198ae4530a63932d8d674b1dd033fc08b0dfe Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 23 Jan 2023 08:55:43 +0100 Subject: [PATCH] Support compiling to wasm32 architectures Enable support for compiling sigstore-rs to WASM, making it usable from browser applications and on wasm32 VMs. Signed-off-by: Ulf Lilleengen --- .github/workflows/tests.yml | 16 ++++++++++++++++ Cargo.toml | 10 ++++++---- README.md | 10 ++++++++++ examples/openidflow/openidconnect/main.rs | 2 +- src/cosign/client.rs | 2 +- src/cosign/mod.rs | 2 +- src/crypto/verification_key.rs | 6 +++++- src/fulcio/oauth.rs | 2 +- src/mock_client.rs | 2 +- src/oauth/openidflow.rs | 7 ++++++- src/registry/mod.rs | 4 ++-- src/registry/oci_caching_client.rs | 2 +- src/registry/oci_client.rs | 2 +- 13 files changed, 52 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f01a4552ba..399041534c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,6 +17,22 @@ jobs: with: command: check + check-wasm: + name: Check WASM + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 + with: + profile: minimal + toolchain: stable + target: wasm32-unknown-unknown + override: true + - uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # v1.0.3 + with: + command: check + args: --no-default-features --features wasm + test: name: Test Suite runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index e45e9778d3..56a076aed5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ readme = "README.md" [features] default = ["full-native-tls", "cached-client", "tuf"] +wasm = ["getrandom/js"] full-native-tls = ["fulcio-native-tls", "rekor-native-tls", "cosign-native-tls", "mock-client-native-tls"] full-rustls-tls = ["fulcio-rustls-tls", "rekor-rustls-tls", "cosign-rustls-tls", "mock-client-rustls-tls"] @@ -28,7 +29,7 @@ oauth = [] rekor-native-tls = [ "reqwest/native-tls", "rekor"] rekor-rustls-tls = [ "reqwest/rustls-tls", "rekor" ] -rekor = [] +rekor = ["reqwest"] tuf = [ "tough", "regex" ] @@ -55,7 +56,7 @@ cfg-if = "1.0.0" chrono = { version = "0.4.23", feature = "clock" } const-oid = "0.9.1" der = "0.6.1" -digest = "0.10.3" +digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.15", features = [ "pkcs8", "digest", "der" ] } ed25519 = { version = "=2.1", features = [ "alloc" ] } ed25519-dalek = { version = "2.0.0-pre.0", features = [ "pkcs8", "rand_core" ] } @@ -63,15 +64,16 @@ elliptic-curve = { version = "0.12.2", features = [ "arithmetic", "pem" ] } lazy_static = "1.4.0" oci-distribution = { version = "0.9", default-features = false, optional = true } olpc-cjson = "0.1" -open = "3.0.1" openidconnect = { version = "2.3", default-features = false, features = [ "reqwest" ], optional = true} p256 = "0.12" p384 = "0.12" +webbrowser = "0.8.4" pem = "1.0.2" picky = { version = "7.0.0-rc.3", default-features = false, features = [ "x509", "ec" ] } pkcs1 = "0.4.0" pkcs8 = { version = "0.9.0", features = ["pem", "alloc", "pkcs5", "encryption"] } rand = { version = "0.8.5", features = [ "getrandom", "std" ] } +getrandom = "0.2.8" regex = { version = "1.5.5", optional = true } reqwest = { version = "0.11", default-features = false, features = ["json", "multipart"], optional = true} rsa = "0.8.0" @@ -81,7 +83,7 @@ serde_json = "1.0.79" sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } thiserror = "1.0.30" -tokio = { version = "1.17.0", features = ["full"] } +tokio = { version = "1.17.0", features = ["rt"] } tough = { version = "0.12", features = [ "http" ], optional = true } tracing = "0.1.31" url = "2.2.2" diff --git a/README.md b/README.md index f5afba2c0e..d119b1192f 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,16 @@ For example, `openidconnect` can be run with the following command: cargo run --example openidconnect ``` +## WebAssembly/WASM support + +To embedded this crate in WASM modules, build it using the `wasm` cargo feature: + +```bash +cargo build --no-default-features --features wasm --target wasm32-unknown-unknown +``` + +NOTE: The wasm32-wasi target architecture is not yet supported. + ## Contributing Contributions are welcome! Please see the [contributing guidelines](CONTRIBUTING.md). diff --git a/examples/openidflow/openidconnect/main.rs b/examples/openidflow/openidconnect/main.rs index 4db193715d..b2f9384139 100644 --- a/examples/openidflow/openidconnect/main.rs +++ b/examples/openidflow/openidconnect/main.rs @@ -27,7 +27,7 @@ fn main() -> Result<(), anyhow::Error> { match oidc_url.as_ref() { Ok(url) => { - open::that(url.0.to_string())?; + webbrowser::open(url.0.as_ref())?; println!( "Open this URL in a browser if it does not automatically open for you:\n{}\n", url.0 diff --git a/src/cosign/client.rs b/src/cosign/client.rs index 72afec425a..b92b4341a1 100644 --- a/src/cosign/client.rs +++ b/src/cosign/client.rs @@ -42,7 +42,7 @@ pub struct Client { pub(crate) fulcio_cert_pool: Option, } -#[async_trait] +#[async_trait(?Send)] impl CosignCapabilities for Client { async fn triangulate(&mut self, image: &str, auth: &Auth) -> Result<(String, String)> { let image_reference: oci_distribution::Reference = diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index d555249794..6b0b156840 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -63,7 +63,7 @@ pub mod payload; pub use payload::simple_signing; pub mod constraint; -#[async_trait] +#[async_trait(?Send)] /// Cosign Abilities that have to be implemented by a /// Cosign client pub trait CosignCapabilities { diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 7947dbd0a0..02c85d3379 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -26,7 +26,10 @@ use super::{ Signature, SigningScheme, }; -use crate::{cosign::constants::ED25519, errors::*}; +use crate::errors::*; + +#[cfg(feature = "cosign")] +use crate::cosign::constants::ED25519; /// A key that can be used to verify signatures. /// @@ -101,6 +104,7 @@ impl<'a> TryFrom<&SubjectPublicKeyInfo<'a>> for CosignVerificationKey { )) } // + #[cfg(feature = "cosign")] ED25519 => Ok(CosignVerificationKey::ED25519( ed25519_dalek::VerifyingKey::try_from(*subject_pub_key_info)?, )), diff --git a/src/fulcio/oauth.rs b/src/fulcio/oauth.rs index 56acae6db9..869e9d270b 100644 --- a/src/fulcio/oauth.rs +++ b/src/fulcio/oauth.rs @@ -90,7 +90,7 @@ impl OauthTokenProvider { match oidc_url.as_ref() { Ok(url) => { - open::that(url.0.to_string())?; + webbrowser::open(url.0.as_ref())?; println!( "Open this URL in a browser if it does not automatically open for you:\n{}\n", url.0, diff --git a/src/mock_client.rs b/src/mock_client.rs index 8be823f2d8..985a859905 100644 --- a/src/mock_client.rs +++ b/src/mock_client.rs @@ -33,7 +33,7 @@ pub(crate) mod test { pub push_response: Option>, } - #[async_trait] + #[async_trait(?Send)] impl crate::registry::ClientCapabilities for MockOciClient { async fn fetch_manifest_digest( &mut self, diff --git a/src/oauth/openidflow.rs b/src/oauth/openidflow.rs index 58c13c41e3..fccaf8e42e 100644 --- a/src/oauth/openidflow.rs +++ b/src/oauth/openidflow.rs @@ -87,12 +87,15 @@ use openidconnect::core::{ CoreClient, CoreIdToken, CoreIdTokenClaims, CoreIdTokenVerifier, CoreProviderMetadata, CoreResponseType, CoreTokenResponse, }; -use openidconnect::reqwest::{async_http_client, http_client}; +use openidconnect::reqwest::async_http_client; use openidconnect::{ AuthenticationFlow, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, Scope, }; +#[cfg(not(target_arch = "wasm32"))] +use openidconnect::reqwest::http_client; + use std::io::{BufRead, BufReader, Write}; use std::net::TcpListener; use url::Url; @@ -157,6 +160,7 @@ impl OpenIDAuthorize { Ok((authorize_url, client, nonce, pkce_verifier)) } + #[cfg(not(target_arch = "wasm32"))] pub fn auth_url(&self) -> Result<(Url, CoreClient, Nonce, PkceCodeVerifier)> { let issuer = IssuerUrl::new(self.oidc_issuer.to_owned()).expect("Missing the OIDC_ISSUER."); @@ -268,6 +272,7 @@ impl RedirectListener { Err(SigstoreError::CodePairError) } + #[cfg(not(target_arch = "wasm32"))] pub fn redirect_listener(self) -> Result<(CoreIdTokenClaims, CoreIdToken)> { let code = self.redirect_listener_internal()?; diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 11e8277f03..6b0b91a9b6 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -30,9 +30,9 @@ use crate::errors::Result; use async_trait::async_trait; -#[async_trait] +#[async_trait(?Send)] /// Capabilities that are expected to be provided by a registry client -pub(crate) trait ClientCapabilities: Send + Sync { +pub(crate) trait ClientCapabilities { async fn fetch_manifest_digest( &mut self, image: &oci_distribution::Reference, diff --git a/src/registry/oci_caching_client.rs b/src/registry/oci_caching_client.rs index f958b14a71..8d4af839bf 100644 --- a/src/registry/oci_caching_client.rs +++ b/src/registry/oci_caching_client.rs @@ -240,7 +240,7 @@ async fn pull_manifest_cached( .map(cached::Return::new) } -#[async_trait] +#[async_trait(?Send)] impl ClientCapabilities for OciCachingClient { async fn fetch_manifest_digest( &mut self, diff --git a/src/registry/oci_client.rs b/src/registry/oci_client.rs index d22d117a92..4befe7a7ea 100644 --- a/src/registry/oci_client.rs +++ b/src/registry/oci_client.rs @@ -27,7 +27,7 @@ pub(crate) struct OciClient { pub registry_client: oci_distribution::Client, } -#[async_trait] +#[async_trait(?Send)] impl ClientCapabilities for OciClient { async fn fetch_manifest_digest( &mut self,