Skip to content

Commit 0989d63

Browse files
committed
conformance: staging, initial trustroot support
Signed-off-by: Andrew Pan <[email protected]>
1 parent e339b67 commit 0989d63

23 files changed

+951
-527
lines changed

Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"]
4040
rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"]
4141
rekor = ["reqwest"]
4242

43-
sign = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"]
44-
verify = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"]
43+
sign = ["fulcio", "rekor", "cert"]
44+
verify = ["fulcio", "rekor", "cert"]
4545
bundle = ["sign", "verify"]
4646

47-
sigstore-trust-root = ["sigstore_protobuf_specs", "futures-util", "tough", "regex", "tokio/sync"]
47+
sigstore-trust-root = ["futures-util", "tough", "regex", "tokio/sync"]
4848

4949
cosign-native-tls = [
5050
"oci-distribution/native-tls",
@@ -116,7 +116,7 @@ serde_json = "1.0.79"
116116
serde_with = { version = "3.4", features = ["base64", "json"], optional = true }
117117
sha2 = { version = "0.10.6", features = ["oid"] }
118118
signature = { version = "2.0" }
119-
sigstore_protobuf_specs = { version = "0.3", optional = true }
119+
sigstore_protobuf_specs = "0.3"
120120
thiserror = "1.0.30"
121121
tokio = { version = "1.17.0", features = ["rt"] }
122122
tokio-util = { version = "0.7.10", features = ["io-util"] }

examples/cosign/verify/main.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use sigstore::cosign::{CosignCapabilities, SignatureLayer};
2222
use sigstore::crypto::SigningScheme;
2323
use sigstore::errors::SigstoreVerifyConstraintsError;
2424
use sigstore::registry::{ClientConfig, ClientProtocol, OciReference};
25-
use sigstore::trust::sigstore::SigstoreTrustRoot;
25+
use sigstore::trust::sigstore::{Instance, TrustRootOptions};
2626
use std::time::Instant;
2727

2828
extern crate anyhow;
@@ -107,7 +107,7 @@ struct Cli {
107107

108108
async fn run_app(
109109
cli: &Cli,
110-
frd: &dyn sigstore::trust::TrustRoot,
110+
frd: &dyn sigstore::trust::RawTrustRoot,
111111
) -> anyhow::Result<(Vec<SignatureLayer>, VerificationConstraintVec)> {
112112
// Note well: this a limitation deliberately introduced by this example.
113113
if cli.cert_email.is_some() && cli.cert_url.is_some() {
@@ -184,7 +184,7 @@ async fn run_app(
184184
}
185185
if let Some(path_to_cert) = cli.cert.as_ref() {
186186
let cert = fs::read(path_to_cert).map_err(|e| anyhow!("Cannot read cert: {:?}", e))?;
187-
let require_rekor_bundle = if !frd.rekor_keys()?.is_empty() {
187+
let require_rekor_bundle = if !frd.raw_tlog_keys().is_empty() {
188188
true
189189
} else {
190190
warn!("certificate based verification is weaker when Rekor integration is disabled");
@@ -225,21 +225,23 @@ async fn run_app(
225225
Ok((trusted_layers, verification_constraints))
226226
}
227227

228-
async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result<Box<dyn sigstore::trust::TrustRoot>> {
228+
async fn fulcio_and_rekor_data(
229+
cli: &Cli,
230+
) -> anyhow::Result<Box<dyn sigstore::trust::RawTrustRoot>> {
229231
if cli.use_sigstore_tuf_data {
230232
info!("Downloading data from Sigstore TUF repository");
231233

232-
let repo: sigstore::errors::Result<SigstoreTrustRoot> = SigstoreTrustRoot::new(None).await;
234+
let repo = Instance::Prod
235+
.trust_config(TrustRootOptions { cache_dir: None })
236+
.await?;
233237

234-
return Ok(Box::new(repo?));
238+
return Ok(Box::new(repo.trust_root));
235239
};
236240

237241
let mut data = sigstore::trust::ManualTrustRoot::default();
238242
if let Some(path) = cli.rekor_pub_key.as_ref() {
239-
data.rekor_key = Some(
240-
fs::read(path)
241-
.map_err(|e| anyhow!("Error reading rekor public key from disk: {}", e))?,
242-
);
243+
data.tlog_keys = vec![fs::read(path)
244+
.map_err(|e| anyhow!("Error reading rekor public key from disk: {}", e))?];
243245
}
244246

245247
if let Some(path) = cli.fulcio_cert.as_ref() {
@@ -250,9 +252,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result<Box<dyn sigstore::tr
250252
encoding: sigstore::registry::CertificateEncoding::Pem,
251253
data: cert_data,
252254
};
253-
data.fulcio_certs
254-
.get_or_insert(Vec::new())
255-
.push(certificate.try_into()?);
255+
data.ca_certs.push(certificate.try_into()?);
256256
}
257257

258258
Ok(Box::new(data))

src/bundle/sign.rs

+39-59
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
//! Types for signing artifacts and producing Sigstore bundles.
1616
17+
use std::error::Error;
1718
use std::io::{self, Read};
1819
use std::time::SystemTime;
1920

@@ -39,19 +40,15 @@ use x509_cert::builder::{Builder, RequestBuilder as CertRequestBuilder};
3940
use x509_cert::ext::pkix as x509_ext;
4041

4142
use crate::bundle::models::Version;
42-
use crate::crypto::keyring::Keyring;
4343
use crate::crypto::transparency::{verify_sct, CertificateEmbeddedSCT};
4444
use crate::errors::{Result as SigstoreResult, SigstoreError};
4545
use crate::fulcio::oauth::OauthTokenProvider;
46-
use crate::fulcio::{self, FulcioClient, FULCIO_ROOT};
46+
use crate::fulcio::{self, FulcioClient};
4747
use crate::oauth::IdentityToken;
4848
use crate::rekor::apis::configuration::Configuration as RekorConfiguration;
4949
use crate::rekor::apis::entries_api::create_log_entry;
5050
use crate::rekor::models::{hashedrekord, proposed_entry::ProposedEntry as ProposedLogEntry};
51-
use crate::trust::TrustRoot;
52-
53-
#[cfg(feature = "sigstore-trust-root")]
54-
use crate::trust::sigstore::SigstoreTrustRoot;
51+
use crate::trust::{CTFEKeyring, TrustConfig, TrustRoot};
5552

5653
/// An asynchronous Sigstore signing session.
5754
///
@@ -85,25 +82,27 @@ impl<'ctx> SigningSession<'ctx> {
8582
fulcio: &FulcioClient,
8683
token: &IdentityToken,
8784
) -> SigstoreResult<(ecdsa::SigningKey<NistP256>, fulcio::CertificateResponse)> {
88-
let subject =
89-
// SEQUENCE OF RelativeDistinguishedName
90-
vec![
91-
// SET OF AttributeTypeAndValue
92-
vec![
93-
// AttributeTypeAndValue, `emailAddress=...`
94-
AttributeTypeAndValue {
95-
oid: const_oid::db::rfc3280::EMAIL_ADDRESS,
96-
value: AttributeValue::new(
97-
pkcs8::der::Tag::Utf8String,
98-
token.unverified_claims().email.as_ref(),
99-
)?,
100-
}
101-
].try_into()?
102-
].into();
85+
let subject = token
86+
.unverified_claims()
87+
.subject()
88+
.ok_or(SigstoreError::UnexpectedError(
89+
"OIDC token does not contain a subject".into(),
90+
))?;
91+
// AttributeTypeAndValue, `emailAddress=...`
92+
let subject_attr = AttributeTypeAndValue {
93+
oid: const_oid::db::rfc3280::EMAIL_ADDRESS,
94+
value: AttributeValue::new(pkcs8::der::Tag::Utf8String, subject.as_ref())?,
95+
};
96+
// SEQUENCE OF RelativeDistinguishedName
97+
let subject_seq = vec![
98+
// SET OF AttributeTypeAndValue
99+
vec![subject_attr].try_into()?,
100+
]
101+
.into();
103102

104103
let mut rng = rand::thread_rng();
105104
let private_key = ecdsa::SigningKey::from(p256::SecretKey::random(&mut rng));
106-
let mut builder = CertRequestBuilder::new(subject, &private_key)?;
105+
let mut builder = CertRequestBuilder::new(subject_seq, &private_key)?;
107106
builder.add_extension(&x509_ext::BasicConstraints {
108107
ca: false,
109108
path_len_constraint: None,
@@ -258,49 +257,30 @@ pub mod blocking {
258257
pub struct SigningContext {
259258
fulcio: FulcioClient,
260259
rekor_config: RekorConfiguration,
261-
ctfe_keyring: Keyring,
260+
ctfe_keyring: CTFEKeyring,
262261
}
263262

264263
impl SigningContext {
265-
/// Manually constructs a [`SigningContext`] from its constituent data.
266-
pub fn new(
267-
fulcio: FulcioClient,
268-
rekor_config: RekorConfiguration,
269-
ctfe_keyring: Keyring,
270-
) -> Self {
271-
Self {
264+
pub fn new<R>(trust: TrustConfig<R>) -> Result<Self, Box<dyn Error + Send + Sync>>
265+
where
266+
R: TrustRoot,
267+
{
268+
let fulcio_url = Url::parse(&trust.signing_config.ca_url)?;
269+
let oauth = OauthTokenProvider::default().with_issuer(&trust.signing_config.oidc_url);
270+
let fulcio = FulcioClient::new(fulcio_url, crate::fulcio::TokenProvider::Oauth(oauth));
271+
272+
let mut rekor_config: RekorConfiguration = Default::default();
273+
// XX: At the time of writing, the ecosystem only uses one log. In the future, we
274+
// may want to check multiple logs to mitigate split-view attacks.
275+
rekor_config.base_path = trust.signing_config.tlog_urls[0].clone();
276+
277+
let ctfe_keyring = trust.trust_root.ctfe_keys()?;
278+
279+
Ok(Self {
272280
fulcio,
273281
rekor_config,
274282
ctfe_keyring,
275-
}
276-
}
277-
278-
/// Returns a [`SigningContext`] configured against the public-good production Sigstore
279-
/// infrastructure.
280-
#[cfg(feature = "sigstore-trust-root")]
281-
pub async fn async_production() -> SigstoreResult<Self> {
282-
let trust_root = SigstoreTrustRoot::new(None).await?;
283-
Ok(Self::new(
284-
FulcioClient::new(
285-
Url::parse(FULCIO_ROOT).expect("constant FULCIO root fails to parse!"),
286-
crate::fulcio::TokenProvider::Oauth(OauthTokenProvider::default()),
287-
),
288-
Default::default(),
289-
Keyring::new(trust_root.ctfe_keys()?)?,
290-
))
291-
}
292-
293-
/// Returns a [`SigningContext`] configured against the public-good production Sigstore
294-
/// infrastructure.
295-
///
296-
/// Async callers should use [`SigningContext::async_production`].
297-
#[cfg(feature = "sigstore-trust-root")]
298-
pub fn production() -> SigstoreResult<Self> {
299-
let rt = tokio::runtime::Builder::new_current_thread()
300-
.enable_all()
301-
.build()?;
302-
303-
rt.block_on(Self::async_production())
283+
})
304284
}
305285

306286
/// Configures and returns a [`SigningSession`] with the held context.

src/bundle/verify/verifier.rs

+28-44
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,13 @@ use x509_cert::der::Encode;
2525
use crate::{
2626
bundle::Bundle,
2727
crypto::{
28-
keyring::Keyring,
2928
transparency::{verify_sct, CertificateEmbeddedSCT},
3029
CertificatePool, CosignVerificationKey, Signature,
3130
},
32-
errors::Result as SigstoreResult,
3331
rekor::apis::configuration::Configuration as RekorConfiguration,
34-
trust::TrustRoot,
32+
trust::{CTFEKeyring, TrustConfig, TrustRoot, TrustRootError},
3533
};
3634

37-
#[cfg(feature = "sigstore-trust-root")]
38-
use crate::trust::sigstore::SigstoreTrustRoot;
39-
4035
use super::{
4136
models::{CertificateErrorKind, CheckedBundle, SignatureErrorKind},
4237
policy::VerificationPolicy,
@@ -50,19 +45,16 @@ pub struct Verifier {
5045
#[allow(dead_code)]
5146
rekor_config: RekorConfiguration,
5247
cert_pool: CertificatePool,
53-
ctfe_keyring: Keyring,
48+
ctfe_keyring: CTFEKeyring,
5449
}
5550

5651
impl Verifier {
57-
/// Constructs a [`Verifier`].
58-
///
59-
/// For verifications against the public-good trust root, use [`Verifier::production()`].
60-
pub fn new<R: TrustRoot>(
52+
fn new_with_rekor_config<R: TrustRoot>(
6153
rekor_config: RekorConfiguration,
6254
trust_repo: R,
63-
) -> SigstoreResult<Self> {
64-
let cert_pool = CertificatePool::from_certificates(trust_repo.fulcio_certs()?, [])?;
65-
let ctfe_keyring = Keyring::new(trust_repo.ctfe_keys()?)?;
55+
) -> Result<Self, TrustRootError> {
56+
let cert_pool = trust_repo.ca_certs()?;
57+
let ctfe_keyring = trust_repo.ctfe_keys()?;
6658

6759
Ok(Self {
6860
rekor_config,
@@ -71,6 +63,21 @@ impl Verifier {
7163
})
7264
}
7365

66+
/// Constructs a [`Verifier`].
67+
///
68+
pub fn new<R>(trust: TrustConfig<R>) -> Result<Self, TrustRootError>
69+
where
70+
R: TrustRoot,
71+
{
72+
let mut rekor_config: RekorConfiguration = Default::default();
73+
74+
// XX: At the time of writing, the ecosystem only uses one log. In the future, we
75+
// may want to check multiple logs to mitigate split-view attacks.
76+
rekor_config.base_path = trust.signing_config.tlog_urls[0].clone();
77+
78+
Self::new_with_rekor_config(rekor_config, trust.trust_root)
79+
}
80+
7481
/// Verifies an input digest against the given Sigstore Bundle, ensuring conformance to the
7582
/// provided [`VerificationPolicy`].
7683
pub async fn verify_digest<P>(
@@ -214,17 +221,9 @@ impl Verifier {
214221
}
215222
}
216223

217-
impl Verifier {
218-
/// Constructs an [`Verifier`] against the public-good trust root.
219-
#[cfg(feature = "sigstore-trust-root")]
220-
pub async fn production() -> SigstoreResult<Verifier> {
221-
let updater = SigstoreTrustRoot::new(None).await?;
222-
223-
Verifier::new(Default::default(), updater)
224-
}
225-
}
226-
227224
pub mod blocking {
225+
use std::error::Error;
226+
228227
use super::{Verifier as AsyncVerifier, *};
229228

230229
/// A synchronous Sigstore verifier.
@@ -235,16 +234,14 @@ pub mod blocking {
235234

236235
impl Verifier {
237236
/// Constructs a synchronous Sigstore verifier.
238-
///
239-
/// For verifications against the public-good trust root, use [`Verifier::production()`].
240-
pub fn new<R: TrustRoot>(
241-
rekor_config: RekorConfiguration,
242-
trust_repo: R,
243-
) -> SigstoreResult<Self> {
237+
pub fn new<R>(trust: TrustConfig<R>) -> Result<Self, Box<dyn Error>>
238+
where
239+
R: TrustRoot,
240+
{
244241
let rt = tokio::runtime::Builder::new_current_thread()
245242
.enable_all()
246243
.build()?;
247-
let inner = AsyncVerifier::new(rekor_config, trust_repo)?;
244+
let inner = AsyncVerifier::new(trust)?;
248245

249246
Ok(Self { rt, inner })
250247
}
@@ -286,17 +283,4 @@ pub mod blocking {
286283
self.verify_digest(hasher, bundle, policy, offline)
287284
}
288285
}
289-
290-
impl Verifier {
291-
/// Constructs a synchronous [`Verifier`] against the public-good trust root.
292-
#[cfg(feature = "sigstore-trust-root")]
293-
pub fn production() -> SigstoreResult<Verifier> {
294-
let rt = tokio::runtime::Builder::new_current_thread()
295-
.enable_all()
296-
.build()?;
297-
let inner = rt.block_on(AsyncVerifier::production())?;
298-
299-
Ok(Verifier { inner, rt })
300-
}
301-
}
302286
}

0 commit comments

Comments
 (0)