Skip to content

Commit

Permalink
refactor: remove async from sigstore::trust::TrustRoot trait
Browse files Browse the repository at this point in the history
Make the `sigstore::trust::TrustRoot` methods not async.

Fixes sigstore#346

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
  • Loading branch information
flavio committed Apr 10, 2024
1 parent f871cb8 commit c6302b0
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 63 deletions.
7 changes: 3 additions & 4 deletions examples/cosign/verify/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ async fn run_app(

let mut client_builder =
sigstore::cosign::ClientBuilder::default().with_oci_client_config(oci_client_config);
client_builder = client_builder.with_trust_repository(frd).await?;
client_builder = client_builder.with_trust_repository(frd)?;

let cert_chain: Option<Vec<sigstore::registry::Certificate>> = match cli.cert_chain.as_ref() {
None => None,
Expand Down Expand Up @@ -184,7 +184,7 @@ async fn run_app(
}
if let Some(path_to_cert) = cli.cert.as_ref() {
let cert = fs::read(path_to_cert).map_err(|e| anyhow!("Cannot read cert: {:?}", e))?;
let require_rekor_bundle = if !frd.rekor_keys().await?.is_empty() {
let require_rekor_bundle = if !frd.rekor_keys()?.is_empty() {
true
} else {
warn!("certificate based verification is weaker when Rekor integration is disabled");
Expand Down Expand Up @@ -229,8 +229,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result<Box<dyn sigstore::tr
if cli.use_sigstore_tuf_data {
info!("Downloading data from Sigstore TUF repository");

let repo: sigstore::errors::Result<SigstoreTrustRoot> =
SigstoreTrustRoot::new(None).await?.prefetch().await;
let repo: sigstore::errors::Result<SigstoreTrustRoot> = SigstoreTrustRoot::new(None).await;

return Ok(Box::new(repo?));
};
Expand Down
9 changes: 3 additions & 6 deletions src/cosign/client_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,12 @@ impl<'a> ClientBuilder<'a> {
///
/// Enables Fulcio and Rekor integration with the given trust repository.
/// See [crate::sigstore::TrustRoot] for more details on trust repositories.
pub async fn with_trust_repository<R: TrustRoot + ?Sized>(
mut self,
repo: &'a R,
) -> Result<Self> {
let rekor_keys = repo.rekor_keys().await?;
pub fn with_trust_repository<R: TrustRoot + ?Sized>(mut self, repo: &'a R) -> Result<Self> {
let rekor_keys = repo.rekor_keys()?;
if !rekor_keys.is_empty() {
self.rekor_pub_key = Some(rekor_keys[0]);
}
self.fulcio_certs = repo.fulcio_certs().await?;
self.fulcio_certs = repo.fulcio_certs()?;

Ok(self)
}
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@
//!
//! let mut client = sigstore::cosign::ClientBuilder::default()
//! .with_trust_repository(&repo)
//! .await
//! .expect("Cannot construct cosign client from given materials")
//! .build()
//! .expect("Unexpected failure while building Client");
Expand Down
12 changes: 4 additions & 8 deletions src/trust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use async_trait::async_trait;
use webpki::types::CertificateDer;

#[cfg(feature = "sigstore-trust-root")]
pub mod sigstore;

/// A `TrustRoot` owns all key material necessary for establishing a root of trust.
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait TrustRoot: Send + Sync {
async fn fulcio_certs(&self) -> crate::errors::Result<Vec<CertificateDer>>;
async fn rekor_keys(&self) -> crate::errors::Result<Vec<&[u8]>>;
fn fulcio_certs(&self) -> crate::errors::Result<Vec<CertificateDer>>;
fn rekor_keys(&self) -> crate::errors::Result<Vec<&[u8]>>;
}

/// A `ManualTrustRoot` is a [TrustRoot] with out-of-band trust materials.
Expand All @@ -36,17 +33,16 @@ pub struct ManualTrustRoot<'a> {
}

#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl TrustRoot for ManualTrustRoot<'_> {
#[cfg(not(target_arch = "wasm32"))]
async fn fulcio_certs(&self) -> crate::errors::Result<Vec<CertificateDer>> {
fn fulcio_certs(&self) -> crate::errors::Result<Vec<CertificateDer>> {
Ok(match &self.fulcio_certs {
Some(certs) => certs.clone(),
None => Vec::new(),
})
}

async fn rekor_keys(&self) -> crate::errors::Result<Vec<&[u8]>> {
fn rekor_keys(&self) -> crate::errors::Result<Vec<&[u8]>> {
Ok(match &self.rekor_keys {
Some(keys) => keys.iter().map(|k| k.as_slice()).collect(),
None => Vec::new(),
Expand Down
63 changes: 19 additions & 44 deletions src/trust/sigstore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,18 @@
//!
//! # Example
//!
//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`]
//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::new`]
//! method.
//!
/// ```rust
/// # use sigstore::trust::sigstore::SigstoreTrustRoot;
/// # use sigstore::errors::Result;
/// # #[tokio::main]
/// # async fn main() -> std::result::Result<(), anyhow::Error> {
/// let repo: Result<SigstoreTrustRoot> = SigstoreTrustRoot::new(None).await?.prefetch().await;
/// let repo: SigstoreTrustRoot = SigstoreTrustRoot::new(None).await?;
/// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust`
/// # Ok(())
/// # }
/// ```
use async_trait::async_trait;
use futures::StreamExt;
use sha2::{Digest, Sha256};
use std::{
Expand Down Expand Up @@ -77,11 +75,14 @@ impl SigstoreTrustRoot {
.await
.map_err(Box::new)?;

Ok(Self {
let tr = Self {
repository,
checkout_dir: checkout_dir.map(ToOwned::to_owned),
trusted_root: OnceCell::default(),
})
};
tr.trusted_root().await?;

Ok(tr)
}

async fn trusted_root(&self) -> Result<&TrustedRoot> {
Expand Down Expand Up @@ -111,27 +112,6 @@ impl SigstoreTrustRoot {
.await
}

/// Prefetches trust materials.
///
/// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreTrustRoot] lazily
/// fetches the requested data, which is problematic for async callers. Those callers should
/// use this method to fetch the trust root ahead of time.
///
/// ```rust
/// # use sigstore::trust::sigstore::SigstoreTrustRoot;
/// # use sigstore::errors::Result;
/// # #[tokio::main]
/// # async fn main() -> std::result::Result<(), anyhow::Error> {
/// let repo: Result<SigstoreTrustRoot> = SigstoreTrustRoot::new(None).await?.prefetch().await;
/// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust`
/// # Ok(())
/// # }
/// ```
pub async fn prefetch(self) -> Result<Self> {
let _ = self.trusted_root().await?;
Ok(self)
}

#[inline]
fn tlog_keys(tlogs: &[TransparencyLogInstance]) -> impl Iterator<Item = &[u8]> {
tlogs
Expand All @@ -153,16 +133,15 @@ impl SigstoreTrustRoot {
}
}

#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl crate::trust::TrustRoot for SigstoreTrustRoot {
/// Fetch Fulcio certificates from the given TUF repository or reuse
/// the local cache if its contents are not outdated.
///
/// The contents of the local cache are updated when they are outdated.
#[cfg(not(target_arch = "wasm32"))]
async fn fulcio_certs(&self) -> Result<Vec<CertificateDer>> {
let root = self.trusted_root().await?;
fn fulcio_certs(&self) -> Result<Vec<CertificateDer>> {
let root = self.trusted_root.get().ok_or_else(|| {
SigstoreError::TufMetadataError("Trusted root not initialized".into())
})?;

// Allow expired certificates: they may have been active when the
// certificate was used to sign.
Expand All @@ -182,8 +161,10 @@ impl crate::trust::TrustRoot for SigstoreTrustRoot {
/// the local cache if it's not outdated.
///
/// The contents of the local cache are updated when they are outdated.
async fn rekor_keys(&self) -> Result<Vec<&[u8]>> {
let root = self.trusted_root().await?;
fn rekor_keys(&self) -> Result<Vec<&[u8]>> {
let root = self.trusted_root.get().ok_or_else(|| {
SigstoreError::TufMetadataError("Trusted root not initialized".into())
})?;
let keys: Vec<_> = Self::tlog_keys(&root.tlogs).collect();

if keys.len() != 1 {
Expand Down Expand Up @@ -315,21 +296,15 @@ mod tests {
use super::*;

#[tokio::test]
async fn prefetch() {
async fn initialize() {
let repo = SigstoreTrustRoot::new(None)
.await
.expect("initialize SigstoreRepository")
.prefetch()
.await
.expect("prefetch");
.expect("initialize SigstoreRepository");

let fulcio_certs = repo
.fulcio_certs()
.await
.expect("cannot fetch Fulcio certs");
let fulcio_certs = repo.fulcio_certs().expect("cannot fetch Fulcio certs");
assert!(!fulcio_certs.is_empty());

let rekor_keys = repo.rekor_keys().await.expect("cannot fetch Rekor keys");
let rekor_keys = repo.rekor_keys().expect("cannot fetch Rekor keys");
assert!(!rekor_keys.is_empty());
}
}

0 comments on commit c6302b0

Please sign in to comment.