Skip to content

Commit

Permalink
Add TUF support
Browse files Browse the repository at this point in the history
Allow Fulcio and Rekor data to be fetched from the official
TUF repository of Sigstore.

A new module is introduced, called `tuf`, which provides helper
structs to interact with a remote TUF repository.

Fixes sigstore#9

Signed-off-by: Flavio Castelli <[email protected]>
  • Loading branch information
flavio committed Jan 28, 2022
1 parent 35fcc0f commit aae2fc6
Show file tree
Hide file tree
Showing 21 changed files with 1,453 additions and 19 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ ecdsa = { version = "0.12.4", features = ["verify", "pem", "der", "pkcs8"] }
oci-distribution = { version = "0.8.1", default-features = false }
olpc-cjson = "0.1.1"
p256 = {version = "0.9.0", features = ["ecdsa-core"]}
sha2 = "0.10.1"
serde_json = "1.0.68"
serde = {version = "1.0.130", features = ["derive"]}
tokio = { version = "1.12.0", features = ["full"]}
tough = { version = "0.12.1", features = [ "http" ] }
tracing = "0.1.29"
url = "2.2.2"
x509-parser = { version = "0.12.0", features = ["verify"]}

[dev-dependencies]
chrono = "0.4.19"
clap = "2.33.3"
openssl = "0.10.38"
tracing-subscriber = "0.2.25"
tempdir = "0.3.7"
tracing-subscriber = "0.2.25"
76 changes: 58 additions & 18 deletions examples/verify/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@
extern crate sigstore;
use sigstore::cosign::CosignCapabilities;
use sigstore::simple_signing::SimpleSigning;
use sigstore::tuf::SigstoreRepository;

extern crate anyhow;
use anyhow::{anyhow, Result};

use std::{collections::HashMap, fs};

extern crate clap;
use clap::{App, Arg};

use std::{collections::HashMap, fs};
use tokio::task::spawn_blocking;

extern crate tracing_subscriber;
use tracing::info;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};

Expand All @@ -41,6 +44,13 @@ fn cli() -> App<'static, 'static> {
.required(false)
.takes_value(true),
)
.arg(
Arg::with_name("use-sigstore-tuf-data")
.long("use-sigstore-tuf-data")
.help("Fetch Rekor and Fulcio data from Sigstore's TUF repository")
.required(false)
.takes_value(false),
)
.arg(
Arg::with_name("rekor-pub-key")
.long("rekor-pub-key")
Expand Down Expand Up @@ -97,27 +107,69 @@ fn cli() -> App<'static, 'static> {
async fn run_app() -> Result<Vec<SimpleSigning>> {
let matches = cli().get_matches();

// setup logging
let level_filter = if matches.is_present("verbose") {
"debug"
} else {
"info"
};
let filter_layer = EnvFilter::new(level_filter);
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt::layer().with_writer(std::io::stderr))
.init();

let auth = &sigstore::registry::Auth::Anonymous;

let rekor_pub_key: Option<String> = matches
.value_of("rekor-pub-key")
.map(|path| fs::read_to_string(path).expect("Error reading rekor public key from disk"));
.map(|path| {
fs::read_to_string(path)
.map_err(|e| anyhow!("Error reading rekor public key from disk: {}", e))
})
.transpose()?;

let fulcio_cert: Option<Vec<u8>> = matches
.value_of("fulcio-crt")
.map(|path| fs::read(path).expect("Error reading fulcio certificate from disk"));
.map(|path| {
fs::read(path).map_err(|e| anyhow!("Error reading fulcio certificate from disk: {}", e))
})
.transpose()?;

let sigstore_repo: Option<SigstoreRepository> = if matches.is_present("use-sigstore-tuf-data") {
let repo: Result<SigstoreRepository> = spawn_blocking(|| {
info!("Downloading data from Sigstore TUF repository");
sigstore::tuf::SigstoreRepository::fetch(None)
})
.await
.map_err(|e| anyhow!("Error spawining blocking task inside of tokio: {}", e))?;

Some(repo?)
} else {
None
};

let mut client_builder = sigstore::cosign::ClientBuilder::default();

if let Some(repo) = sigstore_repo {
client_builder = client_builder.with_rekor_pub_key(repo.rekor_pub_key());
client_builder = client_builder.with_fulcio_cert(repo.fulcio_cert());
}

// Set Rekor public key. Give higher precendece to the key specified by the user over the
// one that can be obtained from Sigstore's TUF repository
if let Some(key) = rekor_pub_key {
client_builder = client_builder.with_rekor_pub_key(&key);
}
// Set Fulcio certificate. Give higher precendece to the certificate specified by the user over the
// one that can be obtained from Sigstore's TUF repository
if let Some(cert) = fulcio_cert {
client_builder = client_builder.with_fulcio_cert(&cert);
}

let mut client = client_builder
.with_cert_email(matches.value_of("cert-email"))
.build()
.expect("Error while building cosign client");
.build()?;

let image: &str = matches.value_of("IMAGE").unwrap();

Expand Down Expand Up @@ -149,18 +201,6 @@ async fn run_app() -> Result<Vec<SimpleSigning>> {
}
};

// setup logging
let level_filter = if matches.is_present("verbose") {
"debug"
} else {
"info"
};
let filter_layer = EnvFilter::new(level_filter);
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt::layer().with_writer(std::io::stderr))
.init();

client
.verify(
auth,
Expand Down
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@
//! }
//! ```
//!
//! ## Fulcio and Rekor integration
//!
//! [`cosign::Client`](crate::cosign::Client) integration with Fulcio and Rekor
//! requires the following data to work: Fulcio's certificate and Rekor's public key.
//!
//! These files are safely distributed by the Sigstore project via a TUF repository.
//! The [`sigstore::tuf`](crate::tuf) module provides the helper structures to deal
//! with it.
//!
//! # Examples
//!
//! Additional examples can be found inside of the [`examples`](https://github.com/sigstore/sigstore-rs/tree/main/examples/)
Expand All @@ -119,3 +128,4 @@ mod mock_client;
pub mod cosign;
pub mod registry;
pub mod simple_signing;
pub mod tuf;
166 changes: 166 additions & 0 deletions src/tuf/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub(crate) const SIGSTORE_METADATA_BASE: &str = "http://sigstore-tuf-root.storage.googleapis.com/";
pub(crate) const SIGSTORE_TARGET_BASE: &str =
"http://sigstore-tuf-root.storage.googleapis.com/targets";

pub(crate) const SIGSTORE_FULCIO_CERT_TARGET: &str = "fulcio.crt.pem";
pub(crate) const SIGSTORE_REKOR_PUB_KEY_TARGET: &str = "rekor.pub";

pub(crate) const SIGSTORE_ROOT: &str = r#"{
"signatures": [
{
"keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"sig": "3046022100d3ea59490b253beae0926c6fa63f54336dea1ed700555be9f27ff55cd347639c0221009157d1ba012cead81948a4ab777d355451d57f5c4a2d333fc68d2e3f358093c2"
},
{
"keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"sig": "304502206eaef40564403ce572c6d062e0c9b0aab5e0223576133e081e1b495e8deb9efd02210080fd6f3464d759601b4afec596bbd5952f3a224cd06ed1cdfc3c399118752ba2"
},
{
"keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"sig": "304502207baace02f56d8e6069f10b6ff098a26e7f53a7f9324ad62cffa0557bdeb9036c022100fb3032baaa090d0040c3f2fd872571c84479309b773208601d65948df87a9720"
},
{
"keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"sig": "304402205180c01905505dd88acd7a2dad979dd75c979b3722513a7bdedac88c6ae8dbeb022056d1ddf7a192f0b1c2c90ff487de2fb3ec9f0c03f66ea937c78d3b6a493504ca"
},
{
"keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209",
"sig": "3046022100c8806d4647c514d80fd8f707d3369444c4fd1d0812a2d25f828e564c99790e3f022100bb51f12e862ef17a7d3da2ac103bebc5c7e792237006c4cafacd76267b249c2f"
}
],
"signed": {
"_type": "root",
"consistent_snapshot": false,
"expires": "2022-05-11T19:09:02.663975009Z",
"keys": {
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803"
},
"scheme": "ecdsa-sha2-nistp256"
},
"b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04fa1a3e42f2300cd3c5487a61509348feb1e936920fef2f83b7cd5dbe7ba045f538725ab8f18a666e6233edb7e0db8766c8dc336633449c5e1bbe0c182b02df0b"
},
"scheme": "ecdsa-sha2-nistp256"
},
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7"
},
"scheme": "ecdsa-sha2-nistp256"
},
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447"
},
"scheme": "ecdsa-sha2-nistp256"
},
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48"
},
"scheme": "ecdsa-sha2-nistp256"
},
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b"
},
"scheme": "ecdsa-sha2-nistp256"
},
"fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "044c7793ab74b9ddd713054e587b8d9c75c5f6025633d0fef7ca855ed5b8d5a474b23598fe33eb4a63630d526f74d4bdaec8adcb51993ed65652d651d7c49203eb"
},
"scheme": "ecdsa-sha2-nistp256"
}
},
"roles": {
"root": {
"keyids": [
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
],
"threshold": 3
},
"snapshot": {
"keyids": [
"fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451"
],
"threshold": 1
},
"targets": {
"keyids": [
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
],
"threshold": 3
},
"timestamp": {
"keyids": [
"b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d"
],
"threshold": 1
}
},
"spec_version": "1.0",
"version": 2
}
}"#;
Loading

0 comments on commit aae2fc6

Please sign in to comment.