From 1934a402b8a2bb0e69657f840cc81e181d0e1fd6 Mon Sep 17 00:00:00 2001 From: Hayden B Date: Tue, 11 Apr 2023 10:12:46 -0700 Subject: [PATCH 001/161] Swap over to using CDN to fetch TUF metadata The TUF root for the production instance of sigstore is now addressable via a CDN endpoint; this change uses the CDN instead of calling GCS directly via its HTTP endpoint Signed-off-by: Hayden B --- src/tuf/constants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index 767ddb982a..e900d97d18 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -21,9 +21,9 @@ lazy_static! { Regex::new(r#"fulcio(_v\d+)?\.crt\.pem"#).expect("cannot compile regexp"); } -pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://sigstore-tuf-root.storage.googleapis.com/"; +pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; pub(crate) const SIGSTORE_TARGET_BASE: &str = - "https://sigstore-tuf-root.storage.googleapis.com/targets"; + "https://tuf-repo-cdn.sigstore.dev/targets"; pub(crate) const SIGSTORE_REKOR_PUB_KEY_TARGET: &str = "rekor.pub"; From fb870cc321bb0bfa399b7d2351bacdd3bb099cad Mon Sep 17 00:00:00 2001 From: Hayden B Date: Tue, 11 Apr 2023 10:14:10 -0700 Subject: [PATCH 002/161] Update constants.rs fix lint Signed-off-by: Hayden B --- src/tuf/constants.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index e900d97d18..1515f08166 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -22,8 +22,7 @@ lazy_static! { } pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; -pub(crate) const SIGSTORE_TARGET_BASE: &str = - "https://tuf-repo-cdn.sigstore.dev/targets"; +pub(crate) const SIGSTORE_TARGET_BASE: &str = "https://tuf-repo-cdn.sigstore.dev/targets"; pub(crate) const SIGSTORE_REKOR_PUB_KEY_TARGET: &str = "rekor.pub"; From afd20a21e3bb8de1e3b74c137cb6ffc3d7657d86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 06:19:32 +0100 Subject: [PATCH 003/161] chore(deps): Bump actions/checkout from 3.5.0 to 3.5.2 (#252) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8f4b7f84864484a7bf31766abe9204da3cbe65b3...8e5e7e5ab8b370d6c329ec480221332ada57f0ab) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 645fd60a2c..18545228d9 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index 761d5fb2f5..fd1c30898b 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index 18378d4ea9..c12f919559 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 80cf260201..1a3022cdd0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 1a2ee55cb225ad677c4f0e82c4362da0f58c3c90 Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Tue, 16 May 2023 11:32:32 +0200 Subject: [PATCH 004/161] upgrade 'der' to 0.7.5 Signed-off-by: Dmitry S --- Cargo.toml | 2 +- examples/rekor/create_log_entry/main.rs | 2 +- src/crypto/certificate.rs | 2 +- src/crypto/verification_key.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f592f44da..816d600922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ cached = { version = "0.42.0", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.23", feature = "clock" } const-oid = "0.9.1" -der = "0.6.1" +der = "0.7.5" digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.15", features = [ "pkcs8", "digest", "der" ] } ed25519 = { version = "=2.1", features = [ "alloc" ] } diff --git a/examples/rekor/create_log_entry/main.rs b/examples/rekor/create_log_entry/main.rs index e14f210417..1753e188a4 100644 --- a/examples/rekor/create_log_entry/main.rs +++ b/examples/rekor/create_log_entry/main.rs @@ -82,7 +82,7 @@ async fn main() { .arg(Arg::new("key_format") .long("key_format") .value_name("KEY_FORMAT") - .help("Accepted formats are : pgp / x509 / minsign / ssh / tuf")) + .help("Accepted formats are : pgp / x509 / minsign / ssh / tuf")) .arg(Arg::new("signature") .long("signature") .value_name("SIGNATURE") diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index e3f94cbe03..6d7da38702 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -126,7 +126,7 @@ mod tests { use crate::crypto::tests::*; use chrono::{Duration, Utc}; - use der::Decode; + use x509_cert::der::Decode; #[test] fn verify_cert_key_usages_success() -> anyhow::Result<()> { diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 02c85d3379..e84c7ed127 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -329,7 +329,7 @@ impl CosignVerificationKey { #[cfg(test)] mod tests { - use der::Decode; + use x509_cert::der::Decode; use x509_cert::Certificate; use super::*; From 020e10c3f3ee49e305277525f13d6d4e678745ae Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Tue, 16 May 2023 17:10:44 +0200 Subject: [PATCH 005/161] remove unused 'clock' feature for chrono Signed-off-by: Dmitry S --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 816d600922..4b9f9830ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ async-trait = "0.1.52" base64 = "0.21.0" cached = { version = "0.42.0", optional = true } cfg-if = "1.0.0" -chrono = { version = "0.4.23", feature = "clock" } +chrono = { version = "0.4.23" } const-oid = "0.9.1" der = "0.7.5" digest = { version = "0.10.3", default-features = false } From 73d732501cfe2e2ded0cede0094b3ab45359c0f8 Mon Sep 17 00:00:00 2001 From: Dmitry Savintsev Date: Fri, 19 May 2023 08:45:59 +0200 Subject: [PATCH 006/161] update pkcs1 from 0.4.0 to 0.7.5 (#260) * Address RUSTSEC-2023-0037 Change references of the unmaintained crate xsalsa20poly1305 to crypto_secretbox. https://rustsec.org/advisories/RUSTSEC-2023-0037.html Signed-off-by: Dmitry S * update pkcs1 from 0.4.0 to 0.7.5 Signed-off-by: Dmitry S --------- Signed-off-by: Dmitry S --- Cargo.toml | 6 +++--- src/crypto/signing_key/kdf.rs | 14 +++++++------- src/errors.rs | 3 +++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4b9f9830ec..a1ddd26fb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,13 +70,13 @@ p384 = "0.12" webbrowser = "0.8.4" pem = "1.0.2" picky = { version = "7.0.0-rc.5", default-features = false, features = [ "x509", "ec" ] } -pkcs1 = "0.4.0" +pkcs1 = { version = "0.7.5", features = [ "std" ] } 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" +rsa = "0.8.2" scrypt = "0.10.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" @@ -88,7 +88,7 @@ tough = { version = "0.13", features = [ "http" ], optional = true } tracing = "0.1.31" url = "2.2.2" x509-cert = { version = "0.1.1", features = [ "pem", "std" ] } -xsalsa20poly1305 = "0.9.0" +crypto_secretbox = "0.1.1" zeroize = "1.5.7" [dev-dependencies] diff --git a/src/crypto/signing_key/kdf.rs b/src/crypto/signing_key/kdf.rs index ad54f9e1d7..b8ae219eea 100644 --- a/src/crypto/signing_key/kdf.rs +++ b/src/crypto/signing_key/kdf.rs @@ -20,9 +20,9 @@ //! for golang version. use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; +use crypto_secretbox::aead::{AeadMut, KeyInit}; use rand::Rng; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use xsalsa20poly1305::aead::{AeadMut, KeyInit}; use crate::errors::*; @@ -157,10 +157,10 @@ impl SecretBoxCipher { )); } self.encrypted = true; - let nonce = xsalsa20poly1305::Nonce::from_slice(&self.nonce); - let key = xsalsa20poly1305::Key::from_slice(key); + let nonce = crypto_secretbox::Nonce::from_slice(&self.nonce); + let key = crypto_secretbox::Key::from_slice(key); - let mut cipher = xsalsa20poly1305::XSalsa20Poly1305::new(key); + let mut cipher = crypto_secretbox::XSalsa20Poly1305::new(key); cipher .encrypt(nonce, plaintext) .map_err(|e| SigstoreError::PrivateKeyEncryptError(e.to_string())) @@ -168,10 +168,10 @@ impl SecretBoxCipher { /// Unseal the ciphertext using the key fn decrypt(&self, ciphertext: &[u8], key: &[u8]) -> Result> { - let nonce = xsalsa20poly1305::Nonce::from_slice(&self.nonce); - let key = xsalsa20poly1305::Key::from_slice(key); + let nonce = crypto_secretbox::Nonce::from_slice(&self.nonce); + let key = crypto_secretbox::Key::from_slice(key); - let mut cipher = xsalsa20poly1305::XSalsa20Poly1305::new(key); + let mut cipher = crypto_secretbox::XSalsa20Poly1305::new(key); cipher .decrypt(nonce, ciphertext) .map_err(|e| SigstoreError::PrivateKeyEncryptError(e.to_string())) diff --git a/src/errors.rs b/src/errors.rs index 43b31dd1cc..d412fd7583 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -206,6 +206,9 @@ pub enum SigstoreError { #[error(transparent)] RSAError(#[from] rsa::errors::Error), + #[error(transparent)] + RSAPKCS1Error(#[from] rsa::pkcs1::Error), + #[error(transparent)] PKCS1Error(#[from] pkcs1::Error), From 4354bf22b39e6bd34ebecdf0321a9712160b3519 Mon Sep 17 00:00:00 2001 From: Dmitry Savintsev Date: Mon, 22 May 2023 13:15:58 +0200 Subject: [PATCH 007/161] use 2021 Rust edition (#261) Signed-off-by: Dmitry S --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a1ddd26fb8..f7f67a401f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "sigstore" description = "An experimental crate to interact with sigstore" version = "0.6.0" -edition = "2018" +edition = "2021" authors = [ "sigstore-rs developers", ] From 4659790eb1ded8400da1ef72f8289b198a829594 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 07:54:15 +0100 Subject: [PATCH 008/161] chore(deps): Update serial_test requirement from 1.0.0 to 2.0.0 (#264) Updates the requirements on [serial_test](https://github.com/palfrey/serial_test) to permit the latest version. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v1.0.0...v2.0.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f7f67a401f..59852451fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,7 +99,7 @@ clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" rstest = "0.16.0" -serial_test = "1.0.0" +serial_test = "2.0.0" tempfile = "3.3.0" testcontainers = "0.14" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From de3aea780738ba329c0fea7b77ba5724467c6cea Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Fri, 19 May 2023 12:35:40 +0200 Subject: [PATCH 009/161] upgrade ed25519-dalek to 2.0.0-rc.2 Upgrade ed25519 from 2.1 to 2.2.1 and ed25519-dalek from 2.0.0-pre.0 to 2.0.0-rc.2 with necessary code adjustments. Closes #262. Signed-off-by: Dmitry S --- Cargo.toml | 94 +++++++++++++++++++++---------- src/crypto/signing_key/ed25519.rs | 20 ++++--- src/crypto/verification_key.rs | 25 ++++---- src/errors.rs | 2 +- 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59852451fa..2168ab12d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,7 @@ name = "sigstore" description = "An experimental crate to interact with sigstore" version = "0.6.0" edition = "2021" -authors = [ - "sigstore-rs developers", -] +authors = ["sigstore-rs developers"] license = "Apache-2.0" readme = "README.md" @@ -13,40 +11,60 @@ readme = "README.md" 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"] +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", +] # This features is used by tests that use docker to create a registry test-registry = [] -fulcio-native-tls = [ "oauth-native-tls", "reqwest/native-tls", "fulcio" ] -fulcio-rustls-tls = [ "oauth-rustls-tls", "reqwest/rustls-tls", "fulcio" ] +fulcio-native-tls = ["oauth-native-tls", "reqwest/native-tls", "fulcio"] +fulcio-rustls-tls = ["oauth-rustls-tls", "reqwest/rustls-tls", "fulcio"] fulcio = [] -oauth-native-tls = [ "openidconnect/native-tls", "oauth" ] -oauth-rustls-tls = [ "openidconnect/rustls-tls", "oauth" ] +oauth-native-tls = ["openidconnect/native-tls", "oauth"] +oauth-rustls-tls = ["openidconnect/rustls-tls", "oauth"] oauth = [] -rekor-native-tls = [ "reqwest/native-tls", "rekor"] -rekor-rustls-tls = [ "reqwest/rustls-tls", "rekor" ] +rekor-native-tls = ["reqwest/native-tls", "rekor"] +rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -tuf = [ "tough", "regex" ] +tuf = ["tough", "regex"] -cosign-native-tls = [ "oci-distribution/native-tls", "cert", "cosign", "registry-native-tls" ] -cosign-rustls-tls = [ "oci-distribution/rustls-tls", "cert", "cosign", "registry-rustls-tls" ] +cosign-native-tls = [ + "oci-distribution/native-tls", + "cert", + "cosign", + "registry-native-tls", +] +cosign-rustls-tls = [ + "oci-distribution/rustls-tls", + "cert", + "cosign", + "registry-rustls-tls", +] cosign = [] cert = [] -registry-native-tls = [ "oci-distribution/native-tls", "registry" ] -registry-rustls-tls = [ "oci-distribution/rustls-tls", "registry" ] +registry-native-tls = ["oci-distribution/native-tls", "registry"] +registry-rustls-tls = ["oci-distribution/rustls-tls", "registry"] registry = [] -mock-client-native-tls = [ "oci-distribution/native-tls", "mock-client" ] -mock-client-rustls-tls = [ "oci-distribution/rustls-tls", "mock-client" ] +mock-client-native-tls = ["oci-distribution/native-tls", "mock-client"] +mock-client-rustls-tls = ["oci-distribution/rustls-tls", "mock-client"] mock-client = [] -cached-client = [ "cached" ] +cached-client = ["cached"] [dependencies] async-trait = "0.1.52" @@ -57,37 +75,51 @@ chrono = { version = "0.4.23" } const-oid = "0.9.1" der = "0.7.5" 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" ] } -elliptic-curve = { version = "0.12.2", features = [ "arithmetic", "pem" ] } +ecdsa = { version = "0.15", features = ["pkcs8", "digest", "der"] } +ed25519 = { version = "2.2.1", features = ["alloc"] } +ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } +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" -openidconnect = { version = "2.3", default-features = false, features = [ "reqwest" ], optional = true} +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.5", default-features = false, features = [ "x509", "ec" ] } -pkcs1 = { version = "0.7.5", features = [ "std" ] } -pkcs8 = { version = "0.9.0", features = ["pem", "alloc", "pkcs5", "encryption"] } -rand = { version = "0.8.5", features = [ "getrandom", "std" ] } +picky = { version = "7.0.0-rc.5", default-features = false, features = [ + "x509", + "ec", +] } +pkcs1 = { version = "0.7.5", features = ["std"] } +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} +reqwest = { version = "0.11", default-features = false, features = [ + "json", + "multipart", +], optional = true } rsa = "0.8.2" scrypt = "0.10.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } +spki = { version = "0.7.2", features = ["pem", "std"] } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } -tough = { version = "0.13", features = [ "http" ], optional = true } +tough = { version = "0.13", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.1.1", features = [ "pem", "std" ] } +x509-cert = { version = "0.1.1", features = ["pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" diff --git a/src/crypto/signing_key/ed25519.rs b/src/crypto/signing_key/ed25519.rs index 1b69981f46..f2b236f5e8 100644 --- a/src/crypto/signing_key/ed25519.rs +++ b/src/crypto/signing_key/ed25519.rs @@ -60,11 +60,11 @@ //! assert!(verification_key.verify_signature(Signature::Raw(&signature),message).is_ok()); //! ``` +use ed25519::pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; use std::convert::TryFrom; use ed25519::KeypairBytes; use ed25519_dalek::{Signer as _, SigningKey}; -use pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; use crate::{ crypto::{verification_key::CosignVerificationKey, SigningScheme}, @@ -110,9 +110,10 @@ impl Ed25519Keys { match &key.tag[..] { COSIGN_PRIVATE_KEY_PEM_LABEL | SIGSTORE_PRIVATE_KEY_PEM_LABEL => { let der = kdf::decrypt(&key.contents, password)?; - let pkcs8 = pkcs8::PrivateKeyInfo::try_from(&der[..]).map_err(|e| { - SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) - })?; + let pkcs8 = + ed25519_dalek::pkcs8::PrivateKeyInfo::try_from(&der[..]).map_err(|e| { + SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) + })?; let key_pair_bytes = KeypairBytes::try_from(pkcs8).map_err(|e| { SigstoreError::PKCS8Error(format!( "Convert from pkcs8 pem to ed25519 private key failed: {e}" @@ -135,9 +136,10 @@ impl Ed25519Keys { match label { PRIVATE_KEY_PEM_LABEL => { - let pkcs8 = pkcs8::PrivateKeyInfo::try_from(document.as_bytes()).map_err(|e| { - SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) - })?; + let pkcs8 = ed25519_dalek::pkcs8::PrivateKeyInfo::try_from(document.as_bytes()) + .map_err(|e| { + SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) + })?; let key_pair_bytes = KeypairBytes::try_from(pkcs8).map_err(|e| { SigstoreError::PKCS8Error(format!( "Convert from pkcs8 pem to ed25519 private key failed: {e}" @@ -223,7 +225,9 @@ impl KeyPair for Ed25519Keys { /// Return the private key in pkcs8 PEM-encoded format. fn private_key_to_pem(&self) -> Result> { self.signing_key - .to_pkcs8_pem(pkcs8::LineEnding::LF) + .to_pkcs8_der() + .map_err(|e| SigstoreError::PKCS8SpkiError(e.to_string()))? + .to_pem(PRIVATE_KEY_PEM_LABEL, pkcs8::LineEnding::LF) .map_err(|e| SigstoreError::PKCS8SpkiError(e.to_string())) } diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index e84c7ed127..2ea8a59d7a 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -15,6 +15,7 @@ use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION}; +use ed25519::pkcs8::DecodePublicKey as ED25519DecodePublicKey; use pkcs8::{DecodePublicKey, SubjectPublicKeyInfo}; use rsa::{pkcs1v15, pss}; use sha2::{Digest, Sha256, Sha384}; @@ -74,18 +75,20 @@ impl<'a> TryFrom<&SubjectPublicKeyInfo<'a>> for CosignVerificationKey { match algorithm { ID_EC_PUBLIC_KEY => match public_key_der.len() { 65 => Ok(CosignVerificationKey::ECDSA_P256_SHA256_ASN1( - ecdsa::VerifyingKey::try_from(*subject_pub_key_info).map_err(|e| { - SigstoreError::PKCS8SpkiError(format!( - "Ecdsa-P256 from der bytes to public key failed: {e}" - )) - })?, + ecdsa::VerifyingKey::try_from(subject_pub_key_info.subject_public_key) + .map_err(|e| { + SigstoreError::PKCS8SpkiError(format!( + "Ecdsa-P256 from der bytes to public key failed: {e}" + )) + })?, )), 97 => Ok(CosignVerificationKey::ECDSA_P384_SHA384_ASN1( - ecdsa::VerifyingKey::try_from(*subject_pub_key_info).map_err(|e| { - SigstoreError::PKCS8SpkiError(format!( - "Ecdsa-P384 from der bytes to public key failed: {e}" - )) - })?, + ecdsa::VerifyingKey::try_from(subject_pub_key_info.subject_public_key) + .map_err(|e| { + SigstoreError::PKCS8SpkiError(format!( + "Ecdsa-P384 from der bytes to public key failed: {e}" + )) + })?, )), _ => Err(SigstoreError::PublicKeyUnsupportedAlgorithmError(format!( "EC with size {} is not supported", @@ -106,7 +109,7 @@ impl<'a> TryFrom<&SubjectPublicKeyInfo<'a>> for CosignVerificationKey { // #[cfg(feature = "cosign")] ED25519 => Ok(CosignVerificationKey::ED25519( - ed25519_dalek::VerifyingKey::try_from(*subject_pub_key_info)?, + ed25519_dalek::VerifyingKey::try_from(subject_pub_key_info.subject_public_key)?, )), _ => Err(SigstoreError::PublicKeyUnsupportedAlgorithmError(format!( "Key with algorithm OID {} is not supported", diff --git a/src/errors.rs b/src/errors.rs index d412fd7583..2f44f563ec 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -213,5 +213,5 @@ pub enum SigstoreError { PKCS1Error(#[from] pkcs1::Error), #[error(transparent)] - ED25519PKCS1Error(#[from] ed25519_dalek::pkcs8::spki::Error), + Ed25519PKCS8Error(#[from] ed25519_dalek::pkcs8::spki::Error), } From 87a86ad0ad2d331b1d0b2e0c6fd509fc09b06764 Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Wed, 24 May 2023 12:13:15 +0200 Subject: [PATCH 010/161] update scrypt to 0.11.0, adapt for API change Signed-off-by: Dmitry S --- Cargo.toml | 2 +- src/crypto/signing_key/kdf.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 59852451fa..33671628df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ 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.2" -scrypt = "0.10.0" +scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" sha2 = { version = "0.10.6", features = ["oid"] } diff --git a/src/crypto/signing_key/kdf.rs b/src/crypto/signing_key/kdf.rs index b8ae219eea..deb4372b8d 100644 --- a/src/crypto/signing_key/kdf.rs +++ b/src/crypto/signing_key/kdf.rs @@ -108,7 +108,12 @@ impl ScryptKDF { /// Derivate a new key from the given password fn key(&self, password: &[u8]) -> Result> { let log_n = (self.params.n as f64).log2() as u8; - let params = scrypt::Params::new(log_n, self.params.r, self.params.p)?; + let params = scrypt::Params::new( + log_n, + self.params.r, + self.params.p, + scrypt::Params::RECOMMENDED_LEN, + )?; let mut res = Vec::new(); res.resize(BOX_KEY_SIZE, 0x00); scrypt::scrypt(password, &self.salt, ¶ms, &mut res)?; From 75efcc9f46fa17534959b2d42e439cc6d0c76f06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 21:21:27 +0100 Subject: [PATCH 011/161] chore(deps): Update openidconnect requirement from 2.3 to 3.0 (#265) Updates the requirements on [openidconnect](https://github.com/ramosbugs/openidconnect-rs) to permit the latest version. - [Release notes](https://github.com/ramosbugs/openidconnect-rs/releases) - [Commits](https://github.com/ramosbugs/openidconnect-rs/compare/2.3.0...3.0.0) --- updated-dependencies: - dependency-name: openidconnect dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 02ae11c651..f4a56c5fc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ 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" -openidconnect = { version = "2.3", default-features = false, features = [ +openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", ], optional = true } p256 = "0.12" From 4245c1de7f6e917df6cfb7e9f0f563f96a3eba6d Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 26 May 2023 09:54:41 +0200 Subject: [PATCH 012/161] chore(deps): massive update of crypto libraries Update to the latest version of a bunch of crypto libraries. These libraries, especially the ones that belong to RustCrypto, have to be updated at the same time. There have been a lot of API changes and deprecation warnigns, which needed quite some adaptation of our codebase. This fixes the following issues: #267, #266, #265, #248, #236, #234, #233, #230 Signed-off-by: Flavio Castelli --- Cargo.toml | 16 +++--- src/cosign/signature_layers.rs | 16 ++++-- src/crypto/signing_key/ecdsa/ec.rs | 67 +++++++++++----------- src/crypto/signing_key/rsa/keypair.rs | 6 +- src/crypto/signing_key/rsa/mod.rs | 6 +- src/crypto/verification_key.rs | 80 ++++++++++++++------------- src/errors.rs | 3 - 7 files changed, 100 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02ae11c651..39520e4ae9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,20 +73,21 @@ cached = { version = "0.42.0", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.23" } const-oid = "0.9.1" +# TODO: consume via x509-cert? der = "0.7.5" digest = { version = "0.10.3", default-features = false } -ecdsa = { version = "0.15", features = ["pkcs8", "digest", "der"] } +ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } ed25519 = { version = "2.2.1", features = ["alloc"] } ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } -elliptic-curve = { version = "0.12.2", features = ["arithmetic", "pem"] } +elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } lazy_static = "1.4.0" oci-distribution = { version = "0.9", default-features = false, optional = true } olpc-cjson = "0.1" openidconnect = { version = "2.3", default-features = false, features = [ "reqwest", ], optional = true } -p256 = "0.12" -p384 = "0.12" +p256 = "0.13.2" +p384 = "0.13" webbrowser = "0.8.4" pem = "1.0.2" picky = { version = "7.0.0-rc.5", default-features = false, features = [ @@ -94,7 +95,7 @@ picky = { version = "7.0.0-rc.5", default-features = false, features = [ "ec", ] } pkcs1 = { version = "0.7.5", features = ["std"] } -pkcs8 = { version = "0.9.0", features = [ +pkcs8 = { version = "0.10.2", features = [ "pem", "alloc", "pkcs5", @@ -107,19 +108,20 @@ reqwest = { version = "0.11", default-features = false, features = [ "json", "multipart", ], optional = true } -rsa = "0.8.2" +rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } +# TODO: consume via x509-cert? spki = { version = "0.7.2", features = ["pem", "std"] } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tough = { version = "0.13", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.1.1", features = ["pem", "std"] } +x509-cert = { version = "0.2.2", features = ["pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index e0a35a82f1..ef862e4ea2 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -16,11 +16,11 @@ use const_oid::ObjectIdentifier; use digest::Digest; use oci_distribution::client::ImageLayer; -use pkcs8::der::Decode; use serde::Serialize; use std::convert::TryFrom; use std::{collections::HashMap, fmt}; use tracing::{debug, info, warn}; +use x509_cert::der::DecodePem; use x509_cert::ext::pkix::name::GeneralName; use x509_cert::ext::pkix::SubjectAltName; use x509_cert::Certificate; @@ -433,9 +433,8 @@ impl CertificateSignature { fulcio_cert_pool: &CertificatePool, trusted_bundle: &Bundle, ) -> Result { - let pem = pem::parse(cert_raw)?; - let cert = Certificate::from_der(&pem.contents) - .map_err(|e| SigstoreError::X509Error(format!("parse from der: {e}")))?; + let cert = Certificate::from_pem(&cert_raw) + .map_err(|e| SigstoreError::X509Error(format!("parse from pem: {e}")))?; let integrated_time = trusted_bundle.payload.integrated_time; // ensure the certificate has been issued by Fulcio @@ -445,7 +444,12 @@ impl CertificateSignature { let subject = CertificateSubject::from_certificate(&cert)?; let verification_key = - CosignVerificationKey::try_from(&cert.tbs_certificate.subject_public_key_info)?; + CosignVerificationKey::try_from(&cert.tbs_certificate.subject_public_key_info) + .map_err(|e| { + SigstoreError::X509Error(format!( + "cannot extract public key from certificate: {e}" + )) + })?; let issuer = get_cert_extension_by_oid(&cert, SIGSTORE_ISSUER_OID, "Issuer")?; @@ -506,7 +510,7 @@ fn get_cert_extension_by_oid( .iter() .find(|ext| ext.extn_id == ext_oid) .map(|ext| { - String::from_utf8(ext.extn_value.to_vec()).map_err(|_| { + String::from_utf8(ext.extn_value.clone().into_bytes()).map_err(|_| { SigstoreError::X509Error(format!( "Certificate's extension Sigstore {ext_oid_name} is not UTF8 compatible" )) diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index 1d068e2318..6fdafaf915 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -73,7 +73,10 @@ use digest::{ }, Digest, FixedOutput, FixedOutputReset, }; -use ecdsa::{hazmat::SignPrimitive, PrimeCurve, SignatureSize, SigningKey}; +use ecdsa::{ + hazmat::{DigestPrimitive, SignPrimitive}, + PrimeCurve, SignatureSize, SigningKey, +}; use elliptic_curve::{ bigint::ArrayEncoding, generic_array::ArrayLength, @@ -81,10 +84,9 @@ use elliptic_curve::{ sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, subtle::CtOption, zeroize::Zeroizing, - AffineArithmetic, AffinePoint, Curve, FieldSize, ProjectiveArithmetic, PublicKey, Scalar, - SecretKey, + AffinePoint, Curve, CurveArithmetic, FieldBytesSize, PublicKey, Scalar, SecretKey, }; -use pkcs8::{der::Encode, AssociatedOid, DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; +use pkcs8::{AssociatedOid, DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; use signature::DigestSigner; use crate::{ @@ -110,7 +112,7 @@ use super::ECDSAKeys; #[derive(Clone, Debug)] pub struct EcdsaKeys where - C: Curve + ProjectiveArithmetic + pkcs8::AssociatedOid, + C: Curve + CurveArithmetic + pkcs8::AssociatedOid, { ec_seckey: SecretKey, public_key: PublicKey, @@ -118,9 +120,9 @@ where impl EcdsaKeys where - C: Curve + AssociatedOid + ProjectiveArithmetic + PrimeCurve, + C: Curve + AssociatedOid + CurveArithmetic + PrimeCurve, AffinePoint: FromEncodedPoint + ToEncodedPoint, - FieldSize: ModulusSize, + FieldBytesSize: ModulusSize, { /// Create a new `EcdsaKeys` Object, the generic parameter indicates /// the elliptic curve. Please refer to @@ -128,7 +130,7 @@ where /// The secret key (private key) will be randomly /// generated. pub fn new() -> Result { - let ec_seckey: SecretKey = SecretKey::random(rand::rngs::OsRng); + let ec_seckey: SecretKey = SecretKey::random(&mut rand::rngs::OsRng); let public_key = ec_seckey.public_key(); Ok(EcdsaKeys { @@ -207,9 +209,9 @@ where impl KeyPair for EcdsaKeys where - C: Curve + AssociatedOid + ProjectiveArithmetic + PrimeCurve, + C: Curve + AssociatedOid + CurveArithmetic + PrimeCurve, AffinePoint: FromEncodedPoint + ToEncodedPoint, - FieldSize: ModulusSize, + FieldBytesSize: ModulusSize, { /// Return the public key in PEM-encoded SPKI format. fn public_key_to_pem(&self) -> Result { @@ -249,9 +251,7 @@ where let pem = match password.len() { 0 => pem::Pem { tag: PRIVATE_KEY_PEM_LABEL.to_string(), - contents: der - .to_vec() - .map_err(|e| SigstoreError::PKCS8DerError(e.to_string()))?, + contents: der.to_vec(), }, _ => pem::Pem { tag: SIGSTORE_PRIVATE_KEY_PEM_LABEL.to_string(), @@ -285,11 +285,11 @@ where #[derive(Clone, Debug)] pub struct EcdsaSigner where - C: PrimeCurve + ProjectiveArithmetic + AssociatedOid, - Scalar: Invert>> + Reduce + SignPrimitive, - C::UInt: for<'a> From<&'a Scalar>, + C: PrimeCurve + CurveArithmetic + AssociatedOid, + Scalar: Invert>> + Reduce + SignPrimitive, + C::Uint: for<'a> From<&'a Scalar>, SignatureSize: ArrayLength, - D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, + D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, { signing_key: SigningKey, ecdsa_keys: EcdsaKeys, @@ -298,13 +298,13 @@ where impl EcdsaSigner where - C: PrimeCurve + ProjectiveArithmetic + AssociatedOid, - Scalar: Invert>> + Reduce + SignPrimitive, + C: PrimeCurve + CurveArithmetic + AssociatedOid, + Scalar: Invert>> + Reduce + SignPrimitive, AffinePoint: FromEncodedPoint + ToEncodedPoint, - FieldSize: ModulusSize, - C::UInt: for<'a> From<&'a Scalar>, + FieldBytesSize: ModulusSize, + C::Uint: for<'a> From<&'a Scalar>, SignatureSize: ArrayLength, - D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, + D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, { /// Create a new `EcdsaSigner` from the given `EcdsaKeys` and `SignatureDigestAlgorithm` pub fn from_ecdsa_keys(ecdsa_keys: &EcdsaKeys) -> Result { @@ -332,20 +332,21 @@ where impl Signer for EcdsaSigner where - C: PrimeCurve + ProjectiveArithmetic + AssociatedOid, - Scalar: Invert>> + Reduce + SignPrimitive, + C: PrimeCurve + CurveArithmetic + AssociatedOid + DigestPrimitive, + Scalar: Invert>> + Reduce + SignPrimitive, SigningKey: ecdsa::signature::Signer>, - C::UInt: for<'a> From<&'a Scalar>, - <<::UInt as ArrayEncoding>::ByteSize as Add>::Output: + C::Uint: for<'a> From<&'a Scalar>, + <::FieldBytesSize as Add>::Output: Add, B0>, B0>, B1>>, - <<<::UInt as ArrayEncoding>::ByteSize as Add>::Output as Add< + <<::FieldBytesSize as Add>::Output as Add< UInt, B0>, B0>, B1>, >>::Output: ArrayLength, SignatureSize: ArrayLength, - <::UInt as ArrayEncoding>::ByteSize: ModulusSize, - ::AffinePoint: ToEncodedPoint, - ::AffinePoint: FromEncodedPoint, - D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, + <::Uint as ArrayEncoding>::ByteSize: ModulusSize, + ::FieldBytesSize: ModulusSize, + ::AffinePoint: ToEncodedPoint, + ::AffinePoint: FromEncodedPoint, + D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, { /// Sign the given message, and generate a signature. /// The message will firstly be hashed with the given @@ -356,9 +357,9 @@ where fn sign(&self, msg: &[u8]) -> Result> { let mut hasher = D::new(); digest::Digest::update(&mut hasher, msg); - let sig = self.signing_key.try_sign_digest(hasher)?.to_der(); + let (sig, _recovery_id) = self.signing_key.try_sign_digest(hasher)?; - Ok(sig.as_bytes().to_vec()) + Ok(sig.to_der().to_bytes().to_vec()) } /// Return the ref to the keypair inside the signer diff --git a/src/crypto/signing_key/rsa/keypair.rs b/src/crypto/signing_key/rsa/keypair.rs index 928ca69e74..ae94c10f52 100644 --- a/src/crypto/signing_key/rsa/keypair.rs +++ b/src/crypto/signing_key/rsa/keypair.rs @@ -183,19 +183,19 @@ impl RSAKeys { PaddingScheme::PKCS1v15 => match digest_algorithm { DigestAlgorithm::Sha256 => { SigStoreSigner::RSA_PKCS1_SHA256(RSASigner::RSA_PKCS1_SHA256( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), self.clone(), )) } DigestAlgorithm::Sha384 => { SigStoreSigner::RSA_PKCS1_SHA384(RSASigner::RSA_PKCS1_SHA384( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), self.clone(), )) } DigestAlgorithm::Sha512 => { SigStoreSigner::RSA_PKCS1_SHA512(RSASigner::RSA_PKCS1_SHA512( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), self.clone(), )) } diff --git a/src/crypto/signing_key/rsa/mod.rs b/src/crypto/signing_key/rsa/mod.rs index 56d7a918df..c04ed83917 100644 --- a/src/crypto/signing_key/rsa/mod.rs +++ b/src/crypto/signing_key/rsa/mod.rs @@ -141,15 +141,15 @@ impl RSASigner { }, PaddingScheme::PKCS1v15 => match digest_algorithm { DigestAlgorithm::Sha256 => RSASigner::RSA_PKCS1_SHA256( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), rsa_keys.clone(), ), DigestAlgorithm::Sha384 => RSASigner::RSA_PKCS1_SHA384( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), rsa_keys.clone(), ), DigestAlgorithm::Sha512 => RSASigner::RSA_PKCS1_SHA512( - SigningKey::::new_with_prefix(private_key), + SigningKey::::new(private_key), rsa_keys.clone(), ), }, diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 2ea8a59d7a..72a2a64156 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -15,12 +15,13 @@ use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION}; +use der::referenced::OwnedToRef; use ed25519::pkcs8::DecodePublicKey as ED25519DecodePublicKey; -use pkcs8::{DecodePublicKey, SubjectPublicKeyInfo}; use rsa::{pkcs1v15, pss}; use sha2::{Digest, Sha256, Sha384}; use signature::{DigestVerifier, Verifier}; use std::convert::TryFrom; +use x509_cert::spki::SubjectPublicKeyInfoOwned; use super::{ signing_key::{KeyPair, SigStoreSigner}, @@ -66,42 +67,45 @@ pub enum CosignVerificationKey { /// * ECDSA P-256: assumes the SHA-256 digest algorithm is used /// * ECDSA P-384: assumes the SHA-384 digest algorithm is used /// * RSA: assumes PKCS1 padding is used -impl<'a> TryFrom<&SubjectPublicKeyInfo<'a>> for CosignVerificationKey { +impl TryFrom<&SubjectPublicKeyInfoOwned> for CosignVerificationKey { type Error = SigstoreError; - fn try_from(subject_pub_key_info: &SubjectPublicKeyInfo<'a>) -> Result { + fn try_from(subject_pub_key_info: &SubjectPublicKeyInfoOwned) -> Result { let algorithm = subject_pub_key_info.algorithm.oid; - let public_key_der = subject_pub_key_info.subject_public_key; + let public_key_der = &subject_pub_key_info.subject_public_key; match algorithm { - ID_EC_PUBLIC_KEY => match public_key_der.len() { - 65 => Ok(CosignVerificationKey::ECDSA_P256_SHA256_ASN1( - ecdsa::VerifyingKey::try_from(subject_pub_key_info.subject_public_key) - .map_err(|e| { - SigstoreError::PKCS8SpkiError(format!( - "Ecdsa-P256 from der bytes to public key failed: {e}" - )) - })?, - )), - 97 => Ok(CosignVerificationKey::ECDSA_P384_SHA384_ASN1( - ecdsa::VerifyingKey::try_from(subject_pub_key_info.subject_public_key) - .map_err(|e| { - SigstoreError::PKCS8SpkiError(format!( - "Ecdsa-P384 from der bytes to public key failed: {e}" - )) - })?, - )), - _ => Err(SigstoreError::PublicKeyUnsupportedAlgorithmError(format!( - "EC with size {} is not supported", - // asn.1 encode caused different length - (public_key_der.len() - 1) * 4 - ))), - }, + ID_EC_PUBLIC_KEY => { + match public_key_der.raw_bytes().len() { + 65 => Ok(CosignVerificationKey::ECDSA_P256_SHA256_ASN1( + ecdsa::VerifyingKey::try_from(subject_pub_key_info.owned_to_ref()) + .map_err(|e| { + SigstoreError::PKCS8SpkiError(format!( + "Ecdsa-P256 from der bytes to public key failed: {e}" + )) + })?, + )), + 97 => Ok(CosignVerificationKey::ECDSA_P384_SHA384_ASN1( + ecdsa::VerifyingKey::try_from(subject_pub_key_info.owned_to_ref()) + .map_err(|e| { + SigstoreError::PKCS8SpkiError(format!( + "Ecdsa-P384 from der bytes to public key failed: {e}" + )) + })?, + )), + _ => Err(SigstoreError::PublicKeyUnsupportedAlgorithmError(format!( + "EC with size {} is not supported", + // asn.1 encode caused different length + (public_key_der.raw_bytes().len() - 1) * 4 + ))), + } + } RSA_ENCRYPTION => { - let pubkey = rsa::RsaPublicKey::try_from(*subject_pub_key_info).map_err(|e| { - SigstoreError::PKCS8SpkiError(format!( - "RSA from der bytes to public key failed: {e}" - )) - })?; + let pubkey = rsa::RsaPublicKey::try_from(subject_pub_key_info.owned_to_ref()) + .map_err(|e| { + SigstoreError::PKCS8SpkiError(format!( + "RSA from der bytes to public key failed: {e}" + )) + })?; Ok(CosignVerificationKey::RSA_PKCS1_SHA256( pkcs1v15::VerifyingKey::::from(pubkey), )) @@ -109,7 +113,7 @@ impl<'a> TryFrom<&SubjectPublicKeyInfo<'a>> for CosignVerificationKey { // #[cfg(feature = "cosign")] ED25519 => Ok(CosignVerificationKey::ED25519( - ed25519_dalek::VerifyingKey::try_from(subject_pub_key_info.subject_public_key)?, + ed25519_dalek::VerifyingKey::try_from(subject_pub_key_info.owned_to_ref())?, )), _ => Err(SigstoreError::PublicKeyUnsupportedAlgorithmError(format!( "Key with algorithm OID {} is not supported", @@ -152,7 +156,7 @@ impl CosignVerificationKey { )) } SigningScheme::RSA_PKCS1_SHA256(_) => { - CosignVerificationKey::RSA_PKCS1_SHA256(pkcs1v15::VerifyingKey::new_with_prefix( + CosignVerificationKey::RSA_PKCS1_SHA256(pkcs1v15::VerifyingKey::new( rsa::RsaPublicKey::from_public_key_der(der_data).map_err(|e| { SigstoreError::PKCS8SpkiError(format!( "read rsa public key from der failed: {e}" @@ -161,7 +165,7 @@ impl CosignVerificationKey { )) } SigningScheme::RSA_PKCS1_SHA384(_) => { - CosignVerificationKey::RSA_PKCS1_SHA384(pkcs1v15::VerifyingKey::new_with_prefix( + CosignVerificationKey::RSA_PKCS1_SHA384(pkcs1v15::VerifyingKey::new( rsa::RsaPublicKey::from_public_key_der(der_data).map_err(|e| { SigstoreError::PKCS8SpkiError(format!( "read rsa public key from der failed: {e}" @@ -170,7 +174,7 @@ impl CosignVerificationKey { )) } SigningScheme::RSA_PKCS1_SHA512(_) => { - CosignVerificationKey::RSA_PKCS1_SHA512(pkcs1v15::VerifyingKey::new_with_prefix( + CosignVerificationKey::RSA_PKCS1_SHA512(pkcs1v15::VerifyingKey::new( rsa::RsaPublicKey::from_public_key_der(der_data).map_err(|e| { SigstoreError::PKCS8SpkiError(format!( "read rsa public key from der failed: {e}" @@ -216,9 +220,7 @@ impl CosignVerificationKey { ed25519bytes.as_ref(), )?)) } else if let Ok(rsapk) = rsa::RsaPublicKey::from_public_key_der(der_data) { - Ok(Self::RSA_PKCS1_SHA256( - pkcs1v15::VerifyingKey::new_with_prefix(rsapk), - )) + Ok(Self::RSA_PKCS1_SHA256(pkcs1v15::VerifyingKey::new(rsapk))) } else { Err(SigstoreError::InvalidKeyFormat { error: "Failed to parse the public key.".to_string(), diff --git a/src/errors.rs b/src/errors.rs index 2f44f563ec..f513f17543 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -206,9 +206,6 @@ pub enum SigstoreError { #[error(transparent)] RSAError(#[from] rsa::errors::Error), - #[error(transparent)] - RSAPKCS1Error(#[from] rsa::pkcs1::Error), - #[error(transparent)] PKCS1Error(#[from] pkcs1::Error), From 83862a17e0ffeffbba6a921a0311466879ce1890 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 26 May 2023 10:00:13 +0200 Subject: [PATCH 013/161] chore(deps): remove `der` and `scrypt` deps These dependencies are also exported by the `x509-cert` crate. We're going to consume them via this crate to simply dependency management. These two dependencies must move in sync with `x509-cert`, hence it makes a lot of sense to leverage the export provided by this crate. Signed-off-by: Flavio Castelli --- Cargo.toml | 4 ---- src/crypto/verification_key.rs | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39520e4ae9..f522930d61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,8 +73,6 @@ cached = { version = "0.42.0", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.23" } const-oid = "0.9.1" -# TODO: consume via x509-cert? -der = "0.7.5" digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } ed25519 = { version = "2.2.1", features = ["alloc"] } @@ -114,8 +112,6 @@ serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } -# TODO: consume via x509-cert? -spki = { version = "0.7.2", features = ["pem", "std"] } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tough = { version = "0.13", features = ["http"], optional = true } diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 72a2a64156..90e4df849b 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -15,13 +15,12 @@ use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION}; -use der::referenced::OwnedToRef; use ed25519::pkcs8::DecodePublicKey as ED25519DecodePublicKey; use rsa::{pkcs1v15, pss}; use sha2::{Digest, Sha256, Sha384}; use signature::{DigestVerifier, Verifier}; use std::convert::TryFrom; -use x509_cert::spki::SubjectPublicKeyInfoOwned; +use x509_cert::{der::referenced::OwnedToRef, spki::SubjectPublicKeyInfoOwned}; use super::{ signing_key::{KeyPair, SigStoreSigner}, From 40e7929c980ba47ee6c065a1b32fe043320811e4 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 26 May 2023 10:06:02 +0200 Subject: [PATCH 014/161] chore(lint): addres warnings Fix clippy warnings Signed-off-by: Flavio Castelli --- examples/rekor/create_log_entry/main.rs | 2 -- examples/rekor/search_log_query/main.rs | 2 -- src/cosign/signature_layers.rs | 6 +++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/rekor/create_log_entry/main.rs b/examples/rekor/create_log_entry/main.rs index 1753e188a4..eff3c93f6d 100644 --- a/examples/rekor/create_log_entry/main.rs +++ b/examples/rekor/create_log_entry/main.rs @@ -98,10 +98,8 @@ async fn main() { // The following default values will be used if the user does not input values using cli flags const HASH: &str = "c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413"; - const URL: &str = "https://raw.githubusercontent.com/jyotsna-penumaka/rekor-rs/rekor-functionality/test_data/data"; const PUBLIC_KEY: &str = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFeEhUTWRSQk80ZThCcGZ3cG5KMlozT2JMRlVrVQpaUVp6WGxtKzdyd1lZKzhSMUZpRWhmS0JZclZraGpHL2lCUjZac2s3Z01iYWZPOG9FM01lUEVvWU93PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="; const SIGNATURE: &str = "MEUCIHWACbBnw+YkJCy2tVQd5i7VH6HgkdVBdP7HRV1IEsDuAiEA19iJNvmkE6We7iZGjHsTkjXV8QhK9iXu0ArUxvJF1N8="; - const KEY_FORMAT: &str = "x509"; const API_VERSION: &str = "0.0.1"; let hash = Hash::new( diff --git a/examples/rekor/search_log_query/main.rs b/examples/rekor/search_log_query/main.rs index 445c9b5089..39d6197702 100644 --- a/examples/rekor/search_log_query/main.rs +++ b/examples/rekor/search_log_query/main.rs @@ -41,10 +41,8 @@ async fn main() { // The following default values will be used if the user does not input values using cli flags const HASH: &str = "c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413"; - const URL: &str = "https://raw.githubusercontent.com/jyotsna-penumaka/rekor-rs/rekor-functionality/test_data/data"; const PUBLIC_KEY: &str = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFeEhUTWRSQk80ZThCcGZ3cG5KMlozT2JMRlVrVQpaUVp6WGxtKzdyd1lZKzhSMUZpRWhmS0JZclZraGpHL2lCUjZac2s3Z01iYWZPOG9FM01lUEVvWU93PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="; const SIGNATURE: &str = "MEUCIHWACbBnw+YkJCy2tVQd5i7VH6HgkdVBdP7HRV1IEsDuAiEA19iJNvmkE6We7iZGjHsTkjXV8QhK9iXu0ArUxvJF1N8="; - const KEY_FORMAT: &str = "x509"; const API_VERSION: &str = "0.0.1"; const ENTRY_UUIDS: &str = "1377da9d9dbad451a5a8acdd28add750815d34e8205f1b8a35a67b8a27dae9bf"; const LOG_INDEXES: &str = "2922253"; diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index ef862e4ea2..de7a7cf8f4 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -429,16 +429,16 @@ impl CertificateSignature { /// Ensures the given certificate can be trusted, then extracts /// its details and returns them as a `CertificateSignature` object pub(crate) fn from_certificate( - cert_raw: &[u8], + cert_pem: &[u8], fulcio_cert_pool: &CertificatePool, trusted_bundle: &Bundle, ) -> Result { - let cert = Certificate::from_pem(&cert_raw) + let cert = Certificate::from_pem(cert_pem) .map_err(|e| SigstoreError::X509Error(format!("parse from pem: {e}")))?; let integrated_time = trusted_bundle.payload.integrated_time; // ensure the certificate has been issued by Fulcio - fulcio_cert_pool.verify_pem_cert(cert_raw)?; + fulcio_cert_pool.verify_pem_cert(cert_pem)?; crypto::certificate::is_trusted(&cert, integrated_time)?; From 7770488dc08346259defd542e8deefce61a4bc83 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 26 May 2023 12:45:48 +0200 Subject: [PATCH 015/161] chore(deps): update pem Update `pem` to the latest version. That caused some API changes Signed-off-by: Flavio Castelli --- Cargo.toml | 2 +- examples/cosign/verify/main.rs | 2 +- examples/fulcio/cert/main.rs | 2 +- src/cosign/mod.rs | 2 +- .../certificate_verifier.rs | 2 +- src/crypto/certificate.rs | 16 ++++++++-------- src/crypto/signing_key/ecdsa/ec.rs | 17 +++++++---------- src/crypto/signing_key/ed25519.rs | 17 +++++++---------- src/crypto/signing_key/rsa/keypair.rs | 17 +++++++---------- src/crypto/verification_key.rs | 14 +++++++------- 10 files changed, 41 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f522930d61..99160ebe8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ openidconnect = { version = "2.3", default-features = false, features = [ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" -pem = "1.0.2" +pem = "2.0" picky = { version = "7.0.0-rc.5", default-features = false, features = [ "x509", "ec", diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 835645c399..8ee5dc91fb 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -349,7 +349,7 @@ fn parse_cert_bundle(bundle_path: &str) -> Result Result<()> { let cert = BASE64_STD_ENGINE.decode(cert)?; let pem = pem::parse(cert)?; - let cert = Certificate::from_der(&pem.contents).map_err(|e| { + let cert = Certificate::from_der(pem.contents()).map_err(|e| { SigstoreError::PKCS8SpkiError(format!("parse der into cert failed: {e}")) })?; let spki = cert.tbs_certificate.subject_public_key_info; diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index 3e28a0ee28..073463d06d 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -35,7 +35,7 @@ impl CertificateVerifier { cert_chain: Option<&[crate::registry::Certificate]>, ) -> Result { let pem = pem::parse(cert_bytes)?; - Self::from_der(&pem.contents, require_rekor_bundle, cert_chain) + Self::from_der(pem.contents(), require_rekor_bundle, cert_chain) } /// Create a new instance of `CertificateVerifier` using the DER encoded diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 6d7da38702..3273261c7d 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -135,7 +135,7 @@ mod tests { let issued_cert = generate_certificate(Some(&ca_data), CertGenerationOptions::default())?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; assert!(verify_key_usages(&cert).is_ok()); Ok(()) @@ -154,7 +154,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; let err = verify_key_usages(&cert).expect_err("Was supposed to return an error"); let found = match err { @@ -179,7 +179,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; let err = verify_key_usages(&cert).expect_err("Was supposed to return an error"); let found = match err { @@ -205,7 +205,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; let error = verify_has_san(&cert).expect_err("Didn't get an error"); let found = match error { @@ -224,7 +224,7 @@ mod tests { let issued_cert = generate_certificate(Some(&ca_data), CertGenerationOptions::default())?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; assert!(verify_validity(&cert).is_ok()); @@ -245,7 +245,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; let err = verify_validity(&cert).expect_err("Was expecting an error"); let found = match err { @@ -273,7 +273,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; assert!(verify_expiration(&cert, integrated_time.timestamp(),).is_ok()); @@ -296,7 +296,7 @@ mod tests { )?; let issued_cert_pem = issued_cert.cert.to_pem().unwrap(); let pem = pem::parse(issued_cert_pem)?; - let cert = x509_cert::Certificate::from_der(&pem.contents)?; + let cert = x509_cert::Certificate::from_der(pem.contents())?; let err = verify_expiration(&cert, integrated_time.timestamp()) .expect_err("Was expecting an error"); diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index 6fdafaf915..7c2dd70cb0 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -144,9 +144,9 @@ where /// [`SIGSTORE_PRIVATE_KEY_PEM_LABEL`]. pub fn from_encrypted_pem(private_key: &[u8], password: &[u8]) -> Result { let key = pem::parse(private_key)?; - match &key.tag[..] { + match key.tag() { COSIGN_PRIVATE_KEY_PEM_LABEL | SIGSTORE_PRIVATE_KEY_PEM_LABEL => { - let der = kdf::decrypt(&key.contents, password)?; + let der = kdf::decrypt(key.contents(), password)?; let pkcs8 = pkcs8::PrivateKeyInfo::try_from(&der[..]).map_err(|e| { SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) })?; @@ -249,14 +249,11 @@ where fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; let pem = match password.len() { - 0 => pem::Pem { - tag: PRIVATE_KEY_PEM_LABEL.to_string(), - contents: der.to_vec(), - }, - _ => pem::Pem { - tag: SIGSTORE_PRIVATE_KEY_PEM_LABEL.to_string(), - contents: kdf::encrypt(&der, password)?, - }, + 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), + _ => pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ), }; let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) diff --git a/src/crypto/signing_key/ed25519.rs b/src/crypto/signing_key/ed25519.rs index f2b236f5e8..0934acd5eb 100644 --- a/src/crypto/signing_key/ed25519.rs +++ b/src/crypto/signing_key/ed25519.rs @@ -107,9 +107,9 @@ impl Ed25519Keys { /// [`SIGSTORE_PRIVATE_KEY_PEM_LABEL`]. pub fn from_encrypted_pem(encrypted_pem: &[u8], password: &[u8]) -> Result { let key = pem::parse(encrypted_pem)?; - match &key.tag[..] { + match key.tag() { COSIGN_PRIVATE_KEY_PEM_LABEL | SIGSTORE_PRIVATE_KEY_PEM_LABEL => { - let der = kdf::decrypt(&key.contents, password)?; + let der = kdf::decrypt(key.contents(), password)?; let pkcs8 = ed25519_dalek::pkcs8::PrivateKeyInfo::try_from(&der[..]).map_err(|e| { SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) @@ -209,14 +209,11 @@ impl KeyPair for Ed25519Keys { fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; let pem = match password.len() { - 0 => pem::Pem { - tag: PRIVATE_KEY_PEM_LABEL.to_string(), - contents: der.to_vec(), - }, - _ => pem::Pem { - tag: SIGSTORE_PRIVATE_KEY_PEM_LABEL.to_string(), - contents: kdf::encrypt(&der, password)?, - }, + 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), + _ => pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ), }; let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) diff --git a/src/crypto/signing_key/rsa/keypair.rs b/src/crypto/signing_key/rsa/keypair.rs index ae94c10f52..c7d12148e5 100644 --- a/src/crypto/signing_key/rsa/keypair.rs +++ b/src/crypto/signing_key/rsa/keypair.rs @@ -90,9 +90,9 @@ impl RSAKeys { /// [`SIGSTORE_PRIVATE_KEY_PEM_LABEL`]. pub fn from_encrypted_pem(encrypted_pem: &[u8], password: &[u8]) -> Result { let key = pem::parse(encrypted_pem)?; - match &key.tag[..] { + match key.tag() { COSIGN_PRIVATE_KEY_PEM_LABEL | SIGSTORE_PRIVATE_KEY_PEM_LABEL => { - let der = kdf::decrypt(&key.contents, password)?; + let der = kdf::decrypt(key.contents(), password)?; let pkcs8 = pkcs8::PrivateKeyInfo::try_from(&der[..]).map_err(|e| { SigstoreError::PKCS8Error(format!("Read PrivateKeyInfo failed: {e}")) })?; @@ -234,14 +234,11 @@ impl KeyPair for RSAKeys { fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; let pem = match password.len() { - 0 => pem::Pem { - tag: PRIVATE_KEY_PEM_LABEL.to_string(), - contents: der.to_vec(), - }, - _ => pem::Pem { - tag: SIGSTORE_PRIVATE_KEY_PEM_LABEL.to_string(), - contents: kdf::encrypt(&der, password)?, - }, + 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), + _ => pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ), }; let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 90e4df849b..9ba0feaaab 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -232,7 +232,7 @@ impl CosignVerificationKey { /// from the DER-encoded bytes. pub fn from_pem(pem_data: &[u8], signing_scheme: &SigningScheme) -> Result { let key_pem = pem::parse(pem_data)?; - Self::from_der(key_pem.contents.as_slice(), signing_scheme) + Self::from_der(key_pem.contents(), signing_scheme) } /// Builds a [`CosignVerificationKey`] from PEM-encoded public key data. This function will @@ -243,7 +243,7 @@ impl CosignVerificationKey { /// * `Ed25519 public key`: `Ed25519` pub fn try_from_pem(pem_data: &[u8]) -> Result { let key_pem = pem::parse(pem_data)?; - Self::try_from_der(key_pem.contents.as_slice()) + Self::try_from_der(key_pem.contents()) } /// Builds a `CosignVerificationKey` from [`SigStoreSigner`]. The methods will derive @@ -451,7 +451,7 @@ DwIDAQAB let issued_cert = generate_certificate(Some(&ca_data), issued_cert_generation_options)?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = Certificate::from_der(&pem.contents)?; + let cert = Certificate::from_der(pem.contents())?; let spki = cert.tbs_certificate.subject_public_key_info; let cosign_verification_key = @@ -478,7 +478,7 @@ DwIDAQAB let issued_cert = generate_certificate(Some(&ca_data), issued_cert_generation_options)?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = Certificate::from_der(&pem.contents)?; + let cert = Certificate::from_der(pem.contents())?; let spki = cert.tbs_certificate.subject_public_key_info; let cosign_verification_key = @@ -505,7 +505,7 @@ DwIDAQAB let issued_cert = generate_certificate(Some(&ca_data), issued_cert_generation_options)?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = Certificate::from_der(&pem.contents)?; + let cert = Certificate::from_der(pem.contents())?; let spki = cert.tbs_certificate.subject_public_key_info; let cosign_verification_key = @@ -532,7 +532,7 @@ DwIDAQAB let issued_cert = generate_certificate(Some(&ca_data), issued_cert_generation_options)?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = Certificate::from_der(&pem.contents)?; + let cert = Certificate::from_der(pem.contents())?; let spki = cert.tbs_certificate.subject_public_key_info; let cosign_verification_key = @@ -560,7 +560,7 @@ DwIDAQAB let issued_cert = generate_certificate(Some(&ca_data), issued_cert_generation_options)?; let issued_cert_pem = issued_cert.cert.to_pem()?; let pem = pem::parse(issued_cert_pem)?; - let cert = Certificate::from_der(&pem.contents)?; + let cert = Certificate::from_der(pem.contents())?; let spki = cert.tbs_certificate.subject_public_key_info; let err = CosignVerificationKey::try_from(&spki); From 007d260e4a074701f4ebb15e2ea65f3dad57ea11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 19:59:52 +0000 Subject: [PATCH 016/161] chore(deps): Update rstest requirement from 0.16.0 to 0.17.0 Updates the requirements on [rstest](https://github.com/la10736/rstest) to permit the latest version. - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/0.16.0...0.17.0) --- updated-dependencies: - dependency-name: rstest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f4a56c5fc9..943baa8cd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,7 +130,7 @@ chrono = "0.4.20" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" -rstest = "0.16.0" +rstest = "0.17.0" serial_test = "2.0.0" tempfile = "3.3.0" testcontainers = "0.14" From 3d6e5c7e1737964dea1b65ca8b4cc0f6555f3142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Guilherme=20Vanz?= Date: Mon, 30 Jan 2023 15:22:04 -0300 Subject: [PATCH 017/161] Update create_log_entry example to create key pair. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the example program create_log_entry to create the key pair used to sign and upload the data file into the Rekor transparency log. Therefore, the users do not need to create the files and generate the hashes required to create an transparency log entry. Signed-off-by: José Guilherme Vanz --- Cargo.toml | 1 + examples/rekor/create_log_entry/main.rs | 113 +++++++++++++++++++----- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 09db64fd79..8c633eea62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,7 @@ serial_test = "2.0.0" tempfile = "3.3.0" testcontainers = "0.14" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } +hex = "0.4.3" # cosign example mappings diff --git a/examples/rekor/create_log_entry/main.rs b/examples/rekor/create_log_entry/main.rs index eff3c93f6d..a76645e0df 100644 --- a/examples/rekor/create_log_entry/main.rs +++ b/examples/rekor/create_log_entry/main.rs @@ -13,6 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use base64::{engine::general_purpose, Engine as _}; +use sha2::Digest; +use sha2::Sha256; +use sigstore::crypto::signing_key::SigStoreSigner; +use sigstore::crypto::SigningScheme; use sigstore::rekor::apis::{configuration::Configuration, entries_api}; use sigstore::rekor::models::{ hashedrekord::{AlgorithmKind, Data, Hash, PublicKey, Signature, Spec}, @@ -21,12 +26,31 @@ use sigstore::rekor::models::{ use clap::{Arg, Command}; +async fn create_signer() -> SigStoreSigner { + SigningScheme::ECDSA_P256_SHA256_ASN1 + .create_signer() + .expect("cannot create sigstore signer") +} + +// function to fetch data and generate the hash of it to be signed and upload to the transparency log +async fn get_file_sha256sum(url: String) -> Result<(Vec, String), reqwest::Error> { + let body = reqwest::get(&url).await?.bytes().await?; + let mut digester = Sha256::new(); + digester.update(body.clone()); + let digest = format!("{:x}", digester.finalize()); + Ok((body.to_vec(), digest)) +} + #[tokio::main] async fn main() { /* - Creates an entry in the transparency log for a detached signature, public key, and content. - Items can be included in the request or fetched by the server when URLs are specified. + Creates an entry in the transparency log. If no command line arguments is provided, + the pram will generate a key pair, download the file available at URL constant, sign it + and create an entry in the transparency log. In the other hand, if the user sets the + command line flags, the program will use that info to create the entry. Therefore, + if the user use information of an entry already present in the transparency log, this + program can print an error. See an example: Example command : cargo run --example create_log_entry -- \ @@ -34,7 +58,6 @@ async fn main() { --url https://raw.githubusercontent.com/jyotsna-penumaka/rekor-rs/rekor-functionality/test_data/data\ --public_key LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFeEhUTWRSQk80ZThCcGZ3cG5KMlozT2JMRlVrVQpaUVp6WGxtKzdyd1lZKzhSMUZpRWhmS0JZclZraGpHL2lCUjZac2s3Z01iYWZPOG9FM01lUEVvWU93PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\ --signature MEUCIHWACbBnw+YkJCy2tVQd5i7VH6HgkdVBdP7HRV1IEsDuAiEA19iJNvmkE6We7iZGjHsTkjXV8QhK9iXu0ArUxvJF1N8=\ - --key_format x509\ --api_version 0.0.1 When the example code is run with the default values, the following error message gets returned: @@ -43,7 +66,7 @@ async fn main() { ResponseError( ResponseContent { status: 409, - content: "{\"code\":409,\"message\":\"An equivalent entry already exists in the transparency log with UUID aa7ba9f2eae2a0c9a40dab33c160ba41c80da877b0207b8f3243fdb4db5cf30c\"}\n", + content: "{\"code\":409,\"message\":\"An equivalent entry already exists in the transparency log with UUID 1377da9d9dbad451a5a8acdd28add750815d34e8205f1b8a35a67b8a27dae9bf\"}\n", entity: Some( Status400( Error { @@ -51,7 +74,7 @@ async fn main() { 409, ), message: Some( - "An equivalent entry already exists in the transparency log with UUID aa7ba9f2eae2a0c9a40dab33c160ba41c80da877b0207b8f3243fdb4db5cf30c", + "An equivalent entry already exists in the transparency log with UUID 1377da9d9dbad451a5a8acdd28add750815d34e8205f1b8a35a67b8a27dae9bf", ), }, ), @@ -61,11 +84,58 @@ async fn main() { ) This is because an equivalent entry with the provided meta data already exists in the transparency log. - When you use the example code to create a new entry with fresh set of input values, - you should be able to run the code without any errors. + When you use the example code to create a new entry with fresh set of input values or levaing the program + to generate the required data, you should be able to run the code without any errors. See an example: + Example command : + cargo run --example create_log_entry -- + + The expected output will be something similar to: + + Ok( + LogEntry { + uuid: "24296fb24b8ad77afa01e2c1f5555326e4fc32a942b40a2d798ae72a8f10c801f6e8dee771dfbacc", + attestation: None, + body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjN2VhZDg3ZmE1YzgyZDJiMTdmZWVjZTFjMmVlMWJkYThlOTQ3ODhmNGIyMDhkZTUwNTdiMzYxN2E0MmI3NDEzIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQWh4elhWZnRyMWpyS0k3dEluWW5iR1pNMDZybFhpQ1lUMTRJbFdFazF4QkFpRUE0SGllM2l4cTRyOG9tVVgwclRDV2o3UmducVhqUEFZTmlkaDlQVllrQXFVPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGYjNCRmJGTlJlbGRTTUM5Sk5raEJZbm9yV21sVmFsVlhWR051WlFvdlUwUndWV1ZOVUhGR04wUXlZbU5xV2tKRlYweGhiak5XTjB3cmVHNW5jVFJHYW1wRGVtdHlLMFkwYlc5bFNEaFJTbWhNYUV0SlQzWlJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=", + integrated_time: 1675277501, + log_i_d: "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d", + log_index: 12425816, + verification: Verification { + inclusion_proof: Some( + InclusionProof { + hashes: [ + "a0b75928818e5fa302c3690a895e0385803a079391a79cf9f25b08a51eebc338", + "3a39532ac61bf4d3f9a982e38f1b3166a3222e9d8a081d31f67d0da745117dc5", + "96ff049c2d122233d7e44b49d5df16b0901dbf85523d90bd739a3a25d26a974c", + "09805d1e21e395d8b82e7269c7ddff2941564f925145196273a993c452059a85", + "37ca98bdb80bdc45768539d15117b2b57531b3ad1f051aaa4f58d030f868f86e", + "e81e08afa83961c36e2a6961f66859620c9ee4ed9be5631ace9f3c27c72f66fb", + "2f01e165e3758aba5fd53d0c03c88b84ccbed7334173d9159d87fed5930bfe03", + "d0509b1c0bde3ba0517efcc5d3e8d2007fe86e5e055cebd5e94307cd0394c22d", + "8a84151f9b8fbbf7b3e77cb658535ec46d27c1cdd1cab714558dc51114922e7a", + "5eb43eca7a763e2eaafb2bc2fc963e7802283cf1a9076638242177b1669942c0", + "5523cd019fea93d01834fc429f708b700aeb72c835a73161cdb9003f8f4e8072", + "4b6df664d9552bc24d48a4c7d5659a8270065e1fedbc39103b010ab235a87850", + "616429db6c7d20c5b0eff1a6e512ea57a0734b94ae0bc7c914679463e01a7fba", + "5a4ad1534b1e770f02bfde0de15008a6971cf1ffbfa963fc9c2a644973a8d2d1", + ], + log_index: 8262385, + root_hash: "41b3e1294d122b2190396de7de92731a378378ac2d7f620eb01d653838e88219", + tree_size: 8262387, + }, + ), + signed_entry_timestamp: "MEUCIG/vIwjuQoiVZtxw48KSMYyxXlpHA/y8kxYTJh46qbejAiEAyFAP5oQjxT6xFK7wKYW33sa/5wFQvqtKsdTLnitrzWA=", + }, + }, + ) */ + const URL: &str = "https://raw.githubusercontent.com/jyotsna-penumaka/rekor-rs/rekor-functionality/test_data/data"; + const API_VERSION: &str = "0.0.1"; + + let data_job = get_file_sha256sum(URL.to_string()); + let signer_job = create_signer(); + let matches = Command::new("cmd") .arg(Arg::new("hash") .long("hash") @@ -79,10 +149,6 @@ async fn main() { .long("public_key") .value_name("PUBLIC_KEY") .help("base64 encoded public_key. Look at https://raw.githubusercontent.com/jyotsna-penumaka/rekor-rs/rekor-functionality/test_data/create_log_entry.md for more details on generating keys.")) - .arg(Arg::new("key_format") - .long("key_format") - .value_name("KEY_FORMAT") - .help("Accepted formats are : pgp / x509 / minsign / ssh / tuf")) .arg(Arg::new("signature") .long("signature") .value_name("SIGNATURE") @@ -96,31 +162,34 @@ async fn main() { let configuration = Configuration::default(); - // The following default values will be used if the user does not input values using cli flags - const HASH: &str = "c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413"; - const PUBLIC_KEY: &str = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFeEhUTWRSQk80ZThCcGZ3cG5KMlozT2JMRlVrVQpaUVp6WGxtKzdyd1lZKzhSMUZpRWhmS0JZclZraGpHL2lCUjZac2s3Z01iYWZPOG9FM01lUEVvWU93PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="; - const SIGNATURE: &str = "MEUCIHWACbBnw+YkJCy2tVQd5i7VH6HgkdVBdP7HRV1IEsDuAiEA19iJNvmkE6We7iZGjHsTkjXV8QhK9iXu0ArUxvJF1N8="; - const API_VERSION: &str = "0.0.1"; + let (data_bytes, digest) = data_job.await.expect("Cannot get data digest"); + let signer = signer_job.await; + let public_key_base64 = general_purpose::STANDARD.encode( + signer + .to_sigstore_keypair() + .expect("Cannot get sigstore keypair") + .public_key_to_pem() + .expect("Cannot set public key"), + ); + + let sig = general_purpose::STANDARD.encode(signer.sign(&data_bytes).expect("Cannot sign data")); let hash = Hash::new( AlgorithmKind::sha256, flags .get_one::("hash") - .unwrap_or(&HASH.to_string()) + .unwrap_or(&digest) .to_owned(), ); let data = Data::new(hash); let public_key = PublicKey::new( flags .get_one::("public_key") - .unwrap_or(&PUBLIC_KEY.to_string()) + .unwrap_or(&public_key_base64) .to_owned(), ); let signature = Signature::new( - flags - .get_one("signature") - .unwrap_or(&SIGNATURE.to_string()) - .to_owned(), + flags.get_one("signature").unwrap_or(&sig).to_owned(), public_key, ); let spec = Spec::new(signature, data); From 0c8d3098d9aad25f5e25bd6e6a5dd7f0b21adcb8 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 30 May 2023 10:05:37 +0200 Subject: [PATCH 018/161] fix: address typos Address some typos inside of the comments Signed-off-by: Flavio Castelli --- examples/rekor/create_log_entry/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rekor/create_log_entry/main.rs b/examples/rekor/create_log_entry/main.rs index a76645e0df..2dcc140016 100644 --- a/examples/rekor/create_log_entry/main.rs +++ b/examples/rekor/create_log_entry/main.rs @@ -46,7 +46,7 @@ async fn main() { /* Creates an entry in the transparency log. If no command line arguments is provided, - the pram will generate a key pair, download the file available at URL constant, sign it + the program will generate a key pair, download the file available at URL constant, sign it and create an entry in the transparency log. In the other hand, if the user sets the command line flags, the program will use that info to create the entry. Therefore, if the user use information of an entry already present in the transparency log, this @@ -84,7 +84,7 @@ async fn main() { ) This is because an equivalent entry with the provided meta data already exists in the transparency log. - When you use the example code to create a new entry with fresh set of input values or levaing the program + When you use the example code to create a new entry with fresh set of input values or leaving the program to generate the required data, you should be able to run the code without any errors. See an example: Example command : From 04972af912eca31caa0beb7ee07cc1271e5dfa21 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 30 May 2023 10:48:59 +0200 Subject: [PATCH 019/161] Release 0.7.0 Tag a new minor release of the library. Signed-off-by: Flavio Castelli --- CHANGELOG.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b02470aa3..b272a145a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,80 @@ +# v0.7.0 + +## What's Changed +* Fix typo in SignatureLayer::new doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/170 +* feat: replace example dependency docker_credential by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/172 +* Clean up readme by @lukehinds in https://github.com/sigstore/sigstore-rs/pull/173 +* chore(deps): Update rstest requirement from 0.15.0 to 0.16.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/174 +* Fix typo in simple_signing.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/175 +* Introduce SignedArtifactBundle by @danbev in https://github.com/sigstore/sigstore-rs/pull/171 +* chore(deps): Update base64 requirement from 0.13.0 to 0.20.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/177 +* chore(deps): Bump actions/checkout from 3.1.0 to 3.2.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/180 +* chore(deps): Update serial_test requirement from 0.9.0 to 0.10.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/182 +* chore(deps): Update cached requirement from 0.40.0 to 0.41.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/181 +* Fix typo in SecretBoxCipher doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/179 +* chore(deps): Update cached requirement from 0.41.0 to 0.42.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/185 +* chore(deps): Bump actions/checkout from 3.2.0 to 3.3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/183 +* chore(deps): Update base64 requirement from 0.20.0 to 0.21.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/184 +* Add cosign verify-bundle example by @danbev in https://github.com/sigstore/sigstore-rs/pull/186 +* Fix incorrect base64_signature doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/188 +* Fix typos in tuf/mod.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/195 +* chore(deps): Update serial_test requirement from 0.10.0 to 1.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/200 +* fix: show actual response status field by @ctron in https://github.com/sigstore/sigstore-rs/pull/197 +* Update target -> target_name for consistency by @danbev in https://github.com/sigstore/sigstore-rs/pull/196 +* fix: make the fields accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/202 +* Add verify-bundle example to README.md by @danbev in https://github.com/sigstore/sigstore-rs/pull/203 +* fix: make fields of hash accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/205 +* Improve public key output and add file output by @Gronner in https://github.com/sigstore/sigstore-rs/pull/194 +* Add TokenProvider::Static doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/208 +* Changed the type of LogEntry.body from String to Body by @Neccolini in https://github.com/sigstore/sigstore-rs/pull/207 +* Fix errors/warnings reported by clippy by @danbev in https://github.com/sigstore/sigstore-rs/pull/210 +* Add fine-grained features to control the compilation by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/189 +* fix: bring tuf feature out of rekor and add related docs by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/211 +* chore: update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/204 +* Replace `x509-parser` with `x509-cert` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/212 +* Fix: Wrong parameter order inside documentation example. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/215 +* Remove lines about timestamp in lib.rs by @naveensrinivasan in https://github.com/sigstore/sigstore-rs/pull/213 +* Fix ed25519 version conflict by @vembacher in https://github.com/sigstore/sigstore-rs/pull/223 +* Support compiling to wasm32 architectures by @lulf in https://github.com/sigstore/sigstore-rs/pull/221 +* Fix link to contributor doc in readme by @oliviacrain in https://github.com/sigstore/sigstore-rs/pull/225 +* refactor: derive `Clone` trait by @flavio in https://gitub.com/sigstore/sigstore-rs/pull/227 +* fix: correct typo in verify/main.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/228 +* chore(deps): Update tough requirement from 0.12 to 0.13 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/237 +* chore(deps): Bump actions/checkout from 3.3.0 to 3.4.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/240 +* dep: update picky version to git rid of `ring` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/226 +* chore(deps): Bump actions/checkout from 3.4.0 to 3.5.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/245 +* fix: make LogEntry Body an enum by @danbev in https://github.com/sigstore/sigstore-rs/pull/244 +* Add verify-blob example by @danbev in https://github.com/sigstore/sigstore-rs/pull/239 +* Introduce Newtype `OciReference` into API for OCI image references. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/216 +* Swap over to using CDN to fetch TUF metadata by @haydentherapper in https://github.com/sigstore/sigstore-rs/pull/251 +* chore(deps): Bump actions/checkout from 3.5.0 to 3.5.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/252 +* upgrade 'der' to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/257 +* remove unused 'clock' feature for chrono by @dmitris in https://github.com/sigstore/sigstore-rs/pull/258 +* update pkcs1 from 0.4.0 to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/260 +* use 2021 Rust edition by @dmitris in https://github.com/sigstore/sigstore-rs/pull/261 +* chore(deps): Update serial_test requirement from 1.0.0 to 2.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/264 +* update scrypt to 0.11.0, adapt for API change (fix #231) by @dmitris in https://github.com/sigstore/sigstore-rs/pull/268 +* upgrade ed25519-dalek to 2.0.0-rc.2 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/263 +* chore(deps): Update openidconnect requirement from 2.3 to 3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/265 +* chore(deps): Update rstest requirement from 0.16.0 to 0.17.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/271 +* Update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/269 +* Update create_log_entry example to create key pair. by @jvanz in https://github.com/sigstore/sigstore-rs/pull/206 + +## New Contributors +* @ctron made their first contribution in https://github.com/sigstore/sigstore-rs/pull/197 +* @Gronner made their first contribution in https://github.com/sigstore/sigstore-rs/pull/194 +* @Neccolini made their first contribution in https://github.com/sigstore/sigstore-rs/pull/207 +* @vembacher made their first contribution in https://github.com/sigstore/sigstore-rs/pull/215 +* @naveensrinivasan made their first contribution in https://github.com/sigstore/sigstore-rs/pull/213 +* @lulf made their first contribution in https://github.com/sigstore/sigstore-rs/pull/221 +* @oliviacrain made their first contribution in https://github.com/sigstore/sigstore-rs/pull/225 +* @haydentherapper made their first contribution in https://github.com/sigstore/sigstore-rs/pull/251 +* @dmitris made their first contribution in https://github.com/sigstore/sigstore-rs/pull/257 +* @jvanz made their first contribution in https://github.com/sigstore/sigstore-rs/pull/206 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.6.0...v0.7.0h + + # v0.6.0 ## Fixes diff --git a/Cargo.toml b/Cargo.toml index 8c633eea62..b74fb92d05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.6.0" +version = "0.7.0" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" From 7f7dc94fc2d7e683c6a25243cd09205d50a79545 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 1 Jun 2023 15:06:10 +0200 Subject: [PATCH 020/161] fix: ensure cosign client can be sent between threads After d9b198ae4530a63932d8d674b1dd033fc08b0dfe got merged, the `Sync` and `Send` supertraits got removed from the `ClientCapabilities` trait. This was done to ensure the code could build when the `wasm32` target is used. Unfortunately, this makes impossible to send the cosign client between threads. This causes an error like the following one: ``` error: future cannot be sent between threads safely the trait `Send` is not implemented for `(dyn sigstore::registry::ClientCapabilities + 'static)` ``` This commit fixes the issue by introducing conditional compilation checks. Signed-off-by: Flavio Castelli --- src/cosign/client.rs | 3 ++- src/cosign/mod.rs | 4 +++- src/mock_client.rs | 5 ++++- src/registry/mod.rs | 25 +++++++++++++++++++++++-- src/registry/oci_caching_client.rs | 7 +++++-- src/registry/oci_client.rs | 7 +++++-- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/cosign/client.rs b/src/cosign/client.rs index f6b72f38c0..1c97e113af 100644 --- a/src/cosign/client.rs +++ b/src/cosign/client.rs @@ -43,7 +43,8 @@ pub struct Client { pub(crate) fulcio_cert_pool: Option, } -#[async_trait(?Send)] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl CosignCapabilities for Client { async fn triangulate( &mut self, diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 4ff6813825..675cc697ac 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -71,7 +71,9 @@ use crate::registry::oci_reference::OciReference; pub use payload::simple_signing; pub mod constraint; -#[async_trait(?Send)] + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] /// Cosign Abilities that have to be implemented by a /// Cosign client pub trait CosignCapabilities { diff --git a/src/mock_client.rs b/src/mock_client.rs index 985a859905..25c00d0943 100644 --- a/src/mock_client.rs +++ b/src/mock_client.rs @@ -33,7 +33,10 @@ pub(crate) mod test { pub push_response: Option>, } - #[async_trait(?Send)] + impl crate::registry::ClientCapabilitiesDeps for MockOciClient {} + + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl crate::registry::ClientCapabilities for MockOciClient { async fn fetch_manifest_digest( &mut self, diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 1d8672eabc..1e946f4432 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -35,9 +35,30 @@ use crate::errors::Result; use async_trait::async_trait; -#[async_trait(?Send)] +/// Workaround to ensure the `Send + Sync` supertraits are +/// required by ClientCapabilities only when the target +/// architecture is NOT wasm32. +/// +/// This intermediate trait has been created to avoid +/// to define ClientCapabilities twice (one with `#[cfg(target_arch = "wasm32")]`, +/// the other with `#[cfg(not(target_arch = "wasm32"))]` +#[cfg(not(target_arch = "wasm32"))] +pub(crate) trait ClientCapabilitiesDeps: Send + Sync {} + +/// Workaround to ensure the `Send + Sync` supertraits are +/// required by ClientCapabilities only when the target +/// architecture is NOT wasm32. +/// +/// This intermediate trait has been created to avoid +/// to define ClientCapabilities twice (one with `#[cfg(target_arch = "wasm32")]`, +/// the other with `#[cfg(not(target_arch = "wasm32"))]` +#[cfg(target_arch = "wasm32")] +pub(crate) trait ClientCapabilitiesDeps {} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] /// Capabilities that are expected to be provided by a registry client -pub(crate) trait ClientCapabilities { +pub(crate) trait ClientCapabilities: ClientCapabilitiesDeps { 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 8d4af839bf..035bd3ad93 100644 --- a/src/registry/oci_caching_client.rs +++ b/src/registry/oci_caching_client.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::ClientCapabilities; +use super::{ClientCapabilities, ClientCapabilitiesDeps}; use crate::errors::{Result, SigstoreError}; use async_trait::async_trait; @@ -240,7 +240,10 @@ async fn pull_manifest_cached( .map(cached::Return::new) } -#[async_trait(?Send)] +impl ClientCapabilitiesDeps for OciCachingClient {} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", 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 4befe7a7ea..c6159ee572 100644 --- a/src/registry/oci_client.rs +++ b/src/registry/oci_client.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::ClientCapabilities; +use super::{ClientCapabilities, ClientCapabilitiesDeps}; use crate::errors::{Result, SigstoreError}; use async_trait::async_trait; @@ -27,7 +27,10 @@ pub(crate) struct OciClient { pub registry_client: oci_distribution::Client, } -#[async_trait(?Send)] +impl ClientCapabilitiesDeps for OciClient {} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl ClientCapabilities for OciClient { async fn fetch_manifest_digest( &mut self, From 7433f8c17fd4c68b9875110ffdb9b1d6fc581848 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 1 Jun 2023 17:04:30 +0200 Subject: [PATCH 021/161] Release v0.7.1 Tag a new patch release to include a small fix. Signed-off-by: Flavio Castelli --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b272a145a5..843bf840db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v0.7.1 + +## What's Changed +* fix: ensure cosign client can be sent between threads by @flavio in https://github.com/sigstore/sigstore-rs/pull/275 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.0...v0.7.1 + # v0.7.0 ## What's Changed diff --git a/Cargo.toml b/Cargo.toml index b74fb92d05..7af6456191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.7.0" +version = "0.7.1" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" From 21d3e39efe3756649824a25e48d19c7ef7223e80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 19:58:02 +0000 Subject: [PATCH 022/161] chore(deps): Update cached requirement from 0.42.0 to 0.44.0 Updates the requirements on [cached](https://github.com/jaemk/cached) to permit the latest version. - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7af6456191..364503a43e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.42.0", optional = true } +cached = { version = "0.44.0", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.23" } const-oid = "0.9.1" From 308d3b6c3d6c045c3ded9bf0c71c50a6c82551ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 19:58:08 +0000 Subject: [PATCH 023/161] chore(deps): Bump actions/checkout from 3.5.2 to 3.5.3 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8e5e7e5ab8b370d6c329ec480221332ada57f0ab...c85c95e3d7251135ab7dc9ce3241c5835cc595a9) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 18545228d9..97356eb785 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index fd1c30898b..d0f82abe89 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index c12f919559..ce58f3334c 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a3022cdd0..894087b3c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From c282ef1aae89d0d17579b4e511a8978e2bcc8844 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Mon, 19 Jun 2023 16:33:26 +0200 Subject: [PATCH 024/161] chore(deps): update picky dependency Use latest RC of picky-rs. This RC now has elliptic curve support enabled by default, because of that the `ec` feature has been removed from the crate. Signed-off-by: Flavio Castelli --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7af6456191..7d763d339e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,9 +88,8 @@ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" pem = "2.0" -picky = { version = "7.0.0-rc.5", default-features = false, features = [ +picky = { version = "7.0.0-rc.6", default-features = false, features = [ "x509", - "ec", ] } pkcs1 = { version = "0.7.5", features = ["std"] } pkcs8 = { version = "0.10.2", features = [ From 4111411119510ebbb09831485c99214225f14353 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 6 Jul 2023 17:32:33 +0200 Subject: [PATCH 025/161] Release 0.7.2 (#281) Bump dependencies Signed-off-by: Flavio Castelli --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 843bf840db..63f4c42268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v0.7.2 + +## What's Changed +* chore(deps): Update cached requirement from 0.42.0 to 0.44.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/277 +* chore(deps): Bump actions/checkout from 3.5.2 to 3.5.3 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/278 +* chore(deps): update picky dependency by @flavio in https://github.com/sigstore/sigstore-rs/pull/279 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.1...v0.7.2 + # v0.7.1 ## What's Changed diff --git a/Cargo.toml b/Cargo.toml index ecb6714750..7c00c4a7f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.7.1" +version = "0.7.2" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" From 9283e91cbd162681a9797b21036ab00b5c58dc7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:21:52 +0000 Subject: [PATCH 026/161] chore(deps): Update rstest requirement from 0.17.0 to 0.18.1 Updates the requirements on [rstest](https://github.com/la10736/rstest) to permit the latest version. - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/0.17.0...v0.18.1) --- updated-dependencies: - dependency-name: rstest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7c00c4a7f8..0284457ba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,7 @@ chrono = "0.4.20" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" -rstest = "0.17.0" +rstest = "0.18.1" serial_test = "2.0.0" tempfile = "3.3.0" testcontainers = "0.14" From b7be13ed9e2d24bae9a7416c660be56a27415bcf Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 26 Jul 2023 09:07:21 +0200 Subject: [PATCH 027/161] chore(deps): do not enable default features of chrono Enabling the default features of chrono causes the time 0.1 crate to be added as a transitive dependency. This old version of time is affected by CVE RUSTSEC-2020-0071 Thanks to work done inside of chrono 0.4, there are high chances the majority of the codebases do not actually need it. Building sigstore with only the cosign feature prevents the inclusion of the vulnerable time dependency. This isn't unfortunately true when rekor is being used, because the openid crate brings the transitive dependency back. Signed-off-by: Flavio Castelli --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0284457ba4..6e82d67050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ async-trait = "0.1.52" base64 = "0.21.0" cached = { version = "0.44.0", optional = true } cfg-if = "1.0.0" -chrono = { version = "0.4.23" } +chrono = { version = "0.4.23", default-features = false } const-oid = "0.9.1" digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } From b7187590098b91213ade4713b8630db9a0816085 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:10:50 +0000 Subject: [PATCH 028/161] chore(deps): Update pem requirement from 2.0 to 3.0 Updates the requirements on [pem](https://github.com/jcreekmore/pem-rs) to permit the latest version. - [Changelog](https://github.com/jcreekmore/pem-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jcreekmore/pem-rs/compare/v2.0.0...v3.0.1) --- updated-dependencies: - dependency-name: pem dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6e82d67050..e88b17539d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ openidconnect = { version = "3.0", default-features = false, features = [ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" -pem = "2.0" +pem = "3.0" picky = { version = "7.0.0-rc.6", default-features = false, features = [ "x509", ] } From 02e36656a121763bd439731616b494998bfabb2c Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Fri, 28 Jul 2023 11:36:47 -0400 Subject: [PATCH 029/161] conformance: cli Signed-off-by: Jack Leightcap --- Cargo.toml | 6 ++ examples/conformance/main.rs | 113 +++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 examples/conformance/main.rs diff --git a/Cargo.toml b/Cargo.toml index 0284457ba4..d284660658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,6 +134,12 @@ testcontainers = "0.14" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } hex = "0.4.3" +# conformance CLI + +[[example]] +name = "conformance_suite" +path = "examples/conformance/main.rs" + # cosign example mappings [[example]] diff --git a/examples/conformance/main.rs b/examples/conformance/main.rs new file mode 100644 index 0000000000..767c6ab747 --- /dev/null +++ b/examples/conformance/main.rs @@ -0,0 +1,113 @@ +// +// Copyright 2023 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. + +// CLI implemented to specification: +// https://github.com/sigstore/sigstore-conformance/blob/main/docs/cli_protocol.md + +use clap::{Parser, Subcommand}; + +#[derive(Parser, Debug)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + Sign, + SignBundle, + Verify, + VerifyBundle, +} + +#[derive(Parser, Debug)] +struct Sign { + // The OIDC identity token to use + #[clap(long)] + identity_token: String, + + // The path to write the signature to + #[clap(long)] + signature: String, + + // The path to write the signing certificate to + #[clap(long)] + certificate: String, + + // The artifact to sign + artifact: String, +} + +#[derive(Parser, Debug)] +struct SignBundle { + // The OIDC identity token to use + #[clap(long)] + identity_token: String, + + // The path to write the bundle to + #[clap(long)] + bundle: String, + + // The artifact to sign + #[clap(long)] + artifact: String, +} + +#[derive(Parser, Debug)] +struct Verify { + // The path to the signature to verify + #[clap(long)] + signature: String, + + // The path to the signing certificate to verify + #[clap(long)] + certificate: String, + + // The expected identity in the signing certificate's SAN extension + #[clap(long)] + certificate_identity: String, + + // The expected OIDC issuer for the signing certificate + #[clap(long)] + certificate_oidc_issuer: String, + + // The path to the artifact to verify + #[clap(long)] + artifact: String, +} + +#[derive(Parser, Debug)] +struct VerifyBundle { + // The path to the Sigstore bundle to verify + #[clap(long)] + bundle: String, + + // The expected identity in the signing certificate's SAN extension + #[clap(long)] + certificate_identity: String, + + // The expected OIDC issuer for the signing certificate + #[clap(long)] + certificate_oidc_issuer: String, + + // The path to the artifact to verify + #[clap(long)] + artifact: String, +} + +#[tokio::main] +pub async fn main() { + let cli = Cli::parse(); +} From b33b1dec2e0e323a6f9ad99befb71a5b1f82d22a Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Fri, 28 Jul 2023 11:53:00 -0400 Subject: [PATCH 030/161] conformance: add action+entrypoint Signed-off-by: Jack Leightcap --- .github/workflows/conformance.yml | 17 +++++++++++++++++ examples/conformance/main.rs | 11 ++++------- 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/conformance.yml diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml new file mode 100644 index 0000000000..76409c1b63 --- /dev/null +++ b/.github/workflows/conformance.yml @@ -0,0 +1,17 @@ +on: [push, pull_request] + +name: Conformance Suite + +jobs: + conformance: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 + with: + profile: minimal + toolchain: stable + override: true + - uses: sigstore/sigstore-conformance@v0.0.5 + with: + entrypoint: cargo run --example conformance_suite -- \ No newline at end of file diff --git a/examples/conformance/main.rs b/examples/conformance/main.rs index 767c6ab747..237b8f5c4d 100644 --- a/examples/conformance/main.rs +++ b/examples/conformance/main.rs @@ -26,10 +26,10 @@ struct Cli { #[derive(Subcommand, Debug)] enum Commands { - Sign, - SignBundle, - Verify, - VerifyBundle, + Sign(Sign), + SignBundle(SignBundle), + Verify(Verify), + VerifyBundle(VerifyBundle), } #[derive(Parser, Debug)] @@ -61,7 +61,6 @@ struct SignBundle { bundle: String, // The artifact to sign - #[clap(long)] artifact: String, } @@ -84,7 +83,6 @@ struct Verify { certificate_oidc_issuer: String, // The path to the artifact to verify - #[clap(long)] artifact: String, } @@ -103,7 +101,6 @@ struct VerifyBundle { certificate_oidc_issuer: String, // The path to the artifact to verify - #[clap(long)] artifact: String, } From d6a7b54d2f28ac2f2603df8ff023c27c3df1dfc5 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Fri, 28 Jul 2023 12:38:35 -0400 Subject: [PATCH 031/161] conformance: use child binary crate Signed-off-by: Jack Leightcap --- .github/workflows/conformance.yml | 8 ++++++-- Cargo.toml | 6 ------ tests/conformance/Cargo.toml | 16 ++++++++++++++++ .../main.rs => tests/conformance/conformance.rs | 0 4 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 tests/conformance/Cargo.toml rename examples/conformance/main.rs => tests/conformance/conformance.rs (100%) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 76409c1b63..4ed3b1c9c4 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -12,6 +12,10 @@ jobs: profile: minimal toolchain: stable override: true - - uses: sigstore/sigstore-conformance@v0.0.5 + - uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # v1.0.3 with: - entrypoint: cargo run --example conformance_suite -- \ No newline at end of file + command: build + args: --manifest-path=tests/conformance/Cargo.toml + - uses: sigstore/sigstore-conformance@main + with: + entrypoint: ${{ github.workspace }}/tests/conformance/target/debug/sigstore \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index d284660658..0284457ba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,12 +134,6 @@ testcontainers = "0.14" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } hex = "0.4.3" -# conformance CLI - -[[example]] -name = "conformance_suite" -path = "examples/conformance/main.rs" - # cosign example mappings [[example]] diff --git a/tests/conformance/Cargo.toml b/tests/conformance/Cargo.toml new file mode 100644 index 0000000000..dfeef1798f --- /dev/null +++ b/tests/conformance/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sigstore-conformance" +description = "sigstore conformance testing workflow" +version = "0.0.1" +edition = "2021" +authors = ["sigstore-rs developers"] +license = "Apache-2.0" + +[dependencies] +clap = { version = "4.0.8", features = ["derive"] } +sigstore = { path = "../../" } +tokio = { version = "1.17.0", features = ["rt"] } + +[[bin]] +name = "sigstore" +path = "conformance.rs" \ No newline at end of file diff --git a/examples/conformance/main.rs b/tests/conformance/conformance.rs similarity index 100% rename from examples/conformance/main.rs rename to tests/conformance/conformance.rs From 7e8b6cf83121d01358c855d7ba123128a3cadf74 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Tue, 1 Aug 2023 14:50:56 -0400 Subject: [PATCH 032/161] conformance: remove async main Signed-off-by: Jack Leightcap --- tests/conformance/Cargo.toml | 1 - tests/conformance/conformance.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/conformance/Cargo.toml b/tests/conformance/Cargo.toml index dfeef1798f..fd511cdc54 100644 --- a/tests/conformance/Cargo.toml +++ b/tests/conformance/Cargo.toml @@ -9,7 +9,6 @@ license = "Apache-2.0" [dependencies] clap = { version = "4.0.8", features = ["derive"] } sigstore = { path = "../../" } -tokio = { version = "1.17.0", features = ["rt"] } [[bin]] name = "sigstore" diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index 237b8f5c4d..c52c822fe1 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -104,7 +104,6 @@ struct VerifyBundle { artifact: String, } -#[tokio::main] -pub async fn main() { +fn main() { let cli = Cli::parse(); } From 32b897c03a825c1f18e466eb12ef5e1c44b7bdb7 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Tue, 1 Aug 2023 14:55:23 -0400 Subject: [PATCH 033/161] conformance: trailing newlines Signed-off-by: Jack Leightcap --- .github/workflows/conformance.yml | 2 +- tests/conformance/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 4ed3b1c9c4..d58372b0de 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -18,4 +18,4 @@ jobs: args: --manifest-path=tests/conformance/Cargo.toml - uses: sigstore/sigstore-conformance@main with: - entrypoint: ${{ github.workspace }}/tests/conformance/target/debug/sigstore \ No newline at end of file + entrypoint: ${{ github.workspace }}/tests/conformance/target/debug/sigstore diff --git a/tests/conformance/Cargo.toml b/tests/conformance/Cargo.toml index fd511cdc54..6c0c5d16a9 100644 --- a/tests/conformance/Cargo.toml +++ b/tests/conformance/Cargo.toml @@ -12,4 +12,4 @@ sigstore = { path = "../../" } [[bin]] name = "sigstore" -path = "conformance.rs" \ No newline at end of file +path = "conformance.rs" From f35ca2bb5a2ecacdd6a4c4b856a389788ee21d67 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Wed, 2 Aug 2023 10:34:21 -0400 Subject: [PATCH 034/161] conformance: workflow dispatch trigger Signed-off-by: Jack Leightcap --- .github/workflows/conformance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index d58372b0de..171620f059 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -1,4 +1,4 @@ -on: [push, pull_request] +on: [workflow_dispatch] name: Conformance Suite From d99461668011e95bf469553f681fb3771d0a7196 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 29 Aug 2023 18:17:39 +0200 Subject: [PATCH 035/161] chore: fix clippy warnings Ensure latest version of clippy shipped with rust 1.72 is happy Signed-off-by: Flavio Castelli --- examples/cosign/verify-blob/main.rs | 2 +- examples/cosign/verify-bundle/main.rs | 2 +- examples/cosign/verify/main.rs | 13 +++++-------- src/cosign/bundle.rs | 2 +- src/crypto/signing_key/kdf.rs | 2 +- src/tuf/constants.rs | 2 +- src/tuf/repository_helper.rs | 6 +++--- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/examples/cosign/verify-blob/main.rs b/examples/cosign/verify-blob/main.rs index 0a9fb5604c..16e941fcd5 100644 --- a/examples/cosign/verify-blob/main.rs +++ b/examples/cosign/verify-blob/main.rs @@ -58,7 +58,7 @@ pub async fn main() { let certificate = fs::read_to_string(&cli.certificate).expect("error reading certificate"); let signature = fs::read_to_string(&cli.signature).expect("error reading signature"); - let blob = fs::read(&cli.blob.as_str()).expect("error reading blob file"); + let blob = fs::read(cli.blob.as_str()).expect("error reading blob file"); match Client::verify_blob(&certificate, &signature, &blob) { Ok(_) => println!("Verification succeeded"), diff --git a/examples/cosign/verify-bundle/main.rs b/examples/cosign/verify-bundle/main.rs index eafc5b75e5..f8730f0765 100644 --- a/examples/cosign/verify-bundle/main.rs +++ b/examples/cosign/verify-bundle/main.rs @@ -59,7 +59,7 @@ pub async fn main() { CosignVerificationKey::from_pem(rekor_pub_pem.as_bytes(), &SigningScheme::default()) .expect("Cannot create Rekor verification key"); let bundle_json = fs::read_to_string(&cli.bundle).expect("error reading bundle json file"); - let blob = fs::read(&cli.blob.as_str()).expect("error reading blob file"); + let blob = fs::read(cli.blob.as_str()).expect("error reading blob file"); let bundle = SignedArtifactBundle::new_verified(&bundle_json, &rekor_pub_key).unwrap(); match Client::verify_blob(&bundle.cert, &bundle.base64_signature, &blob) { diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 8ee5dc91fb..b1d3cdcc67 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -140,7 +140,7 @@ async fn run_app( let cert_chain: Option> = match cli.cert_chain.as_ref() { None => None, - Some(cert_chain_path) => Some(parse_cert_bundle(&cert_chain_path)?), + Some(cert_chain_path) => Some(parse_cert_bundle(cert_chain_path)?), }; if !frd.fulcio_certs.is_empty() { @@ -201,12 +201,9 @@ async fn run_app( false }; - let verifier = CertificateVerifier::from_pem( - &cert, - require_rekor_bundle, - cert_chain.as_ref().map(|v| v.as_slice()), - ) - .map_err(|e| anyhow!("Cannot create certificate verifier: {}", e))?; + let verifier = + CertificateVerifier::from_pem(&cert, require_rekor_bundle, cert_chain.as_deref()) + .map_err(|e| anyhow!("Cannot create certificate verifier: {}", e))?; verification_constraints.push(Box::new(verifier)); } @@ -343,7 +340,7 @@ pub async fn main() { fn parse_cert_bundle(bundle_path: &str) -> Result> { let data = fs::read(bundle_path).map_err(|e| anyhow!("Error reading {}: {}", bundle_path, e))?; - let pems = pem::parse_many(&data)?; + let pems = pem::parse_many(data)?; Ok(pems .iter() diff --git a/src/cosign/bundle.rs b/src/cosign/bundle.rs index e9a94ad647..e484a64796 100644 --- a/src/cosign/bundle.rs +++ b/src/cosign/bundle.rs @@ -160,7 +160,7 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw== {"base64Signature":"MEQCIGp1XZP5zaImosrBhDPCdXn3f8xI9FHGLsGVx6UeRPCgAiAt5GrsdQhOKnZcA3EWecvgJSHzCIjWifFBQkD7Hdsymg==","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNxRENDQWkrZ0F3SUJBZ0lVVFBXVGZPLzFOUmFTRmRlY2FBUS9wQkRHSnA4d0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJeE1USTFNRGN6TnpFeVdoY05Nakl4TVRJMU1EYzBOekV5V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUVE0Vy81WFA5bTRZYldSQlF0SEdXd245dVVoYWUzOFVwY0oKcEVNM0RPczR6VzRNSXJNZlc0V1FEMGZ3cDhQVVVSRFh2UTM5NHBvcWdHRW1Ta3J1THFPQ0FVNHdnZ0ZLTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVvM0tuCmpKUVowWGZpZ2JENWIwT1ZOTjB4cVNvd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0p3WURWUjBSQVFIL0JCMHdHNEVaWkdGdWFXVnNMbUpsZG1WdWFYVnpRR2R0WVdsc0xtTnZiVEFzQmdvcgpCZ0VFQVlPL01BRUJCQjVvZEhSd2N6b3ZMMmRwZEdoMVlpNWpiMjB2Ykc5bmFXNHZiMkYxZEdnd2dZc0dDaXNHCkFRUUIxbmtDQkFJRWZRUjdBSGtBZHdEZFBUQnF4c2NSTW1NWkhoeVpaemNDb2twZXVONDhyZitIaW5LQUx5bnUKamdBQUFZU3R1Qkh5QUFBRUF3QklNRVlDSVFETTVZU1EvR0w2S0k1UjlPZGNuL3BTaytxVkQ2YnNMODMrRXA5UgoyaFdUYXdJaEFLMWppMWxaNTZEc2Z1TGZYN2JCQzluYlIzRWx4YWxCaHYxelFYTVU3dGx3TUFvR0NDcUdTTTQ5CkJBTURBMmNBTUdRQ01CSzh0c2dIZWd1aCtZaGVsM1BpakhRbHlKMVE1SzY0cDB4cURkbzdXNGZ4Zm9BUzl4clAKczJQS1FjZG9EOWJYd2dJd1g2ekxqeWJaa05IUDV4dEJwN3ZLMkZZZVp0ME9XTFJsVWxsY1VETDNULzdKUWZ3YwpHU3E2dlZCTndKMDB3OUhSCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K","rekorBundle":{"SignedEntryTimestamp":"MEUCIC3c+21v9pk6o4BpB/dRAM9lGnyWLi3Xnc+i8LmnNJmeAiEAiqZJbZHx3Idnw+zXv6yM0ipPw/p16R28YGuCJFQ1u8U=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0YmM0NTNiNTNjYjNkOTE0YjQ1ZjRiMjUwMjk0MjM2YWRiYTJjMGUwOWZmNmYwMzc5Mzk0OWU3ZTM5ZmQ0Y2MxIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJR3AxWFpQNXphSW1vc3JCaERQQ2RYbjNmOHhJOUZIR0xzR1Z4NlVlUlBDZ0FpQXQ1R3JzZFFoT0tuWmNBM0VXZWN2Z0pTSHpDSWpXaWZGQlFrRDdIZHN5bWc9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTnhSRU5EUVdrclowRjNTVUpCWjBsVlZGQlhWR1pQTHpGT1VtRlRSbVJsWTJGQlVTOXdRa1JIU25BNGQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcEplRTFVU1RGTlJHTjZUbnBGZVZkb1kwNU5ha2w0VFZSSk1VMUVZekJPZWtWNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZLVVZFMFZ5ODFXRkE1YlRSWllsZFNRbEYwU0VkWGQyNDVkVlZvWVdVek9GVndZMG9LY0VWTk0wUlBjelI2VnpSTlNYSk5abGMwVjFGRU1HWjNjRGhRVlZWU1JGaDJVVE01TkhCdmNXZEhSVzFUYTNKMVRIRlBRMEZWTkhkblowWkxUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZ2TTB0dUNtcEtVVm93V0dacFoySkVOV0l3VDFaT1RqQjRjVk52ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBwM1dVUldVakJTUVZGSUwwSkNNSGRITkVWYVdrZEdkV0ZYVm5OTWJVcHNaRzFXZFdGWVZucFJSMlIwV1Zkc2MweHRUblppVkVGelFtZHZjZ3BDWjBWRlFWbFBMMDFCUlVKQ1FqVnZaRWhTZDJONmIzWk1NbVJ3WkVkb01WbHBOV3BpTWpCMllrYzVibUZYTkhaaU1rWXhaRWRuZDJkWmMwZERhWE5IQ2tGUlVVSXhibXREUWtGSlJXWlJVamRCU0d0QlpIZEVaRkJVUW5GNGMyTlNUVzFOV2tob2VWcGFlbU5EYjJ0d1pYVk9ORGh5Wml0SWFXNUxRVXg1Ym5VS2FtZEJRVUZaVTNSMVFraDVRVUZCUlVGM1FrbE5SVmxEU1ZGRVRUVlpVMUV2UjB3MlMwazFVamxQWkdOdUwzQlRheXR4VmtRMlluTk1PRE1yUlhBNVVnb3lhRmRVWVhkSmFFRkxNV3BwTVd4YU5UWkVjMloxVEdaWU4ySkNRemx1WWxJelJXeDRZV3hDYUhZeGVsRllUVlUzZEd4M1RVRnZSME5EY1VkVFRUUTVDa0pCVFVSQk1tTkJUVWRSUTAxQ1N6aDBjMmRJWldkMWFDdFphR1ZzTTFCcGFraFJiSGxLTVZFMVN6WTBjREI0Y1VSa2J6ZFhOR1o0Wm05QlV6bDRjbEFLY3pKUVMxRmpaRzlFT1dKWWQyZEpkMWcyZWt4cWVXSmFhMDVJVURWNGRFSndOM1pMTWtaWlpWcDBNRTlYVEZKc1ZXeHNZMVZFVEROVUx6ZEtVV1ozWXdwSFUzRTJkbFpDVG5kS01EQjNPVWhTQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=","integratedTime":1669361833,"logIndex":7810348,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}} "#; let rekor_pub_key = get_rekor_public_key(); - let result = SignedArtifactBundle::new_verified(&bundle_raw, &rekor_pub_key); + let result = SignedArtifactBundle::new_verified(bundle_raw, &rekor_pub_key); assert!(result.is_ok()); let bundle = result.unwrap(); assert_eq!(bundle.rekor_bundle.payload.log_index, 7810348); diff --git a/src/crypto/signing_key/kdf.rs b/src/crypto/signing_key/kdf.rs index deb4372b8d..d90cd53000 100644 --- a/src/crypto/signing_key/kdf.rs +++ b/src/crypto/signing_key/kdf.rs @@ -280,7 +280,7 @@ mod tests { }); let data: Data = serde_json::from_value(input_json.clone()).expect("Cannot deserialize json Data"); - let actual_json = serde_json::to_value(&data).expect("Cannot serialize Data back to JSON"); + let actual_json = serde_json::to_value(data).expect("Cannot serialize Data back to JSON"); assert_json_eq!(input_json, actual_json); } } diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index 1515f08166..99231be2da 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -18,7 +18,7 @@ use regex::Regex; lazy_static! { pub(crate) static ref SIGSTORE_FULCIO_CERT_TARGET_REGEX: Regex = - Regex::new(r#"fulcio(_v\d+)?\.crt\.pem"#).expect("cannot compile regexp"); + Regex::new(r"fulcio(_v\d+)?\.crt\.pem").expect("cannot compile regexp"); } pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; diff --git a/src/tuf/repository_helper.rs b/src/tuf/repository_helper.rs index c542caae91..a581619638 100644 --- a/src/tuf/repository_helper.rs +++ b/src/tuf/repository_helper.rs @@ -271,7 +271,7 @@ mod tests { let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); actual.sort(); let mut expected: Vec = - vec!["fulcio.crt.pem", "fulcio_v1.crt.pem"] + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] .iter() .map(|filename| { let data = fs::read( @@ -322,7 +322,7 @@ mod tests { let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); actual.sort(); let mut expected: Vec = - vec!["fulcio.crt.pem", "fulcio_v1.crt.pem"] + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] .iter() .map(|filename| { let data = fs::read( @@ -379,7 +379,7 @@ mod tests { let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); actual.sort(); let mut expected: Vec = - vec!["fulcio.crt.pem", "fulcio_v1.crt.pem"] + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] .iter() .map(|filename| { let data = fs::read( From 3aa96e81fd243458a2d8023942680968cc693a46 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 29 Aug 2023 18:34:56 +0200 Subject: [PATCH 036/161] chore(clippy): remove allowed lint The GH action running clippy was configured to ignore a clippy linter warning. This lint error has been fixed, hence the exception can be removed. Moreover, the Makefile has been updated to ensure clippy is invoked in the same way as by the GH action. Signed-off-by: Flavio Castelli --- .github/workflows/tests.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 894087b3c3..4e9fb654d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -78,4 +78,4 @@ jobs: - uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # v1.0.3 with: command: clippy - args: --workspace -- -D warnings -A clippy::derive-partial-eq-without-eq + args: --workspace -- -D warnings diff --git a/Makefile b/Makefile index c9f7ad6714..1316c293ee 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ fmt: .PHONY: lint lint: - cargo clippy -- -D warnings + cargo clippy --workspace -- -D warnings .PHONY: doc doc: From 658ebfb1a2f9bada56ed97c678bcce0eccb3f7e6 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 30 Aug 2023 09:00:25 +0200 Subject: [PATCH 037/161] chore(deps): update to latest version of chrono Update to latest version of chrono, fix deprecation warnings. Signed-off-by: Flavio Castelli --- Cargo.toml | 4 ++-- src/cosign/verification_constraint/certificate_verifier.rs | 2 +- src/crypto/certificate.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e88b17539d..b61e5812f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ async-trait = "0.1.52" base64 = "0.21.0" cached = { version = "0.44.0", optional = true } cfg-if = "1.0.0" -chrono = { version = "0.4.23", default-features = false } +chrono = { version = "0.4.27", default-features = false } const-oid = "0.9.1" digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } @@ -123,7 +123,7 @@ zeroize = "1.5.7" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } assert-json-diff = "2.0.2" -chrono = "0.4.20" +chrono = "0.4.27" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index 073463d06d..bdfabffe8f 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -83,7 +83,7 @@ impl VerificationConstraint for CertificateVerifier { } match &signature_layer.bundle { Some(bundle) => { - let it = DateTime::::from_utc( + let it = DateTime::::from_naive_utc_and_offset( NaiveDateTime::from_timestamp_opt(bundle.payload.integrated_time, 0).ok_or( SigstoreError::UnexpectedError("timestamp is not legal".into()), )?, diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 3273261c7d..943df12923 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -91,7 +91,7 @@ pub(crate) fn verify_validity(certificate: &Certificate) -> Result<()> { } fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result<()> { - let it = DateTime::::from_utc( + let it = DateTime::::from_naive_utc_and_offset( NaiveDateTime::from_timestamp_opt(integrated_time, 0) .ok_or(SigstoreError::X509Error("timestamp is not legal".into()))?, Utc, From 334742f4625e0035455ea29c8b210f06a1f99b29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:01:25 +0000 Subject: [PATCH 038/161] chore(deps): Bump actions/checkout from 3.5.3 to 3.6.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/c85c95e3d7251135ab7dc9ce3241c5835cc595a9...f43a0e5ff2bd294095638e18286ca9a3d1956744) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 97356eb785..2192dc7696 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 171620f059..44b8401708 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index d0f82abe89..2922b88028 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index ce58f3334c..6dd4bf3b1b 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4e9fb654d7..249e61f645 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From f2824a98a6527c6e5ce9475ba2ec8b709d53f4b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:01:40 +0000 Subject: [PATCH 039/161] chore(deps): Update tough requirement from 0.13 to 0.14 Updates the requirements on [tough](https://github.com/awslabs/tough) to permit the latest version. - [Release notes](https://github.com/awslabs/tough/releases) - [Commits](https://github.com/awslabs/tough/compare/tough-v0.13.0...tough-v0.14.0) --- updated-dependencies: - dependency-name: tough dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b61e5812f8..1a2268827e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,7 +113,7 @@ sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } -tough = { version = "0.13", features = ["http"], optional = true } +tough = { version = "0.14", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" x509-cert = { version = "0.2.2", features = ["pem", "std"] } From f16304cd799ae89a75593c31b4933f1a66e3c74b Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 30 Aug 2023 10:17:21 +0200 Subject: [PATCH 040/161] chore(deps): update to latest version of picky Use latest RC of picky Signed-off-by: Flavio Castelli --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1a2268827e..2daea162cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" pem = "3.0" -picky = { version = "7.0.0-rc.6", default-features = false, features = [ +picky = { version = "7.0.0-rc.8", default-features = false, features = [ "x509", ] } pkcs1 = { version = "0.7.5", features = ["std"] } From 5b1c4d792c031b1ce6b3671d91ff6280f177d9ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:18:28 +0000 Subject: [PATCH 041/161] chore(deps): Bump actions/checkout from 3.6.0 to 4.0.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/f43a0e5ff2bd294095638e18286ca9a3d1956744...3df4ab11eba7bda6032a0b82a6bb43b11571feac) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 2192dc7696..c474680665 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 44b8401708..06d0f69204 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index 2922b88028..f768a24519 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index 6dd4bf3b1b..debc18c4e2 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 249e61f645..50c8073d08 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 77170cc5cb63089885c35581c0ae685469bcf718 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 6 Sep 2023 17:02:40 +0200 Subject: [PATCH 042/161] chore: add repository link to Cargo metadata This fixes issue #295 Signed-off-by: Flavio Castelli --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 2daea162cd..65ba893c06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" readme = "README.md" +repository = "https://github.com/sigstore/sigstore-rs" [features] default = ["full-native-tls", "cached-client", "tuf"] From ec271211f5ee059e9d07cfe7aa3b08f72e5bc8fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:32:06 +0000 Subject: [PATCH 043/161] chore(deps): Update cached requirement from 0.44.0 to 0.45.1 Updates the requirements on [cached](https://github.com/jaemk/cached) to permit the latest version. - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 65ba893c06..c6bba60334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.44.0", optional = true } +cached = { version = "0.45.1", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false } const-oid = "0.9.1" From eb9ffef0319c47195245ea184d2ee662802080cf Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 12 Sep 2023 15:41:07 +0200 Subject: [PATCH 044/161] chore(deps): enable cached async feature This is required now to ensure our code compiles Signed-off-by: Flavio Castelli --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c6bba60334..cd2566d9f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.45.1", optional = true } +cached = { version = "0.45.1", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false } const-oid = "0.9.1" From d9c6b47b8c6bc6695ccc27c28574f60b4701a6d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:29:32 +0000 Subject: [PATCH 045/161] chore(deps): Update cached requirement from 0.45.1 to 0.46.0 Updates the requirements on [cached](https://github.com/jaemk/cached) to permit the latest version. - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd2566d9f5..a8f202c5b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.45.1", optional = true, features = ["async"] } +cached = { version = "0.46.0", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false } const-oid = "0.9.1" From f03580a89a6d94a52ad47141a508600ace127396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:57:42 +0000 Subject: [PATCH 046/161] chore(deps): Bump actions/checkout from 4.0.0 to 4.1.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/3df4ab11eba7bda6032a0b82a6bb43b11571feac...8ade135a41bc03ea155e62e844d188df1ea18608) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index c474680665..7d331cb8f0 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 06d0f69204..947d87031e 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index f768a24519..d8a7904670 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index debc18c4e2..43c8db3cdc 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 50c8073d08..031fdfa51d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 98d430ec60231fd7dc48113f961066e6a29edfc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:26:28 +0000 Subject: [PATCH 047/161] chore(deps): Update testcontainers requirement from 0.14 to 0.15 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/dev/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.14.0...0.15) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a8f202c5b2..2ee5951e55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,7 +131,7 @@ openssl = "0.10.38" rstest = "0.18.1" serial_test = "2.0.0" tempfile = "3.3.0" -testcontainers = "0.14" +testcontainers = "0.15" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } hex = "0.4.3" From 8ab26962239fb072ac9b7a1564ea7db8affed609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Cuadrado=20Juan?= Date: Tue, 10 Oct 2023 11:40:18 +0200 Subject: [PATCH 048/161] chore: Use testcontainers::GenericImage directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testcontainers::images is not exported anymore Signed-off-by: Víctor Cuadrado Juan --- src/cosign/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 675cc697ac..187826bb66 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -297,11 +297,7 @@ mod tests { use crate::crypto::{CosignVerificationKey, SigningScheme}; #[cfg(feature = "test-registry")] - use testcontainers::{ - clients, - core::WaitFor, - images::{self, generic::GenericImage}, - }; + use testcontainers::{clients, core::WaitFor}; pub(crate) const REKOR_PUB_KEY: &str = r#"-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr @@ -674,8 +670,8 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ } #[cfg(feature = "test-registry")] - fn registry_image() -> GenericImage { - images::generic::GenericImage::new("docker.io/library/registry", "2") + fn registry_image() -> testcontainers::GenericImage { + testcontainers::GenericImage::new("docker.io/library/registry", "2") .with_wait_for(WaitFor::message_on_stderr("listening on ")) } } From c94d6f70508a35809d842d683fcf3ed7f819ee55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Cuadrado=20Juan?= Date: Tue, 10 Oct 2023 11:46:18 +0200 Subject: [PATCH 049/161] chore: Effectively init and zero vector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Víctor Cuadrado Juan --- src/crypto/signing_key/kdf.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/crypto/signing_key/kdf.rs b/src/crypto/signing_key/kdf.rs index d90cd53000..4fb5a7f9db 100644 --- a/src/crypto/signing_key/kdf.rs +++ b/src/crypto/signing_key/kdf.rs @@ -114,8 +114,7 @@ impl ScryptKDF { self.params.p, scrypt::Params::RECOMMENDED_LEN, )?; - let mut res = Vec::new(); - res.resize(BOX_KEY_SIZE, 0x00); + let mut res = vec![0; BOX_KEY_SIZE]; scrypt::scrypt(password, &self.salt, ¶ms, &mut res)?; Ok(res) } From 6c97c7dcdd90ca79a6810fd9630f15f5079e0ca3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:04:27 +0000 Subject: [PATCH 050/161] chore(deps): Bump actions/checkout from 4.1.0 to 4.1.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8ade135a41bc03ea155e62e844d188df1ea18608...b4ffde65f46336ab88eb53be808477a3936bae11) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 7d331cb8f0..9ff358f88d 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 947d87031e..a9fb3bcf06 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index d8a7904670..8677bfcee4 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index 43c8db3cdc..ebc720c0e4 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 031fdfa51d..808d929e85 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 8a269a304c3b3ed1c3a6c89e4a6a88f9af22598a Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Fri, 20 Oct 2023 13:32:03 -0400 Subject: [PATCH 051/161] cosign/tuf: init Signed-off-by: Jack Leightcap Co-authored-by: Andrew Pan --- Cargo.toml | 6 +- examples/cosign/verify/main.rs | 42 +- src/cosign/client.rs | 10 +- src/cosign/client_builder.rs | 72 +-- src/cosign/mod.rs | 24 +- src/cosign/signature_layers.rs | 25 +- .../certificate_verifier.rs | 9 +- src/crypto/certificate_pool.rs | 172 +++---- src/errors.rs | 11 +- src/lib.rs | 16 +- src/registry/config.rs | 18 + src/tuf/constants.rs | 174 +------ src/tuf/mod.rs | 393 +++++++++++----- src/tuf/repository_helper.rs | 428 ++++++++---------- src/tuf/trustroot.rs | 148 ++++++ trust_root/prod/root.json | 156 +++++++ trust_root/prod/trusted_root.json | 91 ++++ 17 files changed, 1048 insertions(+), 747 deletions(-) create mode 100644 src/tuf/trustroot.rs create mode 100644 trust_root/prod/root.json create mode 100644 trust_root/prod/trusted_root.json diff --git a/Cargo.toml b/Cargo.toml index 2ee5951e55..b91f598203 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,6 +110,7 @@ rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" +serde_with = { version = "3.4.0", features = ["base64"] } sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } thiserror = "1.0.30" @@ -117,9 +118,12 @@ tokio = { version = "1.17.0", features = ["rt"] } tough = { version = "0.14", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.2.2", features = ["pem", "std"] } +x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" +rustls-webpki = { version = "0.102.0-alpha.4", features = ["alloc"] } +rustls-pki-types = { version = "0.2.1", features = ["std"] } +serde_repr = "0.1.16" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index b1d3cdcc67..60369cf094 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -110,7 +110,7 @@ struct Cli { async fn run_app( cli: &Cli, - frd: &FulcioAndRekorData, + frd: &dyn sigstore::tuf::Repository, ) -> anyhow::Result<(Vec, VerificationConstraintVec)> { // Note well: this a limitation deliberately introduced by this example. if cli.cert_email.is_some() && cli.cert_url.is_some() { @@ -133,20 +133,13 @@ async fn run_app( let mut client_builder = sigstore::cosign::ClientBuilder::default().with_oci_client_config(oci_client_config); - - if let Some(key) = frd.rekor_pub_key.as_ref() { - client_builder = client_builder.with_rekor_pub_key(key); - } + client_builder = client_builder.with_trust_repository(frd)?; let cert_chain: Option> = match cli.cert_chain.as_ref() { None => None, Some(cert_chain_path) => Some(parse_cert_bundle(cert_chain_path)?), }; - if !frd.fulcio_certs.is_empty() { - client_builder = client_builder.with_fulcio_certs(&frd.fulcio_certs); - } - if cli.enable_registry_caching { client_builder = client_builder.enable_registry_caching(); } @@ -194,7 +187,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_pub_key.is_some() { + let require_rekor_bundle = if !frd.rekor_keys()?.is_empty() { true } else { warn!("certificate based verification is weaker when Rekor integration is disabled"); @@ -235,31 +228,22 @@ async fn run_app( Ok((trusted_layers, verification_constraints)) } -#[derive(Default)] -struct FulcioAndRekorData { - pub rekor_pub_key: Option, - pub fulcio_certs: Vec, -} - -async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result { - let mut data = FulcioAndRekorData::default(); - +async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { let repo: sigstore::errors::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); - sigstore::tuf::SigstoreRepository::fetch(None) + SigstoreRepository::new(None)?.prefetch() }) .await .map_err(|e| anyhow!("Error spawning blocking task inside of tokio: {}", e))?; - let repo: SigstoreRepository = repo?; - data.fulcio_certs = repo.fulcio_certs().into(); - data.rekor_pub_key = Some(repo.rekor_pub_key().to_string()); + return Ok(Box::new(repo?)); }; + let mut data = sigstore::tuf::FakeRepository::default(); if let Some(path) = cli.rekor_pub_key.as_ref() { - data.rekor_pub_key = Some( - fs::read_to_string(path) + data.rekor_key = Some( + fs::read(path) .map_err(|e| anyhow!("Error reading rekor public key from disk: {}", e))?, ); } @@ -272,10 +256,12 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result encoding: sigstore::registry::CertificateEncoding::Pem, data: cert_data, }; - data.fulcio_certs.push(certificate); + data.fulcio_certs + .get_or_insert(Vec::new()) + .push(certificate.try_into()?); } - Ok(data) + Ok(Box::new(data)) } #[tokio::main] @@ -304,7 +290,7 @@ pub async fn main() { println!("Loop {}/{}", n + 1, cli.loops); } - match run_app(&cli, &frd).await { + match run_app(&cli, frd.as_ref()).await { Ok((trusted_layers, verification_constraints)) => { let filter_result = sigstore::cosign::verify_constraints( &trusted_layers, diff --git a/src/cosign/client.rs b/src/cosign/client.rs index 1c97e113af..af70bf457b 100644 --- a/src/cosign/client.rs +++ b/src/cosign/client.rs @@ -37,15 +37,15 @@ pub const CONFIG_DATA: &str = "{}"; /// Cosign Client /// /// Instances of `Client` can be built via [`sigstore::cosign::ClientBuilder`](crate::cosign::ClientBuilder). -pub struct Client { +pub struct Client<'a> { pub(crate) registry_client: Box, pub(crate) rekor_pub_key: Option, - pub(crate) fulcio_cert_pool: Option, + pub(crate) fulcio_cert_pool: Option>, } #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl CosignCapabilities for Client { +impl CosignCapabilities for Client<'_> { async fn triangulate( &mut self, image: &OciReference, @@ -140,7 +140,7 @@ impl CosignCapabilities for Client { } } -impl Client { +impl Client<'_> { /// Internal helper method used to fetch data from an OCI registry async fn fetch_manifest_and_layers( &mut self, @@ -177,7 +177,7 @@ mod tests { use crate::crypto::SigningScheme; use crate::mock_client::test::MockOciClient; - fn build_test_client(mock_client: MockOciClient) -> Client { + fn build_test_client(mock_client: MockOciClient) -> Client<'static> { let rekor_pub_key = CosignVerificationKey::from_pem(REKOR_PUB_KEY.as_bytes(), &SigningScheme::default()) .expect("Cannot create CosignVerificationKey"); diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 83b58a9ad1..f076e83528 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -13,13 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use rustls_pki_types::CertificateDer; use tracing::info; use super::client::Client; use crate::crypto::SigningScheme; use crate::crypto::{certificate_pool::CertificatePool, CosignVerificationKey}; use crate::errors::Result; -use crate::registry::{Certificate, ClientConfig}; +use crate::registry::ClientConfig; +use crate::tuf::Repository; /// A builder that generates Client objects. /// @@ -34,7 +36,7 @@ use crate::registry::{Certificate, ClientConfig}; /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via the [`ClientBuilder::with_fulcio_cert`] method. +/// This can be provided via the [`ClientBuilder::with_fulcio_certs`] method. /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -50,15 +52,16 @@ use crate::registry::{Certificate, ClientConfig}; /// /// Each cached entry will automatically expire after 60 seconds. #[derive(Default)] -pub struct ClientBuilder { +pub struct ClientBuilder<'a> { oci_client_config: ClientConfig, - rekor_pub_key: Option, - fulcio_certs: Vec, + rekor_pub_key: Option<&'a [u8]>, + fulcio_certs: Vec>, + // repo: Repository #[cfg(feature = "cached-client")] enable_registry_caching: bool, } -impl ClientBuilder { +impl<'a> ClientBuilder<'a> { /// Enable caching of data returned from remote OCI registries #[cfg(feature = "cached-client")] pub fn enable_registry_caching(mut self) -> Self { @@ -66,47 +69,18 @@ impl ClientBuilder { self } - /// Specify the public key used by Rekor. + /// Optional - Configures the roots of trust. /// - /// The public key can be obtained by using the helper methods under the - /// [`tuf`](crate::tuf) module. - /// - /// `key` is a PEM encoded public key - /// - /// When provided, this enables Rekor's integration. - pub fn with_rekor_pub_key(mut self, key: &str) -> Self { - self.rekor_pub_key = Some(key.to_string()); - self - } - - /// Specify the certificate used by Fulcio. This method can be invoked - /// multiple times to add all the certificates that Fulcio used over the - /// time. - /// - /// `cert` is a PEM encoded certificate - /// - /// The certificates can be obtained by using the helper methods under the - /// [`tuf`](crate::tuf) module. - /// - /// When provided, this enables Fulcio's integration. - pub fn with_fulcio_cert(mut self, cert: &[u8]) -> Self { - let certificate = Certificate { - encoding: crate::registry::CertificateEncoding::Pem, - data: cert.to_owned(), - }; - self.fulcio_certs.push(certificate); - self - } + /// Enables Fulcio and Rekor integration with the given trust repository. + /// See [crate::tuf::Repository] for more details on trust repositories. + pub fn with_trust_repository(mut self, repo: &'a R) -> Result { + 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()?; - /// Specify the certificates used by Fulcio. - /// - /// The certificates can be obtained by using the helper methods under the - /// [`tuf`](crate::tuf) module. - /// - /// When provided, this enables Fulcio's integration. - pub fn with_fulcio_certs(mut self, certs: &[crate::registry::Certificate]) -> Self { - self.fulcio_certs = certs.to_vec(); - self + Ok(self) } /// Optional - the configuration to be used by the OCI client. @@ -118,14 +92,14 @@ impl ClientBuilder { self } - pub fn build(self) -> Result { + pub fn build(self) -> Result> { let rekor_pub_key = match self.rekor_pub_key { None => { info!("Rekor public key not provided. Rekor integration disabled"); None } - Some(data) => Some(CosignVerificationKey::from_pem( - data.as_bytes(), + Some(data) => Some(CosignVerificationKey::from_der( + data, &SigningScheme::default(), )?), }; @@ -134,7 +108,7 @@ impl ClientBuilder { info!("No Fulcio cert has been provided. Fulcio integration disabled"); None } else { - let cert_pool = CertificatePool::from_certificates(&self.fulcio_certs)?; + let cert_pool = CertificatePool::from_certificates(self.fulcio_certs, [])?; Some(cert_pool) }; diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 187826bb66..4e6be029f2 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -282,6 +282,7 @@ where #[cfg(test)] mod tests { + use rustls_pki_types::CertificateDer; use serde_json::json; use std::collections::HashMap; @@ -335,18 +336,15 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ #[cfg(feature = "test-registry")] const SIGNED_IMAGE: &str = "busybox:1.34"; - pub(crate) fn get_fulcio_cert_pool() -> CertificatePool { - let certificates = vec![ - crate::registry::Certificate { - encoding: crate::registry::CertificateEncoding::Pem, - data: FULCIO_CRT_1_PEM.as_bytes().to_vec(), - }, - crate::registry::Certificate { - encoding: crate::registry::CertificateEncoding::Pem, - data: FULCIO_CRT_2_PEM.as_bytes().to_vec(), - }, - ]; - CertificatePool::from_certificates(&certificates).unwrap() + pub(crate) fn get_fulcio_cert_pool() -> CertificatePool<'static> { + fn pem_to_der<'a>(input: &'a str) -> CertificateDer<'a> { + let pem_cert = pem::parse(input).unwrap(); + assert_eq!(pem_cert.tag(), "CERTIFICATE"); + CertificateDer::from(pem_cert.into_contents()) + } + let certificates = vec![pem_to_der(FULCIO_CRT_1_PEM), pem_to_der(FULCIO_CRT_2_PEM)]; + + CertificatePool::from_certificates(certificates, []).unwrap() } pub(crate) fn get_rekor_public_key() -> CosignVerificationKey { @@ -645,7 +643,7 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ } #[cfg(feature = "test-registry")] - async fn prepare_image_to_be_signed(client: &mut Client, image_ref: &OciReference) { + async fn prepare_image_to_be_signed(client: &mut Client<'_>, image_ref: &OciReference) { let data = client .registry_client .pull( diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index de7a7cf8f4..b26fcbc5f0 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -438,7 +438,12 @@ impl CertificateSignature { let integrated_time = trusted_bundle.payload.integrated_time; // ensure the certificate has been issued by Fulcio - fulcio_cert_pool.verify_pem_cert(cert_pem)?; + fulcio_cert_pool.verify_pem_cert( + cert_pem, + Some(rustls_pki_types::UnixTime::since_unix_epoch( + cert.tbs_certificate.validity.not_before.to_unix_duration(), + )), + )?; crypto::certificate::is_trusted(&cert, integrated_time)?; @@ -899,8 +904,10 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== let issued_cert_pem = issued_cert.cert.to_pem()?; - let certs = vec![crate::registry::Certificate::try_from(ca_data.cert).unwrap()]; - let cert_pool = CertificatePool::from_certificates(&certs).unwrap(); + let certs = vec![crate::registry::Certificate::try_from(ca_data.cert) + .unwrap() + .try_into()?]; + let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); let bundle = Bundle { @@ -946,8 +953,10 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== let issued_cert_pem = issued_cert.cert.to_pem()?; - let certs = vec![crate::registry::Certificate::try_from(ca_data.cert).unwrap()]; - let cert_pool = CertificatePool::from_certificates(&certs).unwrap(); + let certs = vec![crate::registry::Certificate::try_from(ca_data.cert) + .unwrap() + .try_into()?]; + let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); let bundle = Bundle { @@ -992,8 +1001,10 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== let issued_cert_pem = issued_cert.cert.to_pem()?; - let certs = vec![crate::registry::Certificate::try_from(ca_data.cert).unwrap()]; - let cert_pool = CertificatePool::from_certificates(&certs).unwrap(); + let certs = vec![crate::registry::Certificate::try_from(ca_data.cert) + .unwrap() + .try_into()?]; + let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); let bundle = Bundle { diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index bdfabffe8f..b002aa3bb3 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -1,5 +1,6 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use pkcs8::der::Decode; +use rustls_pki_types::CertificateDer; use std::convert::TryFrom; use tracing::warn; use x509_cert::Certificate; @@ -61,8 +62,12 @@ impl CertificateVerifier { crate::crypto::certificate::verify_validity(&cert)?; if let Some(certs) = cert_chain { - let cert_pool = CertificatePool::from_certificates(certs)?; - cert_pool.verify_der_cert(cert_bytes)?; + let certs = certs + .iter() + .map(|c| CertificateDer::try_from(c.clone())) + .collect::>>()?; + let cert_pool = CertificatePool::from_certificates(certs, [])?; + cert_pool.verify_der_cert(cert_bytes, None)?; } let subject_public_key_info = &cert.tbs_certificate.subject_public_key_info; diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index 992e61b993..f7f702f804 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -13,141 +13,101 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - errors::{Result, SigstoreError}, - registry::Certificate, -}; +use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; +use rustls_pki_types::{CertificateDer, TrustAnchor, UnixTime}; +use webpki::{EndEntityCert, KeyUsage}; -// The untrusted intermediate CA certificate, used for chain building -// TODO: Remove once this is bundled in TUF metadata. -const FULCIO_INTERMEDIATE_V1: &str = "-----BEGIN CERTIFICATE----- -MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw -KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y -MjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl -LmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C -AQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7 -7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS -0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB -BQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp -KFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI -zj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR -nZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP -mygUY7Ii2zbdCdliiow= ------END CERTIFICATE-----"; +use crate::errors::{Result, SigstoreError}; -/// A collection of trusted root certificates +/// A collection of trusted root certificates. #[derive(Default, Debug)] -pub(crate) struct CertificatePool { - trusted_roots: Vec, - intermediates: Vec, +pub(crate) struct CertificatePool<'a> { + trusted_roots: Vec>, + intermediates: Vec>, } -impl CertificatePool { - /// Build a `CertificatePool` instance using the provided list of [`Certificate`] - pub(crate) fn from_certificates(certs: &[Certificate]) -> Result { - let mut trusted_roots = vec![]; - let mut intermediates = vec![]; - - for c in certs { - let pc = match c.encoding { - crate::registry::CertificateEncoding::Pem => { - let pem_str = String::from_utf8(c.data.clone()).map_err(|_| { - SigstoreError::X509Error("certificate is not PEM encoded".to_string()) - })?; - picky::x509::Cert::from_pem_str(&pem_str) - } - crate::registry::CertificateEncoding::Der => picky::x509::Cert::from_der(&c.data), - }?; - - match pc.ty() { - picky::x509::certificate::CertType::Root => { - trusted_roots.push(pc); - } - picky::x509::certificate::CertType::Intermediate => { - intermediates.push(pc); - } - _ => { - return Err(SigstoreError::CertificatePoolError( - "Cannot add a certificate that is no root or intermediate".to_string(), - )); - } - } - } - - // TODO: Remove once FULCIO_INTERMEDIATE_V1 is bundled in TUF metadata. - if intermediates.is_empty() { - intermediates.push(picky::x509::Cert::from_pem_str(FULCIO_INTERMEDIATE_V1)?); - } - +impl<'a> CertificatePool<'a> { + /// Builds a `CertificatePool` instance using the provided list of [`Certificate`]. + pub(crate) fn from_certificates( + trusted_roots: R, + untrusted_intermediates: I, + ) -> Result> + where + R: IntoIterator>, + I: IntoIterator>, + { Ok(CertificatePool { - trusted_roots, - intermediates, + trusted_roots: trusted_roots + .into_iter() + .map(|x| Ok(webpki::extract_trust_anchor(&x)?.to_owned())) + .collect::, webpki::Error>>()?, + intermediates: untrusted_intermediates.into_iter().collect(), }) } /// Ensures the given certificate has been issued by one of the trusted root certificates /// An `Err` is returned when the verification fails. /// - /// **Note well:** certificates issued by Fulciuo are, by design, valid only + /// **Note well:** certificates issued by Fulcio are, by design, valid only /// for a really limited amount of time. /// Because of that the validity checks performed by this method are more /// relaxed. The validity checks are done inside of /// [`crate::crypto::verify_validity`] and [`crate::crypto::verify_expiration`]. - pub(crate) fn verify_pem_cert(&self, cert_pem: &[u8]) -> Result<()> { - let cert_pem_str = std::str::from_utf8(cert_pem).map_err(|_| { - SigstoreError::UnexpectedError("Cannot convert cert back to string".to_string()) - })?; - let cert = picky::x509::Cert::from_pem_str(cert_pem_str)?; - self.verify(&cert) + pub(crate) fn verify_pem_cert( + &self, + cert_pem: &[u8], + verification_time: Option, + ) -> Result<()> { + let cert_pem = pem::parse(cert_pem)?; + if cert_pem.tag() != "CERTIFICATE" { + return Err(SigstoreError::CertificatePoolError( + "PEM file is not a certificate", + )); + } + + self.verify_der_cert(cert_pem.contents(), verification_time) } /// Ensures the given certificate has been issued by one of the trusted root certificates /// An `Err` is returned when the verification fails. /// - /// **Note well:** certificates issued by Fulciuo are, by design, valid only + /// **Note well:** certificates issued by Fulcio are, by design, valid only /// for a really limited amount of time. /// Because of that the validity checks performed by this method are more /// relaxed. The validity checks are done inside of /// [`crate::crypto::verify_validity`] and [`crate::crypto::verify_expiration`]. - pub(crate) fn verify_der_cert(&self, bytes: &[u8]) -> Result<()> { - let cert = picky::x509::Cert::from_der(bytes)?; - self.verify(&cert) + pub(crate) fn verify_der_cert( + &self, + der: &[u8], + verification_time: Option, + ) -> Result<()> { + self.verify_cert_with_time(der, verification_time.unwrap_or(UnixTime::now())) } - fn verify(&self, cert: &picky::x509::Cert) -> Result<()> { - let verified = self - .create_chains_for_all_certificates() - .iter() - .any(|chain| { - cert.verifier() - .chain(chain.iter().copied()) - .exact_date(&cert.valid_not_before()) - .verify() - .is_ok() - }); + /// TODO(tnytown): nudge webpki into behaving as the cosign code expects + pub(crate) fn verify_cert_with_time( + &self, + cert: &[u8], + verification_time: UnixTime, + ) -> Result<()> { + let der = CertificateDer::from(cert); + let cert = EndEntityCert::try_from(&der)?; - if verified { - Ok(()) - } else { - Err(SigstoreError::CertificateValidityError( - "Not issued by a trusted root".to_string(), - )) - } - } + // TODO(tnytown): Determine which of these algs are used in the Sigstore ecosystem. + let signing_algs = webpki::ALL_VERIFICATION_ALGS; + + let eku_code_signing = ID_KP_CODE_SIGNING.as_bytes(); - fn create_chains_for_all_certificates(&self) -> Vec> { - let mut chains: Vec> = vec![]; - self.trusted_roots.iter().for_each(|trusted_root| { - chains.push([trusted_root].to_vec()); - }); - self.intermediates.iter().for_each(|intermediate| { - for root in self.trusted_roots.iter() { - if root.is_parent_of(intermediate).is_ok() { - chains.push([intermediate, root].to_vec()); - } - } - }); + cert.verify_for_usage( + signing_algs, + &self.trusted_roots, + self.intermediates.as_slice(), + verification_time, + KeyUsage::required(eku_code_signing), + None, + None, + )?; - chains + Ok(()) } } diff --git a/src/errors.rs b/src/errors.rs index f513f17543..430066cc65 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -61,9 +61,6 @@ pub enum SigstoreError { #[error(transparent)] FromPEMError(#[from] pem::PemError), - #[error(transparent)] - CertError(#[from] picky::x509::certificate::CertError), - #[error(transparent)] Base64DecodeError(#[from] base64::DecodeError), @@ -104,7 +101,7 @@ pub enum SigstoreError { CertificateWithIncompleteSubjectAlternativeName, #[error("Certificate pool error: {0}")] - CertificatePoolError(String), + CertificatePoolError(&'static str), #[error("Cannot fetch manifest of {image}: {error}")] RegistryFetchManifestError { image: String, error: String }, @@ -146,6 +143,9 @@ pub enum SigstoreError { #[error("TUF target {0} not found inside of repository")] TufTargetNotFoundError(String), + #[error("{0}")] + TufMetadataError(&'static str), + #[error(transparent)] IOError(#[from] std::io::Error), @@ -200,6 +200,9 @@ pub enum SigstoreError { #[error(transparent)] Utf8Error(#[from] std::str::Utf8Error), + #[error(transparent)] + WebPKIError(#[from] webpki::Error), + #[error("Failed to parse the key: {0}")] KeyParseError(String), diff --git a/src/lib.rs b/src/lib.rs index a6eb641818..f439bfc663 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,15 +82,23 @@ //! //! // Provide both rekor and fulcio data -> this enables keyless verification //! // Read rekor's key from the location generated by `cosign initialize` -//! let rekor_pub_key = fs::read_to_string("~/.sigstore/root/targets/rekor.pub") +//! let rekor_pub_key = fs::read("~/.sigstore/root/targets/rekor.pub") //! .expect("Cannot read rekor public key"); //! // Read fulcio's certificate from the location generated by `cosign initialize` -//! let fulcio_cert = fs::read_to_string("~/.sigstore/root/targets/fulcio.crt.pem") +//! let fulcio_cert_data = fs::read("~/.sigstore/root/targets/fulcio.crt.pem") //! .expect("Cannot read fulcio certificate"); +//! let fulcio_cert = sigstore::registry::Certificate { +//! encoding: sigstore::registry::CertificateEncoding::Pem, +//! data: fulcio_cert_data +//! }; +//! +//! let mut repo = sigstore::tuf::FakeRepository::default(); +//! repo.fulcio_certs.get_or_insert(Vec::new()).push(fulcio_cert.try_into().unwrap()); +//! repo.rekor_key = Some(rekor_pub_key); //! //! let mut client = sigstore::cosign::ClientBuilder::default() -//! .with_rekor_pub_key(&rekor_pub_key) -//! .with_fulcio_cert(fulcio_cert.as_bytes()) +//! .with_trust_repository(&repo) +//! .expect("Cannot construct cosign client from given materials") //! .build() //! .expect("Unexpected failure while building Client"); //! diff --git a/src/registry/config.rs b/src/registry/config.rs index bb6be2c04e..c8cdfa24ab 100644 --- a/src/registry/config.rs +++ b/src/registry/config.rs @@ -15,10 +15,13 @@ //! Set of structs and enums used to define how to interact with OCI registries +use rustls_pki_types::CertificateDer; use serde::Serialize; use std::cmp::Ordering; use std::convert::From; +use crate::errors; + /// A method for authenticating to a registry #[derive(Serialize, Debug)] pub enum Auth { @@ -123,6 +126,21 @@ impl From<&Certificate> for oci_distribution::client::Certificate { } } +impl<'a> TryFrom for CertificateDer<'a> { + type Error = errors::SigstoreError; + fn try_from(value: Certificate) -> errors::Result> { + #[inline] + fn to_der(pem: &[u8]) -> errors::Result> { + Ok(pem::parse(pem)?.into_contents()) + } + + match &value.encoding { + CertificateEncoding::Der => Ok(CertificateDer::from(value.data)), + CertificateEncoding::Pem => Ok(CertificateDer::from(to_der(&value.data)?)), + } + } +} + /// A client configuration #[derive(Debug, Clone)] pub struct ClientConfig { diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index 99231be2da..325989706c 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -13,175 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use lazy_static::lazy_static; -use regex::Regex; - -lazy_static! { - pub(crate) static ref SIGSTORE_FULCIO_CERT_TARGET_REGEX: Regex = - Regex::new(r"fulcio(_v\d+)?\.crt\.pem").expect("cannot compile regexp"); -} - pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; pub(crate) const SIGSTORE_TARGET_BASE: &str = "https://tuf-repo-cdn.sigstore.dev/targets"; -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 - } -}"#; - -#[cfg(test)] -mod test { - use super::*; - use rstest::*; - - #[rstest] - #[case("fulcio.crt.pem", true)] - #[case("fulcio_v1.crt.pem", true)] - #[case("fulcio-v2.crt.pem", false)] - #[case("foo.crt.pem", false)] - fn check_fulcio_regex(#[case] input: &str, #[case] matches: bool) { - assert_eq!(SIGSTORE_FULCIO_CERT_TARGET_REGEX.is_match(input), matches); - } +macro_rules! tuf_resource { + ($path:literal) => { + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/", $path)) + }; } + +pub(crate) const SIGSTORE_ROOT: &[u8] = tuf_resource!("prod/root.json"); diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 982fa99c14..a0f7a84c80 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -23,142 +23,315 @@ //! //! # Example //! -//! The `SigstoreRepository` instance can be created via the [`SigstoreRepository::fetch`] +//! The `SigstoreRepository` instance can be created via the [`SigstoreRepository::prefetch`] //! method. //! //! ```rust,no_run //! use sigstore::tuf::SigstoreRepository; -//! use sigstore::cosign; -//! -//! let repo = SigstoreRepository::fetch(None) -//! .expect("Error while building SigstoreRepository"); -//! let client = cosign::ClientBuilder::default() -//! .with_rekor_pub_key(repo.rekor_pub_key()) -//! .with_fulcio_certs(repo.fulcio_certs()) -//! .build() -//! .expect("Error while building cosign client"); +//! let repo = SigstoreRepository::new(None).unwrap().prefetch().unwrap(); //! ``` -//! -//! The `SigstoreRepository::fetch` method can attempt to leverage local copies -//! of the Rekor and Fulcio files. Please refer to the -//! [method docs](SigstoreRepository::fetch) for more details. -//! -//! **Warning:** the `SigstoreRepository::fetch` method currently needs -//! special handling when invoked inside of an async context. Please refer to the -//! [method docs](SigstoreRepository::fetch) for more details. -//! -use std::path::Path; +use std::{ + cell::OnceCell, + fs, + io::Read, + path::{Path, PathBuf}, +}; mod constants; -use constants::*; +mod trustroot; + +use rustls_pki_types::CertificateDer; +use sha2::{Digest, Sha256}; +use tough::TargetName; -mod repository_helper; -use repository_helper::RepositoryHelper; +use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; use super::errors::{Result, SigstoreError}; -/// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository -#[derive(Clone)] +/// A `Repository` owns all key material necessary for establishing a root of trust. +pub trait Repository { + fn fulcio_certs(&self) -> Result>; + fn rekor_keys(&self) -> Result>; +} + +/// A `FakeRepository` is a [Repository] with out-of-band trust materials. +/// As it does not establish a trust root with TUF, users must initialize its materials themselves. +#[derive(Debug, Default)] +pub struct FakeRepository<'a> { + pub fulcio_certs: Option>>, + pub rekor_key: Option>, +} + +impl Repository for FakeRepository<'_> { + fn fulcio_certs(&self) -> Result> { + Ok(match &self.fulcio_certs { + Some(certs) => certs.clone(), + None => Vec::new(), + }) + } + + fn rekor_keys(&self) -> Result> { + Ok(match &self.rekor_key { + Some(key) => vec![&key[..]], + None => Vec::new(), + }) + } +} + +/// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. +#[derive(Debug)] pub struct SigstoreRepository { - rekor_pub_key: String, - fulcio_certs: Vec, + repository: tough::Repository, + checkout_dir: Option, + trusted_root: OnceCell, } impl SigstoreRepository { - /// Fetch relevant information from the remote Sigstore TUF repository. - /// - /// ## Parameters - /// - /// * `checkout_dir`: path to a local directory where Rekor's public - /// key and Fulcio's certificates can be found - /// - /// ## Behaviour - /// - /// This method requires network connectivity, because it will always - /// reach out to Sigstore's TUF repository. - /// - /// This crates embeds a trusted copy of the `root.json` file of Sigstore's - /// TUF repository. The `fetch` function will always connect to the online - /// Sigstore's repository to update this embedded file to the latest version. - /// The update process happens using the TUF protocol. - /// - /// When `checkout_dir` is specified, this method will look for the - /// Fulcio and Rekor files inside of this directory. It will then compare the - /// checksums of these local files with the ones reported inside of the - /// TUF repository metadata. + /// Constructs a new trust repository established by a [tough::Repository]. + pub fn new(checkout_dir: Option<&Path>) -> Result { + // These are statically defined and should always parse correctly. + let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; + let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; + + let repository = + tough::RepositoryLoader::new(constants::SIGSTORE_ROOT, metadata_base, target_base) + .expiration_enforcement(tough::ExpirationEnforcement::Safe) + .load() + .map_err(Box::new)?; + + Ok(Self { + repository, + checkout_dir: checkout_dir.map(ToOwned::to_owned), + trusted_root: OnceCell::default(), + }) + } + + fn trusted_root(&self) -> Result<&TrustedRoot> { + fn init_trusted_root( + repository: &tough::Repository, + checkout_dir: Option<&PathBuf>, + ) -> Result { + let trusted_root_target = TargetName::new("trusted_root.json").map_err(Box::new)?; + let local_path = checkout_dir.map(|d| d.join(trusted_root_target.raw())); + + let data = fetch_target_or_reuse_local_cache( + repository, + &trusted_root_target, + local_path.as_ref(), + )?; + + println!("data:\n{}", String::from_utf8_lossy(&data)); + + Ok(serde_json::from_slice(&data[..])?) + } + + if let Some(root) = self.trusted_root.get() { + return Ok(root); + } + + let root = init_trusted_root(&self.repository, self.checkout_dir.as_ref())?; + Ok(self.trusted_root.get_or_init(|| root)) + } + + /// Prefetches trust materials. /// - /// If the files are not found, or if their local checksums do not match - /// with the ones reported by TUF's metdata, the files are then downloaded - /// from the TUF repository and then written to the local filesystem. + /// [Repository::fulcio_certs()] and [Repository::rekor_keys()] on [SigstoreRepository] 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. /// - /// When `checkout_dir` is `None`, the `fetch` method will always fetch the - /// Fulcio and Rekor files from the remote TUF repository and keep them - /// in memory. + /// ```rust + /// # use tokio::task::spawn_blocking; + /// # use sigstore::tuf::SigstoreRepository; + /// # use sigstore::errors::Result; + /// # #[tokio::main] + /// # async fn main() -> std::result::Result<(), anyhow::Error> { + /// let repo: Result = spawn_blocking(|| Ok(SigstoreRepository::new(None)?.prefetch()?)).await?; + /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRepository` + /// # Ok(()) + /// # } + /// ``` + pub fn prefetch(self) -> Result { + let _ = self.trusted_root()?; + Ok(self) + } + + #[inline] + fn tlog_keys(tlogs: &[TransparencyLogInstance]) -> impl Iterator { + tlogs + .iter() + .filter(|key| is_timerange_valid(key.public_key.valid_for.as_ref(), false)) + .filter_map(|key| key.public_key.raw_bytes.as_ref()) + .map(|key_bytes| key_bytes.as_slice()) + } + + #[inline] + fn ca_keys( + cas: &[CertificateAuthority], + allow_expired: bool, + ) -> impl Iterator { + cas.iter() + .filter(move |ca| is_timerange_valid(Some(&ca.valid_for), allow_expired)) + .flat_map(|ca| ca.cert_chain.certificates.iter()) + .map(|cert| cert.raw_bytes.as_slice()) + } +} + +impl Repository for SigstoreRepository { + /// Fetch Fulcio certificates from the given TUF repository or reuse + /// the local cache if its contents are not outdated. /// - /// ## Usage inside of async code + /// The contents of the local cache are updated when they are outdated. /// /// **Warning:** this method needs special handling when invoked from /// an async function because it performs blocking operations. + fn fulcio_certs(&self) -> Result> { + let root = self.trusted_root()?; + + // Allow expired certificates: they may have been active when the + // certificate was used to sign. + let certs = Self::ca_keys(&root.certificate_authorities, true); + let certs: Vec<_> = certs.map(CertificateDer::from).collect(); + + if certs.is_empty() { + Err(SigstoreError::TufMetadataError( + "Fulcio certificates not found", + )) + } else { + Ok(certs) + } + } + + /// Fetch Rekor public keys from the given TUF repository or reuse + /// the local cache if it's not outdated. /// - /// If needed, this can be solved in the following way: - /// - /// ```rust,no_run - /// use tokio::task::spawn_blocking; - /// use sigstore::tuf::SigstoreRepository; - /// - /// async fn my_async_function() { - /// // ... your code - /// - /// let repo: sigstore::errors::Result = spawn_blocking(|| - /// sigstore::tuf::SigstoreRepository::fetch(None) - /// ) - /// .await - /// .expect("Error spawning blocking task"); - /// - /// // handle the case of `repo` being an `Err` - /// // ... your code - /// } - /// ``` + /// The contents of the local cache are updated when they are outdated. /// - /// This of course has a performance hit when used inside of an async function. - pub fn fetch(checkout_dir: Option<&Path>) -> Result { - let metadata_base = url::Url::parse(SIGSTORE_METADATA_BASE).map_err(|_| { - SigstoreError::UnexpectedError(String::from("Cannot convert metadata_base to URL")) - })?; - let target_base = url::Url::parse(SIGSTORE_TARGET_BASE).map_err(|_| { - SigstoreError::UnexpectedError(String::from("Cannot convert target_base to URL")) - })?; - - let repository_helper = RepositoryHelper::new( - SIGSTORE_ROOT.as_bytes(), - metadata_base, - target_base, - checkout_dir, - )?; - - let fulcio_certs = repository_helper.fulcio_certs()?; - - let rekor_pub_key = repository_helper.rekor_pub_key().map(|data| { - String::from_utf8(data).map_err(|e| { - SigstoreError::UnexpectedError(format!( - "Cannot parse Rekor's public key obtained from TUF repository: {e}", - )) - }) - })??; - - Ok(SigstoreRepository { - rekor_pub_key, - fulcio_certs, - }) + /// **Warning:** this method needs special handling when invoked from + /// an async function because it performs blocking operations. + fn rekor_keys(&self) -> Result> { + let root = self.trusted_root()?; + let keys: Vec<_> = Self::tlog_keys(&root.tlogs).collect(); + + if keys.len() != 1 { + Err(SigstoreError::TufMetadataError( + "Did not find exactly 1 active Rekor key", + )) + } else { + Ok(keys) + } } +} + +/// Given a `range`, checks that the the current time is not before `start`. If +/// `allow_expired` is `false`, also checks that the current time is not after +/// `end`. +fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { + let time = chrono::Utc::now(); - /// Rekor public key - pub fn rekor_pub_key(&self) -> &str { - &self.rekor_pub_key + match range { + // If there was no validity period specified, the key is always valid. + None => true, + // Active: if the current time is before the starting period, we are not yet valid. + Some(range) if time < range.start => false, + // If we want Expired keys, then the key is valid at this point. + _ if allow_expired => true, + // Otherwise, check that we are in range if the range has an end. + Some(range) => match range.end { + None => true, + Some(end) => time <= end, + }, } +} + +/// Download a file stored inside of a TUF repository, try to reuse a local +/// cache when possible. +/// +/// * `repository`: TUF repository holding the file +/// * `target_name`: TUF representation of the file to be downloaded +/// * `local_file`: location where the file should be downloaded +/// +/// This function will reuse the local copy of the file if contents +/// didn't change. +/// This check is done by comparing the digest of the local file, if found, +/// with the digest reported inside of the TUF repository metadata. +/// +/// **Note well:** the `local_file` is updated whenever its contents are +/// outdated. +fn fetch_target_or_reuse_local_cache( + repository: &tough::Repository, + target_name: &TargetName, + local_file: Option<&PathBuf>, +) -> Result> { + let (local_file_outdated, local_file_contents) = if let Some(path) = local_file { + is_local_file_outdated(repository, target_name, path) + } else { + Ok((true, None)) + }?; - /// Fulcio certificate - pub fn fulcio_certs(&self) -> &[crate::registry::Certificate] { - &self.fulcio_certs + let data = if local_file_outdated { + let data = fetch_target(repository, target_name)?; + if let Some(path) = local_file { + // update the local file to have latest data from the TUF repo + fs::write(path, data.clone())?; + } + data + } else { + local_file_contents + .expect("local file contents to not be 'None'") + .as_bytes() + .to_owned() + }; + + Ok(data) +} + +/// Download a file from a TUF repository +fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { + let data: Vec; + match repository.read_target(target_name).map_err(Box::new)? { + None => Err(SigstoreError::TufTargetNotFoundError( + target_name.raw().to_string(), + )), + Some(reader) => { + data = read_to_end(reader)?; + Ok(data) + } + } +} + +/// Compares the checksum of a local file, with the digest reported inside of +/// TUF repository metadata +fn is_local_file_outdated( + repository: &tough::Repository, + target_name: &TargetName, + local_file: &Path, +) -> Result<(bool, Option)> { + let target = repository + .targets() + .signed + .targets + .get(target_name) + .ok_or_else(|| SigstoreError::TufTargetNotFoundError(target_name.raw().to_string()))?; + + if local_file.exists() { + let data = fs::read_to_string(local_file)?; + let local_checksum = Sha256::digest(data.clone()); + let expected_digest: Vec = target.hashes.sha256.to_vec(); + + if local_checksum.as_slice() == expected_digest.as_slice() { + // local data is not outdated + Ok((false, Some(data))) + } else { + Ok((true, None)) + } + } else { + Ok((true, None)) } } + +/// Gets the goods from a read and makes a Vec +fn read_to_end(mut reader: R) -> Result> { + let mut v = Vec::new(); + reader.read_to_end(&mut v)?; + Ok(v) +} diff --git a/src/tuf/repository_helper.rs b/src/tuf/repository_helper.rs index a581619638..74747d363e 100644 --- a/src/tuf/repository_helper.rs +++ b/src/tuf/repository_helper.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use rustls_pki_types::CertificateDer; use sha2::{Digest, Sha256}; use std::fs; use std::io::Read; @@ -20,14 +21,13 @@ use std::path::{Path, PathBuf}; use tough::{RepositoryLoader, TargetName}; use url::Url; -use super::{ - super::errors::{Result, SigstoreError}, - constants::{SIGSTORE_FULCIO_CERT_TARGET_REGEX, SIGSTORE_REKOR_PUB_KEY_TARGET}, -}; +use super::super::errors::{Result, SigstoreError}; +use super::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; pub(crate) struct RepositoryHelper { repository: tough::Repository, checkout_dir: Option, + trusted_root: Option, } impl RepositoryHelper { @@ -40,7 +40,7 @@ impl RepositoryHelper { where R: Read, { - let repository = RepositoryLoader::new(root, metadata_base, target_base) + let repository = RepositoryLoader::new(SIGSTORE_ROOT, metadata_base, target_base) .expiration_enforcement(tough::ExpirationEnforcement::Safe) .load() .map_err(Box::new)?; @@ -48,162 +48,110 @@ impl RepositoryHelper { Ok(Self { repository, checkout_dir: checkout_dir.map(|s| s.to_owned()), + trusted_root: None, }) } - /// 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. - pub(crate) fn fulcio_certs(&self) -> Result> { - let fulcio_target_names = self.fulcio_cert_target_names(); - let mut certs = vec![]; - - for fulcio_target_name in &fulcio_target_names { - let local_fulcio_path = self - .checkout_dir - .as_ref() - .map(|d| Path::new(d).join(fulcio_target_name.raw())); - - let cert_data = fetch_target_or_reuse_local_cache( - &self.repository, - fulcio_target_name, - local_fulcio_path.as_ref(), - )?; - certs.push(crate::registry::Certificate { - data: cert_data, - encoding: crate::registry::CertificateEncoding::Pem, - }); + pub(crate) fn from_repo(repo: tough::Repository, checkout_dir: Option<&Path>) -> Self { + Self { + repository: repo, + checkout_dir: checkout_dir.map(|s| s.to_owned()), + trusted_root: None, } - Ok(certs) } - fn fulcio_cert_target_names(&self) -> Vec { - self.repository - .targets() - .signed - .targets_iter() - .filter_map(|(target_name, _target)| { - if SIGSTORE_FULCIO_CERT_TARGET_REGEX.is_match(target_name.raw()) { - Some(target_name.clone()) - } else { - None - } - }) - .collect() - } - - /// Fetch Rekor public key from the given TUF repository or reuse - /// the local cache if it's not outdated. - /// - /// The contents of the local cache are updated when they are outdated. - pub(crate) fn rekor_pub_key(&self) -> Result> { - let rekor_target_name = TargetName::new(SIGSTORE_REKOR_PUB_KEY_TARGET).map_err(Box::new)?; + fn trusted_root(&self) -> Result<&TrustedRoot> { + if let Some(result) = self.trusted_root { + return Ok(&result); + } - let local_rekor_path = self + let trusted_root_target = TargetName::new("trusted_root.json").map_err(Box::new)?; + let local_path = self .checkout_dir .as_ref() - .map(|d| Path::new(d).join(SIGSTORE_REKOR_PUB_KEY_TARGET)); + .map(|d| d.join(trusted_root_target.raw())); - fetch_target_or_reuse_local_cache( + let data = fetch_target_or_reuse_local_cache( &self.repository, - &rekor_target_name, - local_rekor_path.as_ref(), - ) + &trusted_root_target, + local_path.as_ref(), + )?; + + let result = serde_json::from_slice(&data[..])?; + Ok(self.trusted_root.insert(result)) } -} -/// Download a file stored inside of a TUF repository, try to reuse a local -/// cache when possible. -/// -/// * `repository`: TUF repository holding the file -/// * `target_name`: TUF representation of the file to be downloaded -/// * `local_file`: location where the file should be downloaded -/// -/// This function will reuse the local copy of the file if contents -/// didn't change. -/// This check is done by comparing the digest of the local file, if found, -/// with the digest reported inside of the TUF repository metadata. -/// -/// **Note well:** the `local_file` is updated whenever its contents are -/// outdated. -fn fetch_target_or_reuse_local_cache( - repository: &tough::Repository, - target_name: &TargetName, - local_file: Option<&PathBuf>, -) -> Result> { - let (local_file_outdated, local_file_contents) = if let Some(path) = local_file { - is_local_file_outdated(repository, target_name, path) - } else { - Ok((true, None)) - }?; - - let data = if local_file_outdated { - let data = fetch_target(repository, target_name)?; - if let Some(path) = local_file { - // update the local file to have latest data from the TUF repo - fs::write(path, data.clone())?; + #[inline] + fn tlog_keys(&self, tlogs: &Vec) -> Vec<&[u8]> { + let mut result = Vec::new(); + + for key in tlogs { + // We won't accept expired keys for transparency logs. + if !is_timerange_valid(key.public_key.valid_for, false) { + continue; + } + + if let Some(raw) = key.public_key.raw_bytes { + result.push(&raw[..]); + } } - data - } else { - local_file_contents - .expect("local file contents to not be 'None'") - .as_bytes() - .to_owned() - }; - - Ok(data) -} -/// Download a file from a TUF repository -fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { - let data: Vec; - match repository.read_target(target_name).map_err(Box::new)? { - None => Err(SigstoreError::TufTargetNotFoundError( - target_name.raw().to_string(), - )), - Some(reader) => { - data = read_to_end(reader)?; - Ok(data) + result + } + + #[inline] + fn ca_keys(&self, cas: &Vec, allow_expired: bool) -> Vec<&[u8]> { + let mut certs = Vec::new(); + + for ca in cas { + if !is_timerange_valid(Some(ca.valid_for), allow_expired) { + continue; + } + + let certs_in_ca = ca.cert_chain.certificates; + certs.extend(certs_in_ca.iter().map(|cert| &cert.raw_bytes[..])); } + + return certs; } -} -/// Compares the checksum of a local file, with the digest reported inside of -/// TUF repository metadata -fn is_local_file_outdated( - repository: &tough::Repository, - target_name: &TargetName, - local_file: &Path, -) -> Result<(bool, Option)> { - let target = repository - .targets() - .signed - .targets - .get(target_name) - .ok_or_else(|| SigstoreError::TufTargetNotFoundError(target_name.raw().to_string()))?; - - if local_file.exists() { - let data = fs::read_to_string(local_file)?; - let local_checksum = Sha256::digest(data.clone()); - let expected_digest: Vec = target.hashes.sha256.to_vec(); - - if local_checksum.as_slice() == expected_digest.as_slice() { - // local data is not outdated - Ok((false, Some(data))) + /// 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. + pub(crate) fn fulcio_certs(&self) -> Result> { + let root = self.trusted_root()?; + + // Allow expired certificates: they may have been active when the + // certificate was used to sign. + let certs = self.ca_keys(&root.certificate_authorities, true); + let certs: Vec<_> = certs.iter().map(|v| CertificateDer::from(*v)).collect(); + + if certs.is_empty() { + Err(SigstoreError::TufMetadataError( + "Fulcio certificates not found", + )) } else { - Ok((true, None)) + Ok(certs) } - } else { - Ok((true, None)) } -} -/// Gets the goods from a read and makes a Vec -fn read_to_end(mut reader: R) -> Result> { - let mut v = Vec::new(); - reader.read_to_end(&mut v)?; - Ok(v) + /// Fetch Rekor public keys from the given TUF repository or reuse + /// the local cache if it's not outdated. + /// + /// The contents of the local cache are updated when they are outdated. + pub(crate) fn rekor_keys(&self) -> Result> { + let root = self.trusted_root()?; + let keys = self.tlog_keys(&root.tlogs); + + if keys.len() != 1 { + Err(SigstoreError::TufMetadataError( + "Did not find exactly 1 active Rekor key", + )) + } else { + Ok(keys) + } + } } #[cfg(test)] @@ -252,63 +200,94 @@ mod tests { )) })?; // It's fine to ignore timestamp.json expiration inside of test env - let repo = - RepositoryLoader::new(SIGSTORE_ROOT.as_bytes(), metadata_base_url, target_base_url) - .expiration_enforcement(tough::ExpirationEnforcement::Unsafe) - .load() - .map_err(Box::new)?; + let repo = RepositoryLoader::new(SIGSTORE_ROOT, metadata_base_url, target_base_url) + .expiration_enforcement(tough::ExpirationEnforcement::Unsafe) + .load() + .map_err(Box::new)?; Ok(repo) } - #[test] - fn get_files_without_using_local_cache() { - let repository = local_tuf_repo().expect("Local TUF repo should not fail"); - let helper = RepositoryHelper { - repository, - checkout_dir: None, - }; + fn find_target(name: &str) -> Result { + let path = test_data().join("repository").join("targets"); + + for entry in fs::read_dir(path)? { + let path = entry?.path(); + if path.is_dir() { + continue; + } + + // Heuristic: Filter for consistent snapshot targets. SHA256 hashes in hexadecimal + // comprise of 64 characters, so our filename must be at least that long. The TUF repo + // shouldn't ever contain paths with invalid Unicode (knock on wood), so we're doing + // the lossy OsStr conversion here. + let filename = path.file_name().unwrap().to_str().unwrap(); + if filename.len() < 64 { + continue; + } + + // Heuristic: see if the filename is in consistent snapshot format (.). + // NB: The consistent snapshot prefix should be ASCII, so indexing the string as + // bytes is safe enough. + if filename.as_bytes()[64] != b'.' { + continue; + } + + // At this point, we're probably dealing with a consistent snapshot. + // Check if the name matches. + if filename.ends_with(name) { + return Ok(path); + } + } + + Err(SigstoreError::UnexpectedError( + "Couldn't find a matching target".to_string(), + )) + } - let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); + fn check_against_disk(helper: &RepositoryHelper) { + let mut actual: Vec<&[u8]> = helper + .fulcio_certs() + .expect("fulcio certs could not be read") + .iter() + .map(|c| c.as_ref()) + .collect(); + let expected = ["fulcio.crt.pem", "fulcio_v1.crt.pem"].iter().map(|t| { + let path = find_target(t)?; + Ok(fs::read(path)?) + }); + let mut expected = expected + .collect::>>>() + .expect("could not find targets"); actual.sort(); - let mut expected: Vec = - ["fulcio.crt.pem", "fulcio_v1.crt.pem"] - .iter() - .map(|filename| { - let data = fs::read( - test_data() - .join("repository") - .join("targets") - .join(filename), - ) - .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); - crate::registry::Certificate { - data, - encoding: crate::registry::CertificateEncoding::Pem, - } - }) - .collect(); expected.sort(); - assert_eq!( - actual, expected, - "The fulcio cert read from the TUF repository is not what was expected" - ); + assert_eq!(actual, expected, "The fulcio cert is not what was expected"); - let actual = helper.rekor_pub_key().expect("rekor key cannot be read"); - let expected = fs::read( - test_data() - .join("repository") - .join("targets") - .join("rekor.pub"), - ) - .expect("cannot read rekor key from test data"); + let actual = helper.rekor_keys().expect("rekor key cannot be read"); + let expected = fs::read(find_target("rekor.pub").expect("could not find targets")) + .expect("cannot read rekor key from test data"); + let expected = pem::parse(expected).unwrap(); + assert_eq!(expected.tag(), "PUBLIC KEY"); assert_eq!( - actual, expected, - "The rekor key read from the TUF repository is not what was expected" + actual, + &[expected.contents()], + "The rekor key is not what was expected" ); } + #[test] + fn get_files_without_using_local_cache() { + let repository = local_tuf_repo().expect("Local TUF repo should not fail"); + let helper = RepositoryHelper { + repository, + checkout_dir: None, + trusted_root: None, + }; + + check_against_disk(&helper); + } + #[test] fn download_files_to_local_cache() { let cache_dir = TempDir::new().expect("Cannot create temp cache dir"); @@ -317,42 +296,10 @@ mod tests { let helper = RepositoryHelper { repository, checkout_dir: Some(cache_dir.path().to_path_buf()), + trusted_root: None, }; - let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); - actual.sort(); - let mut expected: Vec = - ["fulcio.crt.pem", "fulcio_v1.crt.pem"] - .iter() - .map(|filename| { - let data = fs::read( - test_data() - .join("repository") - .join("targets") - .join(filename), - ) - .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); - crate::registry::Certificate { - data, - encoding: crate::registry::CertificateEncoding::Pem, - } - }) - .collect(); - expected.sort(); - - assert_eq!( - actual, expected, - "The fulcio cert read from the cache dir is not what was expected" - ); - - let expected = helper.rekor_pub_key().expect("rekor key cannot be read"); - let actual = fs::read(cache_dir.path().join("rekor.pub")) - .expect("cannot read rekor key from cache dir"); - - assert_eq!( - actual, expected, - "The rekor key read from the cache dir is not what was expected" - ); + check_against_disk(&helper); } #[test] @@ -365,8 +312,8 @@ mod tests { .expect("Cannot write file to cache dir"); } fs::write( - cache_dir.path().join(SIGSTORE_REKOR_PUB_KEY_TARGET), - b"fake rekor", + cache_dir.path().join("trusted_root.json"), + b"fake trusted root", ) .expect("Cannot write file to cache dir"); @@ -374,41 +321,22 @@ mod tests { let helper = RepositoryHelper { repository, checkout_dir: Some(cache_dir.path().to_path_buf()), + trusted_root: None, }; - let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); - actual.sort(); - let mut expected: Vec = - ["fulcio.crt.pem", "fulcio_v1.crt.pem"] - .iter() - .map(|filename| { - let data = fs::read( - test_data() - .join("repository") - .join("targets") - .join(filename), - ) - .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); - crate::registry::Certificate { - data, - encoding: crate::registry::CertificateEncoding::Pem, - } - }) - .collect(); - expected.sort(); + check_against_disk(&helper); + } - assert_eq!( - actual, expected, - "The fulcio cert read from the TUF repository is not what was expected" - ); + #[test] + fn deser_trusted_root() { + let metadata_base_path = test_data().join("repository"); + let targets_base_path = metadata_base_path.join("targets"); - let expected = helper.rekor_pub_key().expect("rekor key cannot be read"); - let actual = fs::read(cache_dir.path().join("rekor.pub")) - .expect("cannot read rekor key from cache dir"); + let repository = local_tuf_repo().expect("Local TUF repo should not fail"); + let helper = RepositoryHelper::from_repo(repository, None); - assert_eq!( - actual, expected, - "The rekor key read from the cache dir is not what was expected" - ); + helper + .trusted_root() + .expect("Trusted Root should deserialize"); } } diff --git a/src/tuf/trustroot.rs b/src/tuf/trustroot.rs new file mode 100644 index 0000000000..61586a8ffa --- /dev/null +++ b/src/tuf/trustroot.rs @@ -0,0 +1,148 @@ +#![allow(dead_code)] + +// HACK(jl): protobuf-specs schemas are currently compiled for direct dependencies of the Bundle schema. +// See note https://github.com/sigstore/protobuf-specs/blob/main/gen/pb-rust/src/lib.rs#L1-L23 +// HACK(ap): We should probably use definitions from sigstore-protobuf-specs, but +// the autogenerated definitions are unergonomic. Declare it locally here. + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_with::base64::Base64; + +use serde_with::serde_as; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[allow(non_camel_case_types)] +pub(crate) enum HashAlgorithm { + HASH_ALGORITHM_UNSPECIFIED = 0, + SHA2_256 = 1, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[allow(non_camel_case_types)] +pub(crate) enum PublicKeyDetails { + PUBLIC_KEY_DETAILS_UNSPECIFIED = 0, + // RSA + PKCS1_RSA_PKCS1V5 = 1, // See RFC8017 + PKCS1_RSA_PSS = 2, // See RFC8017 + PKIX_RSA_PKCS1V5 = 3, + PKIX_RSA_PSS = 4, + // ECDSA + PKIX_ECDSA_P256_SHA_256 = 5, // See NIST FIPS 186-4 + PKIX_ECDSA_P256_HMAC_SHA_256 = 6, // See RFC6979 + // Ed 25519 + PKIX_ED25519 = 7, // See RFC8032 +} + +#[serde_as] +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct LogId { + #[serde_as(as = "Base64")] + pub key_id: Vec, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TimeRange { + pub start: DateTime, + pub end: Option>, +} + +#[serde_as] +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PublicKey { + #[serde_as(as = "Option")] + pub raw_bytes: Option>, + pub key_details: PublicKeyDetails, + pub valid_for: Option, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DistinguishedName { + pub organization: String, + pub common_name: String, +} + +#[serde_as] +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct X509Certificate { + #[serde_as(as = "Base64")] + pub raw_bytes: Vec, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct X509CertificateChain { + pub certificates: Vec, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TransparencyLogInstance { + pub base_url: String, + pub hash_algorithm: HashAlgorithm, + pub public_key: PublicKey, + pub log_id: LogId, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct CertificateAuthority { + pub subject: DistinguishedName, + pub uri: Option, + pub cert_chain: X509CertificateChain, + pub valid_for: TimeRange, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TrustedRoot { + pub media_type: String, + pub tlogs: Vec, + pub certificate_authorities: Vec, + pub ctlogs: Vec, + pub timestamp_authorities: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tuf_serde_as_base64() { + let data = X509Certificate { + raw_bytes: b"Hello World".to_vec(), // NOTE(jl): value not representative + }; + let json = serde_json::json!({"rawBytes": "SGVsbG8gV29ybGQ=",}); + + assert_eq!(json, serde_json::to_value(&data).unwrap()); + assert_eq!(data, serde_json::from_value(json).unwrap()); + } + + #[test] + fn tuf_serde_as_nested_structure_base64() { + let data = PublicKey { + raw_bytes: Some(b"Hello World".to_vec()), + key_details: PublicKeyDetails::PKIX_ED25519, + valid_for: Some(TimeRange { + start: DateTime::from_timestamp(1_500_000_000, 0).unwrap(), + end: None, + }), + }; + let json = serde_json::json!({ + "rawBytes": "SGVsbG8gV29ybGQ=", + "keyDetails": "PKIX_ED25519", + "validFor": { + "start": "2017-07-14T02:40:00Z", + "end": None::> + } + }); + + assert_eq!(json, serde_json::to_value(&data).unwrap()); + assert_eq!(data, serde_json::from_value(json).unwrap()); + } +} diff --git a/trust_root/prod/root.json b/trust_root/prod/root.json new file mode 100644 index 0000000000..38f80f9404 --- /dev/null +++ b/trust_root/prod/root.json @@ -0,0 +1,156 @@ +{ + "signed": { + "_type": "root", + "spec_version": "1.0", + "version": 5, + "expires": "2023-04-18T18:13:43Z", + "keys": { + "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXsz3SZXFb8jMV42j6pJlyjbjR8K\nN3Bwocexq6LMIb5qsWKOQvLN16NUefLc4HswOoumRsVVaajSpQS6fobkRw==\n-----END PUBLIC KEY-----\n" + } + }, + "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0ghrh92Lw1Yr3idGV5WqCtMDB8Cx\n+D8hdC4w2ZLNIplVRoVGLskYa3gheMyOjiJ8kPi15aQ2//7P+oj7UvJPGw==\n-----END PUBLIC KEY-----\n" + } + }, + "45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELrWvNt94v4R085ELeeCMxHp7PldF\n0/T1GxukUh2ODuggLGJE0pc1e8CSBf6CS91Fwo9FUOuRsjBUld+VqSyCdQ==\n-----END PUBLIC KEY-----\n" + } + }, + "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEinikSsAQmYkNeH5eYq/CnIzLaacO\nxlSaawQDOwqKy/tCqxq5xxPSJc21K4WIhs9GyOkKfzueY3GILzcMJZ4cWw==\n-----END PUBLIC KEY-----\n" + } + }, + "e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWRiGr5+j+3J5SsH+Ztr5nE2H2wO7\nBV+nO3s93gLca18qTOzHY1oWyAGDykMSsGTUBSt9D+An0KfKsD2mfSM42Q==\n-----END PUBLIC KEY-----\n" + } + }, + "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBzVOmHCPojMVLSI364WiiV8NPrD\n6IgRxVliskz/v+y3JER5mcVGcONliDcWMC5J2lfHmjPNPhb4H7xm8LzfSA==\n-----END PUBLIC KEY-----\n" + } + }, + "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy8XKsmhBYDI8Jc0GwzBxeKax0cm5\nSTKEU65HPFunUn41sT8pi0FjM4IkHz/YUmwmLUO0Wt7lxhj6BkLIK4qYAw==\n-----END PUBLIC KEY-----\n" + } + } + }, + "roles": { + "root": { + "keyids": [ + "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", + "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", + "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f", + "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", + "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de" + ], + "threshold": 3 + }, + "snapshot": { + "keyids": [ + "45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", + "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", + "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f", + "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", + "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de" + ], + "threshold": 3 + }, + "timestamp": { + "keyids": [ + "e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a" + ], + "threshold": 1 + } + }, + "consistent_snapshot": true + }, + "signatures": [ + { + "keyid": "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", + "sig": "3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a" + }, + { + "keyid": "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", + "sig": "30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d" + }, + { + "keyid": "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", + "sig": "3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b" + }, + { + "keyid": "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de", + "sig": "3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9" + }, + { + "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", + "sig": "3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a" + }, + { + "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", + "sig": "30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d" + }, + { + "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", + "sig": "3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b" + }, + { + "keyid": "75e867ab10e121fdef32094af634707f43ddd79c6bab8ad6c5ab9f03f4ea8c90", + "sig": "3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9" + } + ] +} \ No newline at end of file diff --git a/trust_root/prod/trusted_root.json b/trust_root/prod/trusted_root.json new file mode 100644 index 0000000000..bb4e6fcd88 --- /dev/null +++ b/trust_root/prod/trusted_root.json @@ -0,0 +1,91 @@ +{ + "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", + "tlogs": [ + { + "baseUrl": "https://rekor.sigstore.dev", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-01-12T11:53:27.000Z" + } + }, + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + } + } + ], + "certificateAuthorities": [ + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstore.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==" + } + ] + }, + "validFor": { + "start": "2021-03-07T03:20:29.000Z", + "end": "2022-12-31T23:59:59.999Z" + } + }, + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstore.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" + }, + { + "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" + } + ] + }, + "validFor": { + "start": "2022-04-13T20:06:15.000Z" + } + } + ], + "ctlogs": [ + { + "baseUrl": "https://ctfe.sigstore.dev/test", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-03-14T00:00:00.000Z", + "end": "2022-10-31T23:59:59.999Z" + } + }, + "logId": { + "keyId": "CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=" + } + }, + { + "baseUrl": "https://ctfe.sigstore.dev/2022", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-10-20T00:00:00.000Z" + } + }, + "logId": { + "keyId": "3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4=" + } + } + ], + "timestampAuthorities": [] +} From a4467ee84fb64d279b4eb9ec99f5173eba2c05ad Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Thu, 9 Nov 2023 14:32:57 -0500 Subject: [PATCH 052/161] cosign/tuf: docs Signed-off-by: Jack Leightcap --- src/cosign/client_builder.rs | 4 +-- src/cosign/mod.rs | 13 +++++----- src/crypto/verification_key.rs | 2 +- src/lib.rs | 12 +++++---- src/tuf/trustroot.rs | 46 ++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index f076e83528..4bfae89e2d 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -28,7 +28,7 @@ use crate::tuf::Repository; /// ## Rekor integration /// /// Rekor integration can be enabled by specifying Rekor's public key. -/// This can be provided via the [`ClientBuilder::with_rekor_pub_key`] method. +/// This can be provided via a [`crate::tuf::FakeRepository`]. /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -36,7 +36,7 @@ use crate::tuf::Repository; /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via the [`ClientBuilder::with_fulcio_certs`] method. +/// This can be provided via a [`crate::tuf::FakeRepository`]. /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 4e6be029f2..9ddcdcc813 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -102,13 +102,14 @@ pub trait CosignCapabilities { /// must be satisfied: /// /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Rekor integration enabled (see - /// [`sigstore::cosign::ClientBuilder::with_rekor_pub_key`](crate::cosign::ClientBuilder::with_rekor_pub_key)) + /// have been created with Rekor integration enabled (see [`crate::tuf::FakeRepository`]) /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Fulcio integration enabled (see - /// [`sigstore::cosign::ClientBuilder::with_fulcio_certs`](crate::cosign::ClientBuilder::with_fulcio_certs)) + /// have been created with Fulcio integration enabled (see [`crate::tuf::FakeRepository]) /// * The layer must include a bundle produced by Rekor /// + /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods + /// > to obtain this data from the official TUF repository of the Sigstore project. + /// /// When the embedded certificate cannot be verified, [`SignatureLayer::certificate_signature`] /// is going to be `None`. /// @@ -199,7 +200,7 @@ pub trait CosignCapabilities { /// verification. /// /// Returns a `Result` with either `Ok()` for passed verification or -/// [`SigstoreVerifyConstraintsError`](crate::errors::SigstoreVerifyConstraintsError), +/// [`SigstoreVerifyConstraintsError`] /// which contains a vector of references to unsatisfied constraints. /// /// See the documentation of the [`cosign::verification_constraint`](crate::cosign::verification_constraint) module for more @@ -249,7 +250,7 @@ where /// passes applying constraints process. /// /// Returns a `Result` with either `Ok()` for success or -/// [`SigstoreApplicationConstraintsError`](crate::errors::SigstoreApplicationConstraintsError), +/// [`SigstoreApplicationConstraintsError`] /// which contains a vector of references to unapplied constraints. /// /// See the documentation of the [`cosign::sign_constraint`](crate::cosign::sign_constraint) module for more diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 9ba0feaaab..1cf40d7eed 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -59,7 +59,7 @@ pub enum CosignVerificationKey { ED25519(ed25519_dalek::VerifyingKey), } -/// Attempts to convert a [x509 Subject Public Key Info](SubjectPublicKeyInfo) object into +/// Attempts to convert a [x509 Subject Public Key Info](x509_cert::spki::SubjectPublicKeyInfo) object into /// a `CosignVerificationKey` one. /// /// Currently can convert only the following types of keys: diff --git a/src/lib.rs b/src/lib.rs index f439bfc663..30475140a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! //! # Verification //! -//! Sigstore verification is done using the [`cosign::Client`](crate::cosign::Client) +//! Sigstore verification is done using the [`cosign::Client`] //! struct. //! //! ## Triangulation of Sigstore signature @@ -92,9 +92,11 @@ //! data: fulcio_cert_data //! }; //! -//! let mut repo = sigstore::tuf::FakeRepository::default(); -//! repo.fulcio_certs.get_or_insert(Vec::new()).push(fulcio_cert.try_into().unwrap()); -//! repo.rekor_key = Some(rekor_pub_key); +//! let mut repo = sigstore::tuf::FakeRepository { +//! fulcio_certs: Some(vec![fulcio_cert.try_into().unwrap()]), +//! rekor_key: Some(rekor_pub_key), +//! ..Default::default() +//! }; //! //! let mut client = sigstore::cosign::ClientBuilder::default() //! .with_trust_repository(&repo) @@ -222,7 +224,7 @@ //! //! ## Fulcio and Rekor integration //! -//! [`cosign::Client`](crate::cosign::Client) integration with Fulcio and Rekor +//! [`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. diff --git a/src/tuf/trustroot.rs b/src/tuf/trustroot.rs index 61586a8ffa..aeb321fd92 100644 --- a/src/tuf/trustroot.rs +++ b/src/tuf/trustroot.rs @@ -13,6 +13,13 @@ use serde_with::serde_as; #[derive(Serialize, Deserialize, Debug, PartialEq)] #[allow(non_camel_case_types)] +/// Only a subset of the secure hash standard algorithms are supported. +/// See for more +/// details. +/// UNSPECIFIED SHOULD not be used, primary reason for inclusion is to force +/// any proto JSON serialization to emit the used hash algorithm, as default +/// option is to *omit* the default value of an enum (which is the first +/// value, represented by '0'. pub(crate) enum HashAlgorithm { HASH_ALGORITHM_UNSPECIFIED = 0, SHA2_256 = 1, @@ -20,6 +27,12 @@ pub(crate) enum HashAlgorithm { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[allow(non_camel_case_types)] +/// Details of a specific public key, capturing the the key encoding method, +/// and signature algorithm. +/// To avoid the possibility of contradicting formats such as PKCS1 with +/// ED25519 the valid permutations are listed as a linear set instead of a +/// cartesian set (i.e one combined variable instead of two, one for encoding +/// and one for the signature algorithm). pub(crate) enum PublicKeyDetails { PUBLIC_KEY_DETAILS_UNSPECIFIED = 0, // RSA @@ -37,6 +50,7 @@ pub(crate) enum PublicKeyDetails { #[serde_as] #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// LogId captures the identity of a transparency log. pub(crate) struct LogId { #[serde_as(as = "Base64")] pub key_id: Vec, @@ -44,6 +58,10 @@ pub(crate) struct LogId { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// The time range is closed and includes both the start and end times, +/// (i.e., [start, end]). +/// End is optional to be able to capture a period that has started but +/// has no known end. pub(crate) struct TimeRange { pub start: DateTime, pub end: Option>, @@ -76,12 +94,19 @@ pub(crate) struct X509Certificate { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// A chain of X.509 certificates. pub(crate) struct X509CertificateChain { pub certificates: Vec, } #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// TransparencyLogInstance describes the immutable parameters from a +/// transparency log. +/// See https://www.rfc-editor.org/rfc/rfc9162.html#name-log-parameters +/// for more details. +/// The included parameters are the minimal set required to identify a log, +/// and verify an inclusion proof/promise. pub(crate) struct TransparencyLogInstance { pub base_url: String, pub hash_algorithm: HashAlgorithm, @@ -91,6 +116,8 @@ pub(crate) struct TransparencyLogInstance { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// CertificateAuthority enlists the information required to identify which +/// CA to use and perform signature verification. pub(crate) struct CertificateAuthority { pub subject: DistinguishedName, pub uri: Option, @@ -100,6 +127,25 @@ pub(crate) struct CertificateAuthority { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] +/// TrustedRoot describes the client's complete set of trusted entities. +/// How the TrustedRoot is populated is not specified, but can be a +/// combination of many sources such as TUF repositories, files on disk etc. +/// +/// The TrustedRoot is not meant to be used for any artifact verification, only +/// to capture the complete/global set of trusted verification materials. +/// When verifying an artifact, based on the artifact and policies, a selection +/// of keys/authorities are expected to be extracted and provided to the +/// verification function. This way the set of keys/authorities can be kept to +/// a minimal set by the policy to gain better control over what signatures +/// that are allowed. +/// +/// The embedded transparency logs, CT logs, CAs and TSAs MUST include any +/// previously used instance -- otherwise signatures made in the past cannot +/// be verified. +/// The currently used instances MUST NOT have their 'end' timestamp set in +/// their 'valid_for' attribute for easy identification. +/// All the listed instances SHOULD be sorted by the 'valid_for' in ascending +/// order, that is, the oldest instance first and the current instance last. pub(crate) struct TrustedRoot { pub media_type: String, pub tlogs: Vec, From 2f8150d0f7a1c308564ad28d38e0891200f49412 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Mon, 13 Nov 2023 12:34:29 -0500 Subject: [PATCH 053/161] cosign/tuf: PR feedback Signed-off-by: Jack Leightcap --- Cargo.toml | 2 +- examples/cosign/verify/main.rs | 2 +- src/cosign/client_builder.rs | 5 ++--- src/cosign/mod.rs | 4 ++-- src/lib.rs | 2 +- src/tuf/mod.rs | 9 +++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b91f598203..704975e0b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,7 +118,7 @@ tokio = { version = "1.17.0", features = ["rt"] } tough = { version = "0.14", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } +x509-cert = { version = "0.2.2", features = ["pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" rustls-webpki = { version = "0.102.0-alpha.4", features = ["alloc"] } diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 60369cf094..fc687ba627 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -240,7 +240,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -36,7 +36,7 @@ use crate::tuf::Repository; /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via a [`crate::tuf::FakeRepository`]. +/// This can be provided via a [`crate::tuf::ManualRepository`]. /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -56,7 +56,6 @@ pub struct ClientBuilder<'a> { oci_client_config: ClientConfig, rekor_pub_key: Option<&'a [u8]>, fulcio_certs: Vec>, - // repo: Repository #[cfg(feature = "cached-client")] enable_registry_caching: bool, } diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 9ddcdcc813..50c9ce8c29 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -102,9 +102,9 @@ pub trait CosignCapabilities { /// must be satisfied: /// /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Rekor integration enabled (see [`crate::tuf::FakeRepository`]) + /// have been created with Rekor integration enabled (see [`crate::tuf::ManualRepository`]) /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Fulcio integration enabled (see [`crate::tuf::FakeRepository]) + /// have been created with Fulcio integration enabled (see [`crate::tuf::ManualRepository]) /// * The layer must include a bundle produced by Rekor /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods diff --git a/src/lib.rs b/src/lib.rs index 30475140a1..afea5441a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,7 @@ //! data: fulcio_cert_data //! }; //! -//! let mut repo = sigstore::tuf::FakeRepository { +//! let mut repo = sigstore::tuf::ManualRepository { //! fulcio_certs: Some(vec![fulcio_cert.try_into().unwrap()]), //! rekor_key: Some(rekor_pub_key), //! ..Default::default() diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index a0f7a84c80..4df3c757c2 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -43,6 +43,7 @@ mod trustroot; use rustls_pki_types::CertificateDer; use sha2::{Digest, Sha256}; use tough::TargetName; +use tracing::debug; use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; @@ -54,15 +55,15 @@ pub trait Repository { fn rekor_keys(&self) -> Result>; } -/// A `FakeRepository` is a [Repository] with out-of-band trust materials. +/// A `ManualRepository` is a [Repository] with out-of-band trust materials. /// As it does not establish a trust root with TUF, users must initialize its materials themselves. #[derive(Debug, Default)] -pub struct FakeRepository<'a> { +pub struct ManualRepository<'a> { pub fulcio_certs: Option>>, pub rekor_key: Option>, } -impl Repository for FakeRepository<'_> { +impl Repository for ManualRepository<'_> { fn fulcio_certs(&self) -> Result> { Ok(match &self.fulcio_certs { Some(certs) => certs.clone(), @@ -120,7 +121,7 @@ impl SigstoreRepository { local_path.as_ref(), )?; - println!("data:\n{}", String::from_utf8_lossy(&data)); + debug!("data:\n{}", String::from_utf8_lossy(&data)); Ok(serde_json::from_slice(&data[..])?) } From 34c31175c58ee6a0c05c587e4b072680424a0613 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 1 Dec 2023 10:59:15 +0100 Subject: [PATCH 054/161] docs: fix broken links Signed-off-by: Flavio Castelli --- src/cosign/mod.rs | 2 +- src/cosign/signature_layers.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 50c9ce8c29..b6807d6db8 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -253,7 +253,7 @@ where /// [`SigstoreApplicationConstraintsError`] /// which contains a vector of references to unapplied constraints. /// -/// See the documentation of the [`cosign::sign_constraint`](crate::cosign::sign_constraint) module for more +/// See the documentation of the [`cosign::constraint`](crate::cosign::constraint) module for more /// details about how to define constraints. pub fn apply_constraints<'a, 'b, I>( signature_layer: &'a mut SignatureLayer, diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index b26fcbc5f0..ec11eb9bea 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -98,8 +98,7 @@ pub enum CertificateSubject { Uri(String), } -/// Object that contains all the data about a -/// [`SimpleSigning`](crate::simple_signing::SimpleSigning) object. +/// Object that contains all the data about a `SimpleSigning` object. /// /// The struct provides some helper methods that can be used at verification /// time. From 0ae02e507432ff42eba506f3c3d1d759635501ef Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 1 Dec 2023 10:59:36 +0100 Subject: [PATCH 055/161] deps: update to latest stable versions Update webpki and chrono crates to latest stable versions. Adapt the code to reflect the changes introduced by the latest versions. Signed-off-by: Flavio Castelli --- Cargo.toml | 6 +++--- src/crypto/certificate_pool.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 704975e0b0..1b047cd47b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,14 +121,14 @@ url = "2.2.2" x509-cert = { version = "0.2.2", features = ["pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" -rustls-webpki = { version = "0.102.0-alpha.4", features = ["alloc"] } -rustls-pki-types = { version = "0.2.1", features = ["std"] } +rustls-webpki = { version = "0.102.0", features = ["alloc"] } +rustls-pki-types = { version = "1.0.0", features = ["std"] } serde_repr = "0.1.16" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } assert-json-diff = "2.0.2" -chrono = "0.4.27" +chrono = "0.4.31" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index f7f702f804..0f8b247cd7 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -39,7 +39,7 @@ impl<'a> CertificatePool<'a> { Ok(CertificatePool { trusted_roots: trusted_roots .into_iter() - .map(|x| Ok(webpki::extract_trust_anchor(&x)?.to_owned())) + .map(|x| Ok(webpki::anchor_from_trusted_cert(&x)?.to_owned())) .collect::, webpki::Error>>()?, intermediates: untrusted_intermediates.into_iter().collect(), }) From 6a2d9604caa1fcf91bda6d68df9004f7c31b0f8d Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 1 Dec 2023 11:02:56 +0100 Subject: [PATCH 056/161] deps: remove picky-rs dependency This is no longer needed since we've moved to rustls-webpki This closese https://github.com/sigstore/sigstore-rs/issues/308 Signed-off-by: Flavio Castelli --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1b047cd47b..3f72662e11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,9 +89,6 @@ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" pem = "3.0" -picky = { version = "7.0.0-rc.8", default-features = false, features = [ - "x509", -] } pkcs1 = { version = "0.7.5", features = ["std"] } pkcs8 = { version = "0.10.2", features = [ "pem", From 81a9a805afc0d056e8abe6bb275179da78d7d349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:40:48 +0000 Subject: [PATCH 057/161] chore(deps): Update oci-distribution requirement from 0.9 to 0.10 Updates the requirements on [oci-distribution](https://github.com/krustlet/oci-distribution) to permit the latest version. - [Release notes](https://github.com/krustlet/oci-distribution/releases) - [Commits](https://github.com/krustlet/oci-distribution/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: oci-distribution dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3f72662e11..fe3085ddf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ ed25519 = { version = "2.2.1", features = ["alloc"] } ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } lazy_static = "1.4.0" -oci-distribution = { version = "0.9", default-features = false, optional = true } +oci-distribution = { version = "0.10", default-features = false, optional = true } olpc-cjson = "0.1" openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", From a980e1e11c1106ea975fe838332e9f5ac2ddfdae Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Thu, 30 Nov 2023 09:25:18 +0000 Subject: [PATCH 058/161] Release 0.8.0 What's Changed ============== * chore(deps): Update rstest requirement from 0.17.0 to 0.18.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/282 * chore(deps): do not enable default features of chrono by @flavio in https://github.com/sigstore/sigstore-rs/pull/286 * chore(deps): Update pem requirement from 2.0 to 3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/289 * conformance: add conformance CLI and action by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/287 * chore: fix clippy warnings by @flavio in https://github.com/sigstore/sigstore-rs/pull/292 * chore(deps): Bump actions/checkout from 3.5.3 to 3.6.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/291 * chore(deps): Update tough requirement from 0.13 to 0.14 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/290 * chore(deps): update to latest version of picky by @flavio in https://github.com/sigstore/sigstore-rs/pull/293 * chore(deps): Bump actions/checkout from 3.6.0 to 4.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/294 * chore: add repository link to Cargo metadata by @flavio in https://github.com/sigstore/sigstore-rs/pull/297 * chore(deps): Update cached requirement from 0.44.0 to 0.45.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/298 * chore(deps): Bump actions/checkout from 4.0.0 to 4.1.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/302 * chore(deps): Update cached requirement from 0.45.1 to 0.46.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/301 * chore(deps): Update testcontainers requirement from 0.14 to 0.15 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/303 * chore(deps): Bump actions/checkout from 4.1.0 to 4.1.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/304 * cosign/tuf: use trustroot by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/305 * Fix broken tests, update deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/313 New Contributors ================ * @jleightcap made their first contribution in https://github.com/sigstore/sigstore-rs/pull/287 **Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.2...v0.8.0 Signed-off-by: Luke Hinds Co-authored-by: Flavio Castelli --- CHANGELOG.md | 312 +++++++++++++++++++++++++++++---------------------- Cargo.toml | 2 +- 2 files changed, 177 insertions(+), 137 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f4c42268..8a77aba2c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,211 +1,251 @@ +# v0.8.0 + +## What's Changed + +- chore(deps): Update rstest requirement from 0.17.0 to 0.18.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/282 +- chore(deps): do not enable default features of chrono by @flavio in https://github.com/sigstore/sigstore-rs/pull/286 +- chore(deps): Update pem requirement from 2.0 to 3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/289 +- conformance: add conformance CLI and action by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/287 +- chore: fix clippy warnings by @flavio in https://github.com/sigstore/sigstore-rs/pull/292 +- chore(deps): Bump actions/checkout from 3.5.3 to 3.6.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/291 +- chore(deps): Update tough requirement from 0.13 to 0.14 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/290 +- chore(deps): update to latest version of picky by @flavio in https://github.com/sigstore/sigstore-rs/pull/293 +- chore(deps): Bump actions/checkout from 3.6.0 to 4.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/294 +- chore: add repository link to Cargo metadata by @flavio in https://github.com/sigstore/sigstore-rs/pull/297 +- chore(deps): Update cached requirement from 0.44.0 to 0.45.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/298 +- chore(deps): Bump actions/checkout from 4.0.0 to 4.1.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/302 +- chore(deps): Update cached requirement from 0.45.1 to 0.46.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/301 +- chore(deps): Update testcontainers requirement from 0.14 to 0.15 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/303 +- chore(deps): Bump actions/checkout from 4.1.0 to 4.1.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/304 +- cosign/tuf: use trustroot by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/305 +- Fix broken tests, update deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/313 + +## New Contributors + +- @jleightcap made their first contribution in https://github.com/sigstore/sigstore-rs/pull/287 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.2...v0.8.0 + # v0.7.2 ## What's Changed -* chore(deps): Update cached requirement from 0.42.0 to 0.44.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/277 -* chore(deps): Bump actions/checkout from 3.5.2 to 3.5.3 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/278 -* chore(deps): update picky dependency by @flavio in https://github.com/sigstore/sigstore-rs/pull/279 + +- chore(deps): Update cached requirement from 0.42.0 to 0.44.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/277 +- chore(deps): Bump actions/checkout from 3.5.2 to 3.5.3 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/278 +- chore(deps): update picky dependency by @flavio in https://github.com/sigstore/sigstore-rs/pull/279 **Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.1...v0.7.2 # v0.7.1 ## What's Changed -* fix: ensure cosign client can be sent between threads by @flavio in https://github.com/sigstore/sigstore-rs/pull/275 + +- fix: ensure cosign client can be sent between threads by @flavio in https://github.com/sigstore/sigstore-rs/pull/275 **Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.7.0...v0.7.1 # v0.7.0 ## What's Changed -* Fix typo in SignatureLayer::new doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/170 -* feat: replace example dependency docker_credential by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/172 -* Clean up readme by @lukehinds in https://github.com/sigstore/sigstore-rs/pull/173 -* chore(deps): Update rstest requirement from 0.15.0 to 0.16.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/174 -* Fix typo in simple_signing.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/175 -* Introduce SignedArtifactBundle by @danbev in https://github.com/sigstore/sigstore-rs/pull/171 -* chore(deps): Update base64 requirement from 0.13.0 to 0.20.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/177 -* chore(deps): Bump actions/checkout from 3.1.0 to 3.2.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/180 -* chore(deps): Update serial_test requirement from 0.9.0 to 0.10.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/182 -* chore(deps): Update cached requirement from 0.40.0 to 0.41.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/181 -* Fix typo in SecretBoxCipher doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/179 -* chore(deps): Update cached requirement from 0.41.0 to 0.42.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/185 -* chore(deps): Bump actions/checkout from 3.2.0 to 3.3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/183 -* chore(deps): Update base64 requirement from 0.20.0 to 0.21.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/184 -* Add cosign verify-bundle example by @danbev in https://github.com/sigstore/sigstore-rs/pull/186 -* Fix incorrect base64_signature doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/188 -* Fix typos in tuf/mod.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/195 -* chore(deps): Update serial_test requirement from 0.10.0 to 1.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/200 -* fix: show actual response status field by @ctron in https://github.com/sigstore/sigstore-rs/pull/197 -* Update target -> target_name for consistency by @danbev in https://github.com/sigstore/sigstore-rs/pull/196 -* fix: make the fields accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/202 -* Add verify-bundle example to README.md by @danbev in https://github.com/sigstore/sigstore-rs/pull/203 -* fix: make fields of hash accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/205 -* Improve public key output and add file output by @Gronner in https://github.com/sigstore/sigstore-rs/pull/194 -* Add TokenProvider::Static doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/208 -* Changed the type of LogEntry.body from String to Body by @Neccolini in https://github.com/sigstore/sigstore-rs/pull/207 -* Fix errors/warnings reported by clippy by @danbev in https://github.com/sigstore/sigstore-rs/pull/210 -* Add fine-grained features to control the compilation by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/189 -* fix: bring tuf feature out of rekor and add related docs by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/211 -* chore: update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/204 -* Replace `x509-parser` with `x509-cert` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/212 -* Fix: Wrong parameter order inside documentation example. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/215 -* Remove lines about timestamp in lib.rs by @naveensrinivasan in https://github.com/sigstore/sigstore-rs/pull/213 -* Fix ed25519 version conflict by @vembacher in https://github.com/sigstore/sigstore-rs/pull/223 -* Support compiling to wasm32 architectures by @lulf in https://github.com/sigstore/sigstore-rs/pull/221 -* Fix link to contributor doc in readme by @oliviacrain in https://github.com/sigstore/sigstore-rs/pull/225 -* refactor: derive `Clone` trait by @flavio in https://gitub.com/sigstore/sigstore-rs/pull/227 -* fix: correct typo in verify/main.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/228 -* chore(deps): Update tough requirement from 0.12 to 0.13 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/237 -* chore(deps): Bump actions/checkout from 3.3.0 to 3.4.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/240 -* dep: update picky version to git rid of `ring` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/226 -* chore(deps): Bump actions/checkout from 3.4.0 to 3.5.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/245 -* fix: make LogEntry Body an enum by @danbev in https://github.com/sigstore/sigstore-rs/pull/244 -* Add verify-blob example by @danbev in https://github.com/sigstore/sigstore-rs/pull/239 -* Introduce Newtype `OciReference` into API for OCI image references. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/216 -* Swap over to using CDN to fetch TUF metadata by @haydentherapper in https://github.com/sigstore/sigstore-rs/pull/251 -* chore(deps): Bump actions/checkout from 3.5.0 to 3.5.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/252 -* upgrade 'der' to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/257 -* remove unused 'clock' feature for chrono by @dmitris in https://github.com/sigstore/sigstore-rs/pull/258 -* update pkcs1 from 0.4.0 to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/260 -* use 2021 Rust edition by @dmitris in https://github.com/sigstore/sigstore-rs/pull/261 -* chore(deps): Update serial_test requirement from 1.0.0 to 2.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/264 -* update scrypt to 0.11.0, adapt for API change (fix #231) by @dmitris in https://github.com/sigstore/sigstore-rs/pull/268 -* upgrade ed25519-dalek to 2.0.0-rc.2 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/263 -* chore(deps): Update openidconnect requirement from 2.3 to 3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/265 -* chore(deps): Update rstest requirement from 0.16.0 to 0.17.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/271 -* Update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/269 -* Update create_log_entry example to create key pair. by @jvanz in https://github.com/sigstore/sigstore-rs/pull/206 + +- Fix typo in SignatureLayer::new doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/170 +- feat: replace example dependency docker_credential by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/172 +- Clean up readme by @lukehinds in https://github.com/sigstore/sigstore-rs/pull/173 +- chore(deps): Update rstest requirement from 0.15.0 to 0.16.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/174 +- Fix typo in simple_signing.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/175 +- Introduce SignedArtifactBundle by @danbev in https://github.com/sigstore/sigstore-rs/pull/171 +- chore(deps): Update base64 requirement from 0.13.0 to 0.20.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/177 +- chore(deps): Bump actions/checkout from 3.1.0 to 3.2.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/180 +- chore(deps): Update serial_test requirement from 0.9.0 to 0.10.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/182 +- chore(deps): Update cached requirement from 0.40.0 to 0.41.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/181 +- Fix typo in SecretBoxCipher doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/179 +- chore(deps): Update cached requirement from 0.41.0 to 0.42.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/185 +- chore(deps): Bump actions/checkout from 3.2.0 to 3.3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/183 +- chore(deps): Update base64 requirement from 0.20.0 to 0.21.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/184 +- Add cosign verify-bundle example by @danbev in https://github.com/sigstore/sigstore-rs/pull/186 +- Fix incorrect base64_signature doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/188 +- Fix typos in tuf/mod.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/195 +- chore(deps): Update serial_test requirement from 0.10.0 to 1.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/200 +- fix: show actual response status field by @ctron in https://github.com/sigstore/sigstore-rs/pull/197 +- Update target -> target_name for consistency by @danbev in https://github.com/sigstore/sigstore-rs/pull/196 +- fix: make the fields accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/202 +- Add verify-bundle example to README.md by @danbev in https://github.com/sigstore/sigstore-rs/pull/203 +- fix: make fields of hash accessible by @ctron in https://github.com/sigstore/sigstore-rs/pull/205 +- Improve public key output and add file output by @Gronner in https://github.com/sigstore/sigstore-rs/pull/194 +- Add TokenProvider::Static doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/208 +- Changed the type of LogEntry.body from String to Body by @Neccolini in https://github.com/sigstore/sigstore-rs/pull/207 +- Fix errors/warnings reported by clippy by @danbev in https://github.com/sigstore/sigstore-rs/pull/210 +- Add fine-grained features to control the compilation by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/189 +- fix: bring tuf feature out of rekor and add related docs by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/211 +- chore: update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/204 +- Replace `x509-parser` with `x509-cert` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/212 +- Fix: Wrong parameter order inside documentation example. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/215 +- Remove lines about timestamp in lib.rs by @naveensrinivasan in https://github.com/sigstore/sigstore-rs/pull/213 +- Fix ed25519 version conflict by @vembacher in https://github.com/sigstore/sigstore-rs/pull/223 +- Support compiling to wasm32 architectures by @lulf in https://github.com/sigstore/sigstore-rs/pull/221 +- Fix link to contributor doc in readme by @oliviacrain in https://github.com/sigstore/sigstore-rs/pull/225 +- refactor: derive `Clone` trait by @flavio in https://gitub.com/sigstore/sigstore-rs/pull/227 +- fix: correct typo in verify/main.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/228 +- chore(deps): Update tough requirement from 0.12 to 0.13 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/237 +- chore(deps): Bump actions/checkout from 3.3.0 to 3.4.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/240 +- dep: update picky version to git rid of `ring` by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/226 +- chore(deps): Bump actions/checkout from 3.4.0 to 3.5.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/245 +- fix: make LogEntry Body an enum by @danbev in https://github.com/sigstore/sigstore-rs/pull/244 +- Add verify-blob example by @danbev in https://github.com/sigstore/sigstore-rs/pull/239 +- Introduce Newtype `OciReference` into API for OCI image references. by @vembacher in https://github.com/sigstore/sigstore-rs/pull/216 +- Swap over to using CDN to fetch TUF metadata by @haydentherapper in https://github.com/sigstore/sigstore-rs/pull/251 +- chore(deps): Bump actions/checkout from 3.5.0 to 3.5.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/252 +- upgrade 'der' to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/257 +- remove unused 'clock' feature for chrono by @dmitris in https://github.com/sigstore/sigstore-rs/pull/258 +- update pkcs1 from 0.4.0 to 0.7.5 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/260 +- use 2021 Rust edition by @dmitris in https://github.com/sigstore/sigstore-rs/pull/261 +- chore(deps): Update serial_test requirement from 1.0.0 to 2.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/264 +- update scrypt to 0.11.0, adapt for API change (fix #231) by @dmitris in https://github.com/sigstore/sigstore-rs/pull/268 +- upgrade ed25519-dalek to 2.0.0-rc.2 by @dmitris in https://github.com/sigstore/sigstore-rs/pull/263 +- chore(deps): Update openidconnect requirement from 2.3 to 3.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/265 +- chore(deps): Update rstest requirement from 0.16.0 to 0.17.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/271 +- Update crypto deps by @flavio in https://github.com/sigstore/sigstore-rs/pull/269 +- Update create_log_entry example to create key pair. by @jvanz in https://github.com/sigstore/sigstore-rs/pull/206 ## New Contributors -* @ctron made their first contribution in https://github.com/sigstore/sigstore-rs/pull/197 -* @Gronner made their first contribution in https://github.com/sigstore/sigstore-rs/pull/194 -* @Neccolini made their first contribution in https://github.com/sigstore/sigstore-rs/pull/207 -* @vembacher made their first contribution in https://github.com/sigstore/sigstore-rs/pull/215 -* @naveensrinivasan made their first contribution in https://github.com/sigstore/sigstore-rs/pull/213 -* @lulf made their first contribution in https://github.com/sigstore/sigstore-rs/pull/221 -* @oliviacrain made their first contribution in https://github.com/sigstore/sigstore-rs/pull/225 -* @haydentherapper made their first contribution in https://github.com/sigstore/sigstore-rs/pull/251 -* @dmitris made their first contribution in https://github.com/sigstore/sigstore-rs/pull/257 -* @jvanz made their first contribution in https://github.com/sigstore/sigstore-rs/pull/206 -**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.6.0...v0.7.0h +- @ctron made their first contribution in https://github.com/sigstore/sigstore-rs/pull/197 +- @Gronner made their first contribution in https://github.com/sigstore/sigstore-rs/pull/194 +- @Neccolini made their first contribution in https://github.com/sigstore/sigstore-rs/pull/207 +- @vembacher made their first contribution in https://github.com/sigstore/sigstore-rs/pull/215 +- @naveensrinivasan made their first contribution in https://github.com/sigstore/sigstore-rs/pull/213 +- @lulf made their first contribution in https://github.com/sigstore/sigstore-rs/pull/221 +- @oliviacrain made their first contribution in https://github.com/sigstore/sigstore-rs/pull/225 +- @haydentherapper made their first contribution in https://github.com/sigstore/sigstore-rs/pull/251 +- @dmitris made their first contribution in https://github.com/sigstore/sigstore-rs/pull/257 +- @jvanz made their first contribution in https://github.com/sigstore/sigstore-rs/pull/206 +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.6.0...v0.7.0h # v0.6.0 ## Fixes -* Fix typo in cosign/mod.rs doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/148 -* Fix typo in KeyPair trait doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/149 -* Update cached requirement from 0.39.0 to 0.40.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/154 -* Fix typos in PublicKeyVerifier doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/155 -* Fix: CI error for auto deref by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/160 -* Fix typo and grammar in signature_layers.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/161 -* Remove unused imports in examples/rekor by @danbev in https://github.com/sigstore/sigstore-rs/pull/162 -* Update link to verification example by @danbev in https://github.com/sigstore/sigstore-rs/pull/156 -* Fix typos in from_encrypted_pem doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/164 -* Fix typos in doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/163 -* Update path to fulcio-cert in verify example by @danbev in https://github.com/sigstore/sigstore-rs/pull/168 + +- Fix typo in cosign/mod.rs doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/148 +- Fix typo in KeyPair trait doc comment by @danbev in https://github.com/sigstore/sigstore-rs/pull/149 +- Update cached requirement from 0.39.0 to 0.40.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/154 +- Fix typos in PublicKeyVerifier doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/155 +- Fix: CI error for auto deref by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/160 +- Fix typo and grammar in signature_layers.rs by @danbev in https://github.com/sigstore/sigstore-rs/pull/161 +- Remove unused imports in examples/rekor by @danbev in https://github.com/sigstore/sigstore-rs/pull/162 +- Update link to verification example by @danbev in https://github.com/sigstore/sigstore-rs/pull/156 +- Fix typos in from_encrypted_pem doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/164 +- Fix typos in doc comments by @danbev in https://github.com/sigstore/sigstore-rs/pull/163 +- Update path to fulcio-cert in verify example by @danbev in https://github.com/sigstore/sigstore-rs/pull/168 ## Enhancements -* Add getter functions for LogEntry fields by @lkatalin in https://github.com/sigstore/sigstore-rs/pull/147 -* Add TreeSize alias to Rekor by @avery-blanchard in https://github.com/sigstore/sigstore-rs/pull/151 -* Updates for parsing hashedrekord LogEntry by @lkatalin in https://github.com/sigstore/sigstore-rs/pull/152 -* Add certificate based verification by @flavio in https://github.com/sigstore/sigstore-rs/pull/159 -* Add support for OCI Image signing (spec v1.0) by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/158 + +- Add getter functions for LogEntry fields by @lkatalin in https://github.com/sigstore/sigstore-rs/pull/147 +- Add TreeSize alias to Rekor by @avery-blanchard in https://github.com/sigstore/sigstore-rs/pull/151 +- Updates for parsing hashedrekord LogEntry by @lkatalin in https://github.com/sigstore/sigstore-rs/pull/152 +- Add certificate based verification by @flavio in https://github.com/sigstore/sigstore-rs/pull/159 +- Add support for OCI Image signing (spec v1.0) by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/158 + ## Contributors -* Avery Blanchard (@avery-blanchardmade) -* Daniel Bevenius (@danbev) -* Flavio Castelli (@flavio) -* Lily Sturmann (@lkatalin) -* Xynnn (@Xynnn007) + +- Avery Blanchard (@avery-blanchardmade) +- Daniel Bevenius (@danbev) +- Flavio Castelli (@flavio) +- Lily Sturmann (@lkatalin) +- Xynnn (@Xynnn007) # v0.5.3 ## Fixes -* rustls should not require openssl by (https://github.com/sigstore/sigstore-rs/pull/146) +- rustls should not require openssl by (https://github.com/sigstore/sigstore-rs/pull/146) ## Others -* Rework Rekor module structure and enable doc tests (https://github.com/sigstore/sigstore-rs/pull/145) + +- Rework Rekor module structure and enable doc tests (https://github.com/sigstore/sigstore-rs/pull/145) ## Contributors -* Flavio Castelli (@flavio) -* Lily Sturmann (@lkatalin) + +- Flavio Castelli (@flavio) +- Lily Sturmann (@lkatalin) # v0.5.2 ## Fixes -* Address compilation error (https://github.com/sigstore/sigstore-rs/pull/143) +- Address compilation error (https://github.com/sigstore/sigstore-rs/pull/143) ## Contributors -* Flavio Castelli (@flavio) + +- Flavio Castelli (@flavio) # v0.5.1 ## Fixes -* fix verification of signatures produced with PKI11 (https://github.com/sigstore/sigstore-rs/pull/142) +- fix verification of signatures produced with PKI11 (https://github.com/sigstore/sigstore-rs/pull/142) ## Others -* Update rsa dependency to stable version 0.7.0 (https://github.com/sigstore/sigstore-rs/pull/141) -* Bump actions/checkout from 3.0.2 to 3.1.0 (https://github.com/sigstore/sigstore-rs/pull/140) +- Update rsa dependency to stable version 0.7.0 (https://github.com/sigstore/sigstore-rs/pull/141) +- Bump actions/checkout from 3.0.2 to 3.1.0 (https://github.com/sigstore/sigstore-rs/pull/140) ## Contributors -* Flavio Castelli (@flavio) -* Xynnn (@Xynnn007) + +- Flavio Castelli (@flavio) +- Xynnn (@Xynnn007) # v0.5.0 ## Enhancements -* update user-agent value to be specific to sigstore-rs (https://github.com/sigstore/sigstore-rs/pull/122) -* remove /api/v1/version from client by (https://github.com/sigstore/sigstore-rs/pull/121) -* crate async fulcio client (https://github.com/sigstore/sigstore-rs/pull/132) -* Removed ring dependency (https://github.com/sigstore/sigstore-rs/pull/127) + +- update user-agent value to be specific to sigstore-rs (https://github.com/sigstore/sigstore-rs/pull/122) +- remove /api/v1/version from client by (https://github.com/sigstore/sigstore-rs/pull/121) +- crate async fulcio client (https://github.com/sigstore/sigstore-rs/pull/132) +- Removed ring dependency (https://github.com/sigstore/sigstore-rs/pull/127) ## Others -* Update dependencies -* Refactoring and examples for key interface (https://github.com/sigstore/sigstore-rs/pull/123) -* Fix doc test failures (https://github.com/sigstore/sigstore-rs/pull/136) +- Update dependencies +- Refactoring and examples for key interface (https://github.com/sigstore/sigstore-rs/pull/123) +- Fix doc test failures (https://github.com/sigstore/sigstore-rs/pull/136) ## Contributors -* Bob Callaway (@bobcallaway) -* Bob McWhirter (@bobmcwhirter) -* Flavio Castelli (@flavio) -* Luke Hinds (@lukehinds) -* Xynnn (@Xynnn007) + +- Bob Callaway (@bobcallaway) +- Bob McWhirter (@bobmcwhirter) +- Flavio Castelli (@flavio) +- Luke Hinds (@lukehinds) +- Xynnn (@Xynnn007) # v0.4.0 ## Enhancements -* feat: from and to interface for signing and verification keys (https://github.com/sigstore/sigstore-rs/pulls/115) -* Refactor examples to support subfolder execution (https://github.com/sigstore/sigstore-rs/pulls/111) -* Integrate Rekor with Sigstore-rs (https://github.com/sigstore/sigstore-rs/pulls/88) -* feat: add example case and docs for key interface (https://github.com/sigstore/sigstore-rs/pulls/99) -* feat: add signing key module (https://github.com/sigstore/sigstore-rs/pulls/87) +- feat: from and to interface for signing and verification keys (https://github.com/sigstore/sigstore-rs/pulls/115) +- Refactor examples to support subfolder execution (https://github.com/sigstore/sigstore-rs/pulls/111) +- Integrate Rekor with Sigstore-rs (https://github.com/sigstore/sigstore-rs/pulls/88) +- feat: add example case and docs for key interface (https://github.com/sigstore/sigstore-rs/pulls/99) +- feat: add signing key module (https://github.com/sigstore/sigstore-rs/pulls/87) ## Documention -* Update readme to include new features (https://github.com/sigstore/sigstore-rs/pulls/113) +- Update readme to include new features (https://github.com/sigstore/sigstore-rs/pulls/113) ## Others -* bump crate version (https://github.com/sigstore/sigstore-rs/pulls/118) -* Add RUSTSEC-2021-0139 to audit.toml (https://github.com/sigstore/sigstore-rs/pulls/112) -* Update xsalsa20poly1305 requirement from 0.7.1 to 0.9.0 (https://github.com/sigstore/sigstore-rs/pulls/101) -* ignore derive_partial_eq_without_eq (https://github.com/sigstore/sigstore-rs/pulls/102) -* fix clippy lints (https://github.com/sigstore/sigstore-rs/pulls/98) - +- bump crate version (https://github.com/sigstore/sigstore-rs/pulls/118) +- Add RUSTSEC-2021-0139 to audit.toml (https://github.com/sigstore/sigstore-rs/pulls/112) +- Update xsalsa20poly1305 requirement from 0.7.1 to 0.9.0 (https://github.com/sigstore/sigstore-rs/pulls/101) +- ignore derive_partial_eq_without_eq (https://github.com/sigstore/sigstore-rs/pulls/102) +- fix clippy lints (https://github.com/sigstore/sigstore-rs/pulls/98) ## Contributors -* Carlos Tadeu Panato Junior (@cpanato) -* Flavio Castelli (@flavio) -* Jyotsna (@jyotsna-penumaka) -* Lily Sturmann (@lkatalin) -* Luke Hinds (@lukehinds) -* Tony Arcieri (@tarcieri) -* Xynnn_ (@Xynnn007) +- Carlos Tadeu Panato Junior (@cpanato) +- Flavio Castelli (@flavio) +- Jyotsna (@jyotsna-penumaka) +- Lily Sturmann (@lkatalin) +- Luke Hinds (@lukehinds) +- Tony Arcieri (@tarcieri) +- Xynnn\_ (@Xynnn007) diff --git a/Cargo.toml b/Cargo.toml index fe3085ddf5..4af00c75f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.7.2" +version = "0.8.0" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" From ea5d69bfeeafaa1ce3edd8de1cdaed8624aa2bf3 Mon Sep 17 00:00:00 2001 From: Jack Leightcap <30168080+jleightcap@users.noreply.github.com> Date: Thu, 21 Dec 2023 03:34:51 -0500 Subject: [PATCH 059/161] sign: init (#310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ability to sign artifacts à la sigstore-python, towards Bundle signing and verification. Follow-up from #305, and the prerequisite motivation to #311. Signed-off-by: Jack Leightcap Signed-off-by: Andrew Pan Signed-off-by: Andrew Pan <3821575+tnytown@users.noreply.github.com> Signed-off-by: Jack Leightcap <30168080+jleightcap@users.noreply.github.com> Co-authored-by: Andrew Pan Co-authored-by: Andrew Pan <3821575+tnytown@users.noreply.github.com> Co-authored-by: Andrew Pan Co-authored-by: Flavio Castelli --- Cargo.toml | 17 +- src/bundle/mod.rs | 35 +++ src/crypto/certificate_pool.rs | 2 +- src/errors.rs | 38 ++- src/fulcio/mod.rs | 90 ++++++- src/fulcio/models.rs | 116 +++++++++ src/lib.rs | 7 + src/oauth/mod.rs | 3 + src/oauth/token.rs | 106 ++++++++ src/rekor/models/log_entry.rs | 23 +- src/sign.rs | 376 +++++++++++++++++++++++++++++ src/tuf/mod.rs | 4 +- src/tuf/repository_helper.rs | 428 +++++++++++++++++++-------------- 13 files changed, 1054 insertions(+), 191 deletions(-) create mode 100644 src/bundle/mod.rs create mode 100644 src/fulcio/models.rs create mode 100644 src/oauth/token.rs create mode 100644 src/sign.rs diff --git a/Cargo.toml b/Cargo.toml index 4af00c75f3..6a6422c2e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "tuf"] +default = ["full-native-tls", "cached-client", "tuf", "sign"] wasm = ["getrandom/js"] full-native-tls = [ @@ -42,6 +42,8 @@ rekor = ["reqwest"] tuf = ["tough", "regex"] +sign = [] + cosign-native-tls = [ "oci-distribution/native-tls", "cert", @@ -72,7 +74,7 @@ async-trait = "0.1.52" base64 = "0.21.0" cached = { version = "0.46.0", optional = true, features = ["async"] } cfg-if = "1.0.0" -chrono = { version = "0.4.27", default-features = false } +chrono = { version = "0.4.27", default-features = false, features = ["serde"] } const-oid = "0.9.1" digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } @@ -88,7 +90,7 @@ openidconnect = { version = "3.0", default-features = false, features = [ p256 = "0.13.2" p384 = "0.13" webbrowser = "0.8.4" -pem = "3.0" +pem = { version = "3.0", features = ["serde"] } pkcs1 = { version = "0.7.5", features = ["std"] } pkcs8 = { version = "0.10.2", features = [ "pem", @@ -110,17 +112,21 @@ serde_json = "1.0.79" serde_with = { version = "3.4.0", features = ["base64"] } sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } +sigstore_protobuf_specs = "0.1.0-rc.2" thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } +tokio-util = { version = "0.7.10", features = ["io-util"] } tough = { version = "0.14", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.2.2", features = ["pem", "std"] } +x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" -rustls-webpki = { version = "0.102.0", features = ["alloc"] } +rustls-webpki = { version = "0.102.0-alpha.7", features = ["alloc"] } rustls-pki-types = { version = "1.0.0", features = ["std"] } serde_repr = "0.1.16" +hex = "0.4.3" +json-syntax = { version = "0.9.6", features = ["canonicalize", "serde"] } [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } @@ -134,7 +140,6 @@ serial_test = "2.0.0" tempfile = "3.3.0" testcontainers = "0.15" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } -hex = "0.4.3" # cosign example mappings diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs new file mode 100644 index 0000000000..2b9b9cb73c --- /dev/null +++ b/src/bundle/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2023 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. + +//! Useful types for Sigstore bundles. + +use std::fmt::Display; + +pub use sigstore_protobuf_specs::Bundle; + +// Known Sigstore bundle media types. +#[derive(Clone, Copy, Debug)] +pub enum Version { + _Bundle0_1, + Bundle0_2, +} + +impl Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match &self { + Version::_Bundle0_1 => "application/vnd.dev.sigstore.bundle+json;version=0.1", + Version::Bundle0_2 => "application/vnd.dev.sigstore.bundle+json;version=0.2", + }) + } +} diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index 0f8b247cd7..11ee756133 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -61,7 +61,7 @@ impl<'a> CertificatePool<'a> { let cert_pem = pem::parse(cert_pem)?; if cert_pem.tag() != "CERTIFICATE" { return Err(SigstoreError::CertificatePoolError( - "PEM file is not a certificate", + "PEM file is not a certificate".into(), )); } diff --git a/src/errors.rs b/src/errors.rs index 430066cc65..22b87e95cb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -52,6 +52,9 @@ pub enum SigstoreError { #[error("invalid key format: {error}")] InvalidKeyFormat { error: String }, + #[error("Unable to parse identity token: {0}")] + IdentityTokenError(String), + #[error("unmatched key type {key_typ} and signing scheme {scheme}")] UnmatchedKeyAndSigningScheme { key_typ: String, scheme: String }, @@ -70,6 +73,9 @@ pub enum SigstoreError { #[error("Public key verification error")] PublicKeyVerificationError, + #[error("X.509 certificate version is not V3")] + CertificateUnsupportedVersionError, + #[error("Certificate validity check failed: cannot be used before {0}")] CertificateValidityError(String), @@ -101,7 +107,13 @@ pub enum SigstoreError { CertificateWithIncompleteSubjectAlternativeName, #[error("Certificate pool error: {0}")] - CertificatePoolError(&'static str), + CertificatePoolError(String), + + #[error("Signing session expired")] + ExpiredSigningSession(), + + #[error("Fulcio request unsuccessful: {0}")] + FulcioClientError(String), #[error("Cannot fetch manifest of {image}: {error}")] RegistryFetchManifestError { image: String, error: String }, @@ -115,9 +127,22 @@ pub enum SigstoreError { #[error("Cannot push {image}: {error}")] RegistryPushError { image: String, error: String }, + #[error("Rekor request unsuccessful: {0}")] + RekorClientError(String), + + #[error(transparent)] + JoinError(#[from] tokio::task::JoinError), + + #[cfg(feature = "sign")] + #[error(transparent)] + ReqwestError(#[from] reqwest::Error), + #[error("OCI reference not valid: {reference}")] OciReferenceNotValidError { reference: String }, + #[error("Sigstore bundle malformed: {0}")] + SigstoreBundleMalformedError(String), + #[error("Layer doesn't have Sigstore media type")] SigstoreMediaTypeNotFoundError, @@ -144,7 +169,7 @@ pub enum SigstoreError { TufTargetNotFoundError(String), #[error("{0}")] - TufMetadataError(&'static str), + TufMetadataError(String), #[error(transparent)] IOError(#[from] std::io::Error), @@ -155,6 +180,9 @@ pub enum SigstoreError { #[error("{0}")] VerificationConstraintError(String), + #[error("{0}")] + VerificationMaterialError(String), + #[error("{0}")] ApplyConstraintError(String), @@ -214,4 +242,10 @@ pub enum SigstoreError { #[error(transparent)] Ed25519PKCS8Error(#[from] ed25519_dalek::pkcs8::spki::Error), + + #[error(transparent)] + X509ParseError(#[from] x509_cert::der::Error), + + #[error(transparent)] + X509BuilderError(#[from] x509_cert::builder::Error), } diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 2570a15f15..81477cab6e 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -1,23 +1,33 @@ +mod models; + pub mod oauth; use crate::crypto::signing_key::SigStoreSigner; use crate::crypto::SigningScheme; use crate::errors::{Result, SigstoreError}; +use crate::fulcio::models::{CreateSigningCertificateRequest, SigningCertificate}; use crate::fulcio::oauth::OauthTokenProvider; +use crate::oauth::IdentityToken; use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use openidconnect::core::CoreIdToken; -use reqwest::Body; +use pkcs8::der::Decode; +use reqwest::{header, Body}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; +use tracing::debug; use url::Url; +use x509_cert::Certificate; + +pub use models::CertificateResponse; /// Default public Fulcio server root. pub const FULCIO_ROOT: &str = "https://fulcio.sigstore.dev/"; /// Path within Fulcio to obtain a signing certificate. pub const SIGNING_CERT_PATH: &str = "api/v1/signingCert"; +pub const SIGNING_CERT_V2_PATH: &str = "api/v2/signingCert"; const CONTENT_TYPE_HEADER_NAME: &str = "content-type"; @@ -191,4 +201,82 @@ impl FulcioClient { Ok((signer, FulcioCert(cert))) } + + /// Request a certificate from Fulcio with the V2 endpoint. + /// + /// TODO(tnytown): This (and other API clients) should be autogenerated. See sigstore-rs#209. + /// + /// https://github.com/sigstore/fulcio/blob/main/fulcio.proto + /// + /// Additionally, it might not be reasonable to expect callers to correctly construct and pass + /// in an X509 CSR. + pub async fn request_cert_v2( + &self, + request: x509_cert::request::CertReq, + identity: &IdentityToken, + ) -> Result { + let client = reqwest::Client::new(); + + macro_rules! headers { + ($($key:expr => $val:expr),+) => { + { + let mut map = reqwest::header::HeaderMap::new(); + $( map.insert($key, $val.parse().unwrap()); )+ + map + } + } + } + let headers = headers!( + header::AUTHORIZATION => format!("Bearer {}", identity.to_string()), + header::CONTENT_TYPE => "application/json", + header::ACCEPT => "application/pem-certificate-chain" + ); + + let response: SigningCertificate = client + .post(self.root_url.join(SIGNING_CERT_V2_PATH)?) + .headers(headers) + .json(&CreateSigningCertificateRequest { + certificate_signing_request: request, + }) + .send() + .await? + .json() + .await?; + + let sct_embedded = matches!( + response, + SigningCertificate::SignedCertificateEmbeddedSct(_) + ); + let certs = match response { + SigningCertificate::SignedCertificateDetachedSct(ref sc) => &sc.chain.certificates, + SigningCertificate::SignedCertificateEmbeddedSct(ref sc) => &sc.chain.certificates, + }; + + if certs.len() < 2 { + return Err(SigstoreError::FulcioClientError( + "Certificate chain too short: certs.len() < 2".into(), + )); + } + + let cert = Certificate::from_der(certs[0].contents())?; + let chain = certs[1..] + .iter() + .map(|pem| Certificate::from_der(pem.contents())) + .collect::, _>>()?; + + // TODO(tnytown): Implement SCT extraction. + // see: https://github.com/RustCrypto/formats/pull/1134 + if sct_embedded { + debug!("PrecertificateSignedCertificateTimestamps isn't implemented yet in x509_cert."); + } else { + // No embedded SCT, Fulcio instance that provides detached SCT: + if let SigningCertificate::SignedCertificateDetachedSct(_sct) = response {} + }; + + Ok(CertificateResponse { + cert, + chain, + // sct, + }) + } } diff --git a/src/fulcio/models.rs b/src/fulcio/models.rs new file mode 100644 index 0000000000..b4e7dc367d --- /dev/null +++ b/src/fulcio/models.rs @@ -0,0 +1,116 @@ +// Copyright 2023 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. + +//! Models for interfacing with Fulcio. +//! +//! https://github.com/sigstore/fulcio/blob/9da27be4fb64b85c907ab9ddd8a5d3cbd38041d4/fulcio.proto + +use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; +use pem::Pem; +use pkcs8::der::EncodePem; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_repr::Deserialize_repr; +use x509_cert::Certificate; + +fn serialize_x509_csr( + input: &x509_cert::request::CertReq, + ser: S, +) -> std::result::Result +where + S: Serializer, +{ + let encoded = input + .to_pem(pkcs8::LineEnding::CRLF) + .map_err(serde::ser::Error::custom)?; + let encoded = BASE64_STD_ENGINE.encode(encoded); + + ser.serialize_str(&encoded) +} + +fn deserialize_base64<'de, D>(de: D) -> std::result::Result, D::Error> +where + D: Deserializer<'de>, +{ + let buf: &str = Deserialize::deserialize(de)?; + + BASE64_STD_ENGINE + .decode(buf) + .map_err(serde::de::Error::custom) +} + +fn deserialize_inner_detached_sct<'de, D>(de: D) -> std::result::Result +where + D: Deserializer<'de>, +{ + let buf = deserialize_base64(de)?; + + serde_json::from_slice(&buf).map_err(serde::de::Error::custom) +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateSigningCertificateRequest { + #[serde(serialize_with = "serialize_x509_csr")] + pub certificate_signing_request: x509_cert::request::CertReq, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum SigningCertificate { + SignedCertificateDetachedSct(SigningCertificateDetachedSCT), + SignedCertificateEmbeddedSct(SigningCertificateEmbeddedSCT), +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SigningCertificateDetachedSCT { + pub chain: CertificateChain, + #[serde(deserialize_with = "deserialize_inner_detached_sct")] + pub signed_certificate_timestamp: InnerDetachedSCT, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SigningCertificateEmbeddedSCT { + pub chain: CertificateChain, +} + +#[derive(Deserialize)] +pub struct CertificateChain { + pub certificates: Vec, +} + +#[derive(Deserialize)] +pub struct InnerDetachedSCT { + pub sct_version: SCTVersion, + #[serde(deserialize_with = "deserialize_base64")] + pub id: Vec, + pub timestamp: u64, + #[serde(deserialize_with = "deserialize_base64")] + pub signature: Vec, + #[serde(deserialize_with = "deserialize_base64")] + pub extensions: Vec, +} + +#[derive(Deserialize_repr, PartialEq, Debug)] +#[repr(u8)] +pub enum SCTVersion { + V1 = 0, +} + +pub struct CertificateResponse { + pub cert: Certificate, + pub chain: Vec, + // pub sct: InnerDetachedSCT, +} diff --git a/src/lib.rs b/src/lib.rs index afea5441a0..0c648b5f62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -283,3 +283,10 @@ pub mod rekor; #[cfg(feature = "tuf")] pub mod tuf; + +// Don't export yet -- these types should only be useful internally. +mod bundle; +pub use bundle::Bundle; + +#[cfg(feature = "sign")] +pub mod sign; diff --git a/src/oauth/mod.rs b/src/oauth/mod.rs index 24d240b35f..a5419e7fce 100644 --- a/src/oauth/mod.rs +++ b/src/oauth/mod.rs @@ -14,3 +14,6 @@ // limitations under the License. pub mod openidflow; + +mod token; +pub use token::IdentityToken; diff --git a/src/oauth/token.rs b/src/oauth/token.rs new file mode 100644 index 0000000000..b5b304f9ed --- /dev/null +++ b/src/oauth/token.rs @@ -0,0 +1,106 @@ +// Copyright 2023 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. + +use chrono::{DateTime, Utc}; +use openidconnect::core::CoreIdToken; +use serde::Deserialize; + +use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine as _}; + +use crate::errors::SigstoreError; + +#[derive(Deserialize)] +pub struct Claims { + pub aud: String, + #[serde(with = "chrono::serde::ts_seconds")] + pub exp: DateTime, + #[serde(with = "chrono::serde::ts_seconds_option")] + #[serde(default)] + pub nbf: Option>, + pub email: String, +} + +pub type UnverifiedClaims = Claims; + +/// A Sigstore token. +pub struct IdentityToken { + original_token: String, + claims: UnverifiedClaims, +} + +impl IdentityToken { + /// Returns the **unverified** claim set for the token. + /// + /// The [UnverifiedClaims] returned from this method should not be used to enforce security + /// invariants. + pub fn unverified_claims(&self) -> &UnverifiedClaims { + &self.claims + } + + /// Returns whether or not this token is within its self-stated validity period. + pub fn in_validity_period(&self) -> bool { + let now = Utc::now(); + + if let Some(nbf) = self.claims.nbf { + nbf <= now && now < self.claims.exp + } else { + now < self.claims.exp + } + } +} + +impl TryFrom<&str> for IdentityToken { + type Error = SigstoreError; + + fn try_from(value: &str) -> Result { + let parts: [&str; 3] = value.split('.').collect::>().try_into().or(Err( + SigstoreError::IdentityTokenError("Malformed JWT".into()), + ))?; + + let claims = base64 + .decode(parts[1]) + .or(Err(SigstoreError::IdentityTokenError( + "Malformed JWT: Unable to decode claims".into(), + )))?; + let claims: Claims = serde_json::from_slice(&claims).or(Err( + SigstoreError::IdentityTokenError("Malformed JWT: claims JSON malformed".into()), + ))?; + if claims.aud != "sigstore" { + return Err(SigstoreError::IdentityTokenError( + "Not a Sigstore JWT".into(), + )); + } + + Ok(IdentityToken { + original_token: value.to_owned(), + claims, + }) + } +} + +impl From for IdentityToken { + fn from(value: CoreIdToken) -> Self { + value + .to_string() + .as_str() + .try_into() + .expect("Token conversion failed") + } +} + +impl ToString for IdentityToken { + fn to_string(&self) -> String { + self.original_token.clone() + } +} diff --git a/src/rekor/models/log_entry.rs b/src/rekor/models/log_entry.rs index b3b86f2fef..5caadd3db2 100644 --- a/src/rekor/models/log_entry.rs +++ b/src/rekor/models/log_entry.rs @@ -1,3 +1,18 @@ +// +// Copyright 2023 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. + use crate::errors::SigstoreError; use crate::rekor::TreeSize; use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; @@ -14,7 +29,7 @@ use super::{ /// Stores the response returned by Rekor after making a new entry #[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct LogEntry { pub uuid: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -95,4 +110,10 @@ pub struct InclusionProof { pub log_index: i64, pub root_hash: String, pub tree_size: TreeSize, + + /// A snapshot of the transparency log's state at a specific point in time, + /// in [Signed Note format]. + /// + /// [Signed Note format]: https://github.com/transparency-dev/formats/blob/main/log/README.md + pub checkpoint: String, } diff --git a/src/sign.rs b/src/sign.rs new file mode 100644 index 0000000000..1d4b36838e --- /dev/null +++ b/src/sign.rs @@ -0,0 +1,376 @@ +// Copyright 2023 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. + +//! Types for signing artifacts and producing Sigstore Bundles. + +use std::io::{self, Read}; +use std::time::SystemTime; + +use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use hex; +use json_syntax::Print; +use p256::NistP256; +use pkcs8::der::{Encode, EncodePem}; +use sha2::{Digest, Sha256}; +use signature::DigestSigner; +use sigstore_protobuf_specs::{ + Bundle, DevSigstoreBundleV1VerificationMaterial, DevSigstoreCommonV1HashOutput, + DevSigstoreCommonV1LogId, DevSigstoreCommonV1MessageSignature, + DevSigstoreCommonV1X509Certificate, DevSigstoreCommonV1X509CertificateChain, + DevSigstoreRekorV1Checkpoint, DevSigstoreRekorV1InclusionPromise, + DevSigstoreRekorV1InclusionProof, DevSigstoreRekorV1KindVersion, + DevSigstoreRekorV1TransparencyLogEntry, +}; +use tokio::io::AsyncRead; +use tokio_util::io::SyncIoBridge; +use url::Url; +use x509_cert::attr::{AttributeTypeAndValue, AttributeValue}; +use x509_cert::builder::{Builder, RequestBuilder as CertRequestBuilder}; +use x509_cert::ext::pkix as x509_ext; + +use crate::bundle::Version; +use crate::errors::{Result as SigstoreResult, SigstoreError}; +use crate::fulcio::oauth::OauthTokenProvider; +use crate::fulcio::{self, FulcioClient, FULCIO_ROOT}; +use crate::oauth::IdentityToken; +use crate::rekor::apis::configuration::Configuration as RekorConfiguration; +use crate::rekor::apis::entries_api::create_log_entry; +use crate::rekor::models::LogEntry; +use crate::rekor::models::{hashedrekord, proposed_entry::ProposedEntry as ProposedLogEntry}; + +/// An asynchronous Sigstore signing session. +/// +/// Sessions hold a provided user identity and key materials tied to that identity. A single +/// session may be used to sign multiple items. For more information, see [`AsyncSigningSession::sign`](Self::sign). +/// +/// This signing session operates asynchronously. To construct a synchronous [SigningSession], +/// use [`SigningContext::signer()`]. +pub struct AsyncSigningSession<'ctx> { + context: &'ctx SigningContext, + identity_token: IdentityToken, + private_key: ecdsa::SigningKey, + certs: fulcio::CertificateResponse, +} + +impl<'ctx> AsyncSigningSession<'ctx> { + async fn new( + context: &'ctx SigningContext, + identity_token: IdentityToken, + ) -> SigstoreResult> { + let (private_key, certs) = Self::materials(&context.fulcio, &identity_token).await?; + Ok(Self { + context, + identity_token, + private_key, + certs, + }) + } + + async fn materials( + fulcio: &FulcioClient, + token: &IdentityToken, + ) -> SigstoreResult<(ecdsa::SigningKey, fulcio::CertificateResponse)> { + let subject = + // SEQUENCE OF RelativeDistinguishedName + vec![ + // SET OF AttributeTypeAndValue + vec![ + // AttributeTypeAndValue, `emailAddress=...` + AttributeTypeAndValue { + oid: const_oid::db::rfc3280::EMAIL_ADDRESS, + value: AttributeValue::new( + pkcs8::der::Tag::Utf8String, + token.unverified_claims().email.as_ref(), + )?, + } + ].try_into()? + ].into(); + + let mut rng = rand::thread_rng(); + let private_key = ecdsa::SigningKey::from(p256::SecretKey::random(&mut rng)); + let mut builder = CertRequestBuilder::new(subject, &private_key)?; + builder.add_extension(&x509_ext::BasicConstraints { + ca: false, + path_len_constraint: None, + })?; + + let cert_req = builder.build::()?; + Ok((private_key, fulcio.request_cert_v2(cert_req, token).await?)) + } + + /// Check if the session's identity token or key material is expired. + /// + /// If the session is expired, it cannot be used for signing operations, and a new session + /// must be created with a fresh identity token. + pub fn is_expired(&self) -> bool { + let not_after = self + .certs + .cert + .tbs_certificate + .validity + .not_after + .to_system_time(); + + !self.identity_token.in_validity_period() || SystemTime::now() > not_after + } + + async fn sign_digest(&self, hasher: Sha256) -> SigstoreResult { + if self.is_expired() { + return Err(SigstoreError::ExpiredSigningSession()); + } + + // TODO(tnytown): Verify SCT here. + + // Sign artifact. + let input_hash: &[u8] = &hasher.clone().finalize(); + let artifact_signature: p256::ecdsa::Signature = self.private_key.sign_digest(hasher); + + // Prepare inputs. + let b64_artifact_signature = base64.encode(artifact_signature.to_der()); + let cert = &self.certs.cert; + + // Create the transparency log entry. + let proposed_entry = ProposedLogEntry::Hashedrekord { + api_version: "0.0.1".to_owned(), + spec: hashedrekord::Spec { + signature: hashedrekord::Signature { + content: b64_artifact_signature.clone(), + public_key: hashedrekord::PublicKey::new( + base64.encode(cert.to_pem(pkcs8::LineEnding::CRLF)?), + ), + }, + data: hashedrekord::Data { + hash: hashedrekord::Hash { + algorithm: hashedrekord::AlgorithmKind::sha256, + value: hex::encode(input_hash), + }, + }, + }, + }; + + let entry = create_log_entry(&self.context.rekor_config, proposed_entry) + .await + .map_err(|err| SigstoreError::RekorClientError(err.to_string()))?; + + // TODO(tnytown): Maybe run through the verification flow here? See sigstore-rs#296. + + Ok(SigningArtifact { + input_digest: base64.encode(input_hash), + cert: cert.to_der()?, + b64_signature: b64_artifact_signature, + log_entry: entry, + }) + } + + /// Signs for the input with the session's identity. If the identity is expired, + /// [SigstoreError::ExpiredSigningSession] is returned. + pub async fn sign( + &self, + input: R, + ) -> SigstoreResult { + if self.is_expired() { + return Err(SigstoreError::ExpiredSigningSession()); + } + + let mut sync_input = SyncIoBridge::new(input); + let hasher = tokio::task::spawn_blocking(move || -> SigstoreResult<_> { + let mut hasher = Sha256::new(); + io::copy(&mut sync_input, &mut hasher)?; + Ok(hasher) + }) + .await??; + + self.sign_digest(hasher).await + } +} + +/// A synchronous Sigstore signing session. +/// +/// Sessions hold a provided user identity and key materials tied to that identity. A single +/// session may be used to sign multiple items. For more information, see [`SigningSession::sign`](Self::sign). +/// +/// This signing session operates synchronously, thus it cannot be used in an asynchronous context. +/// To construct an asynchronous [SigningSession], use [`SigningContext::async_signer()`]. +pub struct SigningSession<'ctx> { + inner: AsyncSigningSession<'ctx>, + rt: tokio::runtime::Runtime, +} + +impl<'ctx> SigningSession<'ctx> { + fn new(ctx: &'ctx SigningContext, token: IdentityToken) -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let inner = rt.block_on(AsyncSigningSession::new(ctx, token))?; + Ok(Self { inner, rt }) + } + + /// Check if the session's identity token or key material is expired. + /// + /// If the session is expired, it cannot be used for signing operations, and a new session + /// must be created with a fresh identity token. + pub fn is_expired(&self) -> bool { + self.inner.is_expired() + } + + /// Signs for the input with the session's identity. If the identity is expired, + /// [SigstoreError::ExpiredSigningSession] is returned. + pub fn sign(&self, mut input: R) -> SigstoreResult { + let mut hasher = Sha256::new(); + io::copy(&mut input, &mut hasher)?; + self.rt.block_on(self.inner.sign_digest(hasher)) + } +} + +/// A Sigstore signing context. +/// +/// Contexts hold Fulcio (CA) and Rekor (CT) configurations which signing sessions can be +/// constructed against. Use [`SigningContext::production`](Self::production) to create a context against +/// the public-good Sigstore infrastructure. +pub struct SigningContext { + fulcio: FulcioClient, + rekor_config: RekorConfiguration, +} + +impl SigningContext { + /// Manually constructs a [SigningContext] from its constituent data. + pub fn new(fulcio: FulcioClient, rekor_config: RekorConfiguration) -> Self { + Self { + fulcio, + rekor_config, + } + } + + /// Returns a [SigningContext] configured against the public-good production Sigstore + /// infrastructure. + pub fn production() -> Self { + Self::new( + FulcioClient::new( + Url::parse(FULCIO_ROOT).expect("constant FULCIO root fails to parse!"), + crate::fulcio::TokenProvider::Oauth(OauthTokenProvider::default()), + ), + Default::default(), + ) + } + + /// Configures and returns an [AsyncSigningSession] with the held context. + pub async fn async_signer( + &self, + identity_token: IdentityToken, + ) -> SigstoreResult { + AsyncSigningSession::new(self, identity_token).await + } + + /// Configures and returns a [SigningContext] with the held context. + /// + /// Async contexts must use [`SigningContext::async_signer`](Self::async_signer). + pub fn signer(&self, identity_token: IdentityToken) -> SigstoreResult { + SigningSession::new(self, identity_token) + } +} + +/// A signature and its associated metadata. +pub struct SigningArtifact { + input_digest: String, + cert: Vec, + b64_signature: String, + log_entry: LogEntry, +} + +impl SigningArtifact { + /// Consumes the signing artifact and produces a Sigstore [Bundle]. + /// + /// The resulting bundle can be serialized with [serde_json]. + pub fn to_bundle(self) -> Bundle { + #[inline] + fn hex_to_base64>(hex: S) -> String { + let decoded = hex::decode(hex.as_ref()).expect("Malformed data in Rekor response"); + base64.encode(decoded) + } + + // NOTE: We explicitly only include the leaf certificate in the bundle's "chain" + // here: the specs explicitly forbid the inclusion of the root certificate, + // and discourage inclusion of any intermediates (since they're in the root of + // trust already). + let x_509_certificate_chain = Some(DevSigstoreCommonV1X509CertificateChain { + certificates: Some(vec![DevSigstoreCommonV1X509Certificate { + raw_bytes: Some(base64.encode(&self.cert)), + }]), + }); + + let inclusion_proof = if let Some(proof) = self.log_entry.verification.inclusion_proof { + let hashes = proof.hashes.iter().map(hex_to_base64).collect(); + Some(DevSigstoreRekorV1InclusionProof { + checkpoint: Some(DevSigstoreRekorV1Checkpoint { + envelope: Some(proof.checkpoint), + }), + hashes: Some(hashes), + log_index: Some(proof.log_index.to_string()), + root_hash: Some(hex_to_base64(proof.root_hash)), + tree_size: Some(proof.tree_size.to_string()), + }) + } else { + None + }; + + let canonicalized_body = { + let mut body = json_syntax::to_value(self.log_entry.body) + .expect("failed to parse constructed Body!"); + body.canonicalize(); + Some(base64.encode(body.compact_print().to_string())) + }; + + // TODO(tnytown): When we fix `sigstore_protobuf_specs`, have the Rekor client APIs convert + // responses into types from the specs as opposed to returning the raw `LogEntry` model type. + let tlog_entry = DevSigstoreRekorV1TransparencyLogEntry { + canonicalized_body, + inclusion_promise: Some(DevSigstoreRekorV1InclusionPromise { + // XX: sigstore-python deserializes the SET from base64 here because their protobuf + // library transparently serializes `bytes` fields as base64. + signed_entry_timestamp: Some(self.log_entry.verification.signed_entry_timestamp), + }), + inclusion_proof, + integrated_time: Some(self.log_entry.integrated_time.to_string()), + kind_version: Some(DevSigstoreRekorV1KindVersion { + kind: Some("hashedrekord".to_owned()), + version: Some("0.0.1".to_owned()), + }), + log_id: Some(DevSigstoreCommonV1LogId { + key_id: Some(hex_to_base64(self.log_entry.log_i_d)), + }), + log_index: Some(self.log_entry.log_index.to_string()), + }; + + let verification_material = Some(DevSigstoreBundleV1VerificationMaterial { + public_key: None, + timestamp_verification_data: None, + tlog_entries: Some(vec![tlog_entry]), + x_509_certificate_chain, + }); + + let message_signature = Some(DevSigstoreCommonV1MessageSignature { + message_digest: Some(DevSigstoreCommonV1HashOutput { + algorithm: Some("SHA2_256".to_owned()), + digest: Some(self.input_digest), + }), + signature: Some(self.b64_signature), + }); + Bundle { + dsse_envelope: None, + media_type: Some(Version::Bundle0_2.to_string()), + message_signature, + verification_material, + } + } +} diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 4df3c757c2..201eae8968 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -195,7 +195,7 @@ impl Repository for SigstoreRepository { if certs.is_empty() { Err(SigstoreError::TufMetadataError( - "Fulcio certificates not found", + "Fulcio certificates not found".into(), )) } else { Ok(certs) @@ -215,7 +215,7 @@ impl Repository for SigstoreRepository { if keys.len() != 1 { Err(SigstoreError::TufMetadataError( - "Did not find exactly 1 active Rekor key", + "Did not find exactly 1 active Rekor key".into(), )) } else { Ok(keys) diff --git a/src/tuf/repository_helper.rs b/src/tuf/repository_helper.rs index 74747d363e..a581619638 100644 --- a/src/tuf/repository_helper.rs +++ b/src/tuf/repository_helper.rs @@ -13,7 +13,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use rustls_pki_types::CertificateDer; use sha2::{Digest, Sha256}; use std::fs; use std::io::Read; @@ -21,13 +20,14 @@ use std::path::{Path, PathBuf}; use tough::{RepositoryLoader, TargetName}; use url::Url; -use super::super::errors::{Result, SigstoreError}; -use super::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; +use super::{ + super::errors::{Result, SigstoreError}, + constants::{SIGSTORE_FULCIO_CERT_TARGET_REGEX, SIGSTORE_REKOR_PUB_KEY_TARGET}, +}; pub(crate) struct RepositoryHelper { repository: tough::Repository, checkout_dir: Option, - trusted_root: Option, } impl RepositoryHelper { @@ -40,7 +40,7 @@ impl RepositoryHelper { where R: Read, { - let repository = RepositoryLoader::new(SIGSTORE_ROOT, metadata_base, target_base) + let repository = RepositoryLoader::new(root, metadata_base, target_base) .expiration_enforcement(tough::ExpirationEnforcement::Safe) .load() .map_err(Box::new)?; @@ -48,110 +48,162 @@ impl RepositoryHelper { Ok(Self { repository, checkout_dir: checkout_dir.map(|s| s.to_owned()), - trusted_root: None, }) } - pub(crate) fn from_repo(repo: tough::Repository, checkout_dir: Option<&Path>) -> Self { - Self { - repository: repo, - checkout_dir: checkout_dir.map(|s| s.to_owned()), - trusted_root: None, + /// 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. + pub(crate) fn fulcio_certs(&self) -> Result> { + let fulcio_target_names = self.fulcio_cert_target_names(); + let mut certs = vec![]; + + for fulcio_target_name in &fulcio_target_names { + let local_fulcio_path = self + .checkout_dir + .as_ref() + .map(|d| Path::new(d).join(fulcio_target_name.raw())); + + let cert_data = fetch_target_or_reuse_local_cache( + &self.repository, + fulcio_target_name, + local_fulcio_path.as_ref(), + )?; + certs.push(crate::registry::Certificate { + data: cert_data, + encoding: crate::registry::CertificateEncoding::Pem, + }); } + Ok(certs) } - fn trusted_root(&self) -> Result<&TrustedRoot> { - if let Some(result) = self.trusted_root { - return Ok(&result); - } + fn fulcio_cert_target_names(&self) -> Vec { + self.repository + .targets() + .signed + .targets_iter() + .filter_map(|(target_name, _target)| { + if SIGSTORE_FULCIO_CERT_TARGET_REGEX.is_match(target_name.raw()) { + Some(target_name.clone()) + } else { + None + } + }) + .collect() + } + + /// Fetch Rekor public key from the given TUF repository or reuse + /// the local cache if it's not outdated. + /// + /// The contents of the local cache are updated when they are outdated. + pub(crate) fn rekor_pub_key(&self) -> Result> { + let rekor_target_name = TargetName::new(SIGSTORE_REKOR_PUB_KEY_TARGET).map_err(Box::new)?; - let trusted_root_target = TargetName::new("trusted_root.json").map_err(Box::new)?; - let local_path = self + let local_rekor_path = self .checkout_dir .as_ref() - .map(|d| d.join(trusted_root_target.raw())); + .map(|d| Path::new(d).join(SIGSTORE_REKOR_PUB_KEY_TARGET)); - let data = fetch_target_or_reuse_local_cache( + fetch_target_or_reuse_local_cache( &self.repository, - &trusted_root_target, - local_path.as_ref(), - )?; - - let result = serde_json::from_slice(&data[..])?; - Ok(self.trusted_root.insert(result)) + &rekor_target_name, + local_rekor_path.as_ref(), + ) } +} - #[inline] - fn tlog_keys(&self, tlogs: &Vec) -> Vec<&[u8]> { - let mut result = Vec::new(); - - for key in tlogs { - // We won't accept expired keys for transparency logs. - if !is_timerange_valid(key.public_key.valid_for, false) { - continue; - } - - if let Some(raw) = key.public_key.raw_bytes { - result.push(&raw[..]); - } +/// Download a file stored inside of a TUF repository, try to reuse a local +/// cache when possible. +/// +/// * `repository`: TUF repository holding the file +/// * `target_name`: TUF representation of the file to be downloaded +/// * `local_file`: location where the file should be downloaded +/// +/// This function will reuse the local copy of the file if contents +/// didn't change. +/// This check is done by comparing the digest of the local file, if found, +/// with the digest reported inside of the TUF repository metadata. +/// +/// **Note well:** the `local_file` is updated whenever its contents are +/// outdated. +fn fetch_target_or_reuse_local_cache( + repository: &tough::Repository, + target_name: &TargetName, + local_file: Option<&PathBuf>, +) -> Result> { + let (local_file_outdated, local_file_contents) = if let Some(path) = local_file { + is_local_file_outdated(repository, target_name, path) + } else { + Ok((true, None)) + }?; + + let data = if local_file_outdated { + let data = fetch_target(repository, target_name)?; + if let Some(path) = local_file { + // update the local file to have latest data from the TUF repo + fs::write(path, data.clone())?; } + data + } else { + local_file_contents + .expect("local file contents to not be 'None'") + .as_bytes() + .to_owned() + }; + + Ok(data) +} - result - } - - #[inline] - fn ca_keys(&self, cas: &Vec, allow_expired: bool) -> Vec<&[u8]> { - let mut certs = Vec::new(); - - for ca in cas { - if !is_timerange_valid(Some(ca.valid_for), allow_expired) { - continue; - } - - let certs_in_ca = ca.cert_chain.certificates; - certs.extend(certs_in_ca.iter().map(|cert| &cert.raw_bytes[..])); +/// Download a file from a TUF repository +fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { + let data: Vec; + match repository.read_target(target_name).map_err(Box::new)? { + None => Err(SigstoreError::TufTargetNotFoundError( + target_name.raw().to_string(), + )), + Some(reader) => { + data = read_to_end(reader)?; + Ok(data) } - - return certs; } +} - /// 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. - pub(crate) fn fulcio_certs(&self) -> Result> { - let root = self.trusted_root()?; - - // Allow expired certificates: they may have been active when the - // certificate was used to sign. - let certs = self.ca_keys(&root.certificate_authorities, true); - let certs: Vec<_> = certs.iter().map(|v| CertificateDer::from(*v)).collect(); - - if certs.is_empty() { - Err(SigstoreError::TufMetadataError( - "Fulcio certificates not found", - )) +/// Compares the checksum of a local file, with the digest reported inside of +/// TUF repository metadata +fn is_local_file_outdated( + repository: &tough::Repository, + target_name: &TargetName, + local_file: &Path, +) -> Result<(bool, Option)> { + let target = repository + .targets() + .signed + .targets + .get(target_name) + .ok_or_else(|| SigstoreError::TufTargetNotFoundError(target_name.raw().to_string()))?; + + if local_file.exists() { + let data = fs::read_to_string(local_file)?; + let local_checksum = Sha256::digest(data.clone()); + let expected_digest: Vec = target.hashes.sha256.to_vec(); + + if local_checksum.as_slice() == expected_digest.as_slice() { + // local data is not outdated + Ok((false, Some(data))) } else { - Ok(certs) + Ok((true, None)) } + } else { + Ok((true, None)) } +} - /// Fetch Rekor public keys from the given TUF repository or reuse - /// the local cache if it's not outdated. - /// - /// The contents of the local cache are updated when they are outdated. - pub(crate) fn rekor_keys(&self) -> Result> { - let root = self.trusted_root()?; - let keys = self.tlog_keys(&root.tlogs); - - if keys.len() != 1 { - Err(SigstoreError::TufMetadataError( - "Did not find exactly 1 active Rekor key", - )) - } else { - Ok(keys) - } - } +/// Gets the goods from a read and makes a Vec +fn read_to_end(mut reader: R) -> Result> { + let mut v = Vec::new(); + reader.read_to_end(&mut v)?; + Ok(v) } #[cfg(test)] @@ -200,92 +252,61 @@ mod tests { )) })?; // It's fine to ignore timestamp.json expiration inside of test env - let repo = RepositoryLoader::new(SIGSTORE_ROOT, metadata_base_url, target_base_url) - .expiration_enforcement(tough::ExpirationEnforcement::Unsafe) - .load() - .map_err(Box::new)?; + let repo = + RepositoryLoader::new(SIGSTORE_ROOT.as_bytes(), metadata_base_url, target_base_url) + .expiration_enforcement(tough::ExpirationEnforcement::Unsafe) + .load() + .map_err(Box::new)?; Ok(repo) } - fn find_target(name: &str) -> Result { - let path = test_data().join("repository").join("targets"); - - for entry in fs::read_dir(path)? { - let path = entry?.path(); - if path.is_dir() { - continue; - } - - // Heuristic: Filter for consistent snapshot targets. SHA256 hashes in hexadecimal - // comprise of 64 characters, so our filename must be at least that long. The TUF repo - // shouldn't ever contain paths with invalid Unicode (knock on wood), so we're doing - // the lossy OsStr conversion here. - let filename = path.file_name().unwrap().to_str().unwrap(); - if filename.len() < 64 { - continue; - } - - // Heuristic: see if the filename is in consistent snapshot format (.). - // NB: The consistent snapshot prefix should be ASCII, so indexing the string as - // bytes is safe enough. - if filename.as_bytes()[64] != b'.' { - continue; - } - - // At this point, we're probably dealing with a consistent snapshot. - // Check if the name matches. - if filename.ends_with(name) { - return Ok(path); - } - } - - Err(SigstoreError::UnexpectedError( - "Couldn't find a matching target".to_string(), - )) - } - - fn check_against_disk(helper: &RepositoryHelper) { - let mut actual: Vec<&[u8]> = helper - .fulcio_certs() - .expect("fulcio certs could not be read") - .iter() - .map(|c| c.as_ref()) - .collect(); - let expected = ["fulcio.crt.pem", "fulcio_v1.crt.pem"].iter().map(|t| { - let path = find_target(t)?; - Ok(fs::read(path)?) - }); - let mut expected = expected - .collect::>>>() - .expect("could not find targets"); - actual.sort(); - expected.sort(); - - assert_eq!(actual, expected, "The fulcio cert is not what was expected"); - - let actual = helper.rekor_keys().expect("rekor key cannot be read"); - let expected = fs::read(find_target("rekor.pub").expect("could not find targets")) - .expect("cannot read rekor key from test data"); - let expected = pem::parse(expected).unwrap(); - assert_eq!(expected.tag(), "PUBLIC KEY"); - - assert_eq!( - actual, - &[expected.contents()], - "The rekor key is not what was expected" - ); - } - #[test] fn get_files_without_using_local_cache() { let repository = local_tuf_repo().expect("Local TUF repo should not fail"); let helper = RepositoryHelper { repository, checkout_dir: None, - trusted_root: None, }; - check_against_disk(&helper); + let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); + actual.sort(); + let mut expected: Vec = + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] + .iter() + .map(|filename| { + let data = fs::read( + test_data() + .join("repository") + .join("targets") + .join(filename), + ) + .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); + crate::registry::Certificate { + data, + encoding: crate::registry::CertificateEncoding::Pem, + } + }) + .collect(); + expected.sort(); + + assert_eq!( + actual, expected, + "The fulcio cert read from the TUF repository is not what was expected" + ); + + let actual = helper.rekor_pub_key().expect("rekor key cannot be read"); + let expected = fs::read( + test_data() + .join("repository") + .join("targets") + .join("rekor.pub"), + ) + .expect("cannot read rekor key from test data"); + + assert_eq!( + actual, expected, + "The rekor key read from the TUF repository is not what was expected" + ); } #[test] @@ -296,10 +317,42 @@ mod tests { let helper = RepositoryHelper { repository, checkout_dir: Some(cache_dir.path().to_path_buf()), - trusted_root: None, }; - check_against_disk(&helper); + let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); + actual.sort(); + let mut expected: Vec = + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] + .iter() + .map(|filename| { + let data = fs::read( + test_data() + .join("repository") + .join("targets") + .join(filename), + ) + .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); + crate::registry::Certificate { + data, + encoding: crate::registry::CertificateEncoding::Pem, + } + }) + .collect(); + expected.sort(); + + assert_eq!( + actual, expected, + "The fulcio cert read from the cache dir is not what was expected" + ); + + let expected = helper.rekor_pub_key().expect("rekor key cannot be read"); + let actual = fs::read(cache_dir.path().join("rekor.pub")) + .expect("cannot read rekor key from cache dir"); + + assert_eq!( + actual, expected, + "The rekor key read from the cache dir is not what was expected" + ); } #[test] @@ -312,8 +365,8 @@ mod tests { .expect("Cannot write file to cache dir"); } fs::write( - cache_dir.path().join("trusted_root.json"), - b"fake trusted root", + cache_dir.path().join(SIGSTORE_REKOR_PUB_KEY_TARGET), + b"fake rekor", ) .expect("Cannot write file to cache dir"); @@ -321,22 +374,41 @@ mod tests { let helper = RepositoryHelper { repository, checkout_dir: Some(cache_dir.path().to_path_buf()), - trusted_root: None, }; - check_against_disk(&helper); - } + let mut actual = helper.fulcio_certs().expect("fulcio certs cannot be read"); + actual.sort(); + let mut expected: Vec = + ["fulcio.crt.pem", "fulcio_v1.crt.pem"] + .iter() + .map(|filename| { + let data = fs::read( + test_data() + .join("repository") + .join("targets") + .join(filename), + ) + .unwrap_or_else(|_| panic!("cannot read {} from test data", filename)); + crate::registry::Certificate { + data, + encoding: crate::registry::CertificateEncoding::Pem, + } + }) + .collect(); + expected.sort(); - #[test] - fn deser_trusted_root() { - let metadata_base_path = test_data().join("repository"); - let targets_base_path = metadata_base_path.join("targets"); + assert_eq!( + actual, expected, + "The fulcio cert read from the TUF repository is not what was expected" + ); - let repository = local_tuf_repo().expect("Local TUF repo should not fail"); - let helper = RepositoryHelper::from_repo(repository, None); + let expected = helper.rekor_pub_key().expect("rekor key cannot be read"); + let actual = fs::read(cache_dir.path().join("rekor.pub")) + .expect("cannot read rekor key from cache dir"); - helper - .trusted_root() - .expect("Trusted Root should deserialize"); + assert_eq!( + actual, expected, + "The rekor key read from the cache dir is not what was expected" + ); } } From 1df9ac4263ef3b1bb1771dfc740b33e729c43717 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 19:46:17 +0000 Subject: [PATCH 060/161] chore(deps): Update json-syntax requirement from 0.9.6 to 0.10.0 Updates the requirements on [json-syntax](https://github.com/timothee-haudebourg/json-syntax) to permit the latest version. - [Commits](https://github.com/timothee-haudebourg/json-syntax/compare/0.9.6...0.10.0) --- updated-dependencies: - dependency-name: json-syntax dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6a6422c2e9..c113d4f490 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,7 @@ rustls-webpki = { version = "0.102.0-alpha.7", features = ["alloc"] } rustls-pki-types = { version = "1.0.0", features = ["std"] } serde_repr = "0.1.16" hex = "0.4.3" -json-syntax = { version = "0.9.6", features = ["canonicalize", "serde"] } +json-syntax = { version = "0.10.0", features = ["canonicalize", "serde"] } [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } From 3fa3ebef6b7a15b0bdd417170515d0a446949ae1 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Tue, 2 Jan 2024 16:10:27 -0500 Subject: [PATCH 061/161] cargo audit: ignore RUSTSEC-2023-0071 Signed-off-by: Jack Leightcap --- .cargo/audit.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 74774b4e0e..6b41c0f5ac 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -10,4 +10,7 @@ ignore = [ "RUSTSEC-2021-0139" # ansi_term is no longer maintained, however this is a transient dependency of # the `tracing-subscriber` crate, which is a dev_dependency and so therefore # will not be included in a release build. + "RUSTSEC-2023-0071" # "Classic" RSA timing sidechannel attack from non-constant-time implementation. + # Okay for local use. + # https://rustsec.org/advisories/RUSTSEC-2023-0071.html ] From 5e289d552deef7a129f8998b926cab629cb4518c Mon Sep 17 00:00:00 2001 From: Jack Leightcap <30168080+jleightcap@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:28:26 -0500 Subject: [PATCH 062/161] Update .cargo/audit.toml oops, thanks! Co-authored-by: William Woodruff Signed-off-by: Jack Leightcap <30168080+jleightcap@users.noreply.github.com> --- .cargo/audit.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 6b41c0f5ac..c70474f188 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -7,7 +7,7 @@ ignore = [ # to API breakages. # # This is a transitive depependency of tough - "RUSTSEC-2021-0139" # ansi_term is no longer maintained, however this is a transient dependency of + "RUSTSEC-2021-0139", # ansi_term is no longer maintained, however this is a transient dependency of # the `tracing-subscriber` crate, which is a dev_dependency and so therefore # will not be included in a release build. "RUSTSEC-2023-0071" # "Classic" RSA timing sidechannel attack from non-constant-time implementation. From e991296104cc417c27ac9df7bf08a0b07e7f65be Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Wed, 3 Jan 2024 11:17:49 -0500 Subject: [PATCH 063/161] cargo audit: drop RSUTSEC-2021-0139 ignore Signed-off-by: Jack Leightcap --- .cargo/audit.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index c70474f188..d905f8bcd1 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -7,9 +7,6 @@ ignore = [ # to API breakages. # # This is a transitive depependency of tough - "RUSTSEC-2021-0139", # ansi_term is no longer maintained, however this is a transient dependency of - # the `tracing-subscriber` crate, which is a dev_dependency and so therefore - # will not be included in a release build. "RUSTSEC-2023-0071" # "Classic" RSA timing sidechannel attack from non-constant-time implementation. # Okay for local use. # https://rustsec.org/advisories/RUSTSEC-2023-0071.html From 2184e4c0faa9a97d1278b0d62e17a3384ee38b39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:38:58 +0000 Subject: [PATCH 064/161] chore(deps): Update serial_test requirement from 2.0.0 to 3.0.0 Updates the requirements on [serial_test](https://github.com/palfrey/serial_test) to permit the latest version. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v2.0.0...v3.0.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c113d4f490..50e6364d03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,7 +136,7 @@ clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" rstest = "0.18.1" -serial_test = "2.0.0" +serial_test = "3.0.0" tempfile = "3.3.0" testcontainers = "0.15" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From b201233891eadb937a1d41cc25610329694d4485 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:40:40 +0000 Subject: [PATCH 065/161] chore(deps): Update cached requirement from 0.46.0 to 0.47.0 Updates the requirements on [cached](https://github.com/jaemk/cached) to permit the latest version. - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c113d4f490..9d8e1526e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.46.0", optional = true, features = ["async"] } +cached = { version = "0.47.0", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["serde"] } const-oid = "0.9.1" From 741a2a917b9a9a250b0f4055b87d748f5babf394 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Fri, 12 Jan 2024 12:18:55 -0500 Subject: [PATCH 066/161] dep: update rustls-webpki, fold in pki_types Signed-off-by: Jack Leightcap --- Cargo.toml | 3 +-- src/cosign/client_builder.rs | 2 +- src/cosign/mod.rs | 2 +- src/cosign/signature_layers.rs | 2 +- src/cosign/verification_constraint/certificate_verifier.rs | 2 +- src/crypto/certificate_pool.rs | 6 ++++-- src/registry/config.rs | 2 +- src/tuf/mod.rs | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae67fc5e1c..eb3b152363 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,8 +122,7 @@ url = "2.2.2" x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" -rustls-webpki = { version = "0.102.0-alpha.7", features = ["alloc"] } -rustls-pki-types = { version = "1.0.0", features = ["std"] } +rustls-webpki = { version = "0.102.1", features = ["alloc"] } serde_repr = "0.1.16" hex = "0.4.3" json-syntax = { version = "0.10.0", features = ["canonicalize", "serde"] } diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 1bd2dcfae3..adfaff1f43 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use rustls_pki_types::CertificateDer; use tracing::info; +use webpki::types::CertificateDer; use super::client::Client; use crate::crypto::SigningScheme; diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index b6807d6db8..03d3c0e52d 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -283,9 +283,9 @@ where #[cfg(test)] mod tests { - use rustls_pki_types::CertificateDer; use serde_json::json; use std::collections::HashMap; + use webpki::types::CertificateDer; use super::constraint::{AnnotationMarker, PrivateKeySigner}; use super::*; diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index ec11eb9bea..9934d36f08 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -439,7 +439,7 @@ impl CertificateSignature { // ensure the certificate has been issued by Fulcio fulcio_cert_pool.verify_pem_cert( cert_pem, - Some(rustls_pki_types::UnixTime::since_unix_epoch( + Some(webpki::types::UnixTime::since_unix_epoch( cert.tbs_certificate.validity.not_before.to_unix_duration(), )), )?; diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index b002aa3bb3..78d17f857c 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -1,8 +1,8 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use pkcs8::der::Decode; -use rustls_pki_types::CertificateDer; use std::convert::TryFrom; use tracing::warn; +use webpki::types::CertificateDer; use x509_cert::Certificate; use super::VerificationConstraint; diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index 11ee756133..731a68c6c3 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -14,8 +14,10 @@ // limitations under the License. use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; -use rustls_pki_types::{CertificateDer, TrustAnchor, UnixTime}; -use webpki::{EndEntityCert, KeyUsage}; +use webpki::{ + types::{CertificateDer, TrustAnchor, UnixTime}, + EndEntityCert, KeyUsage, +}; use crate::errors::{Result, SigstoreError}; diff --git a/src/registry/config.rs b/src/registry/config.rs index c8cdfa24ab..95630ef29f 100644 --- a/src/registry/config.rs +++ b/src/registry/config.rs @@ -15,10 +15,10 @@ //! Set of structs and enums used to define how to interact with OCI registries -use rustls_pki_types::CertificateDer; use serde::Serialize; use std::cmp::Ordering; use std::convert::From; +use webpki::types::CertificateDer; use crate::errors; diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 201eae8968..e695cc5044 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -40,10 +40,10 @@ use std::{ mod constants; mod trustroot; -use rustls_pki_types::CertificateDer; use sha2::{Digest, Sha256}; use tough::TargetName; use tracing::debug; +use webpki::types::CertificateDer; use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; From 4607b947820872697d8db3dbad796fee97fda560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:19:35 +0000 Subject: [PATCH 067/161] chore(deps): Update cached requirement from 0.47.0 to 0.48.0 Updates the requirements on [cached](https://github.com/jaemk/cached) to permit the latest version. - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index eb3b152363..c931131da5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.47.0", optional = true, features = ["async"] } +cached = { version = "0.48.0", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["serde"] } const-oid = "0.9.1" From 525181db408d3603081b357efb7f3583f525456a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:15:48 +0000 Subject: [PATCH 068/161] chore(deps): Update json-syntax requirement from 0.10.0 to 0.11.1 Updates the requirements on [json-syntax](https://github.com/timothee-haudebourg/json-syntax) to permit the latest version. - [Commits](https://github.com/timothee-haudebourg/json-syntax/compare/0.10.0...0.11.1) --- updated-dependencies: - dependency-name: json-syntax dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c931131da5..0ce09da945 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,7 @@ zeroize = "1.5.7" rustls-webpki = { version = "0.102.1", features = ["alloc"] } serde_repr = "0.1.16" hex = "0.4.3" -json-syntax = { version = "0.10.0", features = ["canonicalize", "serde"] } +json-syntax = { version = "0.11.1", features = ["canonicalize", "serde"] } [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } From 69f64fa61b57231deff2c884bde3803375edaed7 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Wed, 3 Jan 2024 13:12:02 -0500 Subject: [PATCH 069/161] verify: init Signed-off-by: Jack Leightcap --- Cargo.toml | 3 +- src/bundle/mod.rs | 28 +- src/crypto/certificate.rs | 143 ++++++++- src/crypto/mod.rs | 2 + src/crypto/verification.rs | 8 + src/crypto/verification_key.rs | 64 +++- src/errors.rs | 3 + src/lib.rs | 3 + src/tuf/constants.rs | 1 + src/verify/mod.rs | 24 ++ src/verify/models.rs | 222 ++++++++++++++ src/verify/policy.rs | 284 ++++++++++++++++++ src/verify/verifier.rs | 197 ++++++++++++ tests/conformance/Cargo.toml | 2 + tests/conformance/conformance.rs | 67 +++++ .../data/repository/1.registry.npmjs.org.json | 23 ++ tests/data/repository/1.root.json | 119 ++------ tests/data/repository/1.snapshot.json | 32 ++ tests/data/repository/1.targets.json | 148 +++++++++ .../data/repository/2.registry.npmjs.org.json | 23 ++ tests/data/repository/2.root.json | 129 ++------ tests/data/repository/2.snapshot.json | 32 ++ tests/data/repository/2.targets.json | 135 +++++++++ tests/data/repository/registry.npmjs.org.json | 23 ++ tests/data/repository/rekor.json | 23 -- tests/data/repository/root.json | 129 ++------ tests/data/repository/snapshot.json | 52 ++-- tests/data/repository/staging.json | 15 - tests/data/repository/targets.json | 202 +++++++------ ...67072b6f89ddf1032273a78b.trusted_root.json | 86 ++++++ tests/data/repository/targets/artifact.pub | 4 - tests/data/repository/targets/ctfe.pub | 4 - ...40ca812d8ac47a128bf84963.trusted_root.json | 86 ++++++ tests/data/repository/targets/fulcio.crt.pem | 13 - .../data/repository/targets/fulcio_v1.crt.pem | 13 - ...8f8df61bc7274189122c123446248426.keys.json | 26 ++ ...2551fcaa870a30d4601ba1caf6f63699.keys.json | 26 ++ tests/data/repository/targets/rekor.0.pub | 4 - tests/data/repository/targets/rekor.json | 23 -- tests/data/repository/targets/rekor.pub | 4 - tests/data/repository/timestamp.json | 30 +- 41 files changed, 1906 insertions(+), 549 deletions(-) create mode 100644 src/crypto/verification.rs create mode 100644 src/verify/mod.rs create mode 100644 src/verify/models.rs create mode 100644 src/verify/policy.rs create mode 100644 src/verify/verifier.rs create mode 100644 tests/data/repository/1.registry.npmjs.org.json create mode 100644 tests/data/repository/1.snapshot.json create mode 100644 tests/data/repository/1.targets.json create mode 100644 tests/data/repository/2.registry.npmjs.org.json create mode 100644 tests/data/repository/2.snapshot.json create mode 100644 tests/data/repository/2.targets.json create mode 100644 tests/data/repository/registry.npmjs.org.json delete mode 100644 tests/data/repository/rekor.json delete mode 100644 tests/data/repository/staging.json create mode 100644 tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json delete mode 100644 tests/data/repository/targets/artifact.pub delete mode 100644 tests/data/repository/targets/ctfe.pub create mode 100644 tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json delete mode 100644 tests/data/repository/targets/fulcio.crt.pem delete mode 100644 tests/data/repository/targets/fulcio_v1.crt.pem create mode 100644 tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json create mode 100644 tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json delete mode 100644 tests/data/repository/targets/rekor.0.pub delete mode 100644 tests/data/repository/targets/rekor.json delete mode 100644 tests/data/repository/targets/rekor.pub diff --git a/Cargo.toml b/Cargo.toml index 0ce09da945..80b107eef5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "tuf", "sign"] +default = ["full-native-tls", "cached-client", "tuf", "sign", "verify"] wasm = ["getrandom/js"] full-native-tls = [ @@ -43,6 +43,7 @@ rekor = ["reqwest"] tuf = ["tough", "regex"] sign = [] +verify = [] cosign-native-tls = [ "oci-distribution/native-tls", diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index 2b9b9cb73c..1bf8e8d840 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -15,21 +15,45 @@ //! Useful types for Sigstore bundles. use std::fmt::Display; +use std::str::FromStr; pub use sigstore_protobuf_specs::Bundle; +macro_rules! required { + ($($base:expr )? ; $first_attr:ident $( . $rest_attrs:ident)* $( , $else_err:expr)?) => { + $( $base . )? $first_attr.as_ref() + $( + .and_then(|v| v.$rest_attrs.as_ref()) + )* + $( .ok_or($else_err) )? + } +} +pub(crate) use required; + // Known Sigstore bundle media types. #[derive(Clone, Copy, Debug)] pub enum Version { - _Bundle0_1, + Bundle0_1, Bundle0_2, } impl Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match &self { - Version::_Bundle0_1 => "application/vnd.dev.sigstore.bundle+json;version=0.1", + Version::Bundle0_1 => "application/vnd.dev.sigstore.bundle+json;version=0.1", Version::Bundle0_2 => "application/vnd.dev.sigstore.bundle+json;version=0.2", }) } } + +impl FromStr for Version { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "application/vnd.dev.sigstore.bundle+json;version=0.1" => Ok(Version::Bundle0_1), + "application/vnd.dev.sigstore.bundle+json;version=0.2" => Ok(Version::Bundle0_2), + _ => Err(()), + } + } +} diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 943df12923..34ae09cbc6 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -16,7 +16,7 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use const_oid::db::rfc5912::ID_KP_CODE_SIGNING; use x509_cert::{ - ext::pkix::{ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName}, + ext::pkix::{constraints, ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName}, Certificate, }; @@ -120,6 +120,147 @@ fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result< Ok(()) } +/// Check if the given certificate is a leaf in the context of the Sigstore profile. +/// +/// * It is not a root or intermediate CA; +/// * It has `keyUsage.digitalSignature` +/// * It has `CODE_SIGNING` as an `ExtendedKeyUsage`. +/// +/// This function does not evaluate the trustworthiness of the certificate. +pub(crate) fn is_leaf(certificate: &Certificate) -> Result { + // NOTE(jl): following structure of sigstore-python over the slightly different handling found + // in `verify_key_usages`. + let tbs = &certificate.tbs_certificate; + + // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack + // extensions and have ambiguous CA behavior. + if tbs.version != x509_cert::Version::V3 { + return Err(SigstoreError::CertificateUnsupportedVersionError); + } + + if is_ca(certificate)? { + return Ok(false); + }; + + let digital_signature = match tbs.get::()? { + None => { + return Err(SigstoreError::InvalidCertError( + "invalid X.509 certificate: missing KeyUsage".to_string(), + )) + } + Some((_, key_usage)) => key_usage.digital_signature(), + }; + + if !digital_signature { + return Err(SigstoreError::InvalidCertError( + "invalid certificate for Sigstore purposes: missing digital signature usage" + .to_string(), + )); + } + + // Finally, we check to make sure the leaf has an `ExtendedKeyUsages` + // extension that includes a codesigning entitlement. Sigstore should + // never issue a leaf that doesn't have this extended usage. + + let extended_key_usage = match tbs.get::()? { + None => { + return Err(SigstoreError::InvalidCertError( + "invalid X.509 certificate: missing ExtendedKeyUsage".to_string(), + )) + } + Some((_, extended_key_usage)) => extended_key_usage, + }; + + Ok(extended_key_usage.0.contains(&ID_KP_CODE_SIGNING)) +} + +/// Checks if the given `certificate` is a CA certificate. +/// +/// This does **not** indicate trustworthiness of the given `certificate`, only if it has the +/// appropriate interior state. +/// +/// This function is **not** naively invertible: users **must** use the dedicated `is_leaf` +/// utility function to determine whether a particular leaf upholds Sigstore's invariants. +pub(crate) fn is_ca(certificate: &Certificate) -> Result { + let tbs = &certificate.tbs_certificate; + + // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack + // extensions and have ambiguous CA behavior. + if tbs.version != x509_cert::Version::V3 { + return Err(SigstoreError::CertificateUnsupportedVersionError); + } + + // Valid CA certificates must have the following set: + // + // - `BasicKeyUsage.keyCertSign` + // - `BasicConstraints.ca` + // + // Any other combination of states is inconsistent and invalid, meaning + // that we won't consider the certificate a valid non-CA leaf. + + let ca = match tbs.get::()? { + None => return Ok(false), + Some((false, _)) => { + // BasicConstraints must be marked as critical, per RFC 5280 4.2.1.9. + return Err(SigstoreError::InvalidCertError( + "invalid X.509 certificate: non-critical BasicConstraints in CA".to_string(), + )); + } + Some((true, v)) => v.ca, + }; + + let key_cert_sign = match tbs.get::()? { + None => { + return Err(SigstoreError::InvalidCertError( + "invalid X.509 certificate: missing KeyUsage".to_string(), + )) + } + Some((_, v)) => v.key_cert_sign(), + }; + + // both states set, this is a CA. + if ca && key_cert_sign { + return Ok(true); + } + + if !(ca || key_cert_sign) { + return Ok(false); + } + + // Anything else is an invalid state that should never occur. + Err(SigstoreError::InvalidCertError(format!( + "invalid certificate states: KeyUsage.keyCertSign={}, BasicConstraints.ca={}", + key_cert_sign, ca + ))) +} + +/// Returns `True` if and only if the given `Certificate` indicates +/// that it's a root CA. +/// +/// This is **not** a verification function, and it does not establish +/// the trustworthiness of the given certificate. +pub(crate) fn is_root_ca(certificate: &Certificate) -> Result { + // NOTE(ww): This function is obnoxiously long to make the different + // states explicit. + + let tbs = &certificate.tbs_certificate; + + // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack + // extensions and have ambiguous CA behavior. + if tbs.version != x509_cert::Version::V3 { + return Err(SigstoreError::CertificateUnsupportedVersionError); + } + + // Non-CAs can't possibly be root CAs. + if !is_ca(certificate)? { + return Ok(false); + } + + // A certificate that is its own issuer and signer is considered a root CA. + // TODO(jl): verify_directly_issued_by + todo!() +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index c8a35e22bf..adfa82ba5e 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -177,6 +177,8 @@ pub enum Signature<'a> { pub(crate) mod certificate; #[cfg(feature = "cert")] pub(crate) mod certificate_pool; +#[cfg(feature = "cert")] +pub(crate) use certificate_pool::CertificatePool; pub mod verification_key; diff --git a/src/crypto/verification.rs b/src/crypto/verification.rs new file mode 100644 index 0000000000..a0a6ddcf2d --- /dev/null +++ b/src/crypto/verification.rs @@ -0,0 +1,8 @@ +use rustls_pki_types::CertificateDer; +use webpki::TrustAnchor; + +/// Machinery for Sigstore end entity certificate verification. +struct CertificateVerificationContext<'a> { + pub trust_anchors: Vec>, + pub intermediate_certs: Vec>, +} diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 1cf40d7eed..f6881f2574 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -18,7 +18,7 @@ use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION}; use ed25519::pkcs8::DecodePublicKey as ED25519DecodePublicKey; use rsa::{pkcs1v15, pss}; use sha2::{Digest, Sha256, Sha384}; -use signature::{DigestVerifier, Verifier}; +use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; use std::convert::TryFrom; use x509_cert::{der::referenced::OwnedToRef, spki::SubjectPublicKeyInfoOwned}; @@ -329,6 +329,68 @@ impl CosignVerificationKey { } } } + + /// Verify the signature provided has been actually generated by the given key + /// when signing the provided prehashed message. + pub fn verify_prehash(&self, signature: Signature, msg: &[u8]) -> Result<()> { + let sig = match signature { + Signature::Raw(data) => data.to_owned(), + Signature::Base64Encoded(data) => BASE64_STD_ENGINE.decode(data)?, + }; + + match self { + CosignVerificationKey::RSA_PSS_SHA256(inner) => { + let sig = pss::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::RSA_PSS_SHA384(inner) => { + let sig = pss::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::RSA_PSS_SHA512(inner) => { + let sig = pss::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::RSA_PKCS1_SHA256(inner) => { + let sig = pkcs1v15::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::RSA_PKCS1_SHA384(inner) => { + let sig = pkcs1v15::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::RSA_PKCS1_SHA512(inner) => { + let sig = pkcs1v15::Signature::try_from(sig.as_slice())?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + // ECDSA signatures are encoded in der. + CosignVerificationKey::ECDSA_P256_SHA256_ASN1(inner) => { + let sig = ecdsa::Signature::from_der(&sig)?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + CosignVerificationKey::ECDSA_P384_SHA384_ASN1(inner) => { + let sig = ecdsa::Signature::from_der(&sig)?; + inner + .verify_prehash(msg, &sig) + .map_err(|_| SigstoreError::PublicKeyVerificationError) + } + _ => unimplemented!("Ed25519 doesn't implement verify_prehash"), + } + } } #[cfg(test)] diff --git a/src/errors.rs b/src/errors.rs index 22b87e95cb..e6b6b723ff 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -109,6 +109,9 @@ pub enum SigstoreError { #[error("Certificate pool error: {0}")] CertificatePoolError(String), + #[error("Certificate invalid: {0}")] + InvalidCertError(String), + #[error("Signing session expired")] ExpiredSigningSession(), diff --git a/src/lib.rs b/src/lib.rs index 0c648b5f62..3346d8d57e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -288,5 +288,8 @@ pub mod tuf; mod bundle; pub use bundle::Bundle; +#[cfg(feature = "verify")] +pub mod verify; + #[cfg(feature = "sign")] pub mod sign; diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index 325989706c..0529d1128b 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -23,3 +23,4 @@ macro_rules! tuf_resource { } pub(crate) const SIGSTORE_ROOT: &[u8] = tuf_resource!("prod/root.json"); +pub(crate) const _SIGSTORE_TRUST_BUNDLE: &[u8] = tuf_resource!("prod/trusted_root.json"); diff --git a/src/verify/mod.rs b/src/verify/mod.rs new file mode 100644 index 0000000000..5954d45bd6 --- /dev/null +++ b/src/verify/mod.rs @@ -0,0 +1,24 @@ +// +// Copyright 2023 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. + +//! Verifier for Sigstore bundles and associated types and policies. +mod models; +pub use models::{VerificationError, VerificationMaterials, VerificationResult}; + +pub mod policy; +pub use policy::VerificationPolicy; + +mod verifier; +pub use verifier::Verifier; diff --git a/src/verify/models.rs b/src/verify/models.rs new file mode 100644 index 0000000000..dcf6e39b68 --- /dev/null +++ b/src/verify/models.rs @@ -0,0 +1,222 @@ +// +// Copyright 2023 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. + +use std::{ + cell::OnceCell, + io::{self, Read}, + str::FromStr, +}; + +use crate::{ + bundle::required, + bundle::Version as BundleVersion, + crypto::certificate::{is_leaf, is_root_ca}, + errors::SigstoreError, + rekor::models::log_entry, + rekor::models::{ + log_entry::{InclusionProof, Verification}, + LogEntry, + }, +}; + +use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use pkcs8::der::Decode; +use sha2::{Digest, Sha256}; +use sigstore_protobuf_specs::Bundle; +use thiserror::Error; +use x509_cert::Certificate; + +#[derive(Error, Debug)] +pub enum VerificationError { + #[error("Certificate expired before time of signing")] + CertificateExpired, + + #[error("Certificate malformed")] + CertificateMalformed, + + #[error("Failed to verify certificate")] + CertificateVerificationFailure, + + #[error("Certificate cannot be used for verification: {0}")] + CertificateTypeError(String), + + #[error("Failed to verify that the signature corresponds to the input")] + SignatureVerificationFailure, + + #[error("{0}")] + PolicyFailure(String), +} +pub type VerificationResult = Result<(), VerificationError>; + +pub struct VerificationMaterials { + pub input_digest: Vec, + pub certificate: Certificate, + pub signature: Vec, + rekor_entry: OnceCell, +} + +impl VerificationMaterials { + pub fn new( + input: &mut R, + certificate: Certificate, + signature: Vec, + offline: bool, + rekor_entry: Option, + ) -> Option { + let mut hasher = Sha256::new(); + io::copy(input, &mut hasher).ok()?; + + if offline && rekor_entry.is_none() { + // offline verification requires a Rekor entry + return None; + } + + let rekor_entry = if let Some(rekor_entry) = rekor_entry { + let cell = OnceCell::new(); + + // TODO(tnytown): Switch to setting if offline when Rekor fetching is implemented. + #[allow(clippy::unwrap_used)] + cell.set(rekor_entry).unwrap(); + + cell + } else { + Default::default() + }; + + Some(Self { + input_digest: hasher.finalize().to_vec(), + rekor_entry, + certificate, + signature, + }) + } + + /// Constructs a VerificationMaterials from the given Bundle. + /// + /// For details on bundle semantics, please refer to [VerificationMaterial]. + /// + /// [VerificationMaterial]: sigstore_protobuf_specs::DevSigstoreBundleV1VerificationMaterial + /// + /// TODO(tnytown): Determine if this type should yield SigstoreResult. + pub fn from_bundle(input: &mut R, bundle: Bundle, offline: bool) -> Option { + fn certificate_from_base64(encoded: &str) -> Option { + Certificate::from_der(&base64.decode(encoded).ok()?).ok() + } + + let certs = required!( + bundle; + verification_material.x_509_certificate_chain.certificates, + SigstoreError::SigstoreBundleMalformedError("Cannot find required field in bundle".to_string()) + ).ok()?; + + // Parse the certificates. The first entry in the chain MUST be a leaf certificate, and the + // rest of the chain MUST NOT include a root CA or any intermediate CAs that appear in an + // independent root of trust. + let certs = certs + .iter() + .map(|cert| certificate_from_base64(cert.raw_bytes.as_ref()?)) + .collect::>>()?; + let [leaf_cert, chain_certs @ ..] = &certs[..] else { + return None; + }; + + if is_leaf(leaf_cert).is_err() { + return None; + } + + for chain_cert in chain_certs { + if is_root_ca(chain_cert).is_ok() { + return None; + } + } + + let signature = base64 + .decode(required!(bundle; message_signature.signature)?) + .ok()?; + let tlog_entries = required!(bundle; verification_material.tlog_entries)?; + if tlog_entries.len() != 1 { + // Expected exactly one tlog entry. + return None; + } + let tlog_entry = &tlog_entries[0]; + + let inclusion_promise = &tlog_entry.inclusion_promise; + let inclusion_proof = tlog_entry.inclusion_proof.as_ref(); + + let has_checkpoint = required!(; inclusion_proof.checkpoint.envelope).is_some(); + match BundleVersion::from_str(&bundle.media_type?).ok()? { + BundleVersion::Bundle0_1 => { + if inclusion_promise.is_none() { + // 0.1 bundle must contain inclusion promise + return None; + } + + if inclusion_proof.is_some() && !has_checkpoint { + // TODO(tnytown): Act here. + // NOTE(jl): in sigstore-python, this is a no-op that prints a warning log. + } + } + BundleVersion::Bundle0_2 => { + inclusion_proof?; + if !has_checkpoint { + // inclusion proofs must contain checkpoints + return None; // FIXME(jl): this raises an error in sigstore-python. + } + } + } + + let parsed_inclusion_proof = if inclusion_proof.is_some() && has_checkpoint { + Some(InclusionProof { + checkpoint: required!(; inclusion_proof.checkpoint.envelope)?.clone(), + hashes: required!(; inclusion_proof.hashes)?.clone(), + log_index: required!(; inclusion_proof.log_index)?.parse().ok()?, + root_hash: required!(; inclusion_proof.log_index)?.clone(), + tree_size: required!(; inclusion_proof.tree_size)?.parse().ok()?, + }) + } else { + None + }; + + let canonicalized_body = { + let decoded = base64 + .decode(tlog_entry.canonicalized_body.as_ref()?) + .ok()?; + serde_json::from_slice(&decoded).ok()? + }; + // required!(tlog_entry; log_id.key_id)?.clone(); + let entry = LogEntry { + uuid: "".to_string(), + body: log_entry::Body::hashedrekord(canonicalized_body), + attestation: None, + integrated_time: required!(tlog_entry; integrated_time)?.parse().ok()?, + log_i_d: "".into(), + log_index: required!(tlog_entry; log_index)?.parse().ok()?, + verification: Verification { + inclusion_proof: parsed_inclusion_proof, + signed_entry_timestamp: required!(; inclusion_promise.signed_entry_timestamp)? + .clone(), + }, + }; + + Self::new(input, leaf_cert.clone(), signature, offline, Some(entry)) + } + + /// Retrieves the [LogEntry] for the materials. + pub fn rekor_entry(&self) -> &LogEntry { + // TODO(tnytown): Fetch online Rekor entry, confirm consistency, and get_or_init here. + #[allow(clippy::unwrap_used)] + self.rekor_entry.get().unwrap() + } +} diff --git a/src/verify/policy.rs b/src/verify/policy.rs new file mode 100644 index 0000000000..8f0791c50f --- /dev/null +++ b/src/verify/policy.rs @@ -0,0 +1,284 @@ +// Copyright 2023 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. + +//! Verifiers for certificate metadata. +//! +//! + +use const_oid::ObjectIdentifier; +use x509_cert::ext::pkix::{name::GeneralName, SubjectAltName}; + +use crate::verify::VerificationError; + +use super::models::VerificationResult; + +macro_rules! oids { + ($($name:ident = $value:literal),+) => { + $(const $name: ObjectIdentifier = ObjectIdentifier::new_unwrap($value);)+ + }; +} + +macro_rules! impl_policy { + ($policy:ident, $oid:expr, $doc:literal) => { + #[doc = $doc] + pub struct $policy(pub String); + + impl const_oid::AssociatedOid for $policy { + const OID: ObjectIdentifier = $oid; + } + + impl SingleX509ExtPolicy for $policy { + fn new>(val: S) -> Self { + Self(val.as_ref().to_owned()) + } + + fn name() -> &'static str { + stringify!($policy) + } + + fn value(&self) -> &str { + &self.0 + } + } + }; +} + +oids! { + OIDC_ISSUER_OID = "1.3.6.1.4.1.57264.1.1", + OIDC_GITHUB_WORKFLOW_TRIGGER_OID = "1.3.6.1.4.1.57264.1.2", + OIDC_GITHUB_WORKFLOW_SHA_OID = "1.3.6.1.4.1.57264.1.3", + OIDC_GITHUB_WORKFLOW_NAME_OID = "1.3.6.1.4.1.57264.1.4", + OIDC_GITHUB_WORKFLOW_REPOSITORY_OID = "1.3.6.1.4.1.57264.1.5", + OIDC_GITHUB_WORKFLOW_REF_OID = "1.3.6.1.4.1.57264.1.6", + OTHERNAME_OID = "1.3.6.1.4.1.57264.1.7" + +} + +/// A trait for policies that check a single textual value against a X.509 extension. +pub trait SingleX509ExtPolicy { + fn new>(val: S) -> Self; + fn name() -> &'static str; + fn value(&self) -> &str; +} + +impl VerificationPolicy for T { + fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + let extensions = cert.tbs_certificate.extensions.as_deref().unwrap_or(&[]); + let mut extensions = extensions.iter().filter(|ext| ext.extn_id == T::OID); + + // Check for exactly one extension. + let (Some(ext), None) = (extensions.next(), extensions.next()) else { + return Err(VerificationError::PolicyFailure( + "Cannot get policy extensions from certificate".into(), + )); + }; + + // Parse raw string without DER encoding. + let val = std::str::from_utf8(ext.extn_value.as_bytes()) + .expect("failed to parse constructed Extension!"); + + if val != self.value() { + Err(VerificationError::PolicyFailure(format!( + "Certificate's {} does not match (got {}, expected {})", + T::name(), + val, + self.value() + ))) + } else { + Ok(()) + } + } +} + +impl_policy!( + OIDCIssuer, + OIDC_ISSUER_OID, + "Checks the certificate's OIDC issuer." +); + +impl_policy!( + GitHubWorkflowTrigger, + OIDC_GITHUB_WORKFLOW_TRIGGER_OID, + "Checks the certificate's GitHub Actions workflow trigger." +); + +impl_policy!( + GitHubWorkflowSHA, + OIDC_GITHUB_WORKFLOW_SHA_OID, + "Checks the certificate's GitHub Actions workflow commit SHA." +); + +impl_policy!( + GitHubWorkflowName, + OIDC_GITHUB_WORKFLOW_NAME_OID, + "Checks the certificate's GitHub Actions workflow name." +); + +impl_policy!( + GitHubWorkflowRepository, + OIDC_GITHUB_WORKFLOW_REPOSITORY_OID, + "Checks the certificate's GitHub Actions workflow repository." +); + +impl_policy!( + GitHubWorkflowRef, + OIDC_GITHUB_WORKFLOW_REF_OID, + "Checks the certificate's GitHub Actions workflow ref." +); + +/// An interface that all policies must conform to. +pub trait VerificationPolicy { + fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult; +} + +/// The "any of" policy, corresponding to a logical OR between child policies. +/// +/// An empty list of child policies is considered trivially invalid. +pub struct AnyOf<'a> { + children: Vec<&'a dyn VerificationPolicy>, +} + +impl<'a> AnyOf<'a> { + pub fn new>(policies: I) -> Self { + Self { + children: policies.into_iter().collect(), + } + } +} + +impl VerificationPolicy for AnyOf<'_> { + fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + self.children + .iter() + .find(|policy| policy.verify(cert).is_ok()) + .ok_or(VerificationError::PolicyFailure(format!( + "0 of {} policies succeeded", + self.children.len() + ))) + .map(|_| ()) + } +} + +/// The "all of" policy, corresponding to a logical AND between child policies. +/// +/// An empty list of child policies is considered trivially invalid. +pub struct AllOf<'a> { + children: Vec<&'a dyn VerificationPolicy>, +} + +impl<'a> AllOf<'a> { + pub fn new>(policies: I) -> Self { + Self { + children: policies.into_iter().collect(), + } + } +} + +impl VerificationPolicy for AllOf<'_> { + fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + // Without this, we'd consider empty lists of child policies trivially valid. + // This is almost certainly not what the user wants and is a potential + // source of API misuse, so we explicitly disallow it. + if self.children.is_empty() { + return Err(VerificationError::PolicyFailure( + "no child policies to verify".into(), + )); + } + + let results = self.children.iter().map(|policy| policy.verify(cert)); + let failures: Vec<_> = results + .filter_map(|result| result.err()) + .map(|err| err.to_string()) + .collect(); + + if failures.is_empty() { + Ok(()) + } else { + Err(VerificationError::PolicyFailure(format!( + "{} of {} policies failed:\n- {}", + failures.len(), + self.children.len(), + failures.join("\n- ") + ))) + } + } +} + +pub(crate) struct UnsafeNoOp; + +impl VerificationPolicy for UnsafeNoOp { + fn verify(&self, _cert: &x509_cert::Certificate) -> VerificationResult { + eprintln!("unsafe (no-op) verification policy used! no verification performed!"); + VerificationResult::Ok(()) + } +} + +/// Verifies the certificate's "identity", corresponding to the X.509v3 SAN. +/// Identities are verified modulo an OIDC issuer, so the issuer's URI +/// is also required. +/// +/// Supported SAN types include emails, URIs, and Sigstore-specific "other names". +pub struct Identity { + identity: String, + issuer: OIDCIssuer, +} + +impl Identity { + pub fn new(identity: A, issuer: B) -> Self + where + A: AsRef, + B: AsRef, + { + Self { + identity: identity.as_ref().to_owned(), + issuer: OIDCIssuer::new(issuer), + } + } +} + +impl VerificationPolicy for Identity { + fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + if let err @ Err(_) = self.issuer.verify(cert) { + return err; + } + + let (_, san): (bool, SubjectAltName) = match cert.tbs_certificate.get() { + Ok(Some(result)) => result, + _ => return Err(VerificationError::CertificateMalformed), + }; + + let names: Vec<_> = san + .0 + .iter() + .filter_map(|name| match name { + GeneralName::Rfc822Name(name) => Some(name.as_str()), + GeneralName::UniformResourceIdentifier(name) => Some(name.as_str()), + GeneralName::OtherName(name) if name.type_id == OTHERNAME_OID => { + std::str::from_utf8(name.value.value()).ok() + } + _ => None, + }) + .collect(); + + if names.contains(&self.identity.as_str()) { + Ok(()) + } else { + Err(VerificationError::PolicyFailure(format!( + "Certificate's SANs do not match {}; actual SANs: {}", + self.identity, + names.join(", ") + ))) + } + } +} diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs new file mode 100644 index 0000000000..2645b87919 --- /dev/null +++ b/src/verify/verifier.rs @@ -0,0 +1,197 @@ +// Copyright 2023 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. + +use std::cell::OnceCell; + +use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; +use pkcs8::der::Encode; +use webpki::types::UnixTime; +use x509_cert::ext::pkix::{ExtendedKeyUsage, KeyUsage}; + +use crate::{ + crypto::{CertificatePool, CosignVerificationKey, Signature}, + errors::Result as SigstoreResult, + rekor::apis::configuration::Configuration as RekorConfiguration, + tuf::{Repository, SigstoreRepository}, + verify::VerificationError, +}; + +use super::{models::VerificationMaterials, policy::VerificationPolicy, VerificationResult}; + +pub struct Verifier<'a, R: Repository> { + #[allow(dead_code)] + rekor_config: RekorConfiguration, + trust_repo: R, + cert_pool: OnceCell>, +} + +impl<'a, R: Repository> Verifier<'a, R> { + pub fn new(rekor_config: RekorConfiguration, trust_repo: R) -> SigstoreResult { + Ok(Self { + rekor_config, + cert_pool: Default::default(), + trust_repo, + }) + } + + /// TODO(tnytown): Evil (?) interior mutability hack to work around lifetime issues. + fn cert_pool(&'a self) -> SigstoreResult<&CertificatePool<'a>> { + let init_cert_pool = || { + let certs = self.trust_repo.fulcio_certs()?; + CertificatePool::from_certificates(certs, []) + }; + + let cert_pool = init_cert_pool()?; + Ok(self.cert_pool.get_or_init(|| cert_pool)) + } + + pub fn verify( + &'a self, + materials: VerificationMaterials, + policy: &impl VerificationPolicy, + ) -> VerificationResult { + let store = self + .cert_pool() + .expect("Failed to construct certificate pool"); + + // In order to verify an artifact, we need to achieve the following: + // + // 1) Verify that the signing certificate is signed by the certificate + // chain and that the signing certificate was valid at the time + // of signing. + // 2) Verify that the signing certificate belongs to the signer. + // 3) Verify that the artifact signature was signed by the public key in the + // signing certificate. + // 4) Verify that the Rekor entry is consistent with the other signing + // materials (preventing CVE-2022-36056) + // 5) Verify the inclusion proof supplied by Rekor for this artifact, + // if we're doing online verification. + // 6) Verify the Signed Entry Timestamp (SET) supplied by Rekor for this + // artifact. + // 7) Verify that the signing certificate was valid at the time of + // signing by comparing the expiry against the integrated timestamp. + + // 1) Verify that the signing certificate is signed by the root certificate and that the + // signing certificate was valid at the time of signing. + + // 1) Verify that the signing certificate is signed by the certificate + // chain and that the signing certificate was valid at the time + // of signing. + let issued_at = materials + .certificate + .tbs_certificate + .validity + .not_before + .to_unix_duration(); + let cert_der = &materials + .certificate + .to_der() + .expect("failed to DER-encode constructed Certificate!"); + store + .verify_cert_with_time(cert_der, UnixTime::since_unix_epoch(issued_at)) + .or(Err(VerificationError::CertificateVerificationFailure))?; + + // 2) Verify that the signing certificate belongs to the signer. + + // TODO(tnytown): How likely is a malformed certificate in this position? Do we want to + // account for it and create an error type as opposed to unwrapping? + let (_, key_usage_ext): (bool, KeyUsage) = materials + .certificate + .tbs_certificate + .get() + .expect("Malformed certificate") + .expect("Malformed certificate"); + + if !key_usage_ext.digital_signature() { + return Err(VerificationError::CertificateTypeError( + "Key usage is not of type `digital signature`".into(), + )); + } + + let (_, extended_key_usage_ext): (bool, ExtendedKeyUsage) = materials + .certificate + .tbs_certificate + .get() + .expect("Malformed certificate") + .expect("Malformed certificate"); + + if !extended_key_usage_ext.0.contains(&ID_KP_CODE_SIGNING) { + return Err(VerificationError::CertificateTypeError( + "Extended key usage does not contain `code signing`".into(), + )); + } + + policy.verify(&materials.certificate)?; + + // 3) Verify that the signature was signed by the public key in the signing certificate + let signing_key: SigstoreResult = (&materials + .certificate + .tbs_certificate + .subject_public_key_info) + .try_into(); + + let signing_key = + signing_key.expect("Malformed certificate (cannot deserialize public key)"); + + signing_key + .verify_prehash( + Signature::Raw(&materials.signature), + &materials.input_digest, + ) + .or(Err(VerificationError::SignatureVerificationFailure))?; + + // 4) Verify that the Rekor entry is consistent with the other signing + // materials + let log_entry = materials.rekor_entry(); + + // 5) Verify the inclusion proof supplied by Rekor for this artifact, + // if we're doing online verification. + // TODO(tnytown): Merkle inclusion + + // 6) Verify the Signed Entry Timestamp (SET) supplied by Rekor for this + // artifact. + // TODO(tnytown) SET verification + + // 7) Verify that the signing certificate was valid at the time of + // signing by comparing the expiry against the integrated timestamp. + let integrated_time = log_entry.integrated_time as u64; + let not_before = materials + .certificate + .tbs_certificate + .validity + .not_before + .to_unix_duration() + .as_secs(); + let not_after = materials + .certificate + .tbs_certificate + .validity + .not_after + .to_unix_duration() + .as_secs(); + if !(not_before <= integrated_time && integrated_time <= not_after) { + return Err(VerificationError::CertificateExpired); + } + + Ok(()) + } +} + +impl<'a> Verifier<'a, SigstoreRepository> { + pub fn production() -> SigstoreResult> { + let updater = SigstoreRepository::new(None)?; + + Verifier::<'a, SigstoreRepository>::new(Default::default(), updater) + } +} diff --git a/tests/conformance/Cargo.toml b/tests/conformance/Cargo.toml index 6c0c5d16a9..b5d896c3e3 100644 --- a/tests/conformance/Cargo.toml +++ b/tests/conformance/Cargo.toml @@ -8,6 +8,8 @@ license = "Apache-2.0" [dependencies] clap = { version = "4.0.8", features = ["derive"] } +anyhow = "1.0.75" +serde_json = "1.0.107" sigstore = { path = "../../" } [[bin]] diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index c52c822fe1..801cc6b064 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -16,7 +16,16 @@ // CLI implemented to specification: // https://github.com/sigstore/sigstore-conformance/blob/main/docs/cli_protocol.md +use std::{fs, process::exit}; + +use anyhow::anyhow; use clap::{Parser, Subcommand}; +use sigstore::{ + oauth::IdentityToken, + sign::SigningContext, + verify::VerificationMaterials, + verify::{policy, Verifier}, +}; #[derive(Parser, Debug)] struct Cli { @@ -106,4 +115,62 @@ struct VerifyBundle { fn main() { let cli = Cli::parse(); + + let result = match cli.command { + Commands::SignBundle(args) => sign_bundle(args), + Commands::VerifyBundle(args) => verify_bundle(args), + _ => unimplemented!("sig/cert commands"), + }; + + if let Err(error) = result { + eprintln!("Operation failed:\n{error:?}"); + exit(-1); + } + + eprintln!("Operation succeeded!"); +} + +fn sign_bundle(args: SignBundle) -> anyhow::Result<()> { + let SignBundle { + identity_token, + bundle, + artifact, + } = args; + let identity_token = IdentityToken::try_from(identity_token.as_str())?; + let bundle = fs::File::create(bundle)?; + let mut artifact = fs::File::open(artifact)?; + + let context = SigningContext::production(); + let signer = context.signer(identity_token); + + let signing_artifact = signer.sign(&mut artifact)?; + let bundle_data = signing_artifact.to_bundle(); + + serde_json::to_writer(bundle, &bundle_data)?; + + Ok(()) +} + +fn verify_bundle(args: VerifyBundle) -> anyhow::Result<()> { + let VerifyBundle { + bundle, + certificate_identity, + certificate_oidc_issuer, + artifact, + } = args; + let bundle = fs::File::open(bundle)?; + let mut artifact = fs::File::open(artifact)?; + + let bundle: sigstore::Bundle = serde_json::from_reader(bundle)?; + let materials = VerificationMaterials::from_bundle(&mut artifact, bundle, true) + .ok_or(anyhow!("Unable to construct VerificationMaterials"))?; + + let verifier = Verifier::production()?; + + verifier.verify( + materials, + &policy::Identity::new(certificate_identity, certificate_oidc_issuer), + )?; + + Ok(()) } diff --git a/tests/data/repository/1.registry.npmjs.org.json b/tests/data/repository/1.registry.npmjs.org.json new file mode 100644 index 0000000000..1c8ec2165b --- /dev/null +++ b/tests/data/repository/1.registry.npmjs.org.json @@ -0,0 +1,23 @@ +{ + "signed": { + "_type": "targets", + "spec_version": "1.0", + "version": 1, + "expires": "2024-09-29T16:47:20Z", + "targets": { + "registry.npmjs.org/keys.json": { + "length": 1017, + "hashes": { + "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", + "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" + } + } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3044022059bf01a64dd2793d5b630e26d7b6e455b0d6d8b47c23049ae856a122e5cec2ab022068b99b8bb39457e53d500f698cb43f9e640958ed26e5d3a47c29619df61889bc" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/1.root.json b/tests/data/repository/1.root.json index dcc71f963a..4ca0da87fe 100644 --- a/tests/data/repository/1.root.json +++ b/tests/data/repository/1.root.json @@ -1,130 +1,65 @@ { - "signatures": [ - { - "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "sig": "30450221008a35d51da0f845301a5eac98ad0df00a934f59b709c1eaf81c86be734d9356f80220742942325599749800f52675f6efe124345980a2a636c0dc76f9caf9fc3123b0" - }, - { - "keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "sig": "3045022100ef9157ece2a09baec1eab80adfc00b04da20b1f9a0d1b47c5dabc4506719ef2c022074f72acd57398e4ddc8c2a5040df902961e9615dca48f3fbe38cbb506e500066" - }, - { - "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "sig": "30450220420fdc9a09cd069b8b15fd8db9cedf7d0dee75871bd1cfee77c926d4120a770002210097553b5ad0d6b4a13902ed37509638bb63a9009f78230cd56c802909ffbfead7" - }, - { - "keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "sig": "304502202aaf32e66f90752f658672b085ecfe45cc1ad31ee6cf5c9ad05f3267685f8d88022100b5df02acdaa371123db9d7a42219553fe079b230b168833e951be7ee56ded347" - }, - { - "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", - "sig": "304402205d420c7d05c58980c1c9f7d221f53b5334aae27a447d2a91c2ceddd685269749022039ec83e51f8e1779d7f0142dfa4a5bbecfe327fc0b91b7416090fea2416fd53a" - } - ], "signed": { "_type": "root", - "consistent_snapshot": false, - "expires": "2021-12-18T13:28:12.99008-06:00", + "spec_version": "1.0", + "version": 1, + "expires": "2024-09-29T16:47:17Z", "keys": { - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": { + "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], - "keytype": "ecdsa-sha2-nistp256", "keyval": { - "public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7" - }, - "scheme": "ecdsa-sha2-nistp256" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } }, - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": { + "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], - "keytype": "ecdsa-sha2-nistp256", "keyval": { - "public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b" - }, - "scheme": "ecdsa-sha2-nistp256" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" + } } }, "roles": { "root": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "snapshot": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], - "threshold": 3 + "threshold": 1 }, "targets": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "timestamp": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], - "threshold": 3 + "threshold": 1 } }, - "spec_version": "1.0", - "version": 1 - } + "consistent_snapshot": true + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "304602210085927cdb96e1d9d0876bfc26b6ceea7421a54f959e30b9af3e12d31f6c750543022100dde611b58a1f2b9fb26c43767138c68f4422cdeb898c8b63f3f0193791030d12" + } + ] } \ No newline at end of file diff --git a/tests/data/repository/1.snapshot.json b/tests/data/repository/1.snapshot.json new file mode 100644 index 0000000000..fcb179878c --- /dev/null +++ b/tests/data/repository/1.snapshot.json @@ -0,0 +1,32 @@ +{ + "signed": { + "_type": "snapshot", + "spec_version": "1.0", + "version": 1, + "expires": "2024-04-19T16:47:48Z", + "meta": { + "registry.npmjs.org.json": { + "length": 713, + "hashes": { + "sha256": "17b361687dbb401c2d51d7ce21688d13547eae7f8e7b2183b7dd6d94fa675705", + "sha512": "3f60a08cdbab650ece48ded43b54943dc816580fdb2f5a2a20c30e878eb2489ab817f0308666cac80da03d75d6f5b71959431b1ba7794335fece8a4ed635eb4d" + }, + "version": 1 + }, + "targets.json": { + "length": 4518, + "hashes": { + "sha256": "cc62e5fb1644717c7429c82b6a1cbd085008f9a2e07aad38573f8fdf9d55386c", + "sha512": "5709bc76bc35da403a9a0a5ec96890db49e797c986eda9e5f7973938dbccad96838c8136617c91f5218cfd919d93745d3942ca6d50a52b5fd0e662e6876b395f" + }, + "version": 1 + } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "304602210082d244d5dab0c20ee07b3229964beffaa8bb0bdf4c5107e2f764619878d124a2022100e7c50116ef636c41348ec49a7502f1c98037238b9c717ee781b62c5154f5a1f0" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/1.targets.json b/tests/data/repository/1.targets.json new file mode 100644 index 0000000000..6844bad771 --- /dev/null +++ b/tests/data/repository/1.targets.json @@ -0,0 +1,148 @@ +{ + "signed": { + "_type": "targets", + "spec_version": "1.0", + "version": 1, + "expires": "2024-09-29T16:47:20Z", + "targets": { + "artifact.pub": { + "length": 177, + "hashes": { + "sha256": "59ebf97a9850aecec4bc39c1f5c1dc46e6490a6b5fd2a6cacdcac0c3a6fc4cbf", + "sha512": "308fd1d1d95d7f80aa33b837795251cc3e886792982275e062409e13e4e236ffc34d676682aa96fdc751414de99c864bf132dde71581fa651c6343905e3bf988" + }, + "custom": { + "sigstore": { + "status": "Active", + "usage": "Unknown" + } + } + }, + "ctfe.pub": { + "length": 177, + "hashes": { + "sha256": "7fcb94a5d0ed541260473b990b99a6c39864c1fb16f3f3e594a5a3cebbfe138a", + "sha512": "4b20747d1afe2544238ad38cc0cc3010921b177d60ac743767e0ef675b915489bd01a36606c0ff83c06448622d7160f0d866c83d20f0c0f44653dcc3f9aa0bd4" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstore.dev/test", + "usage": "CTFE" + } + } + }, + "ctfe_2022.pub": { + "length": 178, + "hashes": { + "sha256": "270488a309d22e804eeb245493e87c667658d749006b9fee9cc614572d4fbbdc", + "sha512": "e83fa4f427b24ee7728637fad1b4aa45ebde2ba02751fa860694b1bb16059a490328f9985e51cc70e4d237545315a1bc866dc4fdeef2f6248d99cc7a6077bf85" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstore.dev/2022", + "usage": "CTFE" + } + } + }, + "fulcio.crt.pem": { + "length": 744, + "hashes": { + "sha256": "f360c53b2e13495a628b9b8096455badcb6d375b185c4816d95a5d746ff29908", + "sha512": "0713252a7fd17f7f3ab12f88a64accf2eb14b8ad40ca711d7fe8b4ecba3b24db9e9dffadb997b196d3867b8f9ff217faf930d80e4dab4e235c7fc3f07be69224" + }, + "custom": { + "sigstore": { + "status": "Expired", + "uri": "https://fulcio.sigstore.dev", + "usage": "Fulcio" + } + } + }, + "fulcio_intermediate_v1.crt.pem": { + "length": 789, + "hashes": { + "sha256": "f8cbecf186db7714624a5f4e99da31a917cbef70a94dd6921f5c3ca969dfe30a", + "sha512": "0f99f47dbc26c5f1e3cba0bfd9af4245a26e5cb735d6ef005792ec7e603f66fdb897de985973a6e50940ca7eff5e1849719e967b5ad2dac74a29115a41cf6f21" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstore.dev", + "usage": "Fulcio" + } + } + }, + "fulcio_v1.crt.pem": { + "length": 740, + "hashes": { + "sha256": "f989aa23def87c549404eadba767768d2a3c8d6d30a8b793f9f518a8eafd2cf5", + "sha512": "f2e33a6dc208cee1f51d33bbea675ab0f0ced269617497985f9a0680689ee7073e4b6f8fef64c91bda590d30c129b3070dddce824c05bc165ac9802f0705cab6" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstore.dev", + "usage": "Fulcio" + } + } + }, + "rekor.pub": { + "length": 178, + "hashes": { + "sha256": "dce5ef715502ec9f3cdfd11f8cc384b31a6141023d3e7595e9908a81cb6241bd", + "sha512": "0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://rekor.sigstore.dev", + "usage": "Rekor" + } + } + }, + "trusted_root.json": { + "length": 4567, + "hashes": { + "sha256": "cec894ad77f79b1cb324150f6363012bcef7492954f3ab9134f932e6aa2e2e20", + "sha512": "08be2fd75c19e654caad30852847c566f97e6245f2bbcc54d347d6bdec7e879135e3395b5633b9e3b85d739fdb9b4eb8c09ddc70495792bc2ea65c8caf770d27" + } + } + }, + "delegations": { + "keys": { + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } + } + }, + "roles": [ + { + "name": "registry.npmjs.org", + "keyids": [ + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" + ], + "threshold": 1, + "terminating": true, + "paths": [ + "registry.npmjs.org/*" + ] + } + ] + } + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "304402201662b260e99e59f7271bd9e3fb01aa47a399bef8c5ec808bea6d40ae2d93625d022042fd2a275d84196dc50e17ca9c9408a34349372410febc7217415b11eb978bbb" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/2.registry.npmjs.org.json b/tests/data/repository/2.registry.npmjs.org.json new file mode 100644 index 0000000000..d53f15267b --- /dev/null +++ b/tests/data/repository/2.registry.npmjs.org.json @@ -0,0 +1,23 @@ +{ + "signed": { + "_type": "targets", + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:55Z", + "targets": { + "registry.npmjs.org/keys.json": { + "length": 1017, + "hashes": { + "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", + "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" + } + } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3045022057b9fc8afd9feaf45cf3173d3420fdcd6b68c22e4ef7b47e80a6887e1f20246c0221009f39c42fac630ab354c5197288c9a82ab6d46a59b423f81fff719da57cff16ab" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/2.root.json b/tests/data/repository/2.root.json index 386ebe62c1..f848d7d846 100644 --- a/tests/data/repository/2.root.json +++ b/tests/data/repository/2.root.json @@ -1,144 +1,65 @@ { - "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", + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:11Z", "keys": { - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d": { + "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], - "keytype": "ecdsa-sha2-nistp256", "keyval": { - "public": "04fa1a3e42f2300cd3c5487a61509348feb1e936920fef2f83b7cd5dbe7ba045f538725ab8f18a666e6233edb7e0db8766c8dc336633449c5e1bbe0c182b02df0b" - }, - "scheme": "ecdsa-sha2-nistp256" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } }, - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": { + "scheme": "ecdsa-sha2-nistp256", "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" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" + } } }, "roles": { "root": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "snapshot": { "keyids": [ - "fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], "threshold": 1 }, "targets": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "timestamp": { "keyids": [ - "b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], "threshold": 1 } }, - "spec_version": "1.0", - "version": 2 - } + "consistent_snapshot": true + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "3045022057bbd23dd9f69f8280c5e5d2b0a0b1ace98d6d8efa0f59ef0a3190188f6e2c89022100b39e6c24091c4271d2b8b4cfa75e6120638b276fbffddda8da5bca1778c8f08c" + } + ] } \ No newline at end of file diff --git a/tests/data/repository/2.snapshot.json b/tests/data/repository/2.snapshot.json new file mode 100644 index 0000000000..6c1e4dd147 --- /dev/null +++ b/tests/data/repository/2.snapshot.json @@ -0,0 +1,32 @@ +{ + "signed": { + "_type": "snapshot", + "spec_version": "1.0", + "version": 2, + "expires": "2028-04-19T21:11:16Z", + "meta": { + "registry.npmjs.org.json": { + "length": 715, + "hashes": { + "sha256": "4dc55b2b468b0d1c9629c457c5cfce2cc1c330c59c5a7cf71cb7549f1ef76f1d", + "sha512": "278f4b6112db9d4bd9366e1717cf710ad7eacf44605fd4f894c3374fc5dff850a1a03c24c4a885d050a4ac1a86fa6929537fae12d8c2864c8e0c239b382d5556" + }, + "version": 2 + }, + "targets.json": { + "length": 4120, + "hashes": { + "sha256": "095d093de09350cec021828f49361688b5dd692486ad7bfb03d4150b3269ef8a", + "sha512": "97b9c75f49fb41eaf2f33c5f58b125febc3bbecd4c97f6edd0901423a231e4d0c5760d4780bc180a364d7198b5e0710f07ee0abf84dcd163fe3348d6bce26fab" + }, + "version": 2 + } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3044022013bf1032cf0a37d9f88ab9d33d0abb7a932efd95fadbc354fc21a807c7be29ef0220677e651c3e67e0728591faa20c5a09b8a16953038c3ceeffb7d9cfec766b3245" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/2.targets.json b/tests/data/repository/2.targets.json new file mode 100644 index 0000000000..dea42487f0 --- /dev/null +++ b/tests/data/repository/2.targets.json @@ -0,0 +1,135 @@ +{ + "signed": { + "_type": "targets", + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:55Z", + "targets": { + "ctfe.pub": { + "length": 775, + "hashes": { + "sha256": "bd7a6812a1f239dfddbbb19d36c7423d21510da56d466ba5018401959cd66037", + "sha512": "b861189e48df51186a39612230fba6b02af951f7b35ad9375e8ca182d0e085d470e26d69f7cd4d7450a0f223991e8e5a4ddf8f1968caa15255de8e37035af43a" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/test", + "usage": "CTFE" + } + } + }, + "ctfe_2022.pub": { + "length": 178, + "hashes": { + "sha256": "910d899c7763563095a0fe684c8477573fedc19a78586de6ecfbfd8f289f5423", + "sha512": "ab975a75600fc366a837536d0dcba841b755552d21bb114498ff8ac9d2403f76643f5b91269bce5d124a365514719a3edee9dcc2b046cb173f51af659911fcd3" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/2022", + "usage": "CTFE" + } + } + }, + "ctfe_2022_2.pub": { + "length": 178, + "hashes": { + "sha256": "7054b4f15f969daca1c242bb9e77527abaf0b9acf9818a2a35144e4b32b20dc6", + "sha512": "3d035f94e1b14ac84627a28afdbed9a34861fb84239f76d73aa1a99f52262bfd95c4fa0ee71f1fd7e3bfb998d89cd5e0f0eafcff9fa7fa87c6e23484fc1e0cec" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/2022-2", + "usage": "CTFE" + } + } + }, + "fulcio.crt.pem": { + "length": 741, + "hashes": { + "sha256": "0e6b0442485ad552bea5f62f11c29e2acfda35307d7538430b4cc1dbef49bff1", + "sha512": "c69ae618883a0c89c282c0943a1ad0c16b0a7788f74e47a1adefc631dac48a0c4449d8c3de7455ae7d772e43c4a87e341f180b0614a46a86006969f8a7b84532" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstage.dev", + "usage": "Fulcio" + } + } + }, + "fulcio_intermediate.crt.pem": { + "length": 790, + "hashes": { + "sha256": "782868913fe13c385105ddf33e827191386f58da40a931f2075a7e27b1b6ac7b", + "sha512": "90659875a02f73d1026055427c6d857c556e410e23748ff88aeb493227610fd2f5fbdd95ef2a21565f91438dfb3e073f50c4c9dd06f9a601b5d9b064d5cb60b4" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstage.dev", + "usage": "Fulcio" + } + } + }, + "rekor.pub": { + "length": 178, + "hashes": { + "sha256": "1d80b8f72505a43e65e6e125247cd508f61b459dc457c1d1bcb78d96e1760959", + "sha512": "09ab08698a67354a95d3b8897d9ce7eaef05f06f5ed5f0202d79c228579858ecc5816b7e1b7cc6786abe7d6aaa758e1fcb05900cb749235186c3bf9522d6d7ce" + }, + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://rekor.sigstage.dev", + "usage": "Rekor" + } + } + }, + "trusted_root.json": { + "length": 4521, + "hashes": { + "sha256": "6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b", + "sha512": "fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963" + } + } + }, + "delegations": { + "keys": { + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } + } + }, + "roles": [ + { + "name": "registry.npmjs.org", + "keyids": [ + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" + ], + "threshold": 1, + "terminating": true, + "paths": [ + "registry.npmjs.org/*" + ] + } + ] + } + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "304502210090b089087d1b17b2517c464b7774d76d3ea558ffca874eed63ccbee8f6bc3b76022022b56f551bcd0ac8a9c35cd0724ac5b00b4984544cbf812f47f276a9b48db8db" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/registry.npmjs.org.json b/tests/data/repository/registry.npmjs.org.json new file mode 100644 index 0000000000..d53f15267b --- /dev/null +++ b/tests/data/repository/registry.npmjs.org.json @@ -0,0 +1,23 @@ +{ + "signed": { + "_type": "targets", + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:55Z", + "targets": { + "registry.npmjs.org/keys.json": { + "length": 1017, + "hashes": { + "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", + "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" + } + } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3045022057b9fc8afd9feaf45cf3173d3420fdcd6b68c22e4ef7b47e80a6887e1f20246c0221009f39c42fac630ab354c5197288c9a82ab6d46a59b423f81fff719da57cff16ab" + } + ] +} \ No newline at end of file diff --git a/tests/data/repository/rekor.json b/tests/data/repository/rekor.json deleted file mode 100644 index f86930d537..0000000000 --- a/tests/data/repository/rekor.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "signatures": [ - { - "keyid": "ae0c689c6347ada7359df48934991f4e013193d6ddf3482a5ffb293f74f3b217", - "sig": "3045022076eadd73f6664bac5cc91f12d3a7ddcdd53f9bde661f147651196ff66e7235d1022100f7b3143792405f9e8a75331a05d4128bdf083de302801e99c3d027919a4b03da" - } - ], - "signed": { - "_type": "targets", - "expires": "2022-05-11T19:10:11Z", - "spec_version": "1.0", - "targets": { - "rekor.0.pub": { - "hashes": { - "sha256": "dce5ef715502ec9f3cdfd11f8cc384b31a6141023d3e7595e9908a81cb6241bd", - "sha512": "0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35" - }, - "length": 178 - } - }, - "version": 1 - } -} \ No newline at end of file diff --git a/tests/data/repository/root.json b/tests/data/repository/root.json index 386ebe62c1..f848d7d846 100644 --- a/tests/data/repository/root.json +++ b/tests/data/repository/root.json @@ -1,144 +1,65 @@ { - "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", + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:11Z", "keys": { - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d": { + "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], - "keytype": "ecdsa-sha2-nistp256", "keyval": { - "public": "04fa1a3e42f2300cd3c5487a61509348feb1e936920fef2f83b7cd5dbe7ba045f538725ab8f18a666e6233edb7e0db8766c8dc336633449c5e1bbe0c182b02df0b" - }, - "scheme": "ecdsa-sha2-nistp256" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } }, - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7" - }, - "scheme": "ecdsa-sha2-nistp256" - }, - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": { + "scheme": "ecdsa-sha2-nistp256", "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" + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" + } } }, "roles": { "root": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "snapshot": { "keyids": [ - "fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], "threshold": 1 }, "targets": { "keyids": [ - "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" ], - "threshold": 3 + "threshold": 1 }, "timestamp": { "keyids": [ - "b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d" + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" ], "threshold": 1 } }, - "spec_version": "1.0", - "version": 2 - } + "consistent_snapshot": true + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "3045022057bbd23dd9f69f8280c5e5d2b0a0b1ace98d6d8efa0f59ef0a3190188f6e2c89022100b39e6c24091c4271d2b8b4cfa75e6120638b276fbffddda8da5bca1778c8f08c" + } + ] } \ No newline at end of file diff --git a/tests/data/repository/snapshot.json b/tests/data/repository/snapshot.json index 61636531c6..6c1e4dd147 100644 --- a/tests/data/repository/snapshot.json +++ b/tests/data/repository/snapshot.json @@ -1,48 +1,32 @@ { - "signatures": [ - { - "keyid": "fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451", - "sig": "3046022100f59f6f92d8c61519afd0de0642ff45419ac960954cf412549874c247c6ae509902210085da85c9df818c3072c0b7744b75e92d2ee521402d4bac77c985b8fc6d138e41" - } - ], "signed": { "_type": "snapshot", - "expires": "2022-01-05T00:40:06Z", + "spec_version": "1.0", + "version": 2, + "expires": "2028-04-19T21:11:16Z", "meta": { - "rekor.json": { + "registry.npmjs.org.json": { + "length": 715, "hashes": { - "sha256": "a7412a87f8d7b330e0380b19a4a76c00357c39a1aa7f56fd87445d4e12faafe4", - "sha512": "720cb3c42bac50c5bc3cb7076e730301ef29f1893ea52e25f9393fc05851c7a531638c42d9fc992969805982a2bf51d676e33d28a7382ea589b5a9f87474c63f" + "sha256": "4dc55b2b468b0d1c9629c457c5cfce2cc1c330c59c5a7cf71cb7549f1ef76f1d", + "sha512": "278f4b6112db9d4bd9366e1717cf710ad7eacf44605fd4f894c3374fc5dff850a1a03c24c4a885d050a4ac1a86fa6929537fae12d8c2864c8e0c239b382d5556" }, - "length": 697, - "version": 1 - }, - "root.json": { - "hashes": { - "sha256": "f5ad897c9414cca99629f400ac3585e41bd8ebb44c5af07fb08dd636a9eced9c", - "sha512": "7445ddfdd338ef786c324fc3d68f75be28cb95b7fb581d2a383e3e5dde18aa17029a5636ec0a22e9631931bbcb34057788311718ea41e21e7cdd3c0de13ede42" - }, - "length": 5297, "version": 2 }, - "staging.json": { - "hashes": { - "sha256": "c7f32379c2a76f0ec0af84e86794a8f4fe285e44fb62f336d598810dccdc7343", - "sha512": "5462cb15fe5248a12cc12387a732ad43caf42391361f36113ea3d4b7e5e193cdf39fbe91c309c0691134377cb83afeba50cf6d711537d8280ce16ce9cd8752ba" - }, - "length": 399, - "version": 1 - }, "targets.json": { + "length": 4120, "hashes": { - "sha256": "18d10c07c8d6bd7484772b02dcc988d0abf8a0fa379d5893a502410590c17fe6", - "sha512": "c2ba2a84820288997c8fae264776df7b262dde97c4f9e0320ad354879ce5afabd1d43494734fecffd23253442a14cfe217787de8b65cf7fd1f03130b72a0767c" + "sha256": "095d093de09350cec021828f49361688b5dd692486ad7bfb03d4150b3269ef8a", + "sha512": "97b9c75f49fb41eaf2f33c5f58b125febc3bbecd4c97f6edd0901423a231e4d0c5760d4780bc180a364d7198b5e0710f07ee0abf84dcd163fe3348d6bce26fab" }, - "length": 4167, "version": 2 } - }, - "spec_version": "1.0", - "version": 6 - } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3044022013bf1032cf0a37d9f88ab9d33d0abb7a932efd95fadbc354fc21a807c7be29ef0220677e651c3e67e0728591faa20c5a09b8a16953038c3ceeffb7d9cfec766b3245" + } + ] } \ No newline at end of file diff --git a/tests/data/repository/staging.json b/tests/data/repository/staging.json deleted file mode 100644 index 084010de75..0000000000 --- a/tests/data/repository/staging.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "signatures": [ - { - "keyid": "b811bd53f2d7adcf5d93e6bb4a8ed2e0ca0f83d454a3e51f105c8e8376bc80d4", - "sig": "304502204486f7b23eadb69df87776ac7a4938ac75a8a2b2e93c84c05d962373837ea91c022100aaeb0fa587430f49618711bb4bd0c1092637c22c223d03c0f1b5a09baea0ed9f" - } - ], - "signed": { - "_type": "targets", - "expires": "2022-02-11T20:10:16Z", - "spec_version": "1.0", - "targets": {}, - "version": 1 - } -} \ No newline at end of file diff --git a/tests/data/repository/targets.json b/tests/data/repository/targets.json index b26926a438..dea42487f0 100644 --- a/tests/data/repository/targets.json +++ b/tests/data/repository/targets.json @@ -1,117 +1,135 @@ { - "signatures": [ - { - "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", - "sig": "3046022100cc1b2ed390e75a112c0fdd6bcbd8bb775300a410f5737ae39996b1858753c8e4022100b591f73370e9378914fb2fab837f700661abd1a74c680f139f6164ec12cb538f" - }, - { - "keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", - "sig": "3045022100bc6c45a125e45507339af96aa63983e847565c769f20d7d71bcd2deb7bd36ea902202bf6bd3b76d434c318287899e53f64b4dc178eb0ba403080f1c4fba88a2177ca" - }, - { - "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", - "sig": "304502210085d5bc8a158d31536b4e76cddceef25185c7abbe9091b84f5f2b0d615d9b4ee90220136a36fed2d5986c2519b7d165556f20dfe41fddececda48dffa8dec5258cb95" - }, - { - "keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", - "sig": "304402202fe73a61dfe05b4202bc50f66e52bba3d3475134434dab9576735caed659b03c0220449755a87f4dab9961566f10477204637b2415f87e162b58a23b13327dec53e3" - }, - { - "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", - "sig": "304602210091f453ef75c5178299175734355a65a2fc2d0ee137410f46ba8439d99037fc08022100fc800d15f0b751fa225a77542928f4264835c013054a5c409c674e2ea5a70384" - } - ], "signed": { "_type": "targets", - "delegations": { - "keys": { - "ae0c689c6347ada7359df48934991f4e013193d6ddf3482a5ffb293f74f3b217": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "043463588ae9df33a419d1099761245af52aaf7e638b2047bc0f739a62de9808c50a21ea8a1a273799f857f31a1bcb66e6661dd9d5ac7ac3ca260b0b8130c3fed8" - }, - "scheme": "ecdsa-sha2-nistp256" + "spec_version": "1.0", + "version": 2, + "expires": "2028-09-29T21:10:55Z", + "targets": { + "ctfe.pub": { + "length": 775, + "hashes": { + "sha256": "bd7a6812a1f239dfddbbb19d36c7423d21510da56d466ba5018401959cd66037", + "sha512": "b861189e48df51186a39612230fba6b02af951f7b35ad9375e8ca182d0e085d470e26d69f7cd4d7450a0f223991e8e5a4ddf8f1968caa15255de8e37035af43a" }, - "b811bd53f2d7adcf5d93e6bb4a8ed2e0ca0f83d454a3e51f105c8e8376bc80d4": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ecdsa-sha2-nistp256", - "keyval": { - "public": "041b4b13a6e7110292d284c0dbfc3962a12d2a779a800c99aff59c6afe779296943c75d84aa5bad0be28e4061cf93e0cd3d372d9b2f75ea9f29b907cbccd82006f" - }, - "scheme": "ecdsa-sha2-nistp256" + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/test", + "usage": "CTFE" + } } }, - "roles": [ - { - "keyids": [ - "ae0c689c6347ada7359df48934991f4e013193d6ddf3482a5ffb293f74f3b217" - ], - "name": "rekor", - "paths": [ - "rekor.*.pub" - ], - "terminating": true, - "threshold": 1 - }, - { - "keyids": [ - "b811bd53f2d7adcf5d93e6bb4a8ed2e0ca0f83d454a3e51f105c8e8376bc80d4" - ], - "name": "staging", - "paths": [ - "*" - ], - "terminating": false, - "threshold": 1 - } - ] - }, - "expires": "2022-05-11T19:10:16Z", - "spec_version": "1.0", - "targets": { - "artifact.pub": { + "ctfe_2022.pub": { + "length": 178, "hashes": { - "sha256": "59ebf97a9850aecec4bc39c1f5c1dc46e6490a6b5fd2a6cacdcac0c3a6fc4cbf", - "sha512": "308fd1d1d95d7f80aa33b837795251cc3e886792982275e062409e13e4e236ffc34d676682aa96fdc751414de99c864bf132dde71581fa651c6343905e3bf988" + "sha256": "910d899c7763563095a0fe684c8477573fedc19a78586de6ecfbfd8f289f5423", + "sha512": "ab975a75600fc366a837536d0dcba841b755552d21bb114498ff8ac9d2403f76643f5b91269bce5d124a365514719a3edee9dcc2b046cb173f51af659911fcd3" }, - "length": 177 + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/2022", + "usage": "CTFE" + } + } }, - "ctfe.pub": { + "ctfe_2022_2.pub": { + "length": 178, "hashes": { - "sha256": "7fcb94a5d0ed541260473b990b99a6c39864c1fb16f3f3e594a5a3cebbfe138a", - "sha512": "4b20747d1afe2544238ad38cc0cc3010921b177d60ac743767e0ef675b915489bd01a36606c0ff83c06448622d7160f0d866c83d20f0c0f44653dcc3f9aa0bd4" + "sha256": "7054b4f15f969daca1c242bb9e77527abaf0b9acf9818a2a35144e4b32b20dc6", + "sha512": "3d035f94e1b14ac84627a28afdbed9a34861fb84239f76d73aa1a99f52262bfd95c4fa0ee71f1fd7e3bfb998d89cd5e0f0eafcff9fa7fa87c6e23484fc1e0cec" }, - "length": 177 + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://ctfe.sigstage.dev/2022-2", + "usage": "CTFE" + } + } }, "fulcio.crt.pem": { + "length": 741, "hashes": { - "sha256": "f360c53b2e13495a628b9b8096455badcb6d375b185c4816d95a5d746ff29908", - "sha512": "0713252a7fd17f7f3ab12f88a64accf2eb14b8ad40ca711d7fe8b4ecba3b24db9e9dffadb997b196d3867b8f9ff217faf930d80e4dab4e235c7fc3f07be69224" + "sha256": "0e6b0442485ad552bea5f62f11c29e2acfda35307d7538430b4cc1dbef49bff1", + "sha512": "c69ae618883a0c89c282c0943a1ad0c16b0a7788f74e47a1adefc631dac48a0c4449d8c3de7455ae7d772e43c4a87e341f180b0614a46a86006969f8a7b84532" }, - "length": 744 + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstage.dev", + "usage": "Fulcio" + } + } }, - "fulcio_v1.crt.pem": { + "fulcio_intermediate.crt.pem": { + "length": 790, "hashes": { - "sha256": "f989aa23def87c549404eadba767768d2a3c8d6d30a8b793f9f518a8eafd2cf5", - "sha512": "f2e33a6dc208cee1f51d33bbea675ab0f0ced269617497985f9a0680689ee7073e4b6f8fef64c91bda590d30c129b3070dddce824c05bc165ac9802f0705cab6" + "sha256": "782868913fe13c385105ddf33e827191386f58da40a931f2075a7e27b1b6ac7b", + "sha512": "90659875a02f73d1026055427c6d857c556e410e23748ff88aeb493227610fd2f5fbdd95ef2a21565f91438dfb3e073f50c4c9dd06f9a601b5d9b064d5cb60b4" }, - "length": 740 + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://fulcio.sigstage.dev", + "usage": "Fulcio" + } + } }, "rekor.pub": { + "length": 178, "hashes": { - "sha256": "dce5ef715502ec9f3cdfd11f8cc384b31a6141023d3e7595e9908a81cb6241bd", - "sha512": "0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35" + "sha256": "1d80b8f72505a43e65e6e125247cd508f61b459dc457c1d1bcb78d96e1760959", + "sha512": "09ab08698a67354a95d3b8897d9ce7eaef05f06f5ed5f0202d79c228579858ecc5816b7e1b7cc6786abe7d6aaa758e1fcb05900cb749235186c3bf9522d6d7ce" }, - "length": 178 + "custom": { + "sigstore": { + "status": "Active", + "uri": "https://rekor.sigstage.dev", + "usage": "Rekor" + } + } + }, + "trusted_root.json": { + "length": 4521, + "hashes": { + "sha256": "6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b", + "sha512": "fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963" + } } }, - "version": 2 - } + "delegations": { + "keys": { + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } + } + }, + "roles": [ + { + "name": "registry.npmjs.org", + "keyids": [ + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" + ], + "threshold": 1, + "terminating": true, + "paths": [ + "registry.npmjs.org/*" + ] + } + ] + } + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "304502210090b089087d1b17b2517c464b7774d76d3ea558ffca874eed63ccbee8f6bc3b76022022b56f551bcd0ac8a9c35cd0724ac5b00b4984544cbf812f47f276a9b48db8db" + } + ] } \ No newline at end of file diff --git a/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json b/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json new file mode 100644 index 0000000000..6a1c1f5a40 --- /dev/null +++ b/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json @@ -0,0 +1,86 @@ +{ + "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", + "tlogs": [ + { + "baseUrl": "https://rekor.sigstage.dev", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDODRU688UYGuy54mNUlaEBiQdTE9nYLr0lg6RXowI/QV/RE1azBn4Eg5/2uTOMbhB1/gfcHzijzFi9Tk+g1Prg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-01-12T11:53:27.000Z" + } + }, + "logId": { + "keyId": "0y8wo8MtY5wrdiIFohx7sHeI5oKDpK5vQhGHI6G+pJY=" + } + } + ], + "certificateAuthorities": [ + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstage.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB9jCCAXugAwIBAgITDdEJvluliE0AzYaIE4jTMdnFTzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDMyNTE2NTA0NloXDTMyMDMyMjE2NTA0NVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMo9BUNk9QIYisYysC24+2OytoV72YiLonYcqR3yeVnYziPt7Xv++CYE8yoCTiwedUECCWKOcvQKRCJZb9ht4Hzy+VvBx36hK+C6sECCSR0x6pPSiz+cTk1f788ZjBlUZaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9CMrpofas6cK/cDNQa4j6Hj2ZlMB8GA1UdIwQYMBaAFP9CMrpofas6cK/cDNQa4j6Hj2ZlMAoGCCqGSM49BAMDA2kAMGYCMQD+kojuzMwztNay9Ibzjuk//ZL5m6T2OCsm45l1lY004pcb984L926BowodoirFMcMCMQDIJtFHhP/1D3a+M3dAGomOb6O4CmTry3TTPbPsAFnv22YA0Y+P21NVoxKDjdu0tkw=" + }, + { + "rawBytes": "MIICGTCCAaCgAwIBAgITJta/okfgHvjabGm1BOzuhrwA1TAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDQxNDIxMzg0MFoXDTMyMDMyMjE2NTA0NVowNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASosAySWJQ/tK5r8T5aHqavk0oI+BKQbnLLdmOMRXHQF/4Hx9KtNfpcdjH9hNKQSBxSlLFFN3tvFCco0qFBzWYwZtsYsBe1l91qYn/9VHFTaEVwYQWIJEEvrs0fvPuAqjajezB5MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRxhjCmFHxib/n31vQFGn9f/+tvrDAfBgNVHSMEGDAWgBT/QjK6aH2rOnCv3AzUGuI+h49mZTAKBggqhkjOPQQDAwNnADBkAjAM1lbKkcqQlE/UspMTbWNo1y2TaJ44tx3l/FJFceTSdDZ+0W1OHHeU4twie/lq8XgCMHQxgEv26xNNiAGyPXbkYgrDPvbOqp0UeWX4mJnLSrBr3aN/KX1SBrKQu220FmVL0Q==" + } + ] + }, + "validFor": { + "start": "2022-03-25T16:50:46.000Z" + } + } + ], + "ctlogs": [ + { + "baseUrl": "https://ctfe.sigstage.dev/test", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MIICCgKCAgEA27A2MPQXm0I0v7/Ly5BIauDjRZF5Jor9vU+QheoE2UIIsZHcyYq3slHzSSHy2lLj1ZD2d91CtJ492ZXqnBmsr4TwZ9jQ05tW2mGIRI8u2DqN8LpuNYZGz/f9SZrjhQQmUttqWmtu3UoLfKz6NbNXUnoo+NhZFcFRLXJ8VporVhuiAmL7zqT53cXR3yQfFPCUDeGnRksnlhVIAJc3AHZZSHQJ8DEXMhh35TVv2nYhTI3rID7GwjXXw4ocz7RGDD37ky6p39Tl5NB71gT1eSqhZhGHEYHIPXraEBd5+3w9qIuLWlp5Ej/K6Mu4ELioXKCUimCbwy+Cs8UhHFlqcyg4AysOHJwIadXIa8LsY51jnVSGrGOEBZevopmQPNPtyfFY3dmXSS+6Z3RD2Gd6oDnNGJzpSyEk410Ag5uvNDfYzJLCWX9tU8lIxNwdFYmIwpd89HijyRyoGnoJ3entd63cvKfuuix5r+GHyKp1Xm1L5j5AWM6P+z0xigwkiXnt+adexAl1J9wdDxv/pUFEESRF4DG8DFGVtbdH6aR1A5/vD4krO4tC1QYUSeyL5Mvsw8WRqIFHcXtgybtxylljvNcGMV1KXQC8UFDmpGZVDSHx6v3e/BHMrZ7gjoCCfVMZ/cFcQi0W2AIHPYEMH/C95J2r4XbHMRdYXpovpOoT5Ca78gsCAwEAAQ==", + "keyDetails": "PKCS1_RSA_PKCS1V5", + "validFor": { + "start": "2021-03-14T00:00:00.000Z" + } + }, + "logId": { + "keyId": "s9AOb93xWxr+a4ztxJnxxJCX7VZ0V3IF4jTu/OoL84A=" + } + }, + { + "baseUrl": "https://ctfe.sigstage.dev/2022", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh99xuRi6slBFd8VUJoK/rLigy4bYeSYWO/fE6Br7r0D8NpMI94+A63LR/WvLxpUUGBpY8IJA3iU2telag5CRpA==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-07-01T00:00:00.000Z" + } + }, + "logId": { + "keyId": "++JKOMQt7SJ3ynUHnCfnDhcKP8/58J4TueMqXuk3HmA=" + } + }, + { + "baseUrl": "https://ctfe.sigstage.dev/2022-2", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8gEDKNme8AnXuPBgHjrtXdS6miHqc24CRblNEOFpiJRngeq8Ko73Y+K18yRYVf1DXD4AVLwvKyzdNdl5n0jUSQ==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-07-01T00:00:00.000Z" + } + }, + "logId": { + "keyId": "KzC83GiIyeLh2CYpXnQfSDkxlgLynDPLXkNA/rKshno=" + } + } + ], + "timestampAuthorities": [] +} diff --git a/tests/data/repository/targets/artifact.pub b/tests/data/repository/targets/artifact.pub deleted file mode 100644 index d6e745bdd0..0000000000 --- a/tests/data/repository/targets/artifact.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhyQCx0E9wQWSFI9ULGwy3BuRklnt -IqozONbbdbqz11hlRJy9c7SG+hdcFl9jE9uE/dwtuwU2MqU9T/cN0YkWww== ------END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/data/repository/targets/ctfe.pub b/tests/data/repository/targets/ctfe.pub deleted file mode 100644 index 1bb1488c99..0000000000 --- a/tests/data/repository/targets/ctfe.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3Pyu -dDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w== ------END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json b/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json new file mode 100644 index 0000000000..6a1c1f5a40 --- /dev/null +++ b/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json @@ -0,0 +1,86 @@ +{ + "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", + "tlogs": [ + { + "baseUrl": "https://rekor.sigstage.dev", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDODRU688UYGuy54mNUlaEBiQdTE9nYLr0lg6RXowI/QV/RE1azBn4Eg5/2uTOMbhB1/gfcHzijzFi9Tk+g1Prg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-01-12T11:53:27.000Z" + } + }, + "logId": { + "keyId": "0y8wo8MtY5wrdiIFohx7sHeI5oKDpK5vQhGHI6G+pJY=" + } + } + ], + "certificateAuthorities": [ + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstage.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB9jCCAXugAwIBAgITDdEJvluliE0AzYaIE4jTMdnFTzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDMyNTE2NTA0NloXDTMyMDMyMjE2NTA0NVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMo9BUNk9QIYisYysC24+2OytoV72YiLonYcqR3yeVnYziPt7Xv++CYE8yoCTiwedUECCWKOcvQKRCJZb9ht4Hzy+VvBx36hK+C6sECCSR0x6pPSiz+cTk1f788ZjBlUZaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9CMrpofas6cK/cDNQa4j6Hj2ZlMB8GA1UdIwQYMBaAFP9CMrpofas6cK/cDNQa4j6Hj2ZlMAoGCCqGSM49BAMDA2kAMGYCMQD+kojuzMwztNay9Ibzjuk//ZL5m6T2OCsm45l1lY004pcb984L926BowodoirFMcMCMQDIJtFHhP/1D3a+M3dAGomOb6O4CmTry3TTPbPsAFnv22YA0Y+P21NVoxKDjdu0tkw=" + }, + { + "rawBytes": "MIICGTCCAaCgAwIBAgITJta/okfgHvjabGm1BOzuhrwA1TAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDQxNDIxMzg0MFoXDTMyMDMyMjE2NTA0NVowNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASosAySWJQ/tK5r8T5aHqavk0oI+BKQbnLLdmOMRXHQF/4Hx9KtNfpcdjH9hNKQSBxSlLFFN3tvFCco0qFBzWYwZtsYsBe1l91qYn/9VHFTaEVwYQWIJEEvrs0fvPuAqjajezB5MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRxhjCmFHxib/n31vQFGn9f/+tvrDAfBgNVHSMEGDAWgBT/QjK6aH2rOnCv3AzUGuI+h49mZTAKBggqhkjOPQQDAwNnADBkAjAM1lbKkcqQlE/UspMTbWNo1y2TaJ44tx3l/FJFceTSdDZ+0W1OHHeU4twie/lq8XgCMHQxgEv26xNNiAGyPXbkYgrDPvbOqp0UeWX4mJnLSrBr3aN/KX1SBrKQu220FmVL0Q==" + } + ] + }, + "validFor": { + "start": "2022-03-25T16:50:46.000Z" + } + } + ], + "ctlogs": [ + { + "baseUrl": "https://ctfe.sigstage.dev/test", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MIICCgKCAgEA27A2MPQXm0I0v7/Ly5BIauDjRZF5Jor9vU+QheoE2UIIsZHcyYq3slHzSSHy2lLj1ZD2d91CtJ492ZXqnBmsr4TwZ9jQ05tW2mGIRI8u2DqN8LpuNYZGz/f9SZrjhQQmUttqWmtu3UoLfKz6NbNXUnoo+NhZFcFRLXJ8VporVhuiAmL7zqT53cXR3yQfFPCUDeGnRksnlhVIAJc3AHZZSHQJ8DEXMhh35TVv2nYhTI3rID7GwjXXw4ocz7RGDD37ky6p39Tl5NB71gT1eSqhZhGHEYHIPXraEBd5+3w9qIuLWlp5Ej/K6Mu4ELioXKCUimCbwy+Cs8UhHFlqcyg4AysOHJwIadXIa8LsY51jnVSGrGOEBZevopmQPNPtyfFY3dmXSS+6Z3RD2Gd6oDnNGJzpSyEk410Ag5uvNDfYzJLCWX9tU8lIxNwdFYmIwpd89HijyRyoGnoJ3entd63cvKfuuix5r+GHyKp1Xm1L5j5AWM6P+z0xigwkiXnt+adexAl1J9wdDxv/pUFEESRF4DG8DFGVtbdH6aR1A5/vD4krO4tC1QYUSeyL5Mvsw8WRqIFHcXtgybtxylljvNcGMV1KXQC8UFDmpGZVDSHx6v3e/BHMrZ7gjoCCfVMZ/cFcQi0W2AIHPYEMH/C95J2r4XbHMRdYXpovpOoT5Ca78gsCAwEAAQ==", + "keyDetails": "PKCS1_RSA_PKCS1V5", + "validFor": { + "start": "2021-03-14T00:00:00.000Z" + } + }, + "logId": { + "keyId": "s9AOb93xWxr+a4ztxJnxxJCX7VZ0V3IF4jTu/OoL84A=" + } + }, + { + "baseUrl": "https://ctfe.sigstage.dev/2022", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh99xuRi6slBFd8VUJoK/rLigy4bYeSYWO/fE6Br7r0D8NpMI94+A63LR/WvLxpUUGBpY8IJA3iU2telag5CRpA==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-07-01T00:00:00.000Z" + } + }, + "logId": { + "keyId": "++JKOMQt7SJ3ynUHnCfnDhcKP8/58J4TueMqXuk3HmA=" + } + }, + { + "baseUrl": "https://ctfe.sigstage.dev/2022-2", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8gEDKNme8AnXuPBgHjrtXdS6miHqc24CRblNEOFpiJRngeq8Ko73Y+K18yRYVf1DXD4AVLwvKyzdNdl5n0jUSQ==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-07-01T00:00:00.000Z" + } + }, + "logId": { + "keyId": "KzC83GiIyeLh2CYpXnQfSDkxlgLynDPLXkNA/rKshno=" + } + } + ], + "timestampAuthorities": [] +} diff --git a/tests/data/repository/targets/fulcio.crt.pem b/tests/data/repository/targets/fulcio.crt.pem deleted file mode 100644 index 6a06ff300b..0000000000 --- a/tests/data/repository/targets/fulcio.crt.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq -MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx -MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu -ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy -A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas -taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm -MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE -FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u -Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx -Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup -Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ== ------END CERTIFICATE----- \ No newline at end of file diff --git a/tests/data/repository/targets/fulcio_v1.crt.pem b/tests/data/repository/targets/fulcio_v1.crt.pem deleted file mode 100644 index 3afc46bb6e..0000000000 --- a/tests/data/repository/targets/fulcio_v1.crt.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw -KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y -MTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl -LmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7 -XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex -X69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j -YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY -wB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ -KsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM -WP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9 -TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ ------END CERTIFICATE----- \ No newline at end of file diff --git a/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json b/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json new file mode 100644 index 0000000000..f5667a5f0e --- /dev/null +++ b/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json @@ -0,0 +1,26 @@ +{ + "keys": [ + { + "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "keyUsage": "npm:signatures", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "1999-01-01T00:00:00.000Z" + } + } + }, + { + "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "keyUsage": "npm:attestations", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-12-01T00:00:00.000Z" + } + } + } + ] +} diff --git a/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json b/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json new file mode 100644 index 0000000000..f5667a5f0e --- /dev/null +++ b/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json @@ -0,0 +1,26 @@ +{ + "keys": [ + { + "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "keyUsage": "npm:signatures", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "1999-01-01T00:00:00.000Z" + } + } + }, + { + "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "keyUsage": "npm:attestations", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-12-01T00:00:00.000Z" + } + } + } + ] +} diff --git a/tests/data/repository/targets/rekor.0.pub b/tests/data/repository/targets/rekor.0.pub deleted file mode 100644 index 050ef60149..0000000000 --- a/tests/data/repository/targets/rekor.0.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr -kBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw== ------END PUBLIC KEY----- diff --git a/tests/data/repository/targets/rekor.json b/tests/data/repository/targets/rekor.json deleted file mode 100644 index f86930d537..0000000000 --- a/tests/data/repository/targets/rekor.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "signatures": [ - { - "keyid": "ae0c689c6347ada7359df48934991f4e013193d6ddf3482a5ffb293f74f3b217", - "sig": "3045022076eadd73f6664bac5cc91f12d3a7ddcdd53f9bde661f147651196ff66e7235d1022100f7b3143792405f9e8a75331a05d4128bdf083de302801e99c3d027919a4b03da" - } - ], - "signed": { - "_type": "targets", - "expires": "2022-05-11T19:10:11Z", - "spec_version": "1.0", - "targets": { - "rekor.0.pub": { - "hashes": { - "sha256": "dce5ef715502ec9f3cdfd11f8cc384b31a6141023d3e7595e9908a81cb6241bd", - "sha512": "0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35" - }, - "length": 178 - } - }, - "version": 1 - } -} \ No newline at end of file diff --git a/tests/data/repository/targets/rekor.pub b/tests/data/repository/targets/rekor.pub deleted file mode 100644 index 050ef60149..0000000000 --- a/tests/data/repository/targets/rekor.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr -kBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw== ------END PUBLIC KEY----- diff --git a/tests/data/repository/timestamp.json b/tests/data/repository/timestamp.json index 8cb4f094b7..4b4a4dec2c 100644 --- a/tests/data/repository/timestamp.json +++ b/tests/data/repository/timestamp.json @@ -1,24 +1,24 @@ { - "signatures": [ - { - "keyid": "b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d", - "sig": "30440220590dc4d9eb4e3b2745315348c1ea5481f29f981dfd6c2d72bde13256a25e0caf02205704352c828451bf1e41bba154db9ecb4e901b4bc47d721a91fabfb84a48c61f" - } - ], "signed": { "_type": "timestamp", - "expires": "2022-01-05T00:40:07Z", + "spec_version": "1.0", + "version": 2, + "expires": "2028-04-12T21:11:28Z", "meta": { "snapshot.json": { + "length": 1039, "hashes": { - "sha256": "e202c20580ac4edc7a52ad2bcbe97c5af557c04463f10f2d9a28e2624e0c8edf", - "sha512": "f0b9f17797fe6d89a745f8fc9a39a073823bc04400307711eebe3b00dfe418e4d1d4419697eee29445c9cd5e03c3e24532d4fb03824d7555ecc0de54bd73ffd1" + "sha256": "b480856ab72c80fe10902ffac69ec10340e827e02b2bd114d6f141de910a96c5", + "sha512": "da06f65c1ee242d63820ba646fb1b4037fe355460309d89f98a923d1d009e7d46f11d4272a0d8e07829734baea655f7692d8c23383d6044b4f72263a4dbf3057" }, - "length": 1658, - "version": 6 + "version": 2 } - }, - "spec_version": "1.0", - "version": 6 - } + } + }, + "signatures": [ + { + "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", + "sig": "3044022040e243b1bc8edb798df66803c2460471a4129704421d59f55c825dc549493f840220267e4684875d4803ae0948140af32fc9f560453efb84d9728ee66619e8767d8c" + } + ] } \ No newline at end of file From 6af5c49edcfa40e2dec22a12dbc6582c29655702 Mon Sep 17 00:00:00 2001 From: Jack Leightcap Date: Wed, 31 Jan 2024 10:53:49 -0500 Subject: [PATCH 070/161] pr feedback 2024-01-31 Signed-off-by: Jack Leightcap --- src/crypto/verification.rs | 8 ----- src/tuf/constants.rs | 9 ++++-- src/tuf/mod.rs | 51 ++++++++++++++++++-------------- src/verify/models.rs | 6 ++-- tests/conformance/conformance.rs | 2 +- 5 files changed, 38 insertions(+), 38 deletions(-) delete mode 100644 src/crypto/verification.rs diff --git a/src/crypto/verification.rs b/src/crypto/verification.rs deleted file mode 100644 index a0a6ddcf2d..0000000000 --- a/src/crypto/verification.rs +++ /dev/null @@ -1,8 +0,0 @@ -use rustls_pki_types::CertificateDer; -use webpki::TrustAnchor; - -/// Machinery for Sigstore end entity certificate verification. -struct CertificateVerificationContext<'a> { - pub trust_anchors: Vec>, - pub intermediate_certs: Vec>, -} diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index 0529d1128b..e84909785a 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -13,14 +13,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::path::Path; +use tough::TargetName; + pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; pub(crate) const SIGSTORE_TARGET_BASE: &str = "https://tuf-repo-cdn.sigstore.dev/targets"; macro_rules! tuf_resource { ($path:literal) => { - include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/", $path)) + Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/", $path)) }; } -pub(crate) const SIGSTORE_ROOT: &[u8] = tuf_resource!("prod/root.json"); -pub(crate) const _SIGSTORE_TRUST_BUNDLE: &[u8] = tuf_resource!("prod/trusted_root.json"); +pub(crate) const SIGSTORE_ROOT: &Path = tuf_resource!("prod/root.json"); +pub(crate) const SIGSTORE_TRUST_BUNDLE: &Path = tuf_resource!("prod/trusted_root.json"); diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index e695cc5044..cd7e594581 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -45,6 +45,8 @@ use tough::TargetName; use tracing::debug; use webpki::types::CertificateDer; +use crate::tuf::constants::SIGSTORE_TRUST_BUNDLE; + use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; use super::errors::{Result, SigstoreError}; @@ -94,11 +96,14 @@ impl SigstoreRepository { let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; - let repository = - tough::RepositoryLoader::new(constants::SIGSTORE_ROOT, metadata_base, target_base) - .expiration_enforcement(tough::ExpirationEnforcement::Safe) - .load() - .map_err(Box::new)?; + let repository = tough::RepositoryLoader::new( + include_bytes!(constants::SIGSTORE_ROOT), + metadata_base, + target_base, + ) + .expiration_enforcement(tough::ExpirationEnforcement::Safe) + .load() + .map_err(Box::new)?; Ok(Self { repository, @@ -108,30 +113,30 @@ impl SigstoreRepository { } fn trusted_root(&self) -> Result<&TrustedRoot> { - fn init_trusted_root( - repository: &tough::Repository, - checkout_dir: Option<&PathBuf>, - ) -> Result { - let trusted_root_target = TargetName::new("trusted_root.json").map_err(Box::new)?; - let local_path = checkout_dir.map(|d| d.join(trusted_root_target.raw())); + return if let Some(root) = self.trusted_root.get() { + Ok(root) + } else { + let trusted_root_target = SIGSTORE_TRUST_BUNDLE + .to_str() + .and_then(TargetName::new) + .map_err(Box::new)?; + let local_path = self + .checkout_dir + .as_ref() + .map(|d| d.join(trusted_root_target.raw())); let data = fetch_target_or_reuse_local_cache( - repository, + &self.repository, &trusted_root_target, local_path.as_ref(), )?; debug!("data:\n{}", String::from_utf8_lossy(&data)); - Ok(serde_json::from_slice(&data[..])?) - } + let root = serde_json::from_slice(&data[..])?; - if let Some(root) = self.trusted_root.get() { - return Ok(root); - } - - let root = init_trusted_root(&self.repository, self.checkout_dir.as_ref())?; - Ok(self.trusted_root.get_or_init(|| root)) + Ok(self.trusted_root.get_or_init(|| root)) + }; } /// Prefetches trust materials. @@ -264,10 +269,10 @@ fn fetch_target_or_reuse_local_cache( local_file: Option<&PathBuf>, ) -> Result> { let (local_file_outdated, local_file_contents) = if let Some(path) = local_file { - is_local_file_outdated(repository, target_name, path) + is_local_file_outdated(repository, target_name, path)? } else { - Ok((true, None)) - }?; + (true, None) + }; let data = if local_file_outdated { let data = fetch_target(repository, target_name)?; diff --git a/src/verify/models.rs b/src/verify/models.rs index dcf6e39b68..19c021f75f 100644 --- a/src/verify/models.rs +++ b/src/verify/models.rs @@ -36,6 +36,7 @@ use pkcs8::der::Decode; use sha2::{Digest, Sha256}; use sigstore_protobuf_specs::Bundle; use thiserror::Error; +use tracing::warn; use x509_cert::Certificate; #[derive(Error, Debug)] @@ -164,15 +165,14 @@ impl VerificationMaterials { } if inclusion_proof.is_some() && !has_checkpoint { - // TODO(tnytown): Act here. - // NOTE(jl): in sigstore-python, this is a no-op that prints a warning log. + warn!("0.1 bundle contains inclusion proof without checkpoint; ignoring"); } } BundleVersion::Bundle0_2 => { inclusion_proof?; if !has_checkpoint { // inclusion proofs must contain checkpoints - return None; // FIXME(jl): this raises an error in sigstore-python. + return None; } } } diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index 801cc6b064..cfb11b7f72 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -143,7 +143,7 @@ fn sign_bundle(args: SignBundle) -> anyhow::Result<()> { let context = SigningContext::production(); let signer = context.signer(identity_token); - let signing_artifact = signer.sign(&mut artifact)?; + let signing_artifact = signer?.sign(&mut artifact)?; let bundle_data = signing_artifact.to_bundle(); serde_json::to_writer(bundle, &bundle_data)?; From afdf3b0d83c82d3f0639cc7ab4ca7ae6566cbef6 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 13:50:51 -0600 Subject: [PATCH 071/161] tuf: fixup target fetching Signed-off-by: Andrew Pan --- src/tuf/constants.rs | 23 ++++-- src/tuf/mod.rs | 169 +++++++++++++++---------------------------- 2 files changed, 73 insertions(+), 119 deletions(-) diff --git a/src/tuf/constants.rs b/src/tuf/constants.rs index e84909785a..bb08b5b4e9 100644 --- a/src/tuf/constants.rs +++ b/src/tuf/constants.rs @@ -13,17 +13,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::path::Path; -use tough::TargetName; - pub(crate) const SIGSTORE_METADATA_BASE: &str = "https://tuf-repo-cdn.sigstore.dev"; pub(crate) const SIGSTORE_TARGET_BASE: &str = "https://tuf-repo-cdn.sigstore.dev/targets"; -macro_rules! tuf_resource { - ($path:literal) => { - Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/", $path)) +macro_rules! impl_static_resource { + {$($name:literal,)+} => { + #[inline] + pub(crate) fn static_resource(name: impl AsRef) -> Option<&'static [u8]> { + match name.as_ref() { + $( + $name => Some(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/prod/", $name))) + ),+, + _ => None, + } + } }; } -pub(crate) const SIGSTORE_ROOT: &Path = tuf_resource!("prod/root.json"); -pub(crate) const SIGSTORE_TRUST_BUNDLE: &Path = tuf_resource!("prod/trusted_root.json"); +impl_static_resource! { + "root.json", + "trusted_root.json", +} diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index cd7e594581..07808ee55e 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -32,7 +32,6 @@ //! ``` use std::{ cell::OnceCell, - fs, io::Read, path::{Path, PathBuf}, }; @@ -45,8 +44,6 @@ use tough::TargetName; use tracing::debug; use webpki::types::CertificateDer; -use crate::tuf::constants::SIGSTORE_TRUST_BUNDLE; - use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; use super::errors::{Result, SigstoreError}; @@ -97,7 +94,7 @@ impl SigstoreRepository { let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; let repository = tough::RepositoryLoader::new( - include_bytes!(constants::SIGSTORE_ROOT), + constants::static_resource("root.json").expect("Failed to fetch required resource!"), metadata_base, target_base, ) @@ -116,20 +113,7 @@ impl SigstoreRepository { return if let Some(root) = self.trusted_root.get() { Ok(root) } else { - let trusted_root_target = SIGSTORE_TRUST_BUNDLE - .to_str() - .and_then(TargetName::new) - .map_err(Box::new)?; - let local_path = self - .checkout_dir - .as_ref() - .map(|d| d.join(trusted_root_target.raw())); - - let data = fetch_target_or_reuse_local_cache( - &self.repository, - &trusted_root_target, - local_path.as_ref(), - )?; + let data = self.fetch_target("trusted_root.json")?; debug!("data:\n{}", String::from_utf8_lossy(&data)); @@ -139,6 +123,62 @@ impl SigstoreRepository { }; } + fn fetch_target(&self, name: N) -> Result> + where + N: TryInto, + { + let read_remote_target = |name: &TargetName| -> Result> { + let Some(mut reader) = self.repository.read_target(name).map_err(Box::new)? else { + return Err(SigstoreError::TufTargetNotFoundError(name.raw().to_owned())); + }; + + debug!("fetching target {} from remote", name.raw()); + + let mut repo_data = Vec::new(); + reader.read_to_end(&mut repo_data)?; + Ok(repo_data) + }; + + let name: TargetName = name.try_into().map_err(Box::new)?; + let local_path = self.checkout_dir.as_ref().map(|d| d.join(name.raw())); + + // Try reading the target from disk cache. + let data = if let Some(Ok(local_data)) = local_path.as_ref().map(std::fs::read) { + local_data.to_vec() + // Try reading the target embedded into the binary. + } else if let Some(embedded_data) = constants::static_resource(name.raw()) { + debug!("read embedded target {}", name.raw()); + embedded_data.to_vec() + // If all else fails, read the data from the TUF repo. + } else if let Ok(remote_data) = read_remote_target(&name) { + remote_data + } else { + return Err(SigstoreError::TufTargetNotFoundError(name.raw().to_owned())); + }; + + // Get metadata (hash) of the target and update the disk copy if it doesn't match. + let Some(target) = self.repository.targets().signed.targets.get(&name) else { + return Err(SigstoreError::TufMetadataError(format!( + "couldn't get metadata for {}", + name.raw() + ))); + }; + + let data = if Sha256::digest(&data)[..] != target.hashes.sha256[..] { + read_remote_target(&name)? + } else { + data + }; + + // Write the up-to-date data back to the disk. This doesn't need to succeed, as we can + // always fetch the target again later. + if let Some(local_path) = local_path { + let _ = std::fs::write(local_path, &data); + } + + Ok(data) + } + /// Prefetches trust materials. /// /// [Repository::fulcio_certs()] and [Repository::rekor_keys()] on [SigstoreRepository] lazily @@ -248,96 +288,3 @@ fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { }, } } - -/// Download a file stored inside of a TUF repository, try to reuse a local -/// cache when possible. -/// -/// * `repository`: TUF repository holding the file -/// * `target_name`: TUF representation of the file to be downloaded -/// * `local_file`: location where the file should be downloaded -/// -/// This function will reuse the local copy of the file if contents -/// didn't change. -/// This check is done by comparing the digest of the local file, if found, -/// with the digest reported inside of the TUF repository metadata. -/// -/// **Note well:** the `local_file` is updated whenever its contents are -/// outdated. -fn fetch_target_or_reuse_local_cache( - repository: &tough::Repository, - target_name: &TargetName, - local_file: Option<&PathBuf>, -) -> Result> { - let (local_file_outdated, local_file_contents) = if let Some(path) = local_file { - is_local_file_outdated(repository, target_name, path)? - } else { - (true, None) - }; - - let data = if local_file_outdated { - let data = fetch_target(repository, target_name)?; - if let Some(path) = local_file { - // update the local file to have latest data from the TUF repo - fs::write(path, data.clone())?; - } - data - } else { - local_file_contents - .expect("local file contents to not be 'None'") - .as_bytes() - .to_owned() - }; - - Ok(data) -} - -/// Download a file from a TUF repository -fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { - let data: Vec; - match repository.read_target(target_name).map_err(Box::new)? { - None => Err(SigstoreError::TufTargetNotFoundError( - target_name.raw().to_string(), - )), - Some(reader) => { - data = read_to_end(reader)?; - Ok(data) - } - } -} - -/// Compares the checksum of a local file, with the digest reported inside of -/// TUF repository metadata -fn is_local_file_outdated( - repository: &tough::Repository, - target_name: &TargetName, - local_file: &Path, -) -> Result<(bool, Option)> { - let target = repository - .targets() - .signed - .targets - .get(target_name) - .ok_or_else(|| SigstoreError::TufTargetNotFoundError(target_name.raw().to_string()))?; - - if local_file.exists() { - let data = fs::read_to_string(local_file)?; - let local_checksum = Sha256::digest(data.clone()); - let expected_digest: Vec = target.hashes.sha256.to_vec(); - - if local_checksum.as_slice() == expected_digest.as_slice() { - // local data is not outdated - Ok((false, Some(data))) - } else { - Ok((true, None)) - } - } else { - Ok((true, None)) - } -} - -/// Gets the goods from a read and makes a Vec -fn read_to_end(mut reader: R) -> Result> { - let mut v = Vec::new(); - reader.read_to_end(&mut v)?; - Ok(v) -} From 4dc13fea0bd4ff99f2de68ff1152faf308fd1406 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 16:17:52 -0600 Subject: [PATCH 072/161] sign, verify: use `sigstore-protobuf-specs` 0.3.2 https://github.com/trail-of-forks/sigstore-rs/tree/ap/with-prost-protobuf-specs Co-authored-by: Alex Cameron Signed-off-by: Andrew Pan --- Cargo.toml | 16 ++- src/bundle/mod.rs | 89 +++++++++++-- src/crypto/certificate_pool.rs | 32 ++--- src/fulcio/mod.rs | 36 ++--- src/fulcio/models.rs | 60 +++++---- src/sign.rs | 135 +++++++------------ src/tuf/mod.rs | 68 +++++++--- src/tuf/trustroot.rs | 194 --------------------------- src/verify/mod.rs | 2 +- src/verify/models.rs | 233 +++++++++++++++++++-------------- src/verify/policy.rs | 132 ++++++++++--------- src/verify/verifier.rs | 106 +++++++-------- 12 files changed, 504 insertions(+), 599 deletions(-) delete mode 100644 src/tuf/trustroot.rs diff --git a/Cargo.toml b/Cargo.toml index 80b107eef5..d19a7cb041 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/sigstore/sigstore-rs" [features] default = ["full-native-tls", "cached-client", "tuf", "sign", "verify"] -wasm = ["getrandom/js"] +wasm = ["getrandom/js", "ring/wasm32_unknown_unknown_js"] full-native-tls = [ "fulcio-native-tls", @@ -76,7 +76,7 @@ base64 = "0.21.0" cached = { version = "0.48.0", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["serde"] } -const-oid = "0.9.1" +const-oid = { version = "0.9.6", features = ["db"] } digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } ed25519 = { version = "2.2.1", features = ["alloc"] } @@ -88,9 +88,9 @@ olpc-cjson = "0.1" openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", ], optional = true } -p256 = "0.13.2" +p256 = "0.13" p384 = "0.13" -webbrowser = "0.8.4" +webbrowser = "0.8.12" pem = { version = "3.0", features = ["serde"] } pkcs1 = { version = "0.7.5", features = ["std"] } pkcs8 = { version = "0.10.2", features = [ @@ -110,23 +110,25 @@ rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" -serde_with = { version = "3.4.0", features = ["base64"] } +serde_with = { version = "3.4.0", features = ["base64", "json"] } sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } -sigstore_protobuf_specs = "0.1.0-rc.2" +sigstore_protobuf_specs = "0.3.2" thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tokio-util = { version = "0.7.10", features = ["io-util"] } tough = { version = "0.14", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" -x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } +x509-cert = { version = "0.2.5", features = ["builder", "pem", "std", "sct"] } crypto_secretbox = "0.1.1" zeroize = "1.5.7" rustls-webpki = { version = "0.102.1", features = ["alloc"] } serde_repr = "0.1.16" hex = "0.4.3" json-syntax = { version = "0.11.1", features = ["canonicalize", "serde"] } +tls_codec = { version = "0.4.1", features = ["derive"] } +ring = "0.17.6" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index 1bf8e8d840..b3b040c15f 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -17,18 +17,17 @@ use std::fmt::Display; use std::str::FromStr; -pub use sigstore_protobuf_specs::Bundle; - -macro_rules! required { - ($($base:expr )? ; $first_attr:ident $( . $rest_attrs:ident)* $( , $else_err:expr)?) => { - $( $base . )? $first_attr.as_ref() - $( - .and_then(|v| v.$rest_attrs.as_ref()) - )* - $( .ok_or($else_err) )? - } -} -pub(crate) use required; +use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use json_syntax::Print; +pub use sigstore_protobuf_specs::dev::sigstore::bundle::v1::Bundle; +use sigstore_protobuf_specs::dev::sigstore::{ + common::v1::LogId, + rekor::v1::{Checkpoint, InclusionPromise, InclusionProof, KindVersion, TransparencyLogEntry}, +}; + +use crate::rekor::models::{ + log_entry::InclusionProof as RekorInclusionProof, LogEntry as RekorLogEntry, +}; // Known Sigstore bundle media types. #[derive(Clone, Copy, Debug)] @@ -57,3 +56,69 @@ impl FromStr for Version { } } } + +#[inline] +fn decode_hex>(hex: S) -> Result, ()> { + hex::decode(hex.as_ref()).or(Err(())) +} + +impl TryFrom for InclusionProof { + type Error = (); + + fn try_from(value: RekorInclusionProof) -> Result { + let hashes = value + .hashes + .iter() + .map(decode_hex) + .collect::, _>>()?; + + Ok(InclusionProof { + checkpoint: Some(Checkpoint { + envelope: value.checkpoint, + }), + hashes, + log_index: value.log_index, + root_hash: decode_hex(value.root_hash)?, + tree_size: value.tree_size, + }) + } +} + +/// Convert log entries returned from Rekor into Sigstore Bundle format entries. +impl TryFrom for TransparencyLogEntry { + type Error = (); + + fn try_from(value: RekorLogEntry) -> Result { + let canonicalized_body = { + let mut body = + json_syntax::to_value(value.body).expect("failed to parse constructed Body!"); + body.canonicalize(); + body.compact_print().to_string().into_bytes() + }; + let inclusion_promise = Some(InclusionPromise { + signed_entry_timestamp: base64 + .decode(value.verification.signed_entry_timestamp) + .or(Err(()))?, + }); + let inclusion_proof = value + .verification + .inclusion_proof + .map(|p| p.try_into()) + .transpose()?; + + Ok(TransparencyLogEntry { + canonicalized_body, + inclusion_promise, + inclusion_proof, + integrated_time: value.integrated_time, + kind_version: Some(KindVersion { + kind: "hashedrekord".to_owned(), + version: "0.0.1".to_owned(), + }), + log_id: Some(LogId { + key_id: decode_hex(value.log_i_d)?, + }), + log_index: value.log_index, + }) + } +} diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index 731a68c6c3..cd067aff48 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -16,7 +16,7 @@ use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; use webpki::{ types::{CertificateDer, TrustAnchor, UnixTime}, - EndEntityCert, KeyUsage, + EndEntityCert, KeyUsage, VerifiedPath, }; use crate::errors::{Result, SigstoreError}; @@ -83,24 +83,26 @@ impl<'a> CertificatePool<'a> { der: &[u8], verification_time: Option, ) -> Result<()> { - self.verify_cert_with_time(der, verification_time.unwrap_or(UnixTime::now())) + let der = CertificateDer::from(der); + let cert = EndEntityCert::try_from(&der)?; + + self.verify_cert_with_time(&cert, verification_time.unwrap_or(UnixTime::now()))?; + + Ok(()) } - /// TODO(tnytown): nudge webpki into behaving as the cosign code expects - pub(crate) fn verify_cert_with_time( - &self, - cert: &[u8], + pub(crate) fn verify_cert_with_time<'cert>( + &'a self, + cert: &'cert EndEntityCert<'cert>, verification_time: UnixTime, - ) -> Result<()> { - let der = CertificateDer::from(cert); - let cert = EndEntityCert::try_from(&der)?; - - // TODO(tnytown): Determine which of these algs are used in the Sigstore ecosystem. + ) -> Result> + where + 'a: 'cert, + { let signing_algs = webpki::ALL_VERIFICATION_ALGS; - let eku_code_signing = ID_KP_CODE_SIGNING.as_bytes(); - cert.verify_for_usage( + Ok(cert.verify_for_usage( signing_algs, &self.trusted_roots, self.intermediates.as_slice(), @@ -108,8 +110,6 @@ impl<'a> CertificatePool<'a> { KeyUsage::required(eku_code_signing), None, None, - )?; - - Ok(()) + )?) } } diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 81477cab6e..1330354022 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -10,17 +10,15 @@ use crate::fulcio::oauth::OauthTokenProvider; use crate::oauth::IdentityToken; use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use openidconnect::core::CoreIdToken; -use pkcs8::der::Decode; use reqwest::{header, Body}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; -use tracing::debug; use url::Url; -use x509_cert::Certificate; +use x509_cert::{Certificate, der::Decode}; -pub use models::CertificateResponse; +pub use models::{CertificateResponse, SigningCertificateDetachedSCT}; /// Default public Fulcio server root. pub const FULCIO_ROOT: &str = "https://fulcio.sigstore.dev/"; @@ -232,24 +230,23 @@ impl FulcioClient { header::ACCEPT => "application/pem-certificate-chain" ); - let response: SigningCertificate = client + let response = client .post(self.root_url.join(SIGNING_CERT_V2_PATH)?) .headers(headers) .json(&CreateSigningCertificateRequest { certificate_signing_request: request, }) .send() - .await? - .json() .await?; + let response = response.json().await?; - let sct_embedded = matches!( - response, - SigningCertificate::SignedCertificateEmbeddedSct(_) - ); - let certs = match response { - SigningCertificate::SignedCertificateDetachedSct(ref sc) => &sc.chain.certificates, - SigningCertificate::SignedCertificateEmbeddedSct(ref sc) => &sc.chain.certificates, + let (certs, detached_sct) = match response { + SigningCertificate::SignedCertificateDetachedSct(ref sc) => { + (&sc.chain.certificates, Some(sc.clone())) + } + SigningCertificate::SignedCertificateEmbeddedSct(ref sc) => { + (&sc.chain.certificates, None) + } }; if certs.len() < 2 { @@ -264,19 +261,10 @@ impl FulcioClient { .map(|pem| Certificate::from_der(pem.contents())) .collect::, _>>()?; - // TODO(tnytown): Implement SCT extraction. - // see: https://github.com/RustCrypto/formats/pull/1134 - if sct_embedded { - debug!("PrecertificateSignedCertificateTimestamps isn't implemented yet in x509_cert."); - } else { - // No embedded SCT, Fulcio instance that provides detached SCT: - if let SigningCertificate::SignedCertificateDetachedSct(_sct) = response {} - }; - Ok(CertificateResponse { cert, chain, - // sct, + detached_sct, }) } } diff --git a/src/fulcio/models.rs b/src/fulcio/models.rs index b4e7dc367d..0a784a76d0 100644 --- a/src/fulcio/models.rs +++ b/src/fulcio/models.rs @@ -14,13 +14,17 @@ //! Models for interfacing with Fulcio. //! -//! https://github.com/sigstore/fulcio/blob/9da27be4fb64b85c907ab9ddd8a5d3cbd38041d4/fulcio.proto +//! -use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use pem::Pem; use pkcs8::der::EncodePem; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_repr::Deserialize_repr; +use serde_with::{ + base64::{Base64, Standard}, + formats::Padded, + serde_as, DeserializeAs, SerializeAs, +}; use x509_cert::Certificate; fn serialize_x509_csr( @@ -31,31 +35,36 @@ where S: Serializer, { let encoded = input - .to_pem(pkcs8::LineEnding::CRLF) + .to_pem(pkcs8::LineEnding::LF) .map_err(serde::ser::Error::custom)?; - let encoded = BASE64_STD_ENGINE.encode(encoded); - ser.serialize_str(&encoded) + Base64::::serialize_as(&encoded, ser) } -fn deserialize_base64<'de, D>(de: D) -> std::result::Result, D::Error> +fn deserialize_inner_detached_sct<'de, D>(de: D) -> std::result::Result where D: Deserializer<'de>, { - let buf: &str = Deserialize::deserialize(de)?; - - BASE64_STD_ENGINE - .decode(buf) - .map_err(serde::de::Error::custom) + let buf: Vec = Base64::::deserialize_as(de)?; + serde_json::from_slice(&buf).map_err(serde::de::Error::custom) } -fn deserialize_inner_detached_sct<'de, D>(de: D) -> std::result::Result +fn deserialize_inner_detached_sct_signature<'de, D>(de: D) -> Result, D::Error> where D: Deserializer<'de>, { - let buf = deserialize_base64(de)?; - - serde_json::from_slice(&buf).map_err(serde::de::Error::custom) + let buf: Vec = Base64::::deserialize_as(de)?; + + // The first two bytes indicate the signature and hash algorithms so let's skip those. + // The next two bytes indicate the size of the signature. + let signature_size = u16::from_be_bytes(buf[2..4].try_into().expect("unexpected length")); + + // This should be equal to the length of the remainder of the signature buffer. + let signature = buf[4..].to_vec(); + if signature_size as usize != signature.len() { + return Err(serde::de::Error::custom("signature size mismatch")); + } + Ok(signature) } #[derive(Serialize)] @@ -72,7 +81,7 @@ pub enum SigningCertificate { SignedCertificateEmbeddedSct(SigningCertificateEmbeddedSCT), } -#[derive(Deserialize)] +#[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct SigningCertificateDetachedSCT { pub chain: CertificateChain, @@ -86,31 +95,34 @@ pub struct SigningCertificateEmbeddedSCT { pub chain: CertificateChain, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug, Clone)] pub struct CertificateChain { pub certificates: Vec, } -#[derive(Deserialize)] +#[serde_as] +#[derive(Deserialize, Debug, Clone)] pub struct InnerDetachedSCT { pub sct_version: SCTVersion, - #[serde(deserialize_with = "deserialize_base64")] - pub id: Vec, + #[serde_as(as = "Base64")] + pub id: [u8; 32], pub timestamp: u64, - #[serde(deserialize_with = "deserialize_base64")] + #[serde(deserialize_with = "deserialize_inner_detached_sct_signature")] pub signature: Vec, - #[serde(deserialize_with = "deserialize_base64")] + #[serde_as(as = "Base64")] pub extensions: Vec, } -#[derive(Deserialize_repr, PartialEq, Debug)] +#[derive(Deserialize_repr, PartialEq, Debug, Clone)] #[repr(u8)] pub enum SCTVersion { V1 = 0, } +// TODO(tnytown): Make this type prettier. SigningCertificateDetachedSCT duplicates most of the data +// in cert and chain. pub struct CertificateResponse { pub cert: Certificate, pub chain: Vec, - // pub sct: InnerDetachedSCT, + pub detached_sct: Option, } diff --git a/src/sign.rs b/src/sign.rs index 1d4b36838e..95b3158b48 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -19,19 +19,18 @@ use std::time::SystemTime; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use hex; -use json_syntax::Print; use p256::NistP256; use pkcs8::der::{Encode, EncodePem}; use sha2::{Digest, Sha256}; use signature::DigestSigner; -use sigstore_protobuf_specs::{ - Bundle, DevSigstoreBundleV1VerificationMaterial, DevSigstoreCommonV1HashOutput, - DevSigstoreCommonV1LogId, DevSigstoreCommonV1MessageSignature, - DevSigstoreCommonV1X509Certificate, DevSigstoreCommonV1X509CertificateChain, - DevSigstoreRekorV1Checkpoint, DevSigstoreRekorV1InclusionPromise, - DevSigstoreRekorV1InclusionProof, DevSigstoreRekorV1KindVersion, - DevSigstoreRekorV1TransparencyLogEntry, +use sigstore_protobuf_specs::dev::sigstore::bundle::v1::bundle; +use sigstore_protobuf_specs::dev::sigstore::bundle::v1::{ + verification_material, Bundle, VerificationMaterial, }; +use sigstore_protobuf_specs::dev::sigstore::common::v1::{ + HashAlgorithm, HashOutput, MessageSignature, X509Certificate, X509CertificateChain, +}; +use sigstore_protobuf_specs::dev::sigstore::rekor::v1::TransparencyLogEntry; use tokio::io::AsyncRead; use tokio_util::io::SyncIoBridge; use url::Url; @@ -46,7 +45,6 @@ use crate::fulcio::{self, FulcioClient, FULCIO_ROOT}; use crate::oauth::IdentityToken; use crate::rekor::apis::configuration::Configuration as RekorConfiguration; use crate::rekor::apis::entries_api::create_log_entry; -use crate::rekor::models::LogEntry; use crate::rekor::models::{hashedrekord, proposed_entry::ProposedEntry as ProposedLogEntry}; /// An asynchronous Sigstore signing session. @@ -130,14 +128,15 @@ impl<'ctx> AsyncSigningSession<'ctx> { return Err(SigstoreError::ExpiredSigningSession()); } - // TODO(tnytown): Verify SCT here. - // Sign artifact. let input_hash: &[u8] = &hasher.clone().finalize(); + let mut signature_bytes = Vec::new(); let artifact_signature: p256::ecdsa::Signature = self.private_key.sign_digest(hasher); + artifact_signature + .to_der() + .encode_to_vec(&mut signature_bytes) + .expect("failed to encode Signature!"); - // Prepare inputs. - let b64_artifact_signature = base64.encode(artifact_signature.to_der()); let cert = &self.certs.cert; // Create the transparency log entry. @@ -145,9 +144,9 @@ impl<'ctx> AsyncSigningSession<'ctx> { api_version: "0.0.1".to_owned(), spec: hashedrekord::Spec { signature: hashedrekord::Signature { - content: b64_artifact_signature.clone(), + content: base64.encode(&signature_bytes), public_key: hashedrekord::PublicKey::new( - base64.encode(cert.to_pem(pkcs8::LineEnding::CRLF)?), + base64.encode(cert.to_pem(pkcs8::LineEnding::LF)?), ), }, data: hashedrekord::Data { @@ -166,10 +165,10 @@ impl<'ctx> AsyncSigningSession<'ctx> { // TODO(tnytown): Maybe run through the verification flow here? See sigstore-rs#296. Ok(SigningArtifact { - input_digest: base64.encode(input_hash), + input_digest: input_hash.to_owned(), cert: cert.to_der()?, - b64_signature: b64_artifact_signature, - log_entry: entry, + signature: signature_bytes, + log_entry: entry.try_into().expect("TODO"), }) } @@ -245,7 +244,10 @@ pub struct SigningContext { impl SigningContext { /// Manually constructs a [SigningContext] from its constituent data. - pub fn new(fulcio: FulcioClient, rekor_config: RekorConfiguration) -> Self { + pub fn new( + fulcio: FulcioClient, + rekor_config: RekorConfiguration, + ) -> Self { Self { fulcio, rekor_config, @@ -254,14 +256,14 @@ impl SigningContext { /// Returns a [SigningContext] configured against the public-good production Sigstore /// infrastructure. - pub fn production() -> Self { - Self::new( + pub fn production() -> SigstoreResult { + Ok(Self::new( FulcioClient::new( Url::parse(FULCIO_ROOT).expect("constant FULCIO root fails to parse!"), crate::fulcio::TokenProvider::Oauth(OauthTokenProvider::default()), ), Default::default(), - ) + )) } /// Configures and returns an [AsyncSigningSession] with the held context. @@ -282,10 +284,10 @@ impl SigningContext { /// A signature and its associated metadata. pub struct SigningArtifact { - input_digest: String, + input_digest: Vec, cert: Vec, - b64_signature: String, - log_entry: LogEntry, + signature: Vec, + log_entry: TransparencyLogEntry, } impl SigningArtifact { @@ -293,84 +295,35 @@ impl SigningArtifact { /// /// The resulting bundle can be serialized with [serde_json]. pub fn to_bundle(self) -> Bundle { - #[inline] - fn hex_to_base64>(hex: S) -> String { - let decoded = hex::decode(hex.as_ref()).expect("Malformed data in Rekor response"); - base64.encode(decoded) - } - // NOTE: We explicitly only include the leaf certificate in the bundle's "chain" // here: the specs explicitly forbid the inclusion of the root certificate, // and discourage inclusion of any intermediates (since they're in the root of // trust already). - let x_509_certificate_chain = Some(DevSigstoreCommonV1X509CertificateChain { - certificates: Some(vec![DevSigstoreCommonV1X509Certificate { - raw_bytes: Some(base64.encode(&self.cert)), - }]), - }); - - let inclusion_proof = if let Some(proof) = self.log_entry.verification.inclusion_proof { - let hashes = proof.hashes.iter().map(hex_to_base64).collect(); - Some(DevSigstoreRekorV1InclusionProof { - checkpoint: Some(DevSigstoreRekorV1Checkpoint { - envelope: Some(proof.checkpoint), - }), - hashes: Some(hashes), - log_index: Some(proof.log_index.to_string()), - root_hash: Some(hex_to_base64(proof.root_hash)), - tree_size: Some(proof.tree_size.to_string()), - }) - } else { - None - }; - - let canonicalized_body = { - let mut body = json_syntax::to_value(self.log_entry.body) - .expect("failed to parse constructed Body!"); - body.canonicalize(); - Some(base64.encode(body.compact_print().to_string())) + let x509_certificate_chain = X509CertificateChain { + certificates: vec![X509Certificate { + raw_bytes: self.cert, + }], }; - // TODO(tnytown): When we fix `sigstore_protobuf_specs`, have the Rekor client APIs convert - // responses into types from the specs as opposed to returning the raw `LogEntry` model type. - let tlog_entry = DevSigstoreRekorV1TransparencyLogEntry { - canonicalized_body, - inclusion_promise: Some(DevSigstoreRekorV1InclusionPromise { - // XX: sigstore-python deserializes the SET from base64 here because their protobuf - // library transparently serializes `bytes` fields as base64. - signed_entry_timestamp: Some(self.log_entry.verification.signed_entry_timestamp), - }), - inclusion_proof, - integrated_time: Some(self.log_entry.integrated_time.to_string()), - kind_version: Some(DevSigstoreRekorV1KindVersion { - kind: Some("hashedrekord".to_owned()), - version: Some("0.0.1".to_owned()), - }), - log_id: Some(DevSigstoreCommonV1LogId { - key_id: Some(hex_to_base64(self.log_entry.log_i_d)), - }), - log_index: Some(self.log_entry.log_index.to_string()), - }; - - let verification_material = Some(DevSigstoreBundleV1VerificationMaterial { - public_key: None, + let verification_material = Some(VerificationMaterial { timestamp_verification_data: None, - tlog_entries: Some(vec![tlog_entry]), - x_509_certificate_chain, + tlog_entries: vec![self.log_entry], + content: Some(verification_material::Content::X509CertificateChain( + x509_certificate_chain, + )), }); - let message_signature = Some(DevSigstoreCommonV1MessageSignature { - message_digest: Some(DevSigstoreCommonV1HashOutput { - algorithm: Some("SHA2_256".to_owned()), - digest: Some(self.input_digest), + let message_signature = MessageSignature { + message_digest: Some(HashOutput { + algorithm: HashAlgorithm::Sha2256.into(), + digest: self.input_digest, }), - signature: Some(self.b64_signature), - }); + signature: self.signature, + }; Bundle { - dsse_envelope: None, - media_type: Some(Version::Bundle0_2.to_string()), - message_signature, + media_type: Version::Bundle0_2.to_string(), verification_material, + content: Some(bundle::Content::MessageSignature(message_signature)), } } } diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 07808ee55e..6fd4e2cd9f 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -37,21 +37,23 @@ use std::{ }; mod constants; -mod trustroot; use sha2::{Digest, Sha256}; +use sigstore_protobuf_specs::dev::sigstore::{ + common::v1::TimeRange, + trustroot::v1::{CertificateAuthority, TransparencyLogInstance, TrustedRoot}, +}; use tough::TargetName; use tracing::debug; use webpki::types::CertificateDer; -use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; - use super::errors::{Result, SigstoreError}; /// A `Repository` owns all key material necessary for establishing a root of trust. pub trait Repository { fn fulcio_certs(&self) -> Result>; fn rekor_keys(&self) -> Result>; + fn ctfe_keys(&self) -> Result>; } /// A `ManualRepository` is a [Repository] with out-of-band trust materials. @@ -60,6 +62,7 @@ pub trait Repository { pub struct ManualRepository<'a> { pub fulcio_certs: Option>>, pub rekor_key: Option>, + pub ctfe_keys: Option>>, } impl Repository for ManualRepository<'_> { @@ -76,6 +79,13 @@ impl Repository for ManualRepository<'_> { None => Vec::new(), }) } + + fn ctfe_keys(&self) -> Result> { + Ok(match &self.ctfe_keys { + Some(keys) => keys.iter().map(|v| &v[..]).collect(), + None => Vec::new(), + }) + } } /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. @@ -205,8 +215,9 @@ impl SigstoreRepository { fn tlog_keys(tlogs: &[TransparencyLogInstance]) -> impl Iterator { tlogs .iter() - .filter(|key| is_timerange_valid(key.public_key.valid_for.as_ref(), false)) - .filter_map(|key| key.public_key.raw_bytes.as_ref()) + .filter_map(|tlog| tlog.public_key.as_ref()) + .filter(|key| is_timerange_valid(key.valid_for.as_ref(), false)) + .filter_map(|key| key.raw_bytes.as_ref()) .map(|key_bytes| key_bytes.as_slice()) } @@ -216,8 +227,9 @@ impl SigstoreRepository { allow_expired: bool, ) -> impl Iterator { cas.iter() - .filter(move |ca| is_timerange_valid(Some(&ca.valid_for), allow_expired)) - .flat_map(|ca| ca.cert_chain.certificates.iter()) + .filter(move |ca| is_timerange_valid(ca.valid_for.as_ref(), allow_expired)) + .flat_map(|ca| ca.cert_chain.as_ref()) + .flat_map(|chain| chain.certificates.iter()) .map(|cert| cert.raw_bytes.as_slice()) } } @@ -266,25 +278,47 @@ impl Repository for SigstoreRepository { Ok(keys) } } + + /// Fetch CTFE public keys from the given TUF repository or reuse + /// the local cache if it's not outdated. + /// + /// The contents of the local cache are updated when they are outdated. + /// + /// **Warning:** this method needs special handling when invoked from + /// an async function because it performs blocking operations. + fn ctfe_keys(&self) -> Result> { + let root = self.trusted_root()?; + let keys: Vec<_> = Self::tlog_keys(&root.ctlogs).collect(); + + if keys.is_empty() { + Err(SigstoreError::TufMetadataError( + "CTFE keys not found".into(), + )) + } else { + Ok(keys) + } + } } /// Given a `range`, checks that the the current time is not before `start`. If /// `allow_expired` is `false`, also checks that the current time is not after /// `end`. fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { - let time = chrono::Utc::now(); + let now = chrono::Utc::now().timestamp(); + + let start = range.and_then(|r| r.start.as_ref()).map(|t| t.seconds); + let end = range.and_then(|r| r.end.as_ref()).map(|t| t.seconds); - match range { + match (start, end) { // If there was no validity period specified, the key is always valid. - None => true, + (None, _) => true, // Active: if the current time is before the starting period, we are not yet valid. - Some(range) if time < range.start => false, - // If we want Expired keys, then the key is valid at this point. + (Some(start), _) if now < start => false, + // If we want Expired keys, then we don't need to check the end. _ if allow_expired => true, - // Otherwise, check that we are in range if the range has an end. - Some(range) => match range.end { - None => true, - Some(end) => time <= end, - }, + // If there is no expiry date, the key is valid. + (_, None) => true, + // If we have an expiry date, check it. + (_, Some(end)) => now <= end, } } diff --git a/src/tuf/trustroot.rs b/src/tuf/trustroot.rs deleted file mode 100644 index aeb321fd92..0000000000 --- a/src/tuf/trustroot.rs +++ /dev/null @@ -1,194 +0,0 @@ -#![allow(dead_code)] - -// HACK(jl): protobuf-specs schemas are currently compiled for direct dependencies of the Bundle schema. -// See note https://github.com/sigstore/protobuf-specs/blob/main/gen/pb-rust/src/lib.rs#L1-L23 -// HACK(ap): We should probably use definitions from sigstore-protobuf-specs, but -// the autogenerated definitions are unergonomic. Declare it locally here. - -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use serde_with::base64::Base64; - -use serde_with::serde_as; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[allow(non_camel_case_types)] -/// Only a subset of the secure hash standard algorithms are supported. -/// See for more -/// details. -/// UNSPECIFIED SHOULD not be used, primary reason for inclusion is to force -/// any proto JSON serialization to emit the used hash algorithm, as default -/// option is to *omit* the default value of an enum (which is the first -/// value, represented by '0'. -pub(crate) enum HashAlgorithm { - HASH_ALGORITHM_UNSPECIFIED = 0, - SHA2_256 = 1, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[allow(non_camel_case_types)] -/// Details of a specific public key, capturing the the key encoding method, -/// and signature algorithm. -/// To avoid the possibility of contradicting formats such as PKCS1 with -/// ED25519 the valid permutations are listed as a linear set instead of a -/// cartesian set (i.e one combined variable instead of two, one for encoding -/// and one for the signature algorithm). -pub(crate) enum PublicKeyDetails { - PUBLIC_KEY_DETAILS_UNSPECIFIED = 0, - // RSA - PKCS1_RSA_PKCS1V5 = 1, // See RFC8017 - PKCS1_RSA_PSS = 2, // See RFC8017 - PKIX_RSA_PKCS1V5 = 3, - PKIX_RSA_PSS = 4, - // ECDSA - PKIX_ECDSA_P256_SHA_256 = 5, // See NIST FIPS 186-4 - PKIX_ECDSA_P256_HMAC_SHA_256 = 6, // See RFC6979 - // Ed 25519 - PKIX_ED25519 = 7, // See RFC8032 -} - -#[serde_as] -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// LogId captures the identity of a transparency log. -pub(crate) struct LogId { - #[serde_as(as = "Base64")] - pub key_id: Vec, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// The time range is closed and includes both the start and end times, -/// (i.e., [start, end]). -/// End is optional to be able to capture a period that has started but -/// has no known end. -pub(crate) struct TimeRange { - pub start: DateTime, - pub end: Option>, -} - -#[serde_as] -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -pub(crate) struct PublicKey { - #[serde_as(as = "Option")] - pub raw_bytes: Option>, - pub key_details: PublicKeyDetails, - pub valid_for: Option, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -pub(crate) struct DistinguishedName { - pub organization: String, - pub common_name: String, -} - -#[serde_as] -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -pub(crate) struct X509Certificate { - #[serde_as(as = "Base64")] - pub raw_bytes: Vec, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// A chain of X.509 certificates. -pub(crate) struct X509CertificateChain { - pub certificates: Vec, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// TransparencyLogInstance describes the immutable parameters from a -/// transparency log. -/// See https://www.rfc-editor.org/rfc/rfc9162.html#name-log-parameters -/// for more details. -/// The included parameters are the minimal set required to identify a log, -/// and verify an inclusion proof/promise. -pub(crate) struct TransparencyLogInstance { - pub base_url: String, - pub hash_algorithm: HashAlgorithm, - pub public_key: PublicKey, - pub log_id: LogId, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// CertificateAuthority enlists the information required to identify which -/// CA to use and perform signature verification. -pub(crate) struct CertificateAuthority { - pub subject: DistinguishedName, - pub uri: Option, - pub cert_chain: X509CertificateChain, - pub valid_for: TimeRange, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -/// TrustedRoot describes the client's complete set of trusted entities. -/// How the TrustedRoot is populated is not specified, but can be a -/// combination of many sources such as TUF repositories, files on disk etc. -/// -/// The TrustedRoot is not meant to be used for any artifact verification, only -/// to capture the complete/global set of trusted verification materials. -/// When verifying an artifact, based on the artifact and policies, a selection -/// of keys/authorities are expected to be extracted and provided to the -/// verification function. This way the set of keys/authorities can be kept to -/// a minimal set by the policy to gain better control over what signatures -/// that are allowed. -/// -/// The embedded transparency logs, CT logs, CAs and TSAs MUST include any -/// previously used instance -- otherwise signatures made in the past cannot -/// be verified. -/// The currently used instances MUST NOT have their 'end' timestamp set in -/// their 'valid_for' attribute for easy identification. -/// All the listed instances SHOULD be sorted by the 'valid_for' in ascending -/// order, that is, the oldest instance first and the current instance last. -pub(crate) struct TrustedRoot { - pub media_type: String, - pub tlogs: Vec, - pub certificate_authorities: Vec, - pub ctlogs: Vec, - pub timestamp_authorities: Vec, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn tuf_serde_as_base64() { - let data = X509Certificate { - raw_bytes: b"Hello World".to_vec(), // NOTE(jl): value not representative - }; - let json = serde_json::json!({"rawBytes": "SGVsbG8gV29ybGQ=",}); - - assert_eq!(json, serde_json::to_value(&data).unwrap()); - assert_eq!(data, serde_json::from_value(json).unwrap()); - } - - #[test] - fn tuf_serde_as_nested_structure_base64() { - let data = PublicKey { - raw_bytes: Some(b"Hello World".to_vec()), - key_details: PublicKeyDetails::PKIX_ED25519, - valid_for: Some(TimeRange { - start: DateTime::from_timestamp(1_500_000_000, 0).unwrap(), - end: None, - }), - }; - let json = serde_json::json!({ - "rawBytes": "SGVsbG8gV29ybGQ=", - "keyDetails": "PKIX_ED25519", - "validFor": { - "start": "2017-07-14T02:40:00Z", - "end": None::> - } - }); - - assert_eq!(json, serde_json::to_value(&data).unwrap()); - assert_eq!(data, serde_json::from_value(json).unwrap()); - } -} diff --git a/src/verify/mod.rs b/src/verify/mod.rs index 5954d45bd6..95e3375ed3 100644 --- a/src/verify/mod.rs +++ b/src/verify/mod.rs @@ -18,7 +18,7 @@ mod models; pub use models::{VerificationError, VerificationMaterials, VerificationResult}; pub mod policy; -pub use policy::VerificationPolicy; +pub use policy::{PolicyError, VerificationPolicy}; mod verifier; pub use verifier::Verifier; diff --git a/src/verify/models.rs b/src/verify/models.rs index 19c021f75f..004a664423 100644 --- a/src/verify/models.rs +++ b/src/verify/models.rs @@ -14,31 +14,30 @@ // limitations under the License. use std::{ - cell::OnceCell, io::{self, Read}, str::FromStr, }; use crate::{ - bundle::required, bundle::Version as BundleVersion, crypto::certificate::{is_leaf, is_root_ca}, - errors::SigstoreError, - rekor::models::log_entry, - rekor::models::{ - log_entry::{InclusionProof, Verification}, - LogEntry, - }, + rekor::models as rekor, }; +use crate::Bundle; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use pkcs8::der::Decode; +use pkcs8::der::{Decode, EncodePem}; use sha2::{Digest, Sha256}; -use sigstore_protobuf_specs::Bundle; +use sigstore_protobuf_specs::dev::sigstore::{ + bundle::v1::{bundle, verification_material}, + rekor::v1::{InclusionProof, TransparencyLogEntry}, +}; use thiserror::Error; -use tracing::warn; +use tracing::{debug, error, warn}; use x509_cert::Certificate; +use super::policy::PolicyError; + #[derive(Error, Debug)] pub enum VerificationError { #[error("Certificate expired before time of signing")] @@ -56,16 +55,18 @@ pub enum VerificationError { #[error("Failed to verify that the signature corresponds to the input")] SignatureVerificationFailure, - #[error("{0}")] - PolicyFailure(String), + #[error(transparent)] + PolicyFailure(#[from] PolicyError), } pub type VerificationResult = Result<(), VerificationError>; pub struct VerificationMaterials { - pub input_digest: Vec, - pub certificate: Certificate, - pub signature: Vec, - rekor_entry: OnceCell, + pub(crate) input_digest: Vec, + pub(crate) certificate: Certificate, + pub(crate) signature: Vec, + rekor_entry: TransparencyLogEntry, + + offline: bool, } impl VerificationMaterials { @@ -74,33 +75,29 @@ impl VerificationMaterials { certificate: Certificate, signature: Vec, offline: bool, - rekor_entry: Option, + rekor_entry: TransparencyLogEntry, ) -> Option { let mut hasher = Sha256::new(); io::copy(input, &mut hasher).ok()?; - if offline && rekor_entry.is_none() { - // offline verification requires a Rekor entry + if matches!( + rekor_entry, + TransparencyLogEntry { + inclusion_promise: None, + inclusion_proof: None, + .. + } + ) { + error!("encountered TransparencyLogEntry without any inclusion materials"); return None; } - let rekor_entry = if let Some(rekor_entry) = rekor_entry { - let cell = OnceCell::new(); - - // TODO(tnytown): Switch to setting if offline when Rekor fetching is implemented. - #[allow(clippy::unwrap_used)] - cell.set(rekor_entry).unwrap(); - - cell - } else { - Default::default() - }; - Some(Self { input_digest: hasher.finalize().to_vec(), rekor_entry, certificate, signature, + offline, }) } @@ -108,27 +105,36 @@ impl VerificationMaterials { /// /// For details on bundle semantics, please refer to [VerificationMaterial]. /// - /// [VerificationMaterial]: sigstore_protobuf_specs::DevSigstoreBundleV1VerificationMaterial - /// - /// TODO(tnytown): Determine if this type should yield SigstoreResult. + /// [VerificationMaterial]: sigstore_protobuf_specs::dev::sigstore::bundle::v1::VerificationMaterial pub fn from_bundle(input: &mut R, bundle: Bundle, offline: bool) -> Option { - fn certificate_from_base64(encoded: &str) -> Option { - Certificate::from_der(&base64.decode(encoded).ok()?).ok() - } - - let certs = required!( - bundle; - verification_material.x_509_certificate_chain.certificates, - SigstoreError::SigstoreBundleMalformedError("Cannot find required field in bundle".to_string()) - ).ok()?; + let (content, mut tlog_entries) = match bundle.verification_material { + Some(m) => (m.content, m.tlog_entries), + _ => { + error!("bundle missing VerificationMaterial"); + return None; + } + }; // Parse the certificates. The first entry in the chain MUST be a leaf certificate, and the // rest of the chain MUST NOT include a root CA or any intermediate CAs that appear in an // independent root of trust. + let certs = match content { + Some(verification_material::Content::X509CertificateChain(ch)) => ch.certificates, + Some(verification_material::Content::Certificate(cert)) => { + vec![cert] + } + _ => { + error!("bundle includes unsupported VerificationMaterial Content"); + return None; + } + }; let certs = certs .iter() - .map(|cert| certificate_from_base64(cert.raw_bytes.as_ref()?)) - .collect::>>()?; + .map(|c| c.raw_bytes.as_slice()) + .map(Certificate::from_der) + .collect::, _>>() + .ok()?; + let [leaf_cert, chain_certs @ ..] = &certs[..] else { return None; }; @@ -143,80 +149,107 @@ impl VerificationMaterials { } } - let signature = base64 - .decode(required!(bundle; message_signature.signature)?) - .ok()?; - let tlog_entries = required!(bundle; verification_material.tlog_entries)?; + let signature = match bundle.content? { + bundle::Content::MessageSignature(s) => s.signature, + _ => { + error!("bundle includes unsupported DSSE signature"); + return None; + } + }; + if tlog_entries.len() != 1 { - // Expected exactly one tlog entry. + error!("bundle expected 1 tlog entry; got {}", tlog_entries.len()); return None; } - let tlog_entry = &tlog_entries[0]; + let tlog_entry = tlog_entries.remove(0); - let inclusion_promise = &tlog_entry.inclusion_promise; - let inclusion_proof = tlog_entry.inclusion_proof.as_ref(); + let (inclusion_promise, inclusion_proof) = + (&tlog_entry.inclusion_promise, &tlog_entry.inclusion_proof); - let has_checkpoint = required!(; inclusion_proof.checkpoint.envelope).is_some(); - match BundleVersion::from_str(&bundle.media_type?).ok()? { - BundleVersion::Bundle0_1 => { + // `inclusion_proof` is now a required field in the protobuf spec, + // but older versions of Rekor didn't provide inclusion proofs. + // + // https://github.com/sigstore/sigstore-python/pull/634#discussion_r1182769140 + match BundleVersion::from_str(&bundle.media_type) { + Ok(BundleVersion::Bundle0_1) => { if inclusion_promise.is_none() { - // 0.1 bundle must contain inclusion promise + error!("bundle must contain inclusion promise"); return None; } - if inclusion_proof.is_some() && !has_checkpoint { - warn!("0.1 bundle contains inclusion proof without checkpoint; ignoring"); + if matches!( + inclusion_proof, + Some(InclusionProof { + checkpoint: None, + .. + }) + ) { + debug!("0.1 bundle contains inclusion proof without checkpoint"); } } - BundleVersion::Bundle0_2 => { - inclusion_proof?; - if !has_checkpoint { - // inclusion proofs must contain checkpoints + Ok(BundleVersion::Bundle0_2) => { + if inclusion_proof.is_none() { + error!("bundle must contain inclusion proof"); + return None; + } + + if matches!( + inclusion_proof, + Some(InclusionProof { + checkpoint: None, + .. + }) + ) { + error!("bundle must contain checkpoint"); return None; } } + Err(_) => { + error!("unknown bundle version"); + return None; + } } - let parsed_inclusion_proof = if inclusion_proof.is_some() && has_checkpoint { - Some(InclusionProof { - checkpoint: required!(; inclusion_proof.checkpoint.envelope)?.clone(), - hashes: required!(; inclusion_proof.hashes)?.clone(), - log_index: required!(; inclusion_proof.log_index)?.parse().ok()?, - root_hash: required!(; inclusion_proof.log_index)?.clone(), - tree_size: required!(; inclusion_proof.tree_size)?.parse().ok()?, - }) - } else { - None - }; + Self::new(input, leaf_cert.clone(), signature, offline, tlog_entry) + } - let canonicalized_body = { - let decoded = base64 - .decode(tlog_entry.canonicalized_body.as_ref()?) - .ok()?; - serde_json::from_slice(&decoded).ok()? - }; - // required!(tlog_entry; log_id.key_id)?.clone(); - let entry = LogEntry { - uuid: "".to_string(), - body: log_entry::Body::hashedrekord(canonicalized_body), - attestation: None, - integrated_time: required!(tlog_entry; integrated_time)?.parse().ok()?, - log_i_d: "".into(), - log_index: required!(tlog_entry; log_index)?.parse().ok()?, - verification: Verification { - inclusion_proof: parsed_inclusion_proof, - signed_entry_timestamp: required!(; inclusion_promise.signed_entry_timestamp)? - .clone(), + /// Retrieves the [LogEntry] for the materials. + pub fn rekor_entry(&self) -> Option<&TransparencyLogEntry> { + let base64_pem_certificate = + base64.encode(self.certificate.to_pem(pkcs8::LineEnding::LF).ok()?); + + let expected_entry = rekor::Hashedrekord { + kind: "hashedrekord".to_owned(), + api_version: "0.0.1".to_owned(), + spec: rekor::hashedrekord::Spec { + signature: rekor::hashedrekord::Signature { + content: base64.encode(&self.signature), + public_key: rekor::hashedrekord::PublicKey::new(base64_pem_certificate), + }, + data: rekor::hashedrekord::Data { + hash: rekor::hashedrekord::Hash { + algorithm: rekor::hashedrekord::AlgorithmKind::sha256, + value: hex::encode(&self.input_digest), + }, + }, }, }; - Self::new(input, leaf_cert.clone(), signature, offline, Some(entry)) - } + let entry = if !self.offline && self.rekor_entry.inclusion_proof.is_none() { + warn!("online rekor fetching is not implemented yet, but is necessary for this bundle"); + return None; + } else { + &self.rekor_entry + }; - /// Retrieves the [LogEntry] for the materials. - pub fn rekor_entry(&self) -> &LogEntry { - // TODO(tnytown): Fetch online Rekor entry, confirm consistency, and get_or_init here. - #[allow(clippy::unwrap_used)] - self.rekor_entry.get().unwrap() + let actual: serde_json::Value = + serde_json::from_slice(&self.rekor_entry.canonicalized_body).ok()?; + let expected: serde_json::Value = serde_json::to_value(expected_entry).ok()?; + + if actual != expected { + return None; + } + + Some(entry) } } diff --git a/src/verify/policy.rs b/src/verify/policy.rs index 8f0791c50f..ec6f0409f3 100644 --- a/src/verify/policy.rs +++ b/src/verify/policy.rs @@ -17,12 +17,10 @@ //! use const_oid::ObjectIdentifier; +use thiserror::Error; +use tracing::warn; use x509_cert::ext::pkix::{name::GeneralName, SubjectAltName}; -use crate::verify::VerificationError; - -use super::models::VerificationResult; - macro_rules! oids { ($($name:ident = $value:literal),+) => { $(const $name: ObjectIdentifier = ObjectIdentifier::new_unwrap($value);)+ @@ -65,7 +63,32 @@ oids! { } -/// A trait for policies that check a single textual value against a X.509 extension. +#[derive(Error, Debug)] +pub enum PolicyError { + #[error("did not find exactly 1 of the required extension in the certificate")] + ExtensionNotFound, + + #[error("certificate's {extension} does not match (got {actual}, expected {expected})")] + ExtensionCheckFailed { + extension: String, + expected: String, + actual: String, + }, + + #[error("{0} of {total} policies failed: {1}\n- ", + errors.len(), + errors.iter().map(|e| e.to_string()).collect::>().join("\n- ") + )] + AllOf { + total: usize, + errors: Vec, + }, + + #[error("0 of {total} policies succeeded")] + AnyOf { total: usize }, +} + +/// A policy that checks a single textual value against a X.509 extension. pub trait SingleX509ExtPolicy { fn new>(val: S) -> Self; fn name() -> &'static str; @@ -73,15 +96,13 @@ pub trait SingleX509ExtPolicy { } impl VerificationPolicy for T { - fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + fn verify(&self, cert: &x509_cert::Certificate) -> Option { let extensions = cert.tbs_certificate.extensions.as_deref().unwrap_or(&[]); let mut extensions = extensions.iter().filter(|ext| ext.extn_id == T::OID); // Check for exactly one extension. let (Some(ext), None) = (extensions.next(), extensions.next()) else { - return Err(VerificationError::PolicyFailure( - "Cannot get policy extensions from certificate".into(), - )); + return Some(PolicyError::ExtensionNotFound); }; // Parse raw string without DER encoding. @@ -89,14 +110,13 @@ impl VerificationPolicy for T .expect("failed to parse constructed Extension!"); if val != self.value() { - Err(VerificationError::PolicyFailure(format!( - "Certificate's {} does not match (got {}, expected {})", - T::name(), - val, - self.value() - ))) + Some(PolicyError::ExtensionCheckFailed { + extension: T::name().to_owned(), + expected: self.value().to_owned(), + actual: val.to_owned(), + }) } else { - Ok(()) + None } } } @@ -139,7 +159,7 @@ impl_policy!( /// An interface that all policies must conform to. pub trait VerificationPolicy { - fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult; + fn verify(&self, cert: &x509_cert::Certificate) -> Option; } /// The "any of" policy, corresponding to a logical OR between child policies. @@ -158,15 +178,13 @@ impl<'a> AnyOf<'a> { } impl VerificationPolicy for AnyOf<'_> { - fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { + fn verify(&self, cert: &x509_cert::Certificate) -> Option { self.children .iter() - .find(|policy| policy.verify(cert).is_ok()) - .ok_or(VerificationError::PolicyFailure(format!( - "0 of {} policies succeeded", - self.children.len() - ))) - .map(|_| ()) + .find(|policy| policy.verify(cert).is_some()) + .map(|_| PolicyError::AnyOf { + total: self.children.len(), + }) } } @@ -178,39 +196,33 @@ pub struct AllOf<'a> { } impl<'a> AllOf<'a> { - pub fn new>(policies: I) -> Self { - Self { - children: policies.into_iter().collect(), + pub fn new>(policies: I) -> Option { + let children: Vec<_> = policies.into_iter().collect(); + + // Without this, we'd be able to construct an `AllOf` containing an empty list of child + // policies. This is almost certainly not what the user wants and is a potential source + // of API misuse, so we explicitly disallow it. + if children.is_empty() { + warn!("attempted to construct an AllOf with an empty list of child policies"); + return None; } + + Some(Self { children }) } } impl VerificationPolicy for AllOf<'_> { - fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { - // Without this, we'd consider empty lists of child policies trivially valid. - // This is almost certainly not what the user wants and is a potential - // source of API misuse, so we explicitly disallow it. - if self.children.is_empty() { - return Err(VerificationError::PolicyFailure( - "no child policies to verify".into(), - )); - } - + fn verify(&self, cert: &x509_cert::Certificate) -> Option { let results = self.children.iter().map(|policy| policy.verify(cert)); - let failures: Vec<_> = results - .filter_map(|result| result.err()) - .map(|err| err.to_string()) - .collect(); + let failures: Vec<_> = results.flatten().collect(); if failures.is_empty() { - Ok(()) + None } else { - Err(VerificationError::PolicyFailure(format!( - "{} of {} policies failed:\n- {}", - failures.len(), - self.children.len(), - failures.join("\n- ") - ))) + Some(PolicyError::AllOf { + total: self.children.len(), + errors: failures, + }) } } } @@ -218,9 +230,9 @@ impl VerificationPolicy for AllOf<'_> { pub(crate) struct UnsafeNoOp; impl VerificationPolicy for UnsafeNoOp { - fn verify(&self, _cert: &x509_cert::Certificate) -> VerificationResult { - eprintln!("unsafe (no-op) verification policy used! no verification performed!"); - VerificationResult::Ok(()) + fn verify(&self, _cert: &x509_cert::Certificate) -> Option { + warn!("unsafe (no-op) verification policy used! no verification performed!"); + None } } @@ -248,14 +260,14 @@ impl Identity { } impl VerificationPolicy for Identity { - fn verify(&self, cert: &x509_cert::Certificate) -> VerificationResult { - if let err @ Err(_) = self.issuer.verify(cert) { + fn verify(&self, cert: &x509_cert::Certificate) -> Option { + if let err @ Some(_) = self.issuer.verify(cert) { return err; } let (_, san): (bool, SubjectAltName) = match cert.tbs_certificate.get() { Ok(Some(result)) => result, - _ => return Err(VerificationError::CertificateMalformed), + _ => return Some(PolicyError::ExtensionNotFound), }; let names: Vec<_> = san @@ -272,13 +284,13 @@ impl VerificationPolicy for Identity { .collect(); if names.contains(&self.identity.as_str()) { - Ok(()) + None } else { - Err(VerificationError::PolicyFailure(format!( - "Certificate's SANs do not match {}; actual SANs: {}", - self.identity, - names.join(", ") - ))) + Some(PolicyError::ExtensionCheckFailed { + extension: "SubjectAltName".to_owned(), + expected: self.identity.clone(), + actual: names.join(", "), + }) } } } diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 2645b87919..3c695995cf 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -15,8 +15,13 @@ use std::cell::OnceCell; use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; -use pkcs8::der::Encode; -use webpki::types::UnixTime; +use tracing::debug; +use webpki::{ + types::{CertificateDer, UnixTime}, + EndEntityCert, +}; + +use x509_cert::der::Encode; use x509_cert::ext::pkix::{ExtendedKeyUsage, KeyUsage}; use crate::{ @@ -45,7 +50,6 @@ impl<'a, R: Repository> Verifier<'a, R> { }) } - /// TODO(tnytown): Evil (?) interior mutability hack to work around lifetime issues. fn cert_pool(&'a self) -> SigstoreResult<&CertificatePool<'a>> { let init_cert_pool = || { let certs = self.trust_repo.fulcio_certs()?; @@ -82,36 +86,31 @@ impl<'a, R: Repository> Verifier<'a, R> { // 7) Verify that the signing certificate was valid at the time of // signing by comparing the expiry against the integrated timestamp. - // 1) Verify that the signing certificate is signed by the root certificate and that the - // signing certificate was valid at the time of signing. - // 1) Verify that the signing certificate is signed by the certificate // chain and that the signing certificate was valid at the time // of signing. - let issued_at = materials - .certificate - .tbs_certificate - .validity - .not_before - .to_unix_duration(); - let cert_der = &materials + let tbs_certificate = &materials.certificate.tbs_certificate; + let issued_at = tbs_certificate.validity.not_before.to_unix_duration(); + let cert_der: CertificateDer = materials .certificate .to_der() - .expect("failed to DER-encode constructed Certificate!"); - store - .verify_cert_with_time(cert_der, UnixTime::since_unix_epoch(issued_at)) - .or(Err(VerificationError::CertificateVerificationFailure))?; + .expect("failed to DER-encode constructed Certificate!") + .into(); + let ee_cert: EndEntityCert = (&cert_der).try_into().expect("TODO"); + + let Ok(_trusted_chain) = + store.verify_cert_with_time(&ee_cert, UnixTime::since_unix_epoch(issued_at)) + else { + return Err(VerificationError::CertificateVerificationFailure); + }; + + debug!("signing certificate chains back to trusted root"); // 2) Verify that the signing certificate belongs to the signer. - // TODO(tnytown): How likely is a malformed certificate in this position? Do we want to - // account for it and create an error type as opposed to unwrapping? - let (_, key_usage_ext): (bool, KeyUsage) = materials - .certificate - .tbs_certificate - .get() - .expect("Malformed certificate") - .expect("Malformed certificate"); + let Ok(Some((_, key_usage_ext))) = tbs_certificate.get::() else { + return Err(VerificationError::CertificateMalformed); + }; if !key_usage_ext.digital_signature() { return Err(VerificationError::CertificateTypeError( @@ -119,12 +118,10 @@ impl<'a, R: Repository> Verifier<'a, R> { )); } - let (_, extended_key_usage_ext): (bool, ExtendedKeyUsage) = materials - .certificate - .tbs_certificate - .get() - .expect("Malformed certificate") - .expect("Malformed certificate"); + let Ok(Some((_, extended_key_usage_ext))) = tbs_certificate.get::() + else { + return Err(VerificationError::CertificateMalformed); + }; if !extended_key_usage_ext.0.contains(&ID_KP_CODE_SIGNING) { return Err(VerificationError::CertificateTypeError( @@ -132,28 +129,33 @@ impl<'a, R: Repository> Verifier<'a, R> { )); } - policy.verify(&materials.certificate)?; + if let Some(err) = policy.verify(&materials.certificate) { + return Err(err)?; + } + debug!("signing certificate conforms to policy"); // 3) Verify that the signature was signed by the public key in the signing certificate - let signing_key: SigstoreResult = (&materials - .certificate - .tbs_certificate - .subject_public_key_info) - .try_into(); - - let signing_key = - signing_key.expect("Malformed certificate (cannot deserialize public key)"); + let Ok(signing_key): SigstoreResult = + (&tbs_certificate.subject_public_key_info).try_into() + else { + return Err(VerificationError::CertificateMalformed); + }; - signing_key - .verify_prehash( - Signature::Raw(&materials.signature), - &materials.input_digest, - ) - .or(Err(VerificationError::SignatureVerificationFailure))?; + let verify_sig = signing_key.verify_prehash( + Signature::Raw(&materials.signature), + &materials.input_digest, + ); + if verify_sig.is_err() { + return Err(VerificationError::SignatureVerificationFailure); + } + debug!("signature corresponds to public key"); // 4) Verify that the Rekor entry is consistent with the other signing // materials - let log_entry = materials.rekor_entry(); + let Some(log_entry) = materials.rekor_entry() else { + return Err(VerificationError::CertificateMalformed); + }; + debug!("log entry is consistent with other materials"); // 5) Verify the inclusion proof supplied by Rekor for this artifact, // if we're doing online verification. @@ -166,16 +168,12 @@ impl<'a, R: Repository> Verifier<'a, R> { // 7) Verify that the signing certificate was valid at the time of // signing by comparing the expiry against the integrated timestamp. let integrated_time = log_entry.integrated_time as u64; - let not_before = materials - .certificate - .tbs_certificate + let not_before = tbs_certificate .validity .not_before .to_unix_duration() .as_secs(); - let not_after = materials - .certificate - .tbs_certificate + let not_after = tbs_certificate .validity .not_after .to_unix_duration() @@ -183,7 +181,9 @@ impl<'a, R: Repository> Verifier<'a, R> { if !(not_before <= integrated_time && integrated_time <= not_after) { return Err(VerificationError::CertificateExpired); } + debug!("data signed during validity period"); + debug!("successfully verified!"); Ok(()) } } From 428aa41caa0fa5eb5faf388ba395722773c7efdb Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 16:25:34 -0600 Subject: [PATCH 073/161] Add `bundle` feature Fixes compile on `--no-default-features`. Signed-off-by: Andrew Pan --- Cargo.toml | 5 +++-- src/fulcio/mod.rs | 2 +- src/lib.rs | 2 ++ src/sign.rs | 5 +---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d19a7cb041..46ad8fc908 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,9 @@ rekor = ["reqwest"] tuf = ["tough", "regex"] -sign = [] -verify = [] +bundle = [] +sign = ["bundle"] +verify = ["bundle"] cosign-native-tls = [ "oci-distribution/native-tls", diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 1330354022..a96f0a18ce 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -16,7 +16,7 @@ use serde::{Serialize, Serializer}; use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; use url::Url; -use x509_cert::{Certificate, der::Decode}; +use x509_cert::{der::Decode, Certificate}; pub use models::{CertificateResponse, SigningCertificateDetachedSCT}; diff --git a/src/lib.rs b/src/lib.rs index 3346d8d57e..39ee2ddeb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -285,7 +285,9 @@ pub mod rekor; pub mod tuf; // Don't export yet -- these types should only be useful internally. +#[cfg(feature = "bundle")] mod bundle; +#[cfg(feature = "bundle")] pub use bundle::Bundle; #[cfg(feature = "verify")] diff --git a/src/sign.rs b/src/sign.rs index 95b3158b48..d5419851f0 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -244,10 +244,7 @@ pub struct SigningContext { impl SigningContext { /// Manually constructs a [SigningContext] from its constituent data. - pub fn new( - fulcio: FulcioClient, - rekor_config: RekorConfiguration, - ) -> Self { + pub fn new(fulcio: FulcioClient, rekor_config: RekorConfiguration) -> Self { Self { fulcio, rekor_config, From 7d0db8b1004004b012f9f84c51f70b7733d47c59 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 17:13:37 -0600 Subject: [PATCH 074/161] verify: rework certificate validation Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 29 +++++++++++++++++++---------- src/fulcio/models.rs | 2 -- src/sign.rs | 9 +++++++-- src/verify/verifier.rs | 32 +++++--------------------------- 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 34ae09cbc6..d82b4d07b2 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -20,7 +20,10 @@ use x509_cert::{ Certificate, }; -use crate::errors::{Result, SigstoreError}; +use crate::{ + crypto::certificate, + errors::{Result, SigstoreError}, +}; /// Ensure the given certificate can be trusted for verifying cosign /// signatures. @@ -127,7 +130,7 @@ fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result< /// * It has `CODE_SIGNING` as an `ExtendedKeyUsage`. /// /// This function does not evaluate the trustworthiness of the certificate. -pub(crate) fn is_leaf(certificate: &Certificate) -> Result { +pub(crate) fn is_leaf(certificate: &Certificate) -> Result<()> { // NOTE(jl): following structure of sigstore-python over the slightly different handling found // in `verify_key_usages`. let tbs = &certificate.tbs_certificate; @@ -139,13 +142,15 @@ pub(crate) fn is_leaf(certificate: &Certificate) -> Result { } if is_ca(certificate)? { - return Ok(false); + return Err(SigstoreError::InvalidCertError( + "invalid signing cert: missing KeyUsage".to_string(), + )); }; let digital_signature = match tbs.get::()? { None => { return Err(SigstoreError::InvalidCertError( - "invalid X.509 certificate: missing KeyUsage".to_string(), + "invalid signing cert: missing KeyUsage".to_string(), )) } Some((_, key_usage)) => key_usage.digital_signature(), @@ -153,8 +158,7 @@ pub(crate) fn is_leaf(certificate: &Certificate) -> Result { if !digital_signature { return Err(SigstoreError::InvalidCertError( - "invalid certificate for Sigstore purposes: missing digital signature usage" - .to_string(), + "invalid signing cert: missing digital signature usage".to_string(), )); } @@ -165,13 +169,19 @@ pub(crate) fn is_leaf(certificate: &Certificate) -> Result { let extended_key_usage = match tbs.get::()? { None => { return Err(SigstoreError::InvalidCertError( - "invalid X.509 certificate: missing ExtendedKeyUsage".to_string(), + "invalid signing cert: missing ExtendedKeyUsage".to_string(), )) } Some((_, extended_key_usage)) => extended_key_usage, }; - Ok(extended_key_usage.0.contains(&ID_KP_CODE_SIGNING)) + if !extended_key_usage.0.contains(&ID_KP_CODE_SIGNING) { + return Err(SigstoreError::InvalidCertError( + "invalid signing cert: missing CODE_SIGNING ExtendedKeyUsage".into(), + )); + } + + Ok(()) } /// Checks if the given `certificate` is a CA certificate. @@ -257,8 +267,7 @@ pub(crate) fn is_root_ca(certificate: &Certificate) -> Result { } // A certificate that is its own issuer and signer is considered a root CA. - // TODO(jl): verify_directly_issued_by - todo!() + Ok(tbs.issuer == tbs.subject) } #[cfg(test)] diff --git a/src/fulcio/models.rs b/src/fulcio/models.rs index 0a784a76d0..3441f4ca44 100644 --- a/src/fulcio/models.rs +++ b/src/fulcio/models.rs @@ -119,8 +119,6 @@ pub enum SCTVersion { V1 = 0, } -// TODO(tnytown): Make this type prettier. SigningCertificateDetachedSCT duplicates most of the data -// in cert and chain. pub struct CertificateResponse { pub cert: Certificate, pub chain: Vec, diff --git a/src/sign.rs b/src/sign.rs index d5419851f0..eac694f465 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -158,9 +158,14 @@ impl<'ctx> AsyncSigningSession<'ctx> { }, }; - let entry = create_log_entry(&self.context.rekor_config, proposed_entry) + let log_entry = create_log_entry(&self.context.rekor_config, proposed_entry) .await .map_err(|err| SigstoreError::RekorClientError(err.to_string()))?; + let log_entry = log_entry + .try_into() + .or(Err(SigstoreError::RekorClientError( + "Rekor returned malformed LogEntry".into(), + )))?; // TODO(tnytown): Maybe run through the verification flow here? See sigstore-rs#296. @@ -168,7 +173,7 @@ impl<'ctx> AsyncSigningSession<'ctx> { input_digest: input_hash.to_owned(), cert: cert.to_der()?, signature: signature_bytes, - log_entry: entry.try_into().expect("TODO"), + log_entry, }) } diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 3c695995cf..b3927ac5e6 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -14,7 +14,6 @@ use std::cell::OnceCell; -use const_oid::db::rfc5280::ID_KP_CODE_SIGNING; use tracing::debug; use webpki::{ types::{CertificateDer, UnixTime}, @@ -22,7 +21,6 @@ use webpki::{ }; use x509_cert::der::Encode; -use x509_cert::ext::pkix::{ExtendedKeyUsage, KeyUsage}; use crate::{ crypto::{CertificatePool, CosignVerificationKey, Signature}, @@ -96,7 +94,9 @@ impl<'a, R: Repository> Verifier<'a, R> { .to_der() .expect("failed to DER-encode constructed Certificate!") .into(); - let ee_cert: EndEntityCert = (&cert_der).try_into().expect("TODO"); + let Ok(ee_cert) = (&cert_der).try_into() else { + return Err(VerificationError::CertificateVerificationFailure); + }; let Ok(_trusted_chain) = store.verify_cert_with_time(&ee_cert, UnixTime::since_unix_epoch(issued_at)) @@ -107,28 +107,6 @@ impl<'a, R: Repository> Verifier<'a, R> { debug!("signing certificate chains back to trusted root"); // 2) Verify that the signing certificate belongs to the signer. - - let Ok(Some((_, key_usage_ext))) = tbs_certificate.get::() else { - return Err(VerificationError::CertificateMalformed); - }; - - if !key_usage_ext.digital_signature() { - return Err(VerificationError::CertificateTypeError( - "Key usage is not of type `digital signature`".into(), - )); - } - - let Ok(Some((_, extended_key_usage_ext))) = tbs_certificate.get::() - else { - return Err(VerificationError::CertificateMalformed); - }; - - if !extended_key_usage_ext.0.contains(&ID_KP_CODE_SIGNING) { - return Err(VerificationError::CertificateTypeError( - "Extended key usage does not contain `code signing`".into(), - )); - } - if let Some(err) = policy.verify(&materials.certificate) { return Err(err)?; } @@ -159,11 +137,11 @@ impl<'a, R: Repository> Verifier<'a, R> { // 5) Verify the inclusion proof supplied by Rekor for this artifact, // if we're doing online verification. - // TODO(tnytown): Merkle inclusion + // TODO(tnytown): Merkle inclusion; sigstore-rs#285 // 6) Verify the Signed Entry Timestamp (SET) supplied by Rekor for this // artifact. - // TODO(tnytown) SET verification + // TODO(tnytown) SET verification; sigstore-rs#285 // 7) Verify that the signing certificate was valid at the time of // signing by comparing the expiry against the integrated timestamp. From ffc22325a241d2d11e013188a70dc4d0311284ff Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 18:02:20 -0600 Subject: [PATCH 075/161] crypto: fixup is_root_ca & usages Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 2 +- src/verify/models.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index d82b4d07b2..6c14050fa7 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -262,7 +262,7 @@ pub(crate) fn is_root_ca(certificate: &Certificate) -> Result { } // Non-CAs can't possibly be root CAs. - if !is_ca(certificate)? { + if !matches!(is_ca(certificate), Ok(true)) { return Ok(false); } diff --git a/src/verify/models.rs b/src/verify/models.rs index 004a664423..8d22497995 100644 --- a/src/verify/models.rs +++ b/src/verify/models.rs @@ -144,7 +144,7 @@ impl VerificationMaterials { } for chain_cert in chain_certs { - if is_root_ca(chain_cert).is_ok() { + if matches!(is_root_ca(chain_cert), Ok(true)) { return None; } } From 0e792048eaaa096077ee5508f15d356999494c39 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 18:07:38 -0600 Subject: [PATCH 076/161] conformance: fixup Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 1 - src/verify/verifier.rs | 1 - tests/conformance/conformance.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 6c14050fa7..5a3334d0f6 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -21,7 +21,6 @@ use x509_cert::{ }; use crate::{ - crypto::certificate, errors::{Result, SigstoreError}, }; diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index b3927ac5e6..9b6e4c3da4 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -17,7 +17,6 @@ use std::cell::OnceCell; use tracing::debug; use webpki::{ types::{CertificateDer, UnixTime}, - EndEntityCert, }; use x509_cert::der::Encode; diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index cfb11b7f72..771c0091bb 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -140,7 +140,7 @@ fn sign_bundle(args: SignBundle) -> anyhow::Result<()> { let bundle = fs::File::create(bundle)?; let mut artifact = fs::File::open(artifact)?; - let context = SigningContext::production(); + let context = SigningContext::production()?; let signer = context.signer(identity_token); let signing_artifact = signer?.sign(&mut artifact)?; From 68fea4a2d9a4f1f13b6418d14920ce54b42a90b2 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 15 Feb 2024 18:16:17 -0600 Subject: [PATCH 077/161] `cargo fmt` Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 4 +--- src/verify/verifier.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 5a3334d0f6..0847c3dcc5 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -20,9 +20,7 @@ use x509_cert::{ Certificate, }; -use crate::{ - errors::{Result, SigstoreError}, -}; +use crate::errors::{Result, SigstoreError}; /// Ensure the given certificate can be trusted for verifying cosign /// signatures. diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 9b6e4c3da4..62d07f0345 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -15,9 +15,7 @@ use std::cell::OnceCell; use tracing::debug; -use webpki::{ - types::{CertificateDer, UnixTime}, -}; +use webpki::types::{CertificateDer, UnixTime}; use x509_cert::der::Encode; From 752f87fc2688920ed2b81ddfcb95f9c8eda42914 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 16 Feb 2024 13:05:33 -0600 Subject: [PATCH 078/161] sign, verify: add TODOs for SCT verification Signed-off-by: Andrew Pan --- src/sign.rs | 2 ++ src/verify/verifier.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/sign.rs b/src/sign.rs index eac694f465..124d521591 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -128,6 +128,8 @@ impl<'ctx> AsyncSigningSession<'ctx> { return Err(SigstoreError::ExpiredSigningSession()); } + // TODO(tnytown): verify SCT here, sigstore-rs#326 + // Sign artifact. let input_hash: &[u8] = &hasher.clone().finalize(); let mut signature_bytes = Vec::new(); diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 62d07f0345..b41173448c 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -103,6 +103,8 @@ impl<'a, R: Repository> Verifier<'a, R> { debug!("signing certificate chains back to trusted root"); + // TODO(tnytown): verify SCT here, sigstore-rs#326 + // 2) Verify that the signing certificate belongs to the signer. if let Some(err) = policy.verify(&materials.certificate) { return Err(err)?; From 5c6854a4560f32ffda60c96b5dffb70a9f538e72 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 23 Feb 2024 15:47:43 -0600 Subject: [PATCH 079/161] verify, crypto: asyncify, revamp error handling - remove lifetimes from CertificatePool & co - rework OnceCell usage - enumerate errors for `verify` and its dependencies Signed-off-by: Andrew Pan --- examples/cosign/verify/main.rs | 2 +- src/cosign/client.rs | 10 +- src/cosign/client_builder.rs | 2 +- src/cosign/mod.rs | 4 +- src/crypto/certificate.rs | 160 +++++++++++------ src/crypto/certificate_pool.rs | 35 ++-- src/tuf/mod.rs | 109 +++++------- src/verify/mod.rs | 4 +- src/verify/models.rs | 293 +++++++++++++++++-------------- src/verify/policy.rs | 46 ++--- src/verify/verifier.rs | 169 ++++++++++++------ tests/conformance/conformance.rs | 8 +- 12 files changed, 491 insertions(+), 351 deletions(-) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index fc687ba627..ca3281bdd7 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -232,7 +232,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); - SigstoreRepository::new(None)?.prefetch() + SigstoreRepository::new(None) }) .await .map_err(|e| anyhow!("Error spawning blocking task inside of tokio: {}", e))?; diff --git a/src/cosign/client.rs b/src/cosign/client.rs index af70bf457b..1c97e113af 100644 --- a/src/cosign/client.rs +++ b/src/cosign/client.rs @@ -37,15 +37,15 @@ pub const CONFIG_DATA: &str = "{}"; /// Cosign Client /// /// Instances of `Client` can be built via [`sigstore::cosign::ClientBuilder`](crate::cosign::ClientBuilder). -pub struct Client<'a> { +pub struct Client { pub(crate) registry_client: Box, pub(crate) rekor_pub_key: Option, - pub(crate) fulcio_cert_pool: Option>, + pub(crate) fulcio_cert_pool: Option, } #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl CosignCapabilities for Client<'_> { +impl CosignCapabilities for Client { async fn triangulate( &mut self, image: &OciReference, @@ -140,7 +140,7 @@ impl CosignCapabilities for Client<'_> { } } -impl Client<'_> { +impl Client { /// Internal helper method used to fetch data from an OCI registry async fn fetch_manifest_and_layers( &mut self, @@ -177,7 +177,7 @@ mod tests { use crate::crypto::SigningScheme; use crate::mock_client::test::MockOciClient; - fn build_test_client(mock_client: MockOciClient) -> Client<'static> { + fn build_test_client(mock_client: MockOciClient) -> Client { let rekor_pub_key = CosignVerificationKey::from_pem(REKOR_PUB_KEY.as_bytes(), &SigningScheme::default()) .expect("Cannot create CosignVerificationKey"); diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index adfaff1f43..8c60cc6df7 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -91,7 +91,7 @@ impl<'a> ClientBuilder<'a> { self } - pub fn build(self) -> Result> { + pub fn build(self) -> Result { let rekor_pub_key = match self.rekor_pub_key { None => { info!("Rekor public key not provided. Rekor integration disabled"); diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 03d3c0e52d..99e858cdb4 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -337,7 +337,7 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ #[cfg(feature = "test-registry")] const SIGNED_IMAGE: &str = "busybox:1.34"; - pub(crate) fn get_fulcio_cert_pool() -> CertificatePool<'static> { + pub(crate) fn get_fulcio_cert_pool() -> CertificatePool { fn pem_to_der<'a>(input: &'a str) -> CertificateDer<'a> { let pem_cert = pem::parse(input).unwrap(); assert_eq!(pem_cert.tag(), "CERTIFICATE"); @@ -644,7 +644,7 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ } #[cfg(feature = "test-registry")] - async fn prepare_image_to_be_signed(client: &mut Client<'_>, image_ref: &OciReference) { + async fn prepare_image_to_be_signed(client: &mut Client, image_ref: &OciReference) { let data = client .registry_client .pull( diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 0847c3dcc5..9960b27c23 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -15,6 +15,7 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use const_oid::db::rfc5912::ID_KP_CODE_SIGNING; +use thiserror::Error; use x509_cert::{ ext::pkix::{constraints, ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName}, Certificate, @@ -120,6 +121,56 @@ fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result< Ok(()) } +#[derive(Debug, Error)] +pub enum ExtensionErrorKind { + #[error("certificate missing extension: {0}")] + Missing(&'static str), + + #[error("certificate extension bit not asserted: {0}")] + BitUnset(&'static str), + + #[error("certificate's {0} extension not marked as critical")] + NotCritical(&'static str), +} + +#[derive(Debug, Error)] +pub enum NotLeafErrorKind { + #[error("certificate is a CA: CAs are not leaves")] + IsCA, + + #[error(transparent)] + Extension(#[from] ExtensionErrorKind), +} + +#[derive(Debug, Error)] +pub enum NotCAErrorKind { + #[error("certificate is not a CA: CAs must assert cA and keyCertSign")] + NotCA, + + #[error("certificate is not a root CA")] + NotRootCA, + + #[error("certificate in invalid state: cA={ca}, keyCertSign={key_cert_sign}")] + Invalid { ca: bool, key_cert_sign: bool }, + + #[error(transparent)] + Extension(#[from] ExtensionErrorKind), +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub enum CertificateValidationError { + #[error("only X509 V3 certificates are supported")] + VersionUnsupported, + + #[error("malformed certificate")] + Malformed(#[source] x509_cert::der::Error), + + NotLeaf(#[from] NotLeafErrorKind), + + NotCA(#[from] NotCAErrorKind), +} + /// Check if the given certificate is a leaf in the context of the Sigstore profile. /// /// * It is not a root or intermediate CA; @@ -127,7 +178,9 @@ fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result< /// * It has `CODE_SIGNING` as an `ExtendedKeyUsage`. /// /// This function does not evaluate the trustworthiness of the certificate. -pub(crate) fn is_leaf(certificate: &Certificate) -> Result<()> { +pub(crate) fn is_leaf( + certificate: &Certificate, +) -> core::result::Result<(), CertificateValidationError> { // NOTE(jl): following structure of sigstore-python over the slightly different handling found // in `verify_key_usages`. let tbs = &certificate.tbs_certificate; @@ -135,47 +188,47 @@ pub(crate) fn is_leaf(certificate: &Certificate) -> Result<()> { // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack // extensions and have ambiguous CA behavior. if tbs.version != x509_cert::Version::V3 { - return Err(SigstoreError::CertificateUnsupportedVersionError); + Err(CertificateValidationError::VersionUnsupported)?; } - if is_ca(certificate)? { - return Err(SigstoreError::InvalidCertError( - "invalid signing cert: missing KeyUsage".to_string(), - )); + if is_ca(certificate).is_ok() { + Err(NotLeafErrorKind::IsCA)?; }; - let digital_signature = match tbs.get::()? { - None => { - return Err(SigstoreError::InvalidCertError( - "invalid signing cert: missing KeyUsage".to_string(), - )) - } + let digital_signature = match tbs + .get::() + .map_err(CertificateValidationError::Malformed)? + { + None => Err(NotLeafErrorKind::Extension(ExtensionErrorKind::Missing( + "KeyUsage", + )))?, Some((_, key_usage)) => key_usage.digital_signature(), }; if !digital_signature { - return Err(SigstoreError::InvalidCertError( - "invalid signing cert: missing digital signature usage".to_string(), - )); + Err(NotLeafErrorKind::Extension(ExtensionErrorKind::BitUnset( + "KeyUsage.digitalSignature", + )))?; } // Finally, we check to make sure the leaf has an `ExtendedKeyUsages` // extension that includes a codesigning entitlement. Sigstore should // never issue a leaf that doesn't have this extended usage. - let extended_key_usage = match tbs.get::()? { - None => { - return Err(SigstoreError::InvalidCertError( - "invalid signing cert: missing ExtendedKeyUsage".to_string(), - )) - } + let extended_key_usage = match tbs + .get::() + .map_err(CertificateValidationError::Malformed)? + { + None => Err(NotLeafErrorKind::Extension(ExtensionErrorKind::Missing( + "ExtendedKeyUsage", + )))?, Some((_, extended_key_usage)) => extended_key_usage, }; if !extended_key_usage.0.contains(&ID_KP_CODE_SIGNING) { - return Err(SigstoreError::InvalidCertError( - "invalid signing cert: missing CODE_SIGNING ExtendedKeyUsage".into(), - )); + Err(NotLeafErrorKind::Extension(ExtensionErrorKind::BitUnset( + "ExtendedKeyUsage.digitalSignature", + )))?; } Ok(()) @@ -188,13 +241,15 @@ pub(crate) fn is_leaf(certificate: &Certificate) -> Result<()> { /// /// This function is **not** naively invertible: users **must** use the dedicated `is_leaf` /// utility function to determine whether a particular leaf upholds Sigstore's invariants. -pub(crate) fn is_ca(certificate: &Certificate) -> Result { +pub(crate) fn is_ca( + certificate: &Certificate, +) -> core::result::Result<(), CertificateValidationError> { let tbs = &certificate.tbs_certificate; // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack // extensions and have ambiguous CA behavior. if tbs.version != x509_cert::Version::V3 { - return Err(SigstoreError::CertificateUnsupportedVersionError); + return Err(CertificateValidationError::VersionUnsupported); } // Valid CA certificates must have the following set: @@ -205,40 +260,43 @@ pub(crate) fn is_ca(certificate: &Certificate) -> Result { // Any other combination of states is inconsistent and invalid, meaning // that we won't consider the certificate a valid non-CA leaf. - let ca = match tbs.get::()? { - None => return Ok(false), + let ca = match tbs + .get::() + .map_err(CertificateValidationError::Malformed)? + { + None => Err(NotCAErrorKind::Extension(ExtensionErrorKind::Missing( + "BasicConstraints", + )))?, Some((false, _)) => { // BasicConstraints must be marked as critical, per RFC 5280 4.2.1.9. - return Err(SigstoreError::InvalidCertError( - "invalid X.509 certificate: non-critical BasicConstraints in CA".to_string(), - )); + Err(NotCAErrorKind::Extension(ExtensionErrorKind::NotCritical( + "BasicConstraints", + )))? } Some((true, v)) => v.ca, }; - let key_cert_sign = match tbs.get::()? { - None => { - return Err(SigstoreError::InvalidCertError( - "invalid X.509 certificate: missing KeyUsage".to_string(), - )) - } + let key_cert_sign = match tbs + .get::() + .map_err(CertificateValidationError::Malformed)? + { + None => Err(NotCAErrorKind::Extension(ExtensionErrorKind::Missing( + "KeyUsage", + )))?, Some((_, v)) => v.key_cert_sign(), }; // both states set, this is a CA. if ca && key_cert_sign { - return Ok(true); + return Ok(()); } if !(ca || key_cert_sign) { - return Ok(false); + Err(NotCAErrorKind::NotCA)?; } // Anything else is an invalid state that should never occur. - Err(SigstoreError::InvalidCertError(format!( - "invalid certificate states: KeyUsage.keyCertSign={}, BasicConstraints.ca={}", - key_cert_sign, ca - ))) + Err(NotCAErrorKind::Invalid { ca, key_cert_sign })? } /// Returns `True` if and only if the given `Certificate` indicates @@ -246,7 +304,9 @@ pub(crate) fn is_ca(certificate: &Certificate) -> Result { /// /// This is **not** a verification function, and it does not establish /// the trustworthiness of the given certificate. -pub(crate) fn is_root_ca(certificate: &Certificate) -> Result { +pub(crate) fn is_root_ca( + certificate: &Certificate, +) -> core::result::Result<(), CertificateValidationError> { // NOTE(ww): This function is obnoxiously long to make the different // states explicit. @@ -255,16 +315,18 @@ pub(crate) fn is_root_ca(certificate: &Certificate) -> Result { // Only V3 certificates should appear in the context of Sigstore; earlier versions of X.509 lack // extensions and have ambiguous CA behavior. if tbs.version != x509_cert::Version::V3 { - return Err(SigstoreError::CertificateUnsupportedVersionError); + return Err(CertificateValidationError::VersionUnsupported); } // Non-CAs can't possibly be root CAs. - if !matches!(is_ca(certificate), Ok(true)) { - return Ok(false); - } + is_ca(certificate)?; // A certificate that is its own issuer and signer is considered a root CA. - Ok(tbs.issuer == tbs.subject) + if tbs.issuer == tbs.subject { + Ok(()) + } else { + Err(NotCAErrorKind::NotRootCA)? + } } #[cfg(test)] diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index cd067aff48..9502fdc208 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -19,31 +19,34 @@ use webpki::{ EndEntityCert, KeyUsage, VerifiedPath, }; -use crate::errors::{Result, SigstoreError}; +use crate::errors::{Result as SigstoreResult, SigstoreError}; /// A collection of trusted root certificates. #[derive(Default, Debug)] -pub(crate) struct CertificatePool<'a> { - trusted_roots: Vec>, - intermediates: Vec>, +pub(crate) struct CertificatePool { + trusted_roots: Vec>, + intermediates: Vec>, } -impl<'a> CertificatePool<'a> { +impl CertificatePool { /// Builds a `CertificatePool` instance using the provided list of [`Certificate`]. - pub(crate) fn from_certificates( + pub(crate) fn from_certificates<'r, 'i, R, I>( trusted_roots: R, untrusted_intermediates: I, - ) -> Result> + ) -> SigstoreResult where - R: IntoIterator>, - I: IntoIterator>, + R: IntoIterator>, + I: IntoIterator>, { Ok(CertificatePool { trusted_roots: trusted_roots .into_iter() .map(|x| Ok(webpki::anchor_from_trusted_cert(&x)?.to_owned())) .collect::, webpki::Error>>()?, - intermediates: untrusted_intermediates.into_iter().collect(), + intermediates: untrusted_intermediates + .into_iter() + .map(|i| i.into_owned()) + .collect(), }) } @@ -59,7 +62,7 @@ impl<'a> CertificatePool<'a> { &self, cert_pem: &[u8], verification_time: Option, - ) -> Result<()> { + ) -> SigstoreResult<()> { let cert_pem = pem::parse(cert_pem)?; if cert_pem.tag() != "CERTIFICATE" { return Err(SigstoreError::CertificatePoolError( @@ -82,7 +85,7 @@ impl<'a> CertificatePool<'a> { &self, der: &[u8], verification_time: Option, - ) -> Result<()> { + ) -> SigstoreResult<()> { let der = CertificateDer::from(der); let cert = EndEntityCert::try_from(&der)?; @@ -91,18 +94,18 @@ impl<'a> CertificatePool<'a> { Ok(()) } - pub(crate) fn verify_cert_with_time<'cert>( + pub(crate) fn verify_cert_with_time<'a, 'cert>( &'a self, cert: &'cert EndEntityCert<'cert>, verification_time: UnixTime, - ) -> Result> + ) -> Result, webpki::Error> where 'a: 'cert, { let signing_algs = webpki::ALL_VERIFICATION_ALGS; let eku_code_signing = ID_KP_CODE_SIGNING.as_bytes(); - Ok(cert.verify_for_usage( + cert.verify_for_usage( signing_algs, &self.trusted_roots, self.intermediates.as_slice(), @@ -110,6 +113,6 @@ impl<'a> CertificatePool<'a> { KeyUsage::required(eku_code_signing), None, None, - )?) + ) } } diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 6fd4e2cd9f..31818df732 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -23,15 +23,14 @@ //! //! # Example //! -//! The `SigstoreRepository` instance can be created via the [`SigstoreRepository::prefetch`] +//! The `SigstoreRepository` instance can be created via the [`SigstoreRepository::new`] //! method. //! //! ```rust,no_run //! use sigstore::tuf::SigstoreRepository; -//! let repo = SigstoreRepository::new(None).unwrap().prefetch().unwrap(); +//! let repo = SigstoreRepository::new(None).unwrap(); //! ``` use std::{ - cell::OnceCell, io::Read, path::{Path, PathBuf}, }; @@ -51,7 +50,7 @@ use super::errors::{Result, SigstoreError}; /// A `Repository` owns all key material necessary for establishing a root of trust. pub trait Repository { - fn fulcio_certs(&self) -> Result>; + fn fulcio_certs(&self) -> Result>>; fn rekor_keys(&self) -> Result>; fn ctfe_keys(&self) -> Result>; } @@ -59,14 +58,14 @@ pub trait Repository { /// A `ManualRepository` is a [Repository] with out-of-band trust materials. /// As it does not establish a trust root with TUF, users must initialize its materials themselves. #[derive(Debug, Default)] -pub struct ManualRepository<'a> { - pub fulcio_certs: Option>>, +pub struct ManualRepository { + pub fulcio_certs: Option>>, pub rekor_key: Option>, pub ctfe_keys: Option>>, } -impl Repository for ManualRepository<'_> { - fn fulcio_certs(&self) -> Result> { +impl Repository for ManualRepository { + fn fulcio_certs(&self) -> Result>> { Ok(match &self.fulcio_certs { Some(certs) => certs.clone(), None => Vec::new(), @@ -91,20 +90,33 @@ impl Repository for ManualRepository<'_> { /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] pub struct SigstoreRepository { - repository: tough::Repository, - checkout_dir: Option, - trusted_root: OnceCell, + trusted_root: TrustedRoot, } impl SigstoreRepository { /// Constructs a new trust repository established by a [tough::Repository]. + /// + /// This method synchronously fetches trust materials, from TUF, which is problematic for async + /// callers. Those callers should do the following to fetch the trust root ahead of time. + /// + /// ```rust + /// # use tokio::task::spawn_blocking; + /// # use sigstore::tuf::SigstoreRepository; + /// # use sigstore::errors::Result; + /// # #[tokio::main] + /// # async fn main() -> std::result::Result<(), anyhow::Error> { + /// let repo: Result = spawn_blocking(|| Ok(SigstoreRepository::new(None)?)).await?; + /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRepository` + /// # Ok(()) + /// # } + /// ``` pub fn new(checkout_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; let repository = tough::RepositoryLoader::new( - constants::static_resource("root.json").expect("Failed to fetch required resource!"), + constants::static_resource("root.json").expect("Failed to fetch embedded TUF root!"), metadata_base, target_base, ) @@ -112,33 +124,26 @@ impl SigstoreRepository { .load() .map_err(Box::new)?; - Ok(Self { - repository, - checkout_dir: checkout_dir.map(ToOwned::to_owned), - trusted_root: OnceCell::default(), - }) - } - - fn trusted_root(&self) -> Result<&TrustedRoot> { - return if let Some(root) = self.trusted_root.get() { - Ok(root) - } else { - let data = self.fetch_target("trusted_root.json")?; - - debug!("data:\n{}", String::from_utf8_lossy(&data)); + let checkout_dir = checkout_dir.map(ToOwned::to_owned); - let root = serde_json::from_slice(&data[..])?; - - Ok(self.trusted_root.get_or_init(|| root)) + let trusted_root = { + let data = Self::fetch_target(&repository, &checkout_dir, "trusted_root.json")?; + serde_json::from_slice(&data[..])? }; + + Ok(Self { trusted_root }) } - fn fetch_target(&self, name: N) -> Result> + fn fetch_target( + repository: &tough::Repository, + checkout_dir: &Option, + name: N, + ) -> Result> where N: TryInto, { let read_remote_target = |name: &TargetName| -> Result> { - let Some(mut reader) = self.repository.read_target(name).map_err(Box::new)? else { + let Some(mut reader) = repository.read_target(name).map_err(Box::new)? else { return Err(SigstoreError::TufTargetNotFoundError(name.raw().to_owned())); }; @@ -150,7 +155,7 @@ impl SigstoreRepository { }; let name: TargetName = name.try_into().map_err(Box::new)?; - let local_path = self.checkout_dir.as_ref().map(|d| d.join(name.raw())); + let local_path = checkout_dir.as_ref().map(|d| d.join(name.raw())); // Try reading the target from disk cache. let data = if let Some(Ok(local_data)) = local_path.as_ref().map(std::fs::read) { @@ -167,7 +172,7 @@ impl SigstoreRepository { }; // Get metadata (hash) of the target and update the disk copy if it doesn't match. - let Some(target) = self.repository.targets().signed.targets.get(&name) else { + let Some(target) = repository.targets().signed.targets.get(&name) else { return Err(SigstoreError::TufMetadataError(format!( "couldn't get metadata for {}", name.raw() @@ -189,28 +194,6 @@ impl SigstoreRepository { Ok(data) } - /// Prefetches trust materials. - /// - /// [Repository::fulcio_certs()] and [Repository::rekor_keys()] on [SigstoreRepository] 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 tokio::task::spawn_blocking; - /// # use sigstore::tuf::SigstoreRepository; - /// # use sigstore::errors::Result; - /// # #[tokio::main] - /// # async fn main() -> std::result::Result<(), anyhow::Error> { - /// let repo: Result = spawn_blocking(|| Ok(SigstoreRepository::new(None)?.prefetch()?)).await?; - /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRepository` - /// # Ok(()) - /// # } - /// ``` - pub fn prefetch(self) -> Result { - let _ = self.trusted_root()?; - Ok(self) - } - #[inline] fn tlog_keys(tlogs: &[TransparencyLogInstance]) -> impl Iterator { tlogs @@ -242,13 +225,13 @@ impl Repository for SigstoreRepository { /// /// **Warning:** this method needs special handling when invoked from /// an async function because it performs blocking operations. - fn fulcio_certs(&self) -> Result> { - let root = self.trusted_root()?; - + fn fulcio_certs(&self) -> Result>> { // Allow expired certificates: they may have been active when the // certificate was used to sign. - let certs = Self::ca_keys(&root.certificate_authorities, true); - let certs: Vec<_> = certs.map(CertificateDer::from).collect(); + let certs = Self::ca_keys(&self.trusted_root.certificate_authorities, true); + let certs: Vec<_> = certs + .map(|c| CertificateDer::from(c).into_owned()) + .collect(); if certs.is_empty() { Err(SigstoreError::TufMetadataError( @@ -267,8 +250,7 @@ impl Repository for SigstoreRepository { /// **Warning:** this method needs special handling when invoked from /// an async function because it performs blocking operations. fn rekor_keys(&self) -> Result> { - let root = self.trusted_root()?; - let keys: Vec<_> = Self::tlog_keys(&root.tlogs).collect(); + let keys: Vec<_> = Self::tlog_keys(&self.trusted_root.tlogs).collect(); if keys.len() != 1 { Err(SigstoreError::TufMetadataError( @@ -287,8 +269,7 @@ impl Repository for SigstoreRepository { /// **Warning:** this method needs special handling when invoked from /// an async function because it performs blocking operations. fn ctfe_keys(&self) -> Result> { - let root = self.trusted_root()?; - let keys: Vec<_> = Self::tlog_keys(&root.ctlogs).collect(); + let keys: Vec<_> = Self::tlog_keys(&self.trusted_root.ctlogs).collect(); if keys.is_empty() { Err(SigstoreError::TufMetadataError( diff --git a/src/verify/mod.rs b/src/verify/mod.rs index 95e3375ed3..0504d10060 100644 --- a/src/verify/mod.rs +++ b/src/verify/mod.rs @@ -15,10 +15,10 @@ //! Verifier for Sigstore bundles and associated types and policies. mod models; -pub use models::{VerificationError, VerificationMaterials, VerificationResult}; +pub use models::{VerificationError, VerificationResult}; pub mod policy; pub use policy::{PolicyError, VerificationPolicy}; mod verifier; -pub use verifier::Verifier; +pub use verifier::{AsyncVerifier, Verifier}; diff --git a/src/verify/models.rs b/src/verify/models.rs index 8d22497995..5a6777fca4 100644 --- a/src/verify/models.rs +++ b/src/verify/models.rs @@ -13,106 +13,143 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{ - io::{self, Read}, - str::FromStr, -}; +use std::str::FromStr; use crate::{ bundle::Version as BundleVersion, - crypto::certificate::{is_leaf, is_root_ca}, + crypto::certificate::{is_leaf, is_root_ca, CertificateValidationError}, rekor::models as rekor, }; use crate::Bundle; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use pkcs8::der::{Decode, EncodePem}; -use sha2::{Digest, Sha256}; use sigstore_protobuf_specs::dev::sigstore::{ bundle::v1::{bundle, verification_material}, rekor::v1::{InclusionProof, TransparencyLogEntry}, }; use thiserror::Error; use tracing::{debug, error, warn}; -use x509_cert::Certificate; +use x509_cert::{ + der::{Decode, EncodePem}, + Certificate, +}; use super::policy::PolicyError; #[derive(Error, Debug)] -pub enum VerificationError { - #[error("Certificate expired before time of signing")] - CertificateExpired, +pub enum Bundle01ProfileErrorKind { + #[error("bundle must contain inclusion promise")] + InclusionPromiseMissing, +} + +#[derive(Error, Debug)] +pub enum Bundle02ProfileErrorKind { + #[error("bundle must contain inclusion proof")] + InclusionProofMissing, + + #[error("bundle must contain checkpoint")] + CheckpointMissing, +} + +#[derive(Error, Debug)] +#[error(transparent)] +pub enum BundleProfileErrorKind { + Bundle01Profile(#[from] Bundle01ProfileErrorKind), + + Bundle02Profile(#[from] Bundle02ProfileErrorKind), + + #[error("unknown bundle profile {0}")] + Unknown(String), +} + +#[derive(Error, Debug)] +pub enum BundleErrorKind { + #[error("bundle missing VerificationMaterial")] + VerificationMaterialMissing, + + #[error("bundle includes unsupported VerificationMaterial::Content")] + VerificationMaterialContentUnsupported, - #[error("Certificate malformed")] - CertificateMalformed, + #[error("bundle's certificate(s) are malformed")] + CertificateMalformed(#[source] x509_cert::der::Error), - #[error("Failed to verify certificate")] - CertificateVerificationFailure, + #[error("bundle contains a root certificate")] + RootInChain, - #[error("Certificate cannot be used for verification: {0}")] - CertificateTypeError(String), + #[error("bundle does not contain the signing (leaf) certificate")] + NoLeaf(#[source] CertificateValidationError), - #[error("Failed to verify that the signature corresponds to the input")] - SignatureVerificationFailure, + #[error("bundle does not contain any certificates")] + CertificatesMissing, + + #[error("bundle does not contain signature")] + SignatureMissing, + + #[error("bundle includes unsupported DSSE signature")] + DsseUnsupported, + + #[error("bundle needs 1 tlog entry, got {0}")] + TlogEntry(usize), #[error(transparent)] - PolicyFailure(#[from] PolicyError), + BundleProfile(#[from] BundleProfileErrorKind), +} + +#[derive(Error, Debug)] +pub enum CertificateErrorKind { + #[error("certificate malformed")] + Malformed(#[source] webpki::Error), + + #[error("certificate expired before time of signing")] + Expired, + + #[error("certificate verification failed")] + VerificationFailed(#[source] webpki::Error), +} + +#[derive(Error, Debug)] +pub enum SignatureErrorKind { + #[error("unsupported signature algorithm")] + AlgoUnsupported(#[source] crate::errors::SigstoreError), + + #[error("signature verification failed")] + VerificationFailed(#[source] crate::errors::SigstoreError), + + #[error("signature transparency materials are inconsistent")] + Transparency, +} + +#[derive(Error, Debug)] +#[error(transparent)] +pub enum VerificationError { + #[error("unable to read input")] + Input(#[source] std::io::Error), + + Bundle(#[from] BundleErrorKind), + + Certificate(#[from] CertificateErrorKind), + + Signature(#[from] SignatureErrorKind), + + Policy(#[from] PolicyError), } + pub type VerificationResult = Result<(), VerificationError>; -pub struct VerificationMaterials { - pub(crate) input_digest: Vec, +pub struct CheckedBundle { pub(crate) certificate: Certificate, pub(crate) signature: Vec, - rekor_entry: TransparencyLogEntry, - offline: bool, + tlog_entry: TransparencyLogEntry, } -impl VerificationMaterials { - pub fn new( - input: &mut R, - certificate: Certificate, - signature: Vec, - offline: bool, - rekor_entry: TransparencyLogEntry, - ) -> Option { - let mut hasher = Sha256::new(); - io::copy(input, &mut hasher).ok()?; - - if matches!( - rekor_entry, - TransparencyLogEntry { - inclusion_promise: None, - inclusion_proof: None, - .. - } - ) { - error!("encountered TransparencyLogEntry without any inclusion materials"); - return None; - } - - Some(Self { - input_digest: hasher.finalize().to_vec(), - rekor_entry, - certificate, - signature, - offline, - }) - } +impl TryFrom for CheckedBundle { + type Error = BundleErrorKind; - /// Constructs a VerificationMaterials from the given Bundle. - /// - /// For details on bundle semantics, please refer to [VerificationMaterial]. - /// - /// [VerificationMaterial]: sigstore_protobuf_specs::dev::sigstore::bundle::v1::VerificationMaterial - pub fn from_bundle(input: &mut R, bundle: Bundle, offline: bool) -> Option { - let (content, mut tlog_entries) = match bundle.verification_material { + fn try_from(input: Bundle) -> Result { + let (content, mut tlog_entries) = match input.verification_material { Some(m) => (m.content, m.tlog_entries), - _ => { - error!("bundle missing VerificationMaterial"); - return None; - } + _ => return Err(BundleErrorKind::VerificationMaterialMissing), }; // Parse the certificates. The first entry in the chain MUST be a leaf certificate, and the @@ -123,98 +160,98 @@ impl VerificationMaterials { Some(verification_material::Content::Certificate(cert)) => { vec![cert] } - _ => { - error!("bundle includes unsupported VerificationMaterial Content"); - return None; - } + _ => return Err(BundleErrorKind::VerificationMaterialContentUnsupported), }; let certs = certs .iter() .map(|c| c.raw_bytes.as_slice()) .map(Certificate::from_der) .collect::, _>>() - .ok()?; + .map_err(BundleErrorKind::CertificateMalformed)?; let [leaf_cert, chain_certs @ ..] = &certs[..] else { - return None; + return Err(BundleErrorKind::CertificatesMissing); }; - if is_leaf(leaf_cert).is_err() { - return None; - } + is_leaf(leaf_cert).map_err(BundleErrorKind::NoLeaf)?; for chain_cert in chain_certs { - if matches!(is_root_ca(chain_cert), Ok(true)) { - return None; + if is_root_ca(chain_cert).is_ok() { + return Err(BundleErrorKind::RootInChain); } } - let signature = match bundle.content? { + let signature = match input.content.ok_or(BundleErrorKind::SignatureMissing)? { bundle::Content::MessageSignature(s) => s.signature, - _ => { - error!("bundle includes unsupported DSSE signature"); - return None; - } + _ => return Err(BundleErrorKind::DsseUnsupported), }; if tlog_entries.len() != 1 { - error!("bundle expected 1 tlog entry; got {}", tlog_entries.len()); - return None; + return Err(BundleErrorKind::TlogEntry(tlog_entries.len())); } let tlog_entry = tlog_entries.remove(0); let (inclusion_promise, inclusion_proof) = (&tlog_entry.inclusion_promise, &tlog_entry.inclusion_proof); - // `inclusion_proof` is now a required field in the protobuf spec, - // but older versions of Rekor didn't provide inclusion proofs. + // `inclusion_proof` is a required field in the current protobuf spec, + // but older versions of Rekor didn't provide it. Check invariants + // here and selectively allow for this case. // // https://github.com/sigstore/sigstore-python/pull/634#discussion_r1182769140 - match BundleVersion::from_str(&bundle.media_type) { - Ok(BundleVersion::Bundle0_1) => { - if inclusion_promise.is_none() { - error!("bundle must contain inclusion promise"); - return None; - } - - if matches!( - inclusion_proof, - Some(InclusionProof { - checkpoint: None, - .. - }) - ) { - debug!("0.1 bundle contains inclusion proof without checkpoint"); - } + let check_01_bundle = || -> Result<(), BundleProfileErrorKind> { + if inclusion_promise.is_none() { + return Err(Bundle01ProfileErrorKind::InclusionPromiseMissing)?; } - Ok(BundleVersion::Bundle0_2) => { - if inclusion_proof.is_none() { - error!("bundle must contain inclusion proof"); - return None; - } - - if matches!( - inclusion_proof, - Some(InclusionProof { - checkpoint: None, - .. - }) - ) { - error!("bundle must contain checkpoint"); - return None; - } + + if matches!( + inclusion_proof, + Some(InclusionProof { + checkpoint: None, + .. + }) + ) { + debug!("0.1 bundle contains inclusion proof without checkpoint"); } - Err(_) => { - error!("unknown bundle version"); - return None; + + Ok(()) + }; + let check_02_bundle = || -> Result<(), BundleProfileErrorKind> { + if inclusion_proof.is_none() { + error!("bundle must contain inclusion proof"); + return Err(Bundle02ProfileErrorKind::InclusionProofMissing)?; } + + if matches!( + inclusion_proof, + Some(InclusionProof { + checkpoint: None, + .. + }) + ) { + error!("bundle must contain checkpoint"); + return Err(Bundle02ProfileErrorKind::CheckpointMissing)?; + } + + Ok(()) + }; + match BundleVersion::from_str(&input.media_type) { + Ok(BundleVersion::Bundle0_1) => check_01_bundle()?, + Ok(BundleVersion::Bundle0_2) => check_02_bundle()?, + Err(_) => return Err(BundleProfileErrorKind::Unknown(input.media_type))?, } - Self::new(input, leaf_cert.clone(), signature, offline, tlog_entry) + Ok(Self { + certificate: leaf_cert.clone(), + signature, + tlog_entry, + }) } +} - /// Retrieves the [LogEntry] for the materials. - pub fn rekor_entry(&self) -> Option<&TransparencyLogEntry> { +impl CheckedBundle { + /// Retrieves and checks consistency of the bundle's [TransparencyLogEntry]. + pub fn tlog_entry(&self, offline: bool, input_digest: &[u8]) -> Option<&TransparencyLogEntry> { let base64_pem_certificate = base64.encode(self.certificate.to_pem(pkcs8::LineEnding::LF).ok()?); @@ -229,21 +266,21 @@ impl VerificationMaterials { data: rekor::hashedrekord::Data { hash: rekor::hashedrekord::Hash { algorithm: rekor::hashedrekord::AlgorithmKind::sha256, - value: hex::encode(&self.input_digest), + value: hex::encode(input_digest), }, }, }, }; - let entry = if !self.offline && self.rekor_entry.inclusion_proof.is_none() { + let entry = if !offline && self.tlog_entry.inclusion_proof.is_none() { warn!("online rekor fetching is not implemented yet, but is necessary for this bundle"); return None; } else { - &self.rekor_entry + &self.tlog_entry }; let actual: serde_json::Value = - serde_json::from_slice(&self.rekor_entry.canonicalized_body).ok()?; + serde_json::from_slice(&self.tlog_entry.canonicalized_body).ok()?; let expected: serde_json::Value = serde_json::to_value(expected_entry).ok()?; if actual != expected { diff --git a/src/verify/policy.rs b/src/verify/policy.rs index ec6f0409f3..16befbc32d 100644 --- a/src/verify/policy.rs +++ b/src/verify/policy.rs @@ -88,6 +88,8 @@ pub enum PolicyError { AnyOf { total: usize }, } +pub type PolicyResult = Result<(), PolicyError>; + /// A policy that checks a single textual value against a X.509 extension. pub trait SingleX509ExtPolicy { fn new>(val: S) -> Self; @@ -96,13 +98,13 @@ pub trait SingleX509ExtPolicy { } impl VerificationPolicy for T { - fn verify(&self, cert: &x509_cert::Certificate) -> Option { + fn verify(&self, cert: &x509_cert::Certificate) -> PolicyResult { let extensions = cert.tbs_certificate.extensions.as_deref().unwrap_or(&[]); let mut extensions = extensions.iter().filter(|ext| ext.extn_id == T::OID); // Check for exactly one extension. let (Some(ext), None) = (extensions.next(), extensions.next()) else { - return Some(PolicyError::ExtensionNotFound); + return Err(PolicyError::ExtensionNotFound); }; // Parse raw string without DER encoding. @@ -110,13 +112,13 @@ impl VerificationPolicy for T .expect("failed to parse constructed Extension!"); if val != self.value() { - Some(PolicyError::ExtensionCheckFailed { + Err(PolicyError::ExtensionCheckFailed { extension: T::name().to_owned(), expected: self.value().to_owned(), actual: val.to_owned(), }) } else { - None + Ok(()) } } } @@ -159,7 +161,7 @@ impl_policy!( /// An interface that all policies must conform to. pub trait VerificationPolicy { - fn verify(&self, cert: &x509_cert::Certificate) -> Option; + fn verify(&self, cert: &x509_cert::Certificate) -> PolicyResult; } /// The "any of" policy, corresponding to a logical OR between child policies. @@ -178,12 +180,14 @@ impl<'a> AnyOf<'a> { } impl VerificationPolicy for AnyOf<'_> { - fn verify(&self, cert: &x509_cert::Certificate) -> Option { + fn verify(&self, cert: &x509_cert::Certificate) -> PolicyResult { self.children .iter() - .find(|policy| policy.verify(cert).is_some()) - .map(|_| PolicyError::AnyOf { - total: self.children.len(), + .find(|policy| policy.verify(cert).is_err()) + .map_or(Ok(()), |_| { + Err(PolicyError::AnyOf { + total: self.children.len(), + }) }) } } @@ -212,14 +216,14 @@ impl<'a> AllOf<'a> { } impl VerificationPolicy for AllOf<'_> { - fn verify(&self, cert: &x509_cert::Certificate) -> Option { - let results = self.children.iter().map(|policy| policy.verify(cert)); + fn verify(&self, cert: &x509_cert::Certificate) -> PolicyResult { + let results = self.children.iter().map(|policy| policy.verify(cert).err()); let failures: Vec<_> = results.flatten().collect(); if failures.is_empty() { - None + Ok(()) } else { - Some(PolicyError::AllOf { + Err(PolicyError::AllOf { total: self.children.len(), errors: failures, }) @@ -230,9 +234,9 @@ impl VerificationPolicy for AllOf<'_> { pub(crate) struct UnsafeNoOp; impl VerificationPolicy for UnsafeNoOp { - fn verify(&self, _cert: &x509_cert::Certificate) -> Option { + fn verify(&self, _cert: &x509_cert::Certificate) -> PolicyResult { warn!("unsafe (no-op) verification policy used! no verification performed!"); - None + Ok(()) } } @@ -260,14 +264,12 @@ impl Identity { } impl VerificationPolicy for Identity { - fn verify(&self, cert: &x509_cert::Certificate) -> Option { - if let err @ Some(_) = self.issuer.verify(cert) { - return err; - } + fn verify(&self, cert: &x509_cert::Certificate) -> PolicyResult { + self.issuer.verify(cert)?; let (_, san): (bool, SubjectAltName) = match cert.tbs_certificate.get() { Ok(Some(result)) => result, - _ => return Some(PolicyError::ExtensionNotFound), + _ => return Err(PolicyError::ExtensionNotFound), }; let names: Vec<_> = san @@ -284,9 +286,9 @@ impl VerificationPolicy for Identity { .collect(); if names.contains(&self.identity.as_str()) { - None + Ok(()) } else { - Some(PolicyError::ExtensionCheckFailed { + Err(PolicyError::ExtensionCheckFailed { extension: "SubjectAltName".to_owned(), expected: self.identity.clone(), actual: names.join(", "), diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index b41173448c..f542cb029e 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -12,57 +12,56 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cell::OnceCell; +use std::io::{self, Read}; +use sha2::{Digest, Sha256}; +use tokio::io::{AsyncRead, AsyncReadExt}; use tracing::debug; use webpki::types::{CertificateDer, UnixTime}; - use x509_cert::der::Encode; use crate::{ + bundle::Bundle, crypto::{CertificatePool, CosignVerificationKey, Signature}, errors::Result as SigstoreResult, rekor::apis::configuration::Configuration as RekorConfiguration, tuf::{Repository, SigstoreRepository}, - verify::VerificationError, + verify::{ + models::{CertificateErrorKind, SignatureErrorKind}, + VerificationError, + }, }; -use super::{models::VerificationMaterials, policy::VerificationPolicy, VerificationResult}; +use super::{models::CheckedBundle, policy::VerificationPolicy, VerificationResult}; -pub struct Verifier<'a, R: Repository> { +pub struct AsyncVerifier { #[allow(dead_code)] rekor_config: RekorConfiguration, - trust_repo: R, - cert_pool: OnceCell>, + cert_pool: CertificatePool, } -impl<'a, R: Repository> Verifier<'a, R> { - pub fn new(rekor_config: RekorConfiguration, trust_repo: R) -> SigstoreResult { +impl AsyncVerifier { + pub fn new( + rekor_config: RekorConfiguration, + trust_repo: R, + ) -> SigstoreResult { + let cert_pool = CertificatePool::from_certificates(trust_repo.fulcio_certs()?, [])?; + Ok(Self { rekor_config, - cert_pool: Default::default(), - trust_repo, + cert_pool, }) } - fn cert_pool(&'a self) -> SigstoreResult<&CertificatePool<'a>> { - let init_cert_pool = || { - let certs = self.trust_repo.fulcio_certs()?; - CertificatePool::from_certificates(certs, []) - }; - - let cert_pool = init_cert_pool()?; - Ok(self.cert_pool.get_or_init(|| cert_pool)) - } - - pub fn verify( - &'a self, - materials: VerificationMaterials, + async fn verify_digest( + &self, + input_digest: Sha256, + bundle: Bundle, policy: &impl VerificationPolicy, + offline: bool, ) -> VerificationResult { - let store = self - .cert_pool() - .expect("Failed to construct certificate pool"); + let input_digest = input_digest.finalize(); + let materials: CheckedBundle = bundle.try_into()?; // In order to verify an artifact, we need to achieve the following: // @@ -91,46 +90,38 @@ impl<'a, R: Repository> Verifier<'a, R> { .to_der() .expect("failed to DER-encode constructed Certificate!") .into(); - let Ok(ee_cert) = (&cert_der).try_into() else { - return Err(VerificationError::CertificateVerificationFailure); - }; + let ee_cert = (&cert_der) + .try_into() + .map_err(CertificateErrorKind::Malformed)?; - let Ok(_trusted_chain) = - store.verify_cert_with_time(&ee_cert, UnixTime::since_unix_epoch(issued_at)) - else { - return Err(VerificationError::CertificateVerificationFailure); - }; + let _trusted_chain = self + .cert_pool + .verify_cert_with_time(&ee_cert, UnixTime::since_unix_epoch(issued_at)) + .map_err(CertificateErrorKind::VerificationFailed)?; debug!("signing certificate chains back to trusted root"); // TODO(tnytown): verify SCT here, sigstore-rs#326 // 2) Verify that the signing certificate belongs to the signer. - if let Some(err) = policy.verify(&materials.certificate) { - return Err(err)?; - } + policy.verify(&materials.certificate)?; debug!("signing certificate conforms to policy"); // 3) Verify that the signature was signed by the public key in the signing certificate - let Ok(signing_key): SigstoreResult = - (&tbs_certificate.subject_public_key_info).try_into() - else { - return Err(VerificationError::CertificateMalformed); - }; + let signing_key: CosignVerificationKey = (&tbs_certificate.subject_public_key_info) + .try_into() + .map_err(SignatureErrorKind::AlgoUnsupported)?; + + let verify_sig = + signing_key.verify_prehash(Signature::Raw(&materials.signature), &input_digest); + verify_sig.map_err(SignatureErrorKind::VerificationFailed)?; - let verify_sig = signing_key.verify_prehash( - Signature::Raw(&materials.signature), - &materials.input_digest, - ); - if verify_sig.is_err() { - return Err(VerificationError::SignatureVerificationFailure); - } debug!("signature corresponds to public key"); // 4) Verify that the Rekor entry is consistent with the other signing // materials - let Some(log_entry) = materials.rekor_entry() else { - return Err(VerificationError::CertificateMalformed); + let Some(log_entry) = materials.tlog_entry(offline, &input_digest) else { + return Err(SignatureErrorKind::Transparency)?; }; debug!("log entry is consistent with other materials"); @@ -156,19 +147,85 @@ impl<'a, R: Repository> Verifier<'a, R> { .to_unix_duration() .as_secs(); if !(not_before <= integrated_time && integrated_time <= not_after) { - return Err(VerificationError::CertificateExpired); + return Err(CertificateErrorKind::Expired)?; } debug!("data signed during validity period"); debug!("successfully verified!"); Ok(()) } + + pub async fn verify( + &self, + mut input: R, + bundle: Bundle, + policy: &impl VerificationPolicy, + offline: bool, + ) -> VerificationResult { + // arbitrary buffer size, chosen to be a multiple of the digest size. + let mut buf = [0u8; 1024]; + let mut hasher = Sha256::new(); + + loop { + match input + .read(&mut buf) + .await + .map_err(VerificationError::Input)? + { + 0 => break, + n => hasher.update(&buf[..n]), + } + } + + self.verify_digest(hasher, bundle, policy, offline).await + } +} + +impl AsyncVerifier { + pub fn production() -> SigstoreResult { + let updater = SigstoreRepository::new(None)?; + + AsyncVerifier::new(Default::default(), updater) + } +} + +pub struct Verifier { + inner: AsyncVerifier, + rt: tokio::runtime::Runtime, +} + +impl Verifier { + pub fn new( + rekor_config: RekorConfiguration, + trust_repo: R, + ) -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let inner = AsyncVerifier::new(rekor_config, trust_repo)?; + + Ok(Self { rt, inner }) + } + + pub fn verify( + &self, + mut input: R, + bundle: Bundle, + policy: &impl VerificationPolicy, + offline: bool, + ) -> VerificationResult { + let mut hasher = Sha256::new(); + io::copy(&mut input, &mut hasher).map_err(VerificationError::Input)?; + + self.rt + .block_on(self.inner.verify_digest(hasher, bundle, policy, offline)) + } } -impl<'a> Verifier<'a, SigstoreRepository> { - pub fn production() -> SigstoreResult> { +impl Verifier { + pub fn production() -> SigstoreResult { let updater = SigstoreRepository::new(None)?; - Verifier::<'a, SigstoreRepository>::new(Default::default(), updater) + Verifier::new(Default::default(), updater) } } diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index 771c0091bb..200d6c8545 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -23,7 +23,6 @@ use clap::{Parser, Subcommand}; use sigstore::{ oauth::IdentityToken, sign::SigningContext, - verify::VerificationMaterials, verify::{policy, Verifier}, }; @@ -162,14 +161,13 @@ fn verify_bundle(args: VerifyBundle) -> anyhow::Result<()> { let mut artifact = fs::File::open(artifact)?; let bundle: sigstore::Bundle = serde_json::from_reader(bundle)?; - let materials = VerificationMaterials::from_bundle(&mut artifact, bundle, true) - .ok_or(anyhow!("Unable to construct VerificationMaterials"))?; - let verifier = Verifier::production()?; verifier.verify( - materials, + &mut artifact, + bundle, &policy::Identity::new(certificate_identity, certificate_oidc_issuer), + true, )?; Ok(()) From d9fff7367302989beccf2c5def99a4efa19c57db Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 23 Feb 2024 16:06:52 -0600 Subject: [PATCH 080/161] verify: relax `'static` bound on async `verify` Signed-off-by: Andrew Pan --- src/verify/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index f542cb029e..42d37ff255 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -155,7 +155,7 @@ impl AsyncVerifier { Ok(()) } - pub async fn verify( + pub async fn verify( &self, mut input: R, bundle: Bundle, From 8a80a367748207a1ecedfc5dac23f97011a3fbb2 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 23 Feb 2024 16:22:20 -0600 Subject: [PATCH 081/161] sign, crypto: review comments Signed-off-by: Andrew Pan --- src/crypto/verification_key.rs | 4 +++- src/sign.rs | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index f6881f2574..ea13e10024 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -388,7 +388,9 @@ impl CosignVerificationKey { .verify_prehash(msg, &sig) .map_err(|_| SigstoreError::PublicKeyVerificationError) } - _ => unimplemented!("Ed25519 doesn't implement verify_prehash"), + CosignVerificationKey::ED25519(_) => { + unimplemented!("Ed25519 doesn't implement verify_prehash") + } } } } diff --git a/src/sign.rs b/src/sign.rs index 124d521591..30f75737c7 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -132,12 +132,8 @@ impl<'ctx> AsyncSigningSession<'ctx> { // Sign artifact. let input_hash: &[u8] = &hasher.clone().finalize(); - let mut signature_bytes = Vec::new(); let artifact_signature: p256::ecdsa::Signature = self.private_key.sign_digest(hasher); - artifact_signature - .to_der() - .encode_to_vec(&mut signature_bytes) - .expect("failed to encode Signature!"); + let signature_bytes = artifact_signature.to_der().as_bytes().to_owned(); let cert = &self.certs.cert; From 526c2a28eac39762441d8ff5db52fada1410f70c Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 23 Feb 2024 17:14:04 -0600 Subject: [PATCH 082/161] verify: basic `Verifier` / `AsyncVerifier` docs Signed-off-by: Andrew Pan --- src/verify/verifier.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 42d37ff255..580bb9e24e 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -34,6 +34,9 @@ use crate::{ use super::{models::CheckedBundle, policy::VerificationPolicy, VerificationResult}; +/// An asynchronous Sigstore verifier. +/// +/// For synchronous usage, see [`Verifier`]. pub struct AsyncVerifier { #[allow(dead_code)] rekor_config: RekorConfiguration, @@ -41,6 +44,9 @@ pub struct AsyncVerifier { } impl AsyncVerifier { + /// Constructs an [`AsyncVerifier`]. + /// + /// For verifications against the public-good trust root, use [`AsyncVerifier::production`]. pub fn new( rekor_config: RekorConfiguration, trust_repo: R, @@ -155,6 +161,8 @@ impl AsyncVerifier { Ok(()) } + /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided + /// [`VerificationPolicy`]. pub async fn verify( &self, mut input: R, @@ -182,6 +190,7 @@ impl AsyncVerifier { } impl AsyncVerifier { + /// Constructs an [`AsyncVerifier`] against the public-good trust root. pub fn production() -> SigstoreResult { let updater = SigstoreRepository::new(None)?; @@ -189,12 +198,18 @@ impl AsyncVerifier { } } +/// A synchronous Sigstore verifier. +/// +/// Async callers must use [`AsyncVerifier`]. Async usage of [`Verifier`] will result in a deadlock. pub struct Verifier { inner: AsyncVerifier, rt: tokio::runtime::Runtime, } impl Verifier { + /// Constructs a synchronous Sigstore verifier. + /// + /// For verifications against the public-good trust root, use [`Verifier::production`]. pub fn new( rekor_config: RekorConfiguration, trust_repo: R, @@ -207,6 +222,8 @@ impl Verifier { Ok(Self { rt, inner }) } + /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided + /// [`VerificationPolicy`]. pub fn verify( &self, mut input: R, @@ -223,6 +240,7 @@ impl Verifier { } impl Verifier { + /// Constructs a synchronous [`Verifier`] against the public-good trust root. pub fn production() -> SigstoreResult { let updater = SigstoreRepository::new(None)?; From 230ead748ea2d3f65cb45375729404e8d0b5e1df Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 23 Feb 2024 17:24:58 -0600 Subject: [PATCH 083/161] crypto/certificate: move `ExtensionErrorKind` up Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 9960b27c23..917a95e38a 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -137,9 +137,6 @@ pub enum ExtensionErrorKind { pub enum NotLeafErrorKind { #[error("certificate is a CA: CAs are not leaves")] IsCA, - - #[error(transparent)] - Extension(#[from] ExtensionErrorKind), } #[derive(Debug, Error)] @@ -152,9 +149,6 @@ pub enum NotCAErrorKind { #[error("certificate in invalid state: cA={ca}, keyCertSign={key_cert_sign}")] Invalid { ca: bool, key_cert_sign: bool }, - - #[error(transparent)] - Extension(#[from] ExtensionErrorKind), } #[derive(Debug, Error)] @@ -169,6 +163,8 @@ pub enum CertificateValidationError { NotLeaf(#[from] NotLeafErrorKind), NotCA(#[from] NotCAErrorKind), + + Extension(#[from] ExtensionErrorKind), } /// Check if the given certificate is a leaf in the context of the Sigstore profile. @@ -199,16 +195,12 @@ pub(crate) fn is_leaf( .get::() .map_err(CertificateValidationError::Malformed)? { - None => Err(NotLeafErrorKind::Extension(ExtensionErrorKind::Missing( - "KeyUsage", - )))?, + None => Err(ExtensionErrorKind::Missing("KeyUsage"))?, Some((_, key_usage)) => key_usage.digital_signature(), }; if !digital_signature { - Err(NotLeafErrorKind::Extension(ExtensionErrorKind::BitUnset( - "KeyUsage.digitalSignature", - )))?; + Err(ExtensionErrorKind::BitUnset("KeyUsage.digitalSignature"))?; } // Finally, we check to make sure the leaf has an `ExtendedKeyUsages` @@ -219,16 +211,14 @@ pub(crate) fn is_leaf( .get::() .map_err(CertificateValidationError::Malformed)? { - None => Err(NotLeafErrorKind::Extension(ExtensionErrorKind::Missing( - "ExtendedKeyUsage", - )))?, + None => Err(ExtensionErrorKind::Missing("ExtendedKeyUsage"))?, Some((_, extended_key_usage)) => extended_key_usage, }; if !extended_key_usage.0.contains(&ID_KP_CODE_SIGNING) { - Err(NotLeafErrorKind::Extension(ExtensionErrorKind::BitUnset( + Err(ExtensionErrorKind::BitUnset( "ExtendedKeyUsage.digitalSignature", - )))?; + ))?; } Ok(()) @@ -264,14 +254,10 @@ pub(crate) fn is_ca( .get::() .map_err(CertificateValidationError::Malformed)? { - None => Err(NotCAErrorKind::Extension(ExtensionErrorKind::Missing( - "BasicConstraints", - )))?, + None => Err(ExtensionErrorKind::Missing("BasicConstraints"))?, Some((false, _)) => { // BasicConstraints must be marked as critical, per RFC 5280 4.2.1.9. - Err(NotCAErrorKind::Extension(ExtensionErrorKind::NotCritical( - "BasicConstraints", - )))? + Err(ExtensionErrorKind::NotCritical("BasicConstraints"))? } Some((true, v)) => v.ca, }; @@ -280,9 +266,7 @@ pub(crate) fn is_ca( .get::() .map_err(CertificateValidationError::Malformed)? { - None => Err(NotCAErrorKind::Extension(ExtensionErrorKind::Missing( - "KeyUsage", - )))?, + None => Err(ExtensionErrorKind::Missing("KeyUsage"))?, Some((_, v)) => v.key_cert_sign(), }; From fe83993e2b3a33603ad318fdc4d7563b97be1923 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Mon, 26 Feb 2024 10:56:26 -0600 Subject: [PATCH 084/161] feedback from review Signed-off-by: Andrew Pan --- src/bundle/mod.rs | 3 +-- src/crypto/certificate.rs | 17 ++++++++--------- src/verify/policy.rs | 28 ++++++++++++++-------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index b3b040c15f..c930c0d1c4 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -90,8 +90,7 @@ impl TryFrom for TransparencyLogEntry { fn try_from(value: RekorLogEntry) -> Result { let canonicalized_body = { - let mut body = - json_syntax::to_value(value.body).expect("failed to parse constructed Body!"); + let mut body = json_syntax::to_value(value.body).or(Err(()))?; body.canonicalize(); body.compact_print().to_string().into_bytes() }; diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 917a95e38a..3d519d1d5a 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -248,7 +248,7 @@ pub(crate) fn is_ca( // - `BasicConstraints.ca` // // Any other combination of states is inconsistent and invalid, meaning - // that we won't consider the certificate a valid non-CA leaf. + // that we won't treat the certificate as neither a leaf nor a CA. let ca = match tbs .get::() @@ -270,17 +270,16 @@ pub(crate) fn is_ca( Some((_, v)) => v.key_cert_sign(), }; - // both states set, this is a CA. - if ca && key_cert_sign { - return Ok(()); + if !ca || !key_cert_sign { + Err(NotCAErrorKind::Invalid { ca, key_cert_sign })? } if !(ca || key_cert_sign) { Err(NotCAErrorKind::NotCA)?; } - // Anything else is an invalid state that should never occur. - Err(NotCAErrorKind::Invalid { ca, key_cert_sign })? + // both states set, this is a CA. + return Ok(()); } /// Returns `True` if and only if the given `Certificate` indicates @@ -306,11 +305,11 @@ pub(crate) fn is_root_ca( is_ca(certificate)?; // A certificate that is its own issuer and signer is considered a root CA. - if tbs.issuer == tbs.subject { - Ok(()) - } else { + if tbs.issuer != tbs.subject { Err(NotCAErrorKind::NotRootCA)? } + + Ok(()) } #[cfg(test)] diff --git a/src/verify/policy.rs b/src/verify/policy.rs index 16befbc32d..67389c0290 100644 --- a/src/verify/policy.rs +++ b/src/verify/policy.rs @@ -112,14 +112,14 @@ impl VerificationPolicy for T .expect("failed to parse constructed Extension!"); if val != self.value() { - Err(PolicyError::ExtensionCheckFailed { + return Err(PolicyError::ExtensionCheckFailed { extension: T::name().to_owned(), expected: self.value().to_owned(), actual: val.to_owned(), - }) - } else { - Ok(()) + }); } + + Ok(()) } } @@ -220,14 +220,14 @@ impl VerificationPolicy for AllOf<'_> { let results = self.children.iter().map(|policy| policy.verify(cert).err()); let failures: Vec<_> = results.flatten().collect(); - if failures.is_empty() { - Ok(()) - } else { - Err(PolicyError::AllOf { + if !failures.is_empty() { + return Err(PolicyError::AllOf { total: self.children.len(), errors: failures, - }) + }); } + + Ok(()) } } @@ -285,14 +285,14 @@ impl VerificationPolicy for Identity { }) .collect(); - if names.contains(&self.identity.as_str()) { - Ok(()) - } else { - Err(PolicyError::ExtensionCheckFailed { + if !names.contains(&self.identity.as_str()) { + return Err(PolicyError::ExtensionCheckFailed { extension: "SubjectAltName".to_owned(), expected: self.identity.clone(), actual: names.join(", "), - }) + }); } + + Ok(()) } } From ce0c6cbcb9f885b0284e89317b344699be7b7b7a Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Mon, 26 Feb 2024 11:03:48 -0600 Subject: [PATCH 085/161] certificate: undo is_ca feedback changes Signed-off-by: Andrew Pan --- src/crypto/certificate.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 3d519d1d5a..9711d4d948 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -270,16 +270,17 @@ pub(crate) fn is_ca( Some((_, v)) => v.key_cert_sign(), }; - if !ca || !key_cert_sign { - Err(NotCAErrorKind::Invalid { ca, key_cert_sign })? + // both states set, this is a CA. + if ca && key_cert_sign { + return Ok(()); } if !(ca || key_cert_sign) { Err(NotCAErrorKind::NotCA)?; } - // both states set, this is a CA. - return Ok(()); + // Anything else is an invalid state that should never occur. + Err(NotCAErrorKind::Invalid { ca, key_cert_sign })? } /// Returns `True` if and only if the given `Certificate` indicates From 3332841953e457f83e666308b1959563bdfd9683 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:39:41 +0000 Subject: [PATCH 086/161] chore(deps): Update cached requirement from 0.48.0 to 0.49.2 --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0ce09da945..224054ee0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.21.0" -cached = { version = "0.48.0", optional = true, features = ["async"] } +cached = { version = "0.49.2", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["serde"] } const-oid = "0.9.1" From 4bef45e4ef4f7956af6d04a9970de14c8c982ed0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:40:37 +0000 Subject: [PATCH 087/161] chore(deps): Update json-syntax requirement from 0.11.1 to 0.12.2 Updates the requirements on [json-syntax](https://github.com/timothee-haudebourg/json-syntax) to permit the latest version. - [Commits](https://github.com/timothee-haudebourg/json-syntax/compare/0.11.1...0.12.2) --- updated-dependencies: - dependency-name: json-syntax dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0ce09da945..ad6b021ed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,7 @@ zeroize = "1.5.7" rustls-webpki = { version = "0.102.1", features = ["alloc"] } serde_repr = "0.1.16" hex = "0.4.3" -json-syntax = { version = "0.11.1", features = ["canonicalize", "serde"] } +json-syntax = { version = "0.12.2", features = ["canonicalize", "serde"] } [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } From 126442e8a6a67d08cff4dfb5899039149a268695 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:40:37 +0000 Subject: [PATCH 088/161] chore(deps): Update json-syntax requirement from 0.11.1 to 0.12.2 Updates the requirements on [json-syntax](https://github.com/timothee-haudebourg/json-syntax) to permit the latest version. - [Commits](https://github.com/timothee-haudebourg/json-syntax/compare/0.11.1...0.12.2) --- updated-dependencies: - dependency-name: json-syntax dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 46ad8fc908..b482dc4720 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,7 @@ zeroize = "1.5.7" rustls-webpki = { version = "0.102.1", features = ["alloc"] } serde_repr = "0.1.16" hex = "0.4.3" -json-syntax = { version = "0.11.1", features = ["canonicalize", "serde"] } +json-syntax = { version = "0.12.2", features = ["canonicalize", "serde"] } tls_codec = { version = "0.4.1", features = ["derive"] } ring = "0.17.6" From d86f5196b05988098dbb40ba90177ef57f6e6c50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:11:24 +0000 Subject: [PATCH 089/161] chore(deps): Update base64 requirement from 0.21.0 to 0.22.0 Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version. - [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md) - [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: base64 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d401720339..075d9c1186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" -base64 = "0.21.0" +base64 = "0.22.0" cached = { version = "0.49.2", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["serde"] } From 950f134a599df2a582cf92e1e8f0c60c09177364 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 11 Mar 2024 09:47:20 +0800 Subject: [PATCH 090/161] lint: fix lint error of chrono and tokio Signed-off-by: Xynnn007 --- Cargo.toml | 1 + src/cosign/signature_layers.rs | 14 +++++-- .../certificate_verifier.rs | 10 +++-- src/crypto/certificate.rs | 37 +++++++++++++------ src/crypto/mod.rs | 10 +++-- 5 files changed, 50 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d401720339..177942b048 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,6 +138,7 @@ rstest = "0.18.1" serial_test = "3.0.0" tempfile = "3.3.0" testcontainers = "0.15" +tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } # cosign example mappings diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index 9934d36f08..8e6cce177f 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -876,7 +876,7 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== use crate::cosign::bundle::Payload; use crate::crypto::tests::{generate_certificate, CertGenerationOptions}; use crate::crypto::SigningScheme; - use chrono::{Duration, Utc}; + use chrono::{TimeDelta, Utc}; impl TryFrom for crate::registry::Certificate { type Error = anyhow::Error; @@ -908,7 +908,9 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== .try_into()?]; let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); - let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); + let integrated_time = Utc::now() + .checked_sub_signed(TimeDelta::try_minutes(1).unwrap()) + .unwrap(); let bundle = Bundle { signed_entry_timestamp: "not relevant".to_string(), payload: Payload { @@ -957,7 +959,9 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== .try_into()?]; let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); - let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); + let integrated_time = Utc::now() + .checked_sub_signed(TimeDelta::try_minutes(1).unwrap()) + .unwrap(); let bundle = Bundle { signed_entry_timestamp: "not relevant".to_string(), payload: Payload { @@ -1005,7 +1009,9 @@ JsB89BPhZYch0U0hKANx5TY+ncrm0s8bfJxxHoenAEFhwhuXeb4PqIrtoQ== .try_into()?]; let cert_pool = CertificatePool::from_certificates(certs, []).unwrap(); - let integrated_time = Utc::now().checked_sub_signed(Duration::minutes(1)).unwrap(); + let integrated_time = Utc::now() + .checked_sub_signed(TimeDelta::try_minutes(1).unwrap()) + .unwrap(); let bundle = Bundle { signed_entry_timestamp: "not relevant".to_string(), payload: Payload { diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index 78d17f857c..7370836594 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -1,4 +1,4 @@ -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono::{DateTime, Utc}; use pkcs8::der::Decode; use std::convert::TryFrom; use tracing::warn; @@ -89,9 +89,11 @@ impl VerificationConstraint for CertificateVerifier { match &signature_layer.bundle { Some(bundle) => { let it = DateTime::::from_naive_utc_and_offset( - NaiveDateTime::from_timestamp_opt(bundle.payload.integrated_time, 0).ok_or( - SigstoreError::UnexpectedError("timestamp is not legal".into()), - )?, + DateTime::from_timestamp(bundle.payload.integrated_time, 0) + .ok_or(SigstoreError::UnexpectedError( + "timestamp is not legal".into(), + ))? + .naive_utc(), Utc, ); let not_before: DateTime = diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index 943df12923..c7c14e99f3 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono::{DateTime, Utc}; use const_oid::db::rfc5912::ID_KP_CODE_SIGNING; use x509_cert::{ ext::pkix::{ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName}, @@ -92,8 +92,9 @@ pub(crate) fn verify_validity(certificate: &Certificate) -> Result<()> { fn verify_expiration(certificate: &Certificate, integrated_time: i64) -> Result<()> { let it = DateTime::::from_naive_utc_and_offset( - NaiveDateTime::from_timestamp_opt(integrated_time, 0) - .ok_or(SigstoreError::X509Error("timestamp is not legal".into()))?, + DateTime::from_timestamp(integrated_time, 0) + .ok_or(SigstoreError::X509Error("timestamp is not legal".into()))? + .naive_utc(), Utc, ); let validity = &certificate.tbs_certificate.validity; @@ -125,7 +126,7 @@ mod tests { use super::*; use crate::crypto::tests::*; - use chrono::{Duration, Utc}; + use chrono::{TimeDelta, Utc}; use x509_cert::der::Decode; #[test] @@ -238,8 +239,12 @@ mod tests { let issued_cert = generate_certificate( Some(&ca_data), CertGenerationOptions { - not_before: Utc::now().checked_add_signed(Duration::days(5)).unwrap(), - not_after: Utc::now().checked_add_signed(Duration::days(6)).unwrap(), + not_before: Utc::now() + .checked_add_signed(TimeDelta::try_days(5).unwrap()) + .unwrap(), + not_after: Utc::now() + .checked_add_signed(TimeDelta::try_days(6).unwrap()) + .unwrap(), ..Default::default() }, )?; @@ -266,8 +271,12 @@ mod tests { let issued_cert = generate_certificate( Some(&ca_data), CertGenerationOptions { - not_before: Utc::now().checked_sub_signed(Duration::days(1)).unwrap(), - not_after: Utc::now().checked_add_signed(Duration::days(1)).unwrap(), + not_before: Utc::now() + .checked_sub_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(), + not_after: Utc::now() + .checked_add_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(), ..Default::default() }, )?; @@ -284,13 +293,19 @@ mod tests { fn verify_cert_expiration_failure() -> anyhow::Result<()> { let ca_data = generate_certificate(None, CertGenerationOptions::default())?; - let integrated_time = Utc::now().checked_add_signed(Duration::days(5)).unwrap(); + let integrated_time = Utc::now() + .checked_add_signed(TimeDelta::try_days(5).unwrap()) + .unwrap(); let issued_cert = generate_certificate( Some(&ca_data), CertGenerationOptions { - not_before: Utc::now().checked_sub_signed(Duration::days(1)).unwrap(), - not_after: Utc::now().checked_add_signed(Duration::days(1)).unwrap(), + not_before: Utc::now() + .checked_sub_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(), + not_after: Utc::now() + .checked_add_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(), ..Default::default() }, )?; diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index c8a35e22bf..3db1461c7f 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -190,7 +190,7 @@ pub mod signing_key; #[cfg(test)] pub(crate) mod tests { - use chrono::{DateTime, Duration, Utc}; + use chrono::{DateTime, TimeDelta, Utc}; use openssl::asn1::{Asn1Integer, Asn1Time}; use openssl::bn::{BigNum, MsbOption}; use openssl::conf::{Conf, ConfMethod}; @@ -231,8 +231,12 @@ OSWS1X9vPavpiQOoTTGC0xX57OojUadxF1cdQmrsiReWg2Wn4FneJfa8xw== impl Default for CertGenerationOptions { fn default() -> Self { - let not_before = Utc::now().checked_sub_signed(Duration::days(1)).unwrap(); - let not_after = Utc::now().checked_add_signed(Duration::days(1)).unwrap(); + let not_before = Utc::now() + .checked_sub_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(); + let not_after = Utc::now() + .checked_add_signed(TimeDelta::try_days(1).unwrap()) + .unwrap(); // Sigstore relies on NIST P-256 // NIST P-256 is a Weierstrass curve specified in FIPS 186-4: Digital Signature Standard (DSS): From aa112d68f250d1f34ad83d0410c0d10940721978 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Mon, 26 Feb 2024 13:19:25 -0800 Subject: [PATCH 091/161] Moved the Repository trait and the ManualRepository struct out of 'tuf' feature flag: 'tuf' lacks support for wasm due to its dependencies. Signed-off-by: Tanner Gill --- Cargo.toml | 5 ++-- examples/cosign/verify/main.rs | 6 ++--- src/cosign/client_builder.rs | 2 +- src/lib.rs | 7 ++++-- src/repository/mod.rs | 46 ++++++++++++++++++++++++++++++++++ src/tuf/mod.rs | 32 +---------------------- 6 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 src/repository/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 369f531a75..9061960cb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,8 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -tuf = ["tough", "regex"] +repository = [] +tuf = ["tough", "regex", "repository"] sign = [] @@ -56,7 +57,7 @@ cosign-rustls-tls = [ "cosign", "registry-rustls-tls", ] -cosign = [] +cosign = ["repository"] cert = [] registry-native-tls = ["oci-distribution/native-tls", "registry"] diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index fc687ba627..85be22b89c 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -110,7 +110,7 @@ struct Cli { async fn run_app( cli: &Cli, - frd: &dyn sigstore::tuf::Repository, + frd: &dyn sigstore::repository::Repository, ) -> anyhow::Result<(Vec, VerificationConstraintVec)> { // Note well: this a limitation deliberately introduced by this example. if cli.cert_email.is_some() && cli.cert_url.is_some() { @@ -228,7 +228,7 @@ async fn run_app( Ok((trusted_layers, verification_constraints)) } -async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { +async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { let repo: sigstore::errors::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); @@ -240,7 +240,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result crate::errors::Result>; + fn rekor_keys(&self) -> crate::errors::Result>; +} + +/// A `ManualRepository` is a [Repository] with out-of-band trust materials. +/// As it does not establish a trust root with TUF, users must initialize its materials themselves. +#[derive(Debug, Default)] +pub struct ManualRepository<'a> { + pub fulcio_certs: Option>>, + pub rekor_key: Option>, +} + +impl Repository for ManualRepository<'_> { + fn fulcio_certs(&self) -> crate::errors::Result> { + Ok(match &self.fulcio_certs { + Some(certs) => certs.clone(), + None => Vec::new(), + }) + } + + fn rekor_keys(&self) -> crate::errors::Result> { + Ok(match &self.rekor_key { + Some(key) => vec![&key[..]], + None => Vec::new(), + }) + } +} \ No newline at end of file diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index e695cc5044..9f3d52ceb6 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -49,36 +49,6 @@ use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, use super::errors::{Result, SigstoreError}; -/// A `Repository` owns all key material necessary for establishing a root of trust. -pub trait Repository { - fn fulcio_certs(&self) -> Result>; - fn rekor_keys(&self) -> Result>; -} - -/// A `ManualRepository` is a [Repository] with out-of-band trust materials. -/// As it does not establish a trust root with TUF, users must initialize its materials themselves. -#[derive(Debug, Default)] -pub struct ManualRepository<'a> { - pub fulcio_certs: Option>>, - pub rekor_key: Option>, -} - -impl Repository for ManualRepository<'_> { - fn fulcio_certs(&self) -> Result> { - Ok(match &self.fulcio_certs { - Some(certs) => certs.clone(), - None => Vec::new(), - }) - } - - fn rekor_keys(&self) -> Result> { - Ok(match &self.rekor_key { - Some(key) => vec![&key[..]], - None => Vec::new(), - }) - } -} - /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] pub struct SigstoreRepository { @@ -177,7 +147,7 @@ impl SigstoreRepository { } } -impl Repository for SigstoreRepository { +impl crate::repository::Repository for SigstoreRepository { /// Fetch Fulcio certificates from the given TUF repository or reuse /// the local cache if its contents are not outdated. /// From 66dc4dff8e809c82884d6b2e385d8e277d69e6e6 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Mon, 26 Feb 2024 13:20:12 -0800 Subject: [PATCH 092/161] Re-exported Repository trait and ManualRepository struct out of 'tuf' mod to preserve backwards compatibility. Signed-off-by: Tanner Gill --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e7881173a9..bb6e7417e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,6 +259,8 @@ #![forbid(unsafe_code)] #![warn(clippy::unwrap_used, clippy::panic)] +pub use repository::{Repository, ManualRepository}; + pub mod crypto; #[cfg(feature = "mock-client")] From 860465b02e841ae7d6644b1e3bc1db56e46feee4 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Tue, 27 Feb 2024 09:02:55 -0800 Subject: [PATCH 093/161] Corrected previous commit: had previously re-exported from the wrong place. Signed-off-by: Tanner Gill --- src/lib.rs | 2 -- src/tuf/mod.rs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bb6e7417e6..e7881173a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,8 +259,6 @@ #![forbid(unsafe_code)] #![warn(clippy::unwrap_used, clippy::panic)] -pub use repository::{Repository, ManualRepository}; - pub mod crypto; #[cfg(feature = "mock-client")] diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index 9f3d52ceb6..1e9101fd21 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -49,6 +49,8 @@ use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, use super::errors::{Result, SigstoreError}; +pub use crate::repository::{Repository, ManualRepository}; + /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] pub struct SigstoreRepository { From c2ba30e4b1e7b211561f9e8a2ce11032c5fa1706 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Tue, 27 Feb 2024 09:04:18 -0800 Subject: [PATCH 094/161] Corrected copyright date: 2021 -> 2024 Signed-off-by: Tanner Gill --- src/repository/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repository/mod.rs b/src/repository/mod.rs index da69270625..518c831e1b 100644 --- a/src/repository/mod.rs +++ b/src/repository/mod.rs @@ -1,5 +1,5 @@ // -// Copyright 2021 The Sigstore Authors. +// Copyright 2024 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. From 57f7bd2370b5f3a5b34e1899098d88cb3dd80d86 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Tue, 27 Feb 2024 09:08:18 -0800 Subject: [PATCH 095/161] Removed 'repository' feature flag: 'repository' modual is no longer hidden behind a feature flag. Signed-off-by: Tanner Gill --- Cargo.toml | 5 ++--- src/lib.rs | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9061960cb6..369f531a75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,8 +40,7 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -repository = [] -tuf = ["tough", "regex", "repository"] +tuf = ["tough", "regex"] sign = [] @@ -57,7 +56,7 @@ cosign-rustls-tls = [ "cosign", "registry-rustls-tls", ] -cosign = ["repository"] +cosign = [] cert = [] registry-native-tls = ["oci-distribution/native-tls", "registry"] diff --git a/src/lib.rs b/src/lib.rs index e7881173a9..e8b9a6ab9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -260,6 +260,7 @@ #![warn(clippy::unwrap_used, clippy::panic)] pub mod crypto; +pub mod repository; #[cfg(feature = "mock-client")] mod mock_client; @@ -281,10 +282,7 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -#[cfg(feature = "repository")] -pub mod repository; - -#[cfg(all(feature = "tuf", feature = "repository"))] +#[cfg(all(feature = "tuf"))] pub mod tuf; // Don't export yet -- these types should only be useful internally. From e737da2394d146e0860faf84722843de329ac5f4 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Tue, 27 Feb 2024 09:10:12 -0800 Subject: [PATCH 096/161] Renamed 'tuf' feature flag to 'sigstore-repository' Signed-off-by: Tanner Gill --- Cargo.toml | 4 ++-- src/errors.rs | 2 +- src/lib.rs | 2 +- src/rekor/models/proposed_entry.rs | 2 +- src/rekor/models/search_index_public_key.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 369f531a75..f4fc1a4c10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "tuf", "sign"] +default = ["full-native-tls", "cached-client", "sigstore-repository", "sign"] wasm = ["getrandom/js"] full-native-tls = [ @@ -40,7 +40,7 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -tuf = ["tough", "regex"] +sigstore-repository = ["tough", "regex"] sign = [] diff --git a/src/errors.rs b/src/errors.rs index 22b87e95cb..fdc9c7e03f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -161,7 +161,7 @@ pub enum SigstoreError { #[error("No Signature Layer passed verification")] SigstoreNoVerifiedLayer, - #[cfg(feature = "tuf")] + #[cfg(feature = "sigstore-repository")] #[error(transparent)] TufError(#[from] Box), diff --git a/src/lib.rs b/src/lib.rs index e8b9a6ab9a..e09b90146d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,7 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -#[cfg(all(feature = "tuf"))] +#[cfg(all(feature = "sigstore-repository"))] pub mod tuf; // Don't export yet -- these types should only be useful internally. diff --git a/src/rekor/models/proposed_entry.rs b/src/rekor/models/proposed_entry.rs index a395d49209..5d587b5c0b 100644 --- a/src/rekor/models/proposed_entry.rs +++ b/src/rekor/models/proposed_entry.rs @@ -68,7 +68,7 @@ pub enum ProposedEntry { #[serde(rename = "spec")] spec: serde_json::Value, }, - #[serde(rename = "tuf")] + #[serde(rename = "sigstore-repository")] Tuf { #[serde(rename = "apiVersion")] api_version: String, diff --git a/src/rekor/models/search_index_public_key.rs b/src/rekor/models/search_index_public_key.rs index 7f2fb3a038..06ddcc2ecd 100644 --- a/src/rekor/models/search_index_public_key.rs +++ b/src/rekor/models/search_index_public_key.rs @@ -41,7 +41,7 @@ pub enum Format { Minisign, #[serde(rename = "ssh")] Ssh, - #[serde(rename = "tuf")] + #[serde(rename = "sigstore-repository")] Tuf, } From 912f733f729c736470759d9aaaacb0f247cc9e50 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Wed, 28 Feb 2024 09:13:07 -0800 Subject: [PATCH 097/161] Revert "Renamed 'tuf' feature flag to 'sigstore-repository'" This reverts commit 1e6a059f69d80bf7cd4f34105e40c294f937b9f0. Signed-off-by: Tanner Gill --- Cargo.toml | 4 ++-- src/errors.rs | 2 +- src/lib.rs | 2 +- src/rekor/models/proposed_entry.rs | 2 +- src/rekor/models/search_index_public_key.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4fc1a4c10..369f531a75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "sigstore-repository", "sign"] +default = ["full-native-tls", "cached-client", "tuf", "sign"] wasm = ["getrandom/js"] full-native-tls = [ @@ -40,7 +40,7 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -sigstore-repository = ["tough", "regex"] +tuf = ["tough", "regex"] sign = [] diff --git a/src/errors.rs b/src/errors.rs index fdc9c7e03f..22b87e95cb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -161,7 +161,7 @@ pub enum SigstoreError { #[error("No Signature Layer passed verification")] SigstoreNoVerifiedLayer, - #[cfg(feature = "sigstore-repository")] + #[cfg(feature = "tuf")] #[error(transparent)] TufError(#[from] Box), diff --git a/src/lib.rs b/src/lib.rs index e09b90146d..e8b9a6ab9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,7 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -#[cfg(all(feature = "sigstore-repository"))] +#[cfg(all(feature = "tuf"))] pub mod tuf; // Don't export yet -- these types should only be useful internally. diff --git a/src/rekor/models/proposed_entry.rs b/src/rekor/models/proposed_entry.rs index 5d587b5c0b..a395d49209 100644 --- a/src/rekor/models/proposed_entry.rs +++ b/src/rekor/models/proposed_entry.rs @@ -68,7 +68,7 @@ pub enum ProposedEntry { #[serde(rename = "spec")] spec: serde_json::Value, }, - #[serde(rename = "sigstore-repository")] + #[serde(rename = "tuf")] Tuf { #[serde(rename = "apiVersion")] api_version: String, diff --git a/src/rekor/models/search_index_public_key.rs b/src/rekor/models/search_index_public_key.rs index 06ddcc2ecd..7f2fb3a038 100644 --- a/src/rekor/models/search_index_public_key.rs +++ b/src/rekor/models/search_index_public_key.rs @@ -41,7 +41,7 @@ pub enum Format { Minisign, #[serde(rename = "ssh")] Ssh, - #[serde(rename = "sigstore-repository")] + #[serde(rename = "tuf")] Tuf, } From 445f295a94a1f1da25c2fd07306d9ac08b5cb0b3 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Wed, 28 Feb 2024 09:18:33 -0800 Subject: [PATCH 098/161] Renamed 'repository' modual to 'repo' Signed-off-by: Tanner Gill --- examples/cosign/verify/main.rs | 6 +++--- src/cosign/client_builder.rs | 2 +- src/lib.rs | 4 ++-- src/{repository => repo}/mod.rs | 0 src/tuf/mod.rs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename src/{repository => repo}/mod.rs (100%) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 85be22b89c..6b09c8eed3 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -110,7 +110,7 @@ struct Cli { async fn run_app( cli: &Cli, - frd: &dyn sigstore::repository::Repository, + frd: &dyn sigstore::repo::Repository, ) -> anyhow::Result<(Vec, VerificationConstraintVec)> { // Note well: this a limitation deliberately introduced by this example. if cli.cert_email.is_some() && cli.cert_url.is_some() { @@ -228,7 +228,7 @@ async fn run_app( Ok((trusted_layers, verification_constraints)) } -async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { +async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { let repo: sigstore::errors::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); @@ -240,7 +240,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result Date: Wed, 28 Feb 2024 09:20:01 -0800 Subject: [PATCH 099/161] Removed left over 'all' cfg attr, left behind from a revert. Signed-off-by: Tanner Gill --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 7b61377181..c7a83062a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,7 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -#[cfg(all(feature = "tuf"))] +#[cfg(feature = "tuf")] pub mod tuf; // Don't export yet -- these types should only be useful internally. From d492e861874515c503e22cff3e19a1b3fc097c12 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 10:23:34 -0800 Subject: [PATCH 100/161] Ran cargo fmt: set up Rust Rover recently and negelected to enable that on save. Signed-off-by: Tanner Gill --- src/repo/mod.rs | 2 +- src/tuf/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repo/mod.rs b/src/repo/mod.rs index 518c831e1b..ea8ba3b784 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -43,4 +43,4 @@ impl Repository for ManualRepository<'_> { None => Vec::new(), }) } -} \ No newline at end of file +} diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index bc67f1e735..b40de77018 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -49,7 +49,7 @@ use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, use super::errors::{Result, SigstoreError}; -pub use crate::repo::{Repository, ManualRepository}; +pub use crate::repo::{ManualRepository, Repository}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] From 07973b26a814c63e01b9ca502c75a1acc6eb8bc6 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 12:55:56 -0800 Subject: [PATCH 101/161] Renames: mod repo -> mod trust. trait Repository -> trait TrustRoot. struct ManualRepository -> ManualTrustRoot. Signed-off-by: Tanner Gill --- examples/cosign/verify/main.rs | 2 +- src/cosign/client_builder.rs | 10 +++++----- src/cosign/mod.rs | 4 ++-- src/lib.rs | 6 +++--- src/{repo => trust}/mod.rs | 10 +++++----- src/tuf/mod.rs | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) rename src/{repo => trust}/mod.rs (84%) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 6b09c8eed3..939c6f35b7 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -240,7 +240,7 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -36,7 +36,7 @@ use crate::repo::Repository; /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via a [`crate::tuf::ManualRepository`]. +/// This can be provided via a [`crate::tuf::ManualTrustRoot`]. /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. @@ -71,8 +71,8 @@ impl<'a> ClientBuilder<'a> { /// Optional - Configures the roots of trust. /// /// Enables Fulcio and Rekor integration with the given trust repository. - /// See [crate::tuf::Repository] for more details on trust repositories. - pub fn with_trust_repository(mut self, repo: &'a R) -> Result { + /// See [crate::tuf::TrustRoot] for more details on trust repositories. + pub fn with_trust_repository(mut self, repo: &'a R) -> Result { let rekor_keys = repo.rekor_keys()?; if !rekor_keys.is_empty() { self.rekor_pub_key = Some(rekor_keys[0]); diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 03d3c0e52d..ec44fc6724 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -102,9 +102,9 @@ pub trait CosignCapabilities { /// must be satisfied: /// /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Rekor integration enabled (see [`crate::tuf::ManualRepository`]) + /// have been created with Rekor integration enabled (see [`crate::tuf::ManualTrustRoot`]) /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Fulcio integration enabled (see [`crate::tuf::ManualRepository]) + /// have been created with Fulcio integration enabled (see [`crate::tuf::ManualTrustRoot]) /// * The layer must include a bundle produced by Rekor /// /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods diff --git a/src/lib.rs b/src/lib.rs index c7a83062a6..3891d0b889 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,7 @@ //! //! Verify the signature of a container image/oci artifact: //! -//! ```rust,no_run +//!```rust,no_run //! use crate::sigstore::cosign::{ //! CosignCapabilities, //! verify_constraints, @@ -92,7 +92,7 @@ //! data: fulcio_cert_data //! }; //! -//! let mut repo = sigstore::repo::ManualRepository { +//! let mut repo = sigstore::trust::ManualTrustRoot { //! fulcio_certs: Some(vec![fulcio_cert.try_into().unwrap()]), //! rekor_key: Some(rekor_pub_key), //! ..Default::default() @@ -260,7 +260,7 @@ #![warn(clippy::unwrap_used, clippy::panic)] pub mod crypto; -pub mod repo; +pub mod trust; #[cfg(feature = "mock-client")] mod mock_client; diff --git a/src/repo/mod.rs b/src/trust/mod.rs similarity index 84% rename from src/repo/mod.rs rename to src/trust/mod.rs index ea8ba3b784..61a5504023 100644 --- a/src/repo/mod.rs +++ b/src/trust/mod.rs @@ -15,21 +15,21 @@ use webpki::types::CertificateDer; -/// A `Repository` owns all key material necessary for establishing a root of trust. -pub trait Repository { +/// A `TrustRoot` owns all key material necessary for establishing a root of trust. +pub trait TrustRoot { fn fulcio_certs(&self) -> crate::errors::Result>; fn rekor_keys(&self) -> crate::errors::Result>; } -/// A `ManualRepository` is a [Repository] with out-of-band trust materials. +/// A `ManualTrustRoot` is a [TrustRoot] with out-of-band trust materials. /// As it does not establish a trust root with TUF, users must initialize its materials themselves. #[derive(Debug, Default)] -pub struct ManualRepository<'a> { +pub struct ManualTrustRoot<'a> { pub fulcio_certs: Option>>, pub rekor_key: Option>, } -impl Repository for ManualRepository<'_> { +impl TrustRoot for ManualTrustRoot<'_> { fn fulcio_certs(&self) -> crate::errors::Result> { Ok(match &self.fulcio_certs { Some(certs) => certs.clone(), diff --git a/src/tuf/mod.rs b/src/tuf/mod.rs index b40de77018..70221ae7b7 100644 --- a/src/tuf/mod.rs +++ b/src/tuf/mod.rs @@ -49,7 +49,7 @@ use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, use super::errors::{Result, SigstoreError}; -pub use crate::repo::{ManualRepository, Repository}; +pub use crate::trust::{ManualTrustRoot, TrustRoot}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] @@ -108,7 +108,7 @@ impl SigstoreRepository { /// Prefetches trust materials. /// - /// [Repository::fulcio_certs()] and [Repository::rekor_keys()] on [SigstoreRepository] lazily + /// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreRepository] 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. /// @@ -149,7 +149,7 @@ impl SigstoreRepository { } } -impl crate::repo::Repository for SigstoreRepository { +impl crate::trust::TrustRoot for SigstoreRepository { /// Fetch Fulcio certificates from the given TUF repository or reuse /// the local cache if its contents are not outdated. /// From f26aacc389283c9076b755d64ccc799515e9d8f1 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:01:43 -0800 Subject: [PATCH 102/161] Renames: mod tuf -> mod sigstore Signed-off-by: Tanner Gill --- examples/cosign/verify/main.rs | 2 +- src/cosign/client_builder.rs | 10 +++++----- src/cosign/mod.rs | 6 +++--- src/lib.rs | 4 ++-- src/{tuf => sigstore}/constants.rs | 0 src/{tuf => sigstore}/mod.rs | 4 ++-- src/{tuf => sigstore}/repository_helper.rs | 0 src/{tuf => sigstore}/trustroot.rs | 0 8 files changed, 13 insertions(+), 13 deletions(-) rename src/{tuf => sigstore}/constants.rs (100%) rename src/{tuf => sigstore}/mod.rs (99%) rename src/{tuf => sigstore}/repository_helper.rs (100%) rename src/{tuf => sigstore}/trustroot.rs (100%) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 939c6f35b7..4cde5287d9 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -22,7 +22,7 @@ use sigstore::cosign::{CosignCapabilities, SignatureLayer}; use sigstore::crypto::SigningScheme; use sigstore::errors::SigstoreVerifyConstraintsError; use sigstore::registry::{ClientConfig, ClientProtocol, OciReference}; -use sigstore::tuf::SigstoreRepository; +use sigstore::sigstore::SigstoreRepository; use std::boxed::Box; use std::convert::TryFrom; use std::time::Instant; diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 08cbf8effc..2e7d494082 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -28,17 +28,17 @@ use crate::trust::TrustRoot; /// ## Rekor integration /// /// Rekor integration can be enabled by specifying Rekor's public key. -/// This can be provided via a [`crate::tuf::ManualTrustRoot`]. +/// This can be provided via a [`crate::sigstore::ManualTrustRoot`]. /// -/// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods +/// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via a [`crate::tuf::ManualTrustRoot`]. +/// This can be provided via a [`crate::sigstore::ManualTrustRoot`]. /// -/// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods +/// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// ## Registry caching @@ -71,7 +71,7 @@ impl<'a> ClientBuilder<'a> { /// Optional - Configures the roots of trust. /// /// Enables Fulcio and Rekor integration with the given trust repository. - /// See [crate::tuf::TrustRoot] for more details on trust repositories. + /// See [crate::sigstore::TrustRoot] for more details on trust repositories. pub fn with_trust_repository(mut self, repo: &'a R) -> Result { let rekor_keys = repo.rekor_keys()?; if !rekor_keys.is_empty() { diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index ec44fc6724..4f560b3530 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -102,12 +102,12 @@ pub trait CosignCapabilities { /// must be satisfied: /// /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Rekor integration enabled (see [`crate::tuf::ManualTrustRoot`]) + /// have been created with Rekor integration enabled (see [`crate::sigstore::ManualTrustRoot`]) /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Fulcio integration enabled (see [`crate::tuf::ManualTrustRoot]) + /// have been created with Fulcio integration enabled (see [`crate::sigstore::ManualTrustRoot]) /// * The layer must include a bundle produced by Rekor /// - /// > Note well: the [`tuf`](crate::tuf) module provides helper structs and methods + /// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// When the embedded certificate cannot be verified, [`SignatureLayer::certificate_signature`] diff --git a/src/lib.rs b/src/lib.rs index 3891d0b889..34ae6bf6e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! 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 +//! The [`sigstore::sigstore`](crate::sigstore) module provides the helper structures to deal //! with it. //! //! # Feature Flags @@ -283,7 +283,7 @@ pub mod registry; pub mod rekor; #[cfg(feature = "tuf")] -pub mod tuf; +pub mod sigstore; // Don't export yet -- these types should only be useful internally. mod bundle; diff --git a/src/tuf/constants.rs b/src/sigstore/constants.rs similarity index 100% rename from src/tuf/constants.rs rename to src/sigstore/constants.rs diff --git a/src/tuf/mod.rs b/src/sigstore/mod.rs similarity index 99% rename from src/tuf/mod.rs rename to src/sigstore/mod.rs index 70221ae7b7..2452c45799 100644 --- a/src/tuf/mod.rs +++ b/src/sigstore/mod.rs @@ -27,7 +27,7 @@ //! method. //! //! ```rust,no_run -//! use sigstore::tuf::SigstoreRepository; +//! use sigstore::sigstore::SigstoreRepository; //! let repo = SigstoreRepository::new(None).unwrap().prefetch().unwrap(); //! ``` use std::{ @@ -114,7 +114,7 @@ impl SigstoreRepository { /// /// ```rust /// # use tokio::task::spawn_blocking; - /// # use sigstore::tuf::SigstoreRepository; + /// # use sigstore::sigstore::SigstoreRepository; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { diff --git a/src/tuf/repository_helper.rs b/src/sigstore/repository_helper.rs similarity index 100% rename from src/tuf/repository_helper.rs rename to src/sigstore/repository_helper.rs diff --git a/src/tuf/trustroot.rs b/src/sigstore/trustroot.rs similarity index 100% rename from src/tuf/trustroot.rs rename to src/sigstore/trustroot.rs From b07e2c21bfa363d34287d61c321fcf88368ba6c0 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:09:03 -0800 Subject: [PATCH 103/161] Moved: sigstore module (previously tuf module) moved into sub-module of 'trust' Signed-off-by: Tanner Gill --- src/lib.rs | 3 --- src/trust/mod.rs | 3 +++ src/{ => trust}/sigstore/constants.rs | 0 src/{ => trust}/sigstore/mod.rs | 6 +++--- src/{ => trust}/sigstore/repository_helper.rs | 0 src/{ => trust}/sigstore/trustroot.rs | 0 6 files changed, 6 insertions(+), 6 deletions(-) rename src/{ => trust}/sigstore/constants.rs (100%) rename src/{ => trust}/sigstore/mod.rs (98%) rename src/{ => trust}/sigstore/repository_helper.rs (100%) rename src/{ => trust}/sigstore/trustroot.rs (100%) diff --git a/src/lib.rs b/src/lib.rs index 34ae6bf6e4..aca41eb315 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,9 +282,6 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -#[cfg(feature = "tuf")] -pub mod sigstore; - // Don't export yet -- these types should only be useful internally. mod bundle; pub use bundle::Bundle; diff --git a/src/trust/mod.rs b/src/trust/mod.rs index 61a5504023..66d65d903d 100644 --- a/src/trust/mod.rs +++ b/src/trust/mod.rs @@ -15,6 +15,9 @@ use webpki::types::CertificateDer; +#[cfg(feature = "tuf")] +pub mod sigstore; + /// A `TrustRoot` owns all key material necessary for establishing a root of trust. pub trait TrustRoot { fn fulcio_certs(&self) -> crate::errors::Result>; diff --git a/src/sigstore/constants.rs b/src/trust/sigstore/constants.rs similarity index 100% rename from src/sigstore/constants.rs rename to src/trust/sigstore/constants.rs diff --git a/src/sigstore/mod.rs b/src/trust/sigstore/mod.rs similarity index 98% rename from src/sigstore/mod.rs rename to src/trust/sigstore/mod.rs index 2452c45799..3a7a247c12 100644 --- a/src/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -27,7 +27,7 @@ //! method. //! //! ```rust,no_run -//! use sigstore::sigstore::SigstoreRepository; +//! use sigstore::trust::sigstore::SigstoreRepository; //! let repo = SigstoreRepository::new(None).unwrap().prefetch().unwrap(); //! ``` use std::{ @@ -47,7 +47,7 @@ use webpki::types::CertificateDer; use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; -use super::errors::{Result, SigstoreError}; +use crate::errors::{Result, SigstoreError}; pub use crate::trust::{ManualTrustRoot, TrustRoot}; @@ -114,7 +114,7 @@ impl SigstoreRepository { /// /// ```rust /// # use tokio::task::spawn_blocking; - /// # use sigstore::sigstore::SigstoreRepository; + /// # use sigstore::trust::sigstore::SigstoreRepository; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { diff --git a/src/sigstore/repository_helper.rs b/src/trust/sigstore/repository_helper.rs similarity index 100% rename from src/sigstore/repository_helper.rs rename to src/trust/sigstore/repository_helper.rs diff --git a/src/sigstore/trustroot.rs b/src/trust/sigstore/trustroot.rs similarity index 100% rename from src/sigstore/trustroot.rs rename to src/trust/sigstore/trustroot.rs From 03643d586d46135cfe945e87a918511b2217e951 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:18:11 -0800 Subject: [PATCH 104/161] Renamed impacted types in /examples Signed-off-by: Tanner Gill --- examples/cosign/verify/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 4cde5287d9..aae38d629d 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -22,7 +22,7 @@ use sigstore::cosign::{CosignCapabilities, SignatureLayer}; use sigstore::crypto::SigningScheme; use sigstore::errors::SigstoreVerifyConstraintsError; use sigstore::registry::{ClientConfig, ClientProtocol, OciReference}; -use sigstore::sigstore::SigstoreRepository; +use sigstore::trust::sigstore::SigstoreRepository; use std::boxed::Box; use std::convert::TryFrom; use std::time::Instant; @@ -110,7 +110,7 @@ struct Cli { async fn run_app( cli: &Cli, - frd: &dyn sigstore::repo::Repository, + frd: &dyn sigstore::trust::TrustRoot, ) -> anyhow::Result<(Vec, VerificationConstraintVec)> { // Note well: this a limitation deliberately introduced by this example. if cli.cert_email.is_some() && cli.cert_url.is_some() { @@ -228,7 +228,7 @@ async fn run_app( Ok((trusted_layers, verification_constraints)) } -async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { +async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { let repo: sigstore::errors::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); From 406a5744888751bbe8e287519425661c1b648866 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:20:48 -0800 Subject: [PATCH 105/161] Renames: SigstoreRespository -> SigstoreTrustRoot Signed-off-by: Tanner Gill --- examples/cosign/verify/main.rs | 6 +++--- src/trust/sigstore/mod.rs | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index aae38d629d..081decd5f2 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -22,7 +22,7 @@ use sigstore::cosign::{CosignCapabilities, SignatureLayer}; use sigstore::crypto::SigningScheme; use sigstore::errors::SigstoreVerifyConstraintsError; use sigstore::registry::{ClientConfig, ClientProtocol, OciReference}; -use sigstore::trust::sigstore::SigstoreRepository; +use sigstore::trust::sigstore::SigstoreTrustRoot; use std::boxed::Box; use std::convert::TryFrom; use std::time::Instant; @@ -230,9 +230,9 @@ async fn run_app( async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { - let repo: sigstore::errors::Result = spawn_blocking(|| { + let repo: sigstore::errors::Result = spawn_blocking(|| { info!("Downloading data from Sigstore TUF repository"); - SigstoreRepository::new(None)?.prefetch() + SigstoreTrustRoot::new(None)?.prefetch() }) .await .map_err(|e| anyhow!("Error spawning blocking task inside of tokio: {}", e))?; diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 3a7a247c12..ba25967edb 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -15,7 +15,7 @@ //! Helper Structs to interact with the Sigstore TUF repository. //! -//! The main interaction point is [`SigstoreRepository`], which fetches Rekor's +//! The main interaction point is [`SigstoreTrustRoot`], which fetches Rekor's //! public key and Fulcio's certificate. //! //! These can later be given to [`cosign::ClientBuilder`](crate::cosign::ClientBuilder) @@ -23,12 +23,12 @@ //! //! # Example //! -//! The `SigstoreRepository` instance can be created via the [`SigstoreRepository::prefetch`] +//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`] //! method. //! //! ```rust,no_run -//! use sigstore::trust::sigstore::SigstoreRepository; -//! let repo = SigstoreRepository::new(None).unwrap().prefetch().unwrap(); +//! use sigstore::trust::sigstore::SigstoreTrustRoot; +//! let repo = SigstoreTrustRoot::new(None).unwrap().prefetch().unwrap(); //! ``` use std::{ cell::OnceCell, @@ -53,13 +53,13 @@ pub use crate::trust::{ManualTrustRoot, TrustRoot}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] -pub struct SigstoreRepository { +pub struct SigstoreTrustRoot { repository: tough::Repository, checkout_dir: Option, trusted_root: OnceCell, } -impl SigstoreRepository { +impl SigstoreTrustRoot { /// Constructs a new trust repository established by a [tough::Repository]. pub fn new(checkout_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. @@ -108,18 +108,18 @@ impl SigstoreRepository { /// Prefetches trust materials. /// - /// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreRepository] lazily + /// [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 tokio::task::spawn_blocking; - /// # use sigstore::trust::sigstore::SigstoreRepository; + /// # use sigstore::trust::sigstore::SigstoreTrustRoot; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { - /// let repo: Result = spawn_blocking(|| Ok(SigstoreRepository::new(None)?.prefetch()?)).await?; - /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRepository` + /// let repo: Result = spawn_blocking(|| Ok(SigstoreTrustRoot::new(None)?.prefetch()?)).await?; + /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust` /// # Ok(()) /// # } /// ``` @@ -149,7 +149,7 @@ impl SigstoreRepository { } } -impl crate::trust::TrustRoot for SigstoreRepository { +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. /// From 4208d54f2b79ce28d1980a7d37b30d931f767232 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:27:25 -0800 Subject: [PATCH 106/161] Renames: struct SigstoreRepository -> struct SigstoreTrustRoot. feature 'tuf' -> feature 'sigstore-trust-root' Signed-off-by: Tanner Gill --- Cargo.toml | 4 ++-- src/errors.rs | 2 +- src/trust/mod.rs | 2 +- src/trust/sigstore/mod.rs | 20 ++++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 369f531a75..6cc4023360 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "tuf", "sign"] +default = ["full-native-tls", "cached-client", "sigstore-trust-root", "sign"] wasm = ["getrandom/js"] full-native-tls = [ @@ -40,7 +40,7 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -tuf = ["tough", "regex"] +sigstore-trust-root = ["tough", "regex"] sign = [] diff --git a/src/errors.rs b/src/errors.rs index 22b87e95cb..5ba05393cb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -161,7 +161,7 @@ pub enum SigstoreError { #[error("No Signature Layer passed verification")] SigstoreNoVerifiedLayer, - #[cfg(feature = "tuf")] + #[cfg(feature = "sigstore-trust-root")] #[error(transparent)] TufError(#[from] Box), diff --git a/src/trust/mod.rs b/src/trust/mod.rs index 66d65d903d..09345e3cdf 100644 --- a/src/trust/mod.rs +++ b/src/trust/mod.rs @@ -15,7 +15,7 @@ use webpki::types::CertificateDer; -#[cfg(feature = "tuf")] +#[cfg(feature = "sigstore-trust-root")] pub mod sigstore; /// A `TrustRoot` owns all key material necessary for establishing a root of trust. diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index ba25967edb..63cbeed38d 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -15,7 +15,7 @@ //! Helper Structs to interact with the Sigstore TUF repository. //! -//! The main interaction point is [`SigstoreTrustRoot`], which fetches Rekor's +//! The main interaction point is [`SigstoreRootTrust`], which fetches Rekor's //! public key and Fulcio's certificate. //! //! These can later be given to [`cosign::ClientBuilder`](crate::cosign::ClientBuilder) @@ -23,12 +23,12 @@ //! //! # Example //! -//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`] +//! The `SigstoreRootTrust` instance can be created via the [`SigstoreRootTrust::prefetch`] //! method. //! //! ```rust,no_run -//! use sigstore::trust::sigstore::SigstoreTrustRoot; -//! let repo = SigstoreTrustRoot::new(None).unwrap().prefetch().unwrap(); +//! use sigstore::trust::sigstore::SigstoreRootTrust; +//! let repo = SigstoreRootTrust::new(None).unwrap().prefetch().unwrap(); //! ``` use std::{ cell::OnceCell, @@ -53,13 +53,13 @@ pub use crate::trust::{ManualTrustRoot, TrustRoot}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] -pub struct SigstoreTrustRoot { +pub struct SigstoreRootTrust { repository: tough::Repository, checkout_dir: Option, trusted_root: OnceCell, } -impl SigstoreTrustRoot { +impl SigstoreRootTrust { /// Constructs a new trust repository established by a [tough::Repository]. pub fn new(checkout_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. @@ -108,17 +108,17 @@ impl SigstoreTrustRoot { /// Prefetches trust materials. /// - /// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreTrustRoot] lazily + /// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreRootTrust] 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 tokio::task::spawn_blocking; - /// # use sigstore::trust::sigstore::SigstoreTrustRoot; + /// # use sigstore::trust::sigstore::SigstoreRootTrust; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { - /// let repo: Result = spawn_blocking(|| Ok(SigstoreTrustRoot::new(None)?.prefetch()?)).await?; + /// let repo: Result = spawn_blocking(|| Ok(SigstoreRootTrust::new(None)?.prefetch()?)).await?; /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust` /// # Ok(()) /// # } @@ -149,7 +149,7 @@ impl SigstoreTrustRoot { } } -impl crate::trust::TrustRoot for SigstoreTrustRoot { +impl crate::trust::TrustRoot for SigstoreRootTrust { /// Fetch Fulcio certificates from the given TUF repository or reuse /// the local cache if its contents are not outdated. /// From 4a5001718f6ee43766fe2ca8fcaff4428820a281 Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:32:21 -0800 Subject: [PATCH 107/161] Fixed up comments that still referenced previous iterations of the refactor Signed-off-by: Tanner Gill --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aca41eb315..03f26d6e1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ //! 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::sigstore`](crate::sigstore) module provides the helper structures to deal +//! The [`sigstore::trust::sigstore`](crate::trust::sigstore) module provides the helper structures to deal //! with it. //! //! # Feature Flags @@ -254,7 +254,7 @@ //! - `cached-client`: Enables support for OCI registry client caching. //! //! - `test-registry`: Enables tests based on a temporary OCI registry. -//! - `tuf`: Enables support for TUF to request for fulcio certs and rekor public key. +//! - `sigstore-trust-root`: Enables support for TUF to request for fulcio certs and rekor public key. #![forbid(unsafe_code)] #![warn(clippy::unwrap_used, clippy::panic)] From d5ba303182318495a081d1c4ad50d5c27be015cc Mon Sep 17 00:00:00 2001 From: Tanner Gill Date: Fri, 1 Mar 2024 13:33:59 -0800 Subject: [PATCH 108/161] Renamed SigstoreRootTrust to SigstoreTrustRoot Signed-off-by: Tanner Gill --- src/trust/sigstore/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 63cbeed38d..ba25967edb 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -15,7 +15,7 @@ //! Helper Structs to interact with the Sigstore TUF repository. //! -//! The main interaction point is [`SigstoreRootTrust`], which fetches Rekor's +//! The main interaction point is [`SigstoreTrustRoot`], which fetches Rekor's //! public key and Fulcio's certificate. //! //! These can later be given to [`cosign::ClientBuilder`](crate::cosign::ClientBuilder) @@ -23,12 +23,12 @@ //! //! # Example //! -//! The `SigstoreRootTrust` instance can be created via the [`SigstoreRootTrust::prefetch`] +//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`] //! method. //! //! ```rust,no_run -//! use sigstore::trust::sigstore::SigstoreRootTrust; -//! let repo = SigstoreRootTrust::new(None).unwrap().prefetch().unwrap(); +//! use sigstore::trust::sigstore::SigstoreTrustRoot; +//! let repo = SigstoreTrustRoot::new(None).unwrap().prefetch().unwrap(); //! ``` use std::{ cell::OnceCell, @@ -53,13 +53,13 @@ pub use crate::trust::{ManualTrustRoot, TrustRoot}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. #[derive(Debug)] -pub struct SigstoreRootTrust { +pub struct SigstoreTrustRoot { repository: tough::Repository, checkout_dir: Option, trusted_root: OnceCell, } -impl SigstoreRootTrust { +impl SigstoreTrustRoot { /// Constructs a new trust repository established by a [tough::Repository]. pub fn new(checkout_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. @@ -108,17 +108,17 @@ impl SigstoreRootTrust { /// Prefetches trust materials. /// - /// [TrustRoot::fulcio_certs()] and [TrustRoot::rekor_keys()] on [SigstoreRootTrust] lazily + /// [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 tokio::task::spawn_blocking; - /// # use sigstore::trust::sigstore::SigstoreRootTrust; + /// # use sigstore::trust::sigstore::SigstoreTrustRoot; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { - /// let repo: Result = spawn_blocking(|| Ok(SigstoreRootTrust::new(None)?.prefetch()?)).await?; + /// let repo: Result = spawn_blocking(|| Ok(SigstoreTrustRoot::new(None)?.prefetch()?)).await?; /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust` /// # Ok(()) /// # } @@ -149,7 +149,7 @@ impl SigstoreRootTrust { } } -impl crate::trust::TrustRoot for SigstoreRootTrust { +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. /// From 8b1a4ec983b6b2a4c52041fd8c1842a7fd3e09ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:47:04 +0000 Subject: [PATCH 109/161] chore(deps): Bump actions/checkout from 4.1.1 to 4.1.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/b4ffde65f46336ab88eb53be808477a3936bae11...9bb56186c3b09b4f86b1c65136769dd318469633) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 9ff358f88d..b8746ea75a 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index a9fb3bcf06..09749783d1 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index 8677bfcee4..354dd71967 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index ebc720c0e4..216975e78b 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 808d929e85..74e9e5c277 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From dd803ca2ed15624ea2adf8112d2470626632768a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:05:23 +0000 Subject: [PATCH 110/161] chore(deps): Update reqwest requirement from 0.11 to 0.12 Updates the requirements on [reqwest](https://github.com/seanmonstar/reqwest) to permit the latest version. - [Release notes](https://github.com/seanmonstar/reqwest/releases) - [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md) - [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.0...v0.12.2) --- updated-dependencies: - dependency-name: reqwest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6cc4023360..71f58e5e75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,7 @@ pkcs8 = { version = "0.10.2", features = [ 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 = [ +reqwest = { version = "0.12", default-features = false, features = [ "json", "multipart", ], optional = true } From e817116fa365975c3b63632303120f50e294acd3 Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Thu, 21 Mar 2024 14:56:34 -0400 Subject: [PATCH 111/161] update tough dep, clippy fixes This commit updates the tough dependency, which changes how many of our trait definitions work. Additionally move from the use of std::sync::oncecell to tokio::sync::oncecell for sigstoretrustroot.trusted_root so that SigstoreTrustRoot can be Send. Update examples and tests. Remove some unused types Fixup clippy warnings Signed-off-by: Andrew Stoycos --- Cargo.toml | 6 +- examples/cosign/sign/main.rs | 1 - examples/cosign/verify/main.rs | 17 +-- src/cosign/client_builder.rs | 9 +- src/cosign/mod.rs | 4 +- src/cosign/signature_layers.rs | 3 - .../cert_subject_email_verifier.rs | 1 - .../cert_subject_url_verifier.rs | 1 - .../certificate_verifier.rs | 1 - src/crypto/certificate.rs | 2 +- src/crypto/mod.rs | 28 ++--- src/crypto/signing_key/ecdsa/ec.rs | 2 +- src/crypto/signing_key/ecdsa/mod.rs | 15 +-- src/crypto/signing_key/ed25519.rs | 1 - src/crypto/signing_key/mod.rs | 10 +- src/crypto/signing_key/rsa/keypair.rs | 2 - src/crypto/verification_key.rs | 1 - src/fulcio/mod.rs | 3 +- src/fulcio/models.rs | 36 +----- src/lib.rs | 1 + src/oauth/token.rs | 6 +- src/registry/config.rs | 1 - src/rekor/apis/configuration.rs | 2 - src/rekor/apis/entries_api.rs | 2 - src/rekor/apis/index_api.rs | 2 - src/rekor/apis/pubkey_api.rs | 2 - src/rekor/apis/tlog_api.rs | 2 - src/sign.rs | 1 - src/trust/mod.rs | 16 ++- src/trust/sigstore/mod.rs | 118 ++++++++++-------- 30 files changed, 127 insertions(+), 169 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6cc4023360..46ae0c5dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -sigstore-trust-root = ["tough", "regex"] +sigstore-trust-root = ["futures-util", "tough", "regex", "tokio/sync"] sign = [] @@ -81,6 +81,8 @@ ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } ed25519 = { version = "2.2.1", features = ["alloc"] } ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } +futures = "0.3" +futures-util = { version = "0.3.30", optional = true } lazy_static = "1.4.0" oci-distribution = { version = "0.10", default-features = false, optional = true } olpc-cjson = "0.1" @@ -116,7 +118,7 @@ sigstore_protobuf_specs = "0.1.0-rc.2" thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tokio-util = { version = "0.7.10", features = ["io-util"] } -tough = { version = "0.14", features = ["http"], optional = true } +tough = { version = "0.17.1", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" x509-cert = { version = "0.2.2", features = ["builder", "pem", "std"] } diff --git a/examples/cosign/sign/main.rs b/examples/cosign/sign/main.rs index 28302c0e39..b5ce9ebf98 100644 --- a/examples/cosign/sign/main.rs +++ b/examples/cosign/sign/main.rs @@ -18,7 +18,6 @@ use sigstore::cosign::constraint::{AnnotationMarker, PrivateKeySigner}; use sigstore::cosign::{Constraint, CosignCapabilities, SignatureLayer}; use sigstore::crypto::SigningScheme; use sigstore::registry::{Auth, ClientConfig, ClientProtocol, OciReference}; -use std::convert::TryFrom; use tracing::{debug, warn}; use zeroize::Zeroizing; diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index 081decd5f2..24980d1b04 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -23,8 +23,6 @@ use sigstore::crypto::SigningScheme; use sigstore::errors::SigstoreVerifyConstraintsError; use sigstore::registry::{ClientConfig, ClientProtocol, OciReference}; use sigstore::trust::sigstore::SigstoreTrustRoot; -use std::boxed::Box; -use std::convert::TryFrom; use std::time::Instant; extern crate anyhow; @@ -34,7 +32,6 @@ extern crate clap; use clap::Parser; use std::{collections::HashMap, fs}; -use tokio::task::spawn_blocking; extern crate tracing_subscriber; use tracing::{info, warn}; @@ -133,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)?; + client_builder = client_builder.with_trust_repository(frd).await?; let cert_chain: Option> = match cli.cert_chain.as_ref() { None => None, @@ -187,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()?.is_empty() { + let require_rekor_bundle = if !frd.rekor_keys().await?.is_empty() { true } else { warn!("certificate based verification is weaker when Rekor integration is disabled"); @@ -230,12 +227,10 @@ async fn run_app( async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result> { if cli.use_sigstore_tuf_data { - let repo: sigstore::errors::Result = spawn_blocking(|| { - info!("Downloading data from Sigstore TUF repository"); - SigstoreTrustRoot::new(None)?.prefetch() - }) - .await - .map_err(|e| anyhow!("Error spawning blocking task inside of tokio: {}", e))?; + info!("Downloading data from Sigstore TUF repository"); + + let repo: sigstore::errors::Result = + SigstoreTrustRoot::new(None).await?.prefetch().await; return Ok(Box::new(repo?)); }; diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 2e7d494082..d37bca7cf9 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -72,12 +72,15 @@ 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 fn with_trust_repository(mut self, repo: &'a R) -> Result { - let rekor_keys = repo.rekor_keys()?; + pub async fn with_trust_repository( + mut self, + repo: &'a R, + ) -> Result { + let rekor_keys = repo.rekor_keys().await?; if !rekor_keys.is_empty() { self.rekor_pub_key = Some(rekor_keys[0]); } - self.fulcio_certs = repo.fulcio_certs()?; + self.fulcio_certs = repo.fulcio_certs().await?; Ok(self) } diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 4f560b3530..2debf012f4 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -48,7 +48,6 @@ use crate::crypto::{CosignVerificationKey, Signature}; use crate::errors::SigstoreError; use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use pkcs8::der::Decode; -use std::convert::TryFrom; use x509_cert::Certificate; pub mod bundle; @@ -284,7 +283,6 @@ where #[cfg(test)] mod tests { use serde_json::json; - use std::collections::HashMap; use webpki::types::CertificateDer; use super::constraint::{AnnotationMarker, PrivateKeySigner}; @@ -296,7 +294,7 @@ mod tests { AnnotationVerifier, CertSubjectEmailVerifier, VerificationConstraintVec, }; use crate::crypto::certificate_pool::CertificatePool; - use crate::crypto::{CosignVerificationKey, SigningScheme}; + use crate::crypto::SigningScheme; #[cfg(feature = "test-registry")] use testcontainers::{clients, core::WaitFor}; diff --git a/src/cosign/signature_layers.rs b/src/cosign/signature_layers.rs index 8e6cce177f..879e548fd2 100644 --- a/src/cosign/signature_layers.rs +++ b/src/cosign/signature_layers.rs @@ -17,7 +17,6 @@ use const_oid::ObjectIdentifier; use digest::Digest; use oci_distribution::client::ImageLayer; use serde::Serialize; -use std::convert::TryFrom; use std::{collections::HashMap, fmt}; use tracing::{debug, info, warn}; use x509_cert::der::DecodePem; @@ -550,8 +549,6 @@ pub(crate) mod tests { use super::*; use openssl::x509::X509; use serde_json::json; - use std::collections::HashMap; - use std::convert::TryFrom; use crate::cosign::tests::{get_fulcio_cert_pool, get_rekor_public_key}; diff --git a/src/cosign/verification_constraint/cert_subject_email_verifier.rs b/src/cosign/verification_constraint/cert_subject_email_verifier.rs index 1b7a8b2f08..e1fe799e8a 100644 --- a/src/cosign/verification_constraint/cert_subject_email_verifier.rs +++ b/src/cosign/verification_constraint/cert_subject_email_verifier.rs @@ -126,7 +126,6 @@ mod tests { build_correct_signature_layer_with_certificate, build_correct_signature_layer_without_bundle, }; - use crate::cosign::signature_layers::CertificateSubject; use crate::cosign::verification_constraint::CertSubjectUrlVerifier; #[test] diff --git a/src/cosign/verification_constraint/cert_subject_url_verifier.rs b/src/cosign/verification_constraint/cert_subject_url_verifier.rs index 739aa9e66d..d171980351 100644 --- a/src/cosign/verification_constraint/cert_subject_url_verifier.rs +++ b/src/cosign/verification_constraint/cert_subject_url_verifier.rs @@ -74,7 +74,6 @@ mod tests { build_correct_signature_layer_with_certificate, build_correct_signature_layer_without_bundle, }; - use crate::cosign::signature_layers::CertificateSubject; use crate::cosign::verification_constraint::CertSubjectEmailVerifier; #[test] diff --git a/src/cosign/verification_constraint/certificate_verifier.rs b/src/cosign/verification_constraint/certificate_verifier.rs index 7370836594..d918803bd6 100644 --- a/src/cosign/verification_constraint/certificate_verifier.rs +++ b/src/cosign/verification_constraint/certificate_verifier.rs @@ -1,6 +1,5 @@ use chrono::{DateTime, Utc}; use pkcs8::der::Decode; -use std::convert::TryFrom; use tracing::warn; use webpki::types::CertificateDer; use x509_cert::Certificate; diff --git a/src/crypto/certificate.rs b/src/crypto/certificate.rs index c7c14e99f3..0b5685764e 100644 --- a/src/crypto/certificate.rs +++ b/src/crypto/certificate.rs @@ -126,7 +126,7 @@ mod tests { use super::*; use crate::crypto::tests::*; - use chrono::{TimeDelta, Utc}; + use chrono::TimeDelta; use x509_cert::der::Decode; #[test] diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 3db1461c7f..fd1667f9b7 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -16,7 +16,6 @@ //! Structures and constants required to perform cryptographic operations. use sha2::{Sha256, Sha384}; -use std::convert::TryFrom; use crate::errors::*; @@ -60,20 +59,19 @@ pub enum SigningScheme { ED25519, } -impl ToString for SigningScheme { - fn to_string(&self) -> String { - let str = match self { - SigningScheme::RSA_PSS_SHA256(_) => "RSA_PSS_SHA256", - SigningScheme::RSA_PSS_SHA384(_) => "RSA_PSS_SHA384", - SigningScheme::RSA_PSS_SHA512(_) => "RSA_PSS_SHA512", - SigningScheme::RSA_PKCS1_SHA256(_) => "RSA_PKCS1_SHA256", - SigningScheme::RSA_PKCS1_SHA384(_) => "RSA_PKCS1_SHA384", - SigningScheme::RSA_PKCS1_SHA512(_) => "RSA_PKCS1_SHA512", - SigningScheme::ECDSA_P256_SHA256_ASN1 => "ECDSA_P256_SHA256_ASN1", - SigningScheme::ECDSA_P384_SHA384_ASN1 => "ECDSA_P384_SHA384_ASN1", - SigningScheme::ED25519 => "ED25519", - }; - String::from(str) +impl std::fmt::Display for SigningScheme { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SigningScheme::RSA_PSS_SHA256(_) => write!(f, "RSA_PSS_SHA256"), + SigningScheme::RSA_PSS_SHA384(_) => write!(f, "RSA_PSS_SHA384"), + SigningScheme::RSA_PSS_SHA512(_) => write!(f, "RSA_PSS_SHA512"), + SigningScheme::RSA_PKCS1_SHA256(_) => write!(f, "RSA_PKCS1_SHA256"), + SigningScheme::RSA_PKCS1_SHA384(_) => write!(f, "RSA_PKCS1_SHA384"), + SigningScheme::RSA_PKCS1_SHA512(_) => write!(f, "RSA_PKCS1_SHA512"), + SigningScheme::ECDSA_P256_SHA256_ASN1 => write!(f, "ECDSA_P256_SHA256_ASN1"), + SigningScheme::ECDSA_P384_SHA384_ASN1 => write!(f, "ECDSA_P384_SHA384_ASN1"), + SigningScheme::ED25519 => write!(f, "ED25519"), + } } } diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index 7c2dd70cb0..a9cd15b5d3 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -63,7 +63,7 @@ //! let signature = ec_signer.sign(b"some message"); //! ``` -use std::{convert::TryFrom, marker::PhantomData, ops::Add}; +use std::{marker::PhantomData, ops::Add}; use digest::{ core_api::BlockSizeUser, diff --git a/src/crypto/signing_key/ecdsa/mod.rs b/src/crypto/signing_key/ecdsa/mod.rs index 4374a83e7a..b7680b007b 100644 --- a/src/crypto/signing_key/ecdsa/mod.rs +++ b/src/crypto/signing_key/ecdsa/mod.rs @@ -73,8 +73,6 @@ //! // verify //! assert!(verification_key.verify_signature(Signature::Raw(&signature_data),message).is_ok()); /// ``` -use p256; - use crate::errors::*; use self::ec::{EcdsaKeys, EcdsaSigner}; @@ -88,13 +86,12 @@ pub enum ECDSAKeys { P384(EcdsaKeys), } -impl ToString for ECDSAKeys { - fn to_string(&self) -> String { - let str = match self { - ECDSAKeys::P256(_) => "ECDSA P256", - ECDSAKeys::P384(_) => "ECDSA P384", - }; - String::from(str) +impl std::fmt::Display for ECDSAKeys { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ECDSAKeys::P256(_) => write!(f, "ECDSA P256"), + ECDSAKeys::P384(_) => write!(f, "ECDSA P384"), + } } } diff --git a/src/crypto/signing_key/ed25519.rs b/src/crypto/signing_key/ed25519.rs index 0934acd5eb..f1292c12a7 100644 --- a/src/crypto/signing_key/ed25519.rs +++ b/src/crypto/signing_key/ed25519.rs @@ -61,7 +61,6 @@ //! ``` use ed25519::pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; -use std::convert::TryFrom; use ed25519::KeypairBytes; use ed25519_dalek::{Signer as _, SigningKey}; diff --git a/src/crypto/signing_key/mod.rs b/src/crypto/signing_key/mod.rs index efc34b60ac..f209ba2a60 100644 --- a/src/crypto/signing_key/mod.rs +++ b/src/crypto/signing_key/mod.rs @@ -139,12 +139,12 @@ pub enum SigStoreKeyPair { RSA(RSAKeys), } -impl ToString for SigStoreKeyPair { - fn to_string(&self) -> String { +impl std::fmt::Display for SigStoreKeyPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - SigStoreKeyPair::ECDSA(_) => String::from("EC Key"), - SigStoreKeyPair::ED25519(_) => String::from("Ed25519 Key"), - SigStoreKeyPair::RSA(_) => String::from("RSA Key"), + SigStoreKeyPair::ECDSA(_) => write!(f, "EC Key"), + SigStoreKeyPair::ED25519(_) => write!(f, "Ed25519 Key"), + SigStoreKeyPair::RSA(_) => write!(f, "RSA Key"), } } } diff --git a/src/crypto/signing_key/rsa/keypair.rs b/src/crypto/signing_key/rsa/keypair.rs index c7d12148e5..f54294f598 100644 --- a/src/crypto/signing_key/rsa/keypair.rs +++ b/src/crypto/signing_key/rsa/keypair.rs @@ -39,8 +39,6 @@ //! let rsa_keys2 = RSAKeys::from_encrypted_pem(privkey_pem.as_bytes(), b"password").unwrap(); //! ``` -use std::convert::TryFrom; - use pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey}; use rsa::{ pkcs1::DecodeRsaPrivateKey, pkcs1v15::SigningKey, pss::BlindedSigningKey, RsaPrivateKey, diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index 1cf40d7eed..b1c54a3c0c 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -19,7 +19,6 @@ use ed25519::pkcs8::DecodePublicKey as ED25519DecodePublicKey; use rsa::{pkcs1v15, pss}; use sha2::{Digest, Sha256, Sha384}; use signature::{DigestVerifier, Verifier}; -use std::convert::TryFrom; use x509_cert::{der::referenced::OwnedToRef, spki::SubjectPublicKeyInfoOwned}; use super::{ diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 81477cab6e..8c034e2333 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -14,7 +14,6 @@ use pkcs8::der::Decode; use reqwest::{header, Body}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; -use std::convert::{TryFrom, TryInto}; use std::fmt::{Debug, Display, Formatter}; use tracing::debug; use url::Url; @@ -55,7 +54,7 @@ impl TryFrom for Body { struct PublicKey(String, SigningScheme); impl Serialize for PublicKey { - fn serialize(&self, serializer: S) -> std::result::Result + fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, { diff --git a/src/fulcio/models.rs b/src/fulcio/models.rs index b4e7dc367d..e39e335895 100644 --- a/src/fulcio/models.rs +++ b/src/fulcio/models.rs @@ -19,7 +19,7 @@ use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _}; use pem::Pem; use pkcs8::der::EncodePem; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer}; use serde_repr::Deserialize_repr; use x509_cert::Certificate; @@ -38,26 +38,6 @@ where ser.serialize_str(&encoded) } -fn deserialize_base64<'de, D>(de: D) -> std::result::Result, D::Error> -where - D: Deserializer<'de>, -{ - let buf: &str = Deserialize::deserialize(de)?; - - BASE64_STD_ENGINE - .decode(buf) - .map_err(serde::de::Error::custom) -} - -fn deserialize_inner_detached_sct<'de, D>(de: D) -> std::result::Result -where - D: Deserializer<'de>, -{ - let buf = deserialize_base64(de)?; - - serde_json::from_slice(&buf).map_err(serde::de::Error::custom) -} - #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct CreateSigningCertificateRequest { @@ -76,8 +56,6 @@ pub enum SigningCertificate { #[serde(rename_all = "camelCase")] pub struct SigningCertificateDetachedSCT { pub chain: CertificateChain, - #[serde(deserialize_with = "deserialize_inner_detached_sct")] - pub signed_certificate_timestamp: InnerDetachedSCT, } #[derive(Deserialize)] @@ -91,18 +69,6 @@ pub struct CertificateChain { pub certificates: Vec, } -#[derive(Deserialize)] -pub struct InnerDetachedSCT { - pub sct_version: SCTVersion, - #[serde(deserialize_with = "deserialize_base64")] - pub id: Vec, - pub timestamp: u64, - #[serde(deserialize_with = "deserialize_base64")] - pub signature: Vec, - #[serde(deserialize_with = "deserialize_base64")] - pub extensions: Vec, -} - #[derive(Deserialize_repr, PartialEq, Debug)] #[repr(u8)] pub enum SCTVersion { diff --git a/src/lib.rs b/src/lib.rs index 03f26d6e1c..e715b80078 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,7 @@ //! //! 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"); diff --git a/src/oauth/token.rs b/src/oauth/token.rs index b5b304f9ed..e916a30003 100644 --- a/src/oauth/token.rs +++ b/src/oauth/token.rs @@ -99,8 +99,8 @@ impl From for IdentityToken { } } -impl ToString for IdentityToken { - fn to_string(&self) -> String { - self.original_token.clone() +impl std::fmt::Display for IdentityToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.original_token.clone()) } } diff --git a/src/registry/config.rs b/src/registry/config.rs index 95630ef29f..b7dd3d2da1 100644 --- a/src/registry/config.rs +++ b/src/registry/config.rs @@ -17,7 +17,6 @@ use serde::Serialize; use std::cmp::Ordering; -use std::convert::From; use webpki::types::CertificateDer; use crate::errors; diff --git a/src/rekor/apis/configuration.rs b/src/rekor/apis/configuration.rs index 3016db1991..a878be3840 100644 --- a/src/rekor/apis/configuration.rs +++ b/src/rekor/apis/configuration.rs @@ -8,8 +8,6 @@ * Generated by: https://openapi-generator.tech */ -use reqwest; - const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); #[derive(Debug, Clone)] diff --git a/src/rekor/apis/entries_api.rs b/src/rekor/apis/entries_api.rs index 6f29a8ae38..1f7bac8062 100644 --- a/src/rekor/apis/entries_api.rs +++ b/src/rekor/apis/entries_api.rs @@ -8,8 +8,6 @@ * Generated by: https://openapi-generator.tech */ -use reqwest; - use super::{configuration, Error}; use crate::rekor::apis::ResponseContent; use crate::rekor::models::log_entry::LogEntry; diff --git a/src/rekor/apis/index_api.rs b/src/rekor/apis/index_api.rs index 563876a060..ab0680f951 100644 --- a/src/rekor/apis/index_api.rs +++ b/src/rekor/apis/index_api.rs @@ -8,8 +8,6 @@ * Generated by: https://openapi-generator.tech */ -use reqwest; - use super::{configuration, Error}; use crate::rekor::apis::ResponseContent; use serde::{Deserialize, Serialize}; diff --git a/src/rekor/apis/pubkey_api.rs b/src/rekor/apis/pubkey_api.rs index c2680cea99..33091276ab 100644 --- a/src/rekor/apis/pubkey_api.rs +++ b/src/rekor/apis/pubkey_api.rs @@ -8,8 +8,6 @@ * Generated by: https://openapi-generator.tech */ -use reqwest; - use super::{configuration, Error}; use crate::rekor::apis::ResponseContent; use serde::{Deserialize, Serialize}; diff --git a/src/rekor/apis/tlog_api.rs b/src/rekor/apis/tlog_api.rs index ed935c7dca..bbb0a1d3ec 100644 --- a/src/rekor/apis/tlog_api.rs +++ b/src/rekor/apis/tlog_api.rs @@ -8,8 +8,6 @@ * Generated by: https://openapi-generator.tech */ -use reqwest; - use super::{configuration, Error}; use crate::rekor::apis::ResponseContent; use serde::{Deserialize, Serialize}; diff --git a/src/sign.rs b/src/sign.rs index 1d4b36838e..e8e17ec407 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -18,7 +18,6 @@ use std::io::{self, Read}; use std::time::SystemTime; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use hex; use json_syntax::Print; use p256::NistP256; use pkcs8::der::{Encode, EncodePem}; diff --git a/src/trust/mod.rs b/src/trust/mod.rs index 09345e3cdf..b4c7f7507f 100644 --- a/src/trust/mod.rs +++ b/src/trust/mod.rs @@ -13,15 +13,18 @@ // 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. -pub trait TrustRoot { - fn fulcio_certs(&self) -> crate::errors::Result>; - fn rekor_keys(&self) -> crate::errors::Result>; +#[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>; + async fn rekor_keys(&self) -> crate::errors::Result>; } /// A `ManualTrustRoot` is a [TrustRoot] with out-of-band trust materials. @@ -32,15 +35,18 @@ pub struct ManualTrustRoot<'a> { pub rekor_key: Option>, } +#[cfg(not(target_arch = "wasm32"))] +#[async_trait] impl TrustRoot for ManualTrustRoot<'_> { - fn fulcio_certs(&self) -> crate::errors::Result> { + #[cfg(not(target_arch = "wasm32"))] + async fn fulcio_certs(&self) -> crate::errors::Result> { Ok(match &self.fulcio_certs { Some(certs) => certs.clone(), None => Vec::new(), }) } - fn rekor_keys(&self) -> crate::errors::Result> { + async fn rekor_keys(&self) -> crate::errors::Result> { Ok(match &self.rekor_key { Some(key) => vec![&key[..]], None => Vec::new(), diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index ba25967edb..b843b9b783 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -26,29 +26,33 @@ //! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`] //! method. //! -//! ```rust,no_run -//! use sigstore::trust::sigstore::SigstoreTrustRoot; -//! let repo = SigstoreTrustRoot::new(None).unwrap().prefetch().unwrap(); -//! ``` +/// ```rust +/// # use sigstore::trust::sigstore::SigstoreTrustRoot; +/// # use sigstore::errors::Result; +/// # #[tokio::main] +/// # async fn main() -> std::result::Result<(), anyhow::Error> { +/// let repo: Result = SigstoreTrustRoot::new(None).await?.prefetch().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::{ - cell::OnceCell, fs, - io::Read, path::{Path, PathBuf}, }; - -mod constants; -mod trustroot; - -use sha2::{Digest, Sha256}; +use tokio::sync::OnceCell; use tough::TargetName; use tracing::debug; use webpki::types::CertificateDer; -use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; +mod constants; +mod trustroot; +use self::trustroot::{CertificateAuthority, TimeRange, TransparencyLogInstance, TrustedRoot}; use crate::errors::{Result, SigstoreError}; - pub use crate::trust::{ManualTrustRoot, TrustRoot}; /// Securely fetches Rekor public key and Fulcio certificates from Sigstore's TUF repository. @@ -61,15 +65,16 @@ pub struct SigstoreTrustRoot { impl SigstoreTrustRoot { /// Constructs a new trust repository established by a [tough::Repository]. - pub fn new(checkout_dir: Option<&Path>) -> Result { + pub async fn new(checkout_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; let repository = - tough::RepositoryLoader::new(constants::SIGSTORE_ROOT, metadata_base, target_base) + tough::RepositoryLoader::new(&constants::SIGSTORE_ROOT, metadata_base, target_base) .expiration_enforcement(tough::ExpirationEnforcement::Safe) .load() + .await .map_err(Box::new)?; Ok(Self { @@ -79,8 +84,8 @@ impl SigstoreTrustRoot { }) } - fn trusted_root(&self) -> Result<&TrustedRoot> { - fn init_trusted_root( + async fn trusted_root(&self) -> Result<&TrustedRoot> { + async fn init_trusted_root( repository: &tough::Repository, checkout_dir: Option<&PathBuf>, ) -> Result { @@ -91,19 +96,19 @@ impl SigstoreTrustRoot { repository, &trusted_root_target, local_path.as_ref(), - )?; + ) + .await?; debug!("data:\n{}", String::from_utf8_lossy(&data)); - Ok(serde_json::from_slice(&data[..])?) - } - - if let Some(root) = self.trusted_root.get() { - return Ok(root); + serde_json::from_slice(&data[..]).map_err(SigstoreError::from) } - let root = init_trusted_root(&self.repository, self.checkout_dir.as_ref())?; - Ok(self.trusted_root.get_or_init(|| root)) + self.trusted_root + .get_or_try_init(|| async { + init_trusted_root(&self.repository, self.checkout_dir.as_ref()).await + }) + .await } /// Prefetches trust materials. @@ -113,18 +118,17 @@ impl SigstoreTrustRoot { /// use this method to fetch the trust root ahead of time. /// /// ```rust - /// # use tokio::task::spawn_blocking; /// # use sigstore::trust::sigstore::SigstoreTrustRoot; /// # use sigstore::errors::Result; /// # #[tokio::main] /// # async fn main() -> std::result::Result<(), anyhow::Error> { - /// let repo: Result = spawn_blocking(|| Ok(SigstoreTrustRoot::new(None)?.prefetch()?)).await?; + /// let repo: Result = SigstoreTrustRoot::new(None).await?.prefetch().await; /// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust` /// # Ok(()) /// # } /// ``` - pub fn prefetch(self) -> Result { - let _ = self.trusted_root()?; + pub async fn prefetch(self) -> Result { + let _ = self.trusted_root().await?; Ok(self) } @@ -149,16 +153,16 @@ 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. - /// - /// **Warning:** this method needs special handling when invoked from - /// an async function because it performs blocking operations. - fn fulcio_certs(&self) -> Result> { - let root = self.trusted_root()?; + #[cfg(not(target_arch = "wasm32"))] + async fn fulcio_certs(&self) -> Result> { + let root = self.trusted_root().await?; // Allow expired certificates: they may have been active when the // certificate was used to sign. @@ -178,11 +182,8 @@ 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. - /// - /// **Warning:** this method needs special handling when invoked from - /// an async function because it performs blocking operations. - fn rekor_keys(&self) -> Result> { - let root = self.trusted_root()?; + async fn rekor_keys(&self) -> Result> { + let root = self.trusted_root().await?; let keys: Vec<_> = Self::tlog_keys(&root.tlogs).collect(); if keys.len() != 1 { @@ -230,7 +231,7 @@ fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { /// /// **Note well:** the `local_file` is updated whenever its contents are /// outdated. -fn fetch_target_or_reuse_local_cache( +async fn fetch_target_or_reuse_local_cache( repository: &tough::Repository, target_name: &TargetName, local_file: Option<&PathBuf>, @@ -242,7 +243,7 @@ fn fetch_target_or_reuse_local_cache( }?; let data = if local_file_outdated { - let data = fetch_target(repository, target_name)?; + let data = fetch_target(repository, target_name).await?; if let Some(path) = local_file { // update the local file to have latest data from the TUF repo fs::write(path, data.clone())?; @@ -259,14 +260,21 @@ fn fetch_target_or_reuse_local_cache( } /// Download a file from a TUF repository -fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { - let data: Vec; - match repository.read_target(target_name).map_err(Box::new)? { +async fn fetch_target(repository: &tough::Repository, target_name: &TargetName) -> Result> { + match repository + .read_target(target_name) + .await + .map_err(Box::new)? + { None => Err(SigstoreError::TufTargetNotFoundError( target_name.raw().to_string(), )), - Some(reader) => { - data = read_to_end(reader)?; + Some(mut stream) => { + let mut data = vec![]; + while let Some(d) = stream.next().await { + let mut d = Into::>::into(d.map_err(Box::new)?); + data.append(&mut d); + } Ok(data) } } @@ -302,9 +310,17 @@ fn is_local_file_outdated( } } -/// Gets the goods from a read and makes a Vec -fn read_to_end(mut reader: R) -> Result> { - let mut v = Vec::new(); - reader.read_to_end(&mut v)?; - Ok(v) +#[cfg(test)] +mod tests { + use crate::trust::sigstore::SigstoreTrustRoot; + + #[tokio::test] + async fn prefetch() { + let _repo = SigstoreTrustRoot::new(None) + .await + .expect("initialize SigstoreRepository") + .prefetch() + .await + .expect("prefetch"); + } } From 062ae7932e00c10f9933062520bfc10830bbeeb8 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 27 Mar 2024 16:04:07 +0100 Subject: [PATCH 112/161] Tag the 0.9.0 release == What's Changed * sign: init by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/310 * cargo audit: ignore RUSTSEC-2023-0071 by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/321 * chore(deps): Update json-syntax requirement from 0.9.6 to 0.10.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/319 * chore(deps): Update cached requirement from 0.46.0 to 0.47.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/323 * chore(deps): Update serial_test requirement from 2.0.0 to 3.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/322 * dep: update rustls-webpki, fold in pki_types by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/324 * chore(deps): Update cached requirement from 0.47.0 to 0.48.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/325 * chore(deps): Update json-syntax requirement from 0.10.0 to 0.11.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/327 * chore(deps): Update cached requirement from 0.48.0 to 0.49.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/329 * chore(deps): Update json-syntax requirement from 0.11.1 to 0.12.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/330 * lint: fix lint error of chrono and tokio by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/334 * chore(deps): Update base64 requirement from 0.21.0 to 0.22.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/332 * The `Repository` trait and `ManualRepository` struct no longer require a feature flag by @tannaurus in https://github.com/sigstore/sigstore-rs/pull/331 * chore(deps): Bump actions/checkout from 4.1.1 to 4.1.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/336 * chore(deps): Update reqwest requirement from 0.11 to 0.12 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/341 * update tough dep by @astoycos in https://github.com/sigstore/sigstore-rs/pull/340 == New Contributors * @tannaurus made their first contribution in https://github.com/sigstore/sigstore-rs/pull/331 * @astoycos made their first contribution in https://github.com/sigstore/sigstore-rs/pull/340 **Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.8.0...v0.9.0 Signed-off-by: Flavio Castelli --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 944b534324..86adfec55d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0" From cba52fe158ac49a628608ce09afd52819872de29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 19:36:08 +0000 Subject: [PATCH 113/161] chore(deps): Update oci-distribution requirement from 0.10 to 0.11 Updates the requirements on [oci-distribution](https://github.com/krustlet/oci-distribution) to permit the latest version. - [Release notes](https://github.com/krustlet/oci-distribution/releases) - [Commits](https://github.com/krustlet/oci-distribution/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: oci-distribution dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 86adfec55d..007981d74b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } futures = "0.3" futures-util = { version = "0.3.30", optional = true } lazy_static = "1.4.0" -oci-distribution = { version = "0.10", default-features = false, optional = true } +oci-distribution = { version = "0.11", default-features = false, optional = true } olpc-cjson = "0.1" openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", From caec81c2262a79fe83aeef76be3bca3e00a9ea8f Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 14:17:07 -0500 Subject: [PATCH 114/161] 2024-04-10 pr feedback Signed-off-by: Andrew Pan --- Cargo.toml | 12 +++++------ src/crypto/verification_key.rs | 2 +- src/errors.rs | 3 --- src/trust/sigstore/constants.rs | 2 +- src/trust/sigstore/mod.rs | 17 ++++++++-------- src/verify/policy.rs | 12 ++++++++--- src/verify/verifier.rs | 35 ++++++++++++++++++++++----------- 7 files changed, 48 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 545e30806d..793ebf7a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ test-registry = [] fulcio-native-tls = ["oauth-native-tls", "reqwest/native-tls", "fulcio"] fulcio-rustls-tls = ["oauth-rustls-tls", "reqwest/rustls-tls", "fulcio"] -fulcio = [] +fulcio = ["serde_with"] oauth-native-tls = ["openidconnect/native-tls", "oauth"] oauth-rustls-tls = ["openidconnect/rustls-tls", "oauth"] @@ -40,12 +40,12 @@ rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -sigstore-trust-root = ["futures-util", "tough", "regex", "tokio/sync"] - -bundle = [] +bundle = ["sigstore_protobuf_specs"] sign = ["bundle"] verify = ["bundle"] +sigstore-trust-root = ["bundle", "futures-util", "tough", "regex", "tokio/sync"] + cosign-native-tls = [ "oci-distribution/native-tls", "cert", @@ -113,10 +113,10 @@ rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" -serde_with = { version = "3.4.0", features = ["base64", "json"] } +serde_with = { version = "3.4.0", features = ["base64", "json"], optional = true } sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } -sigstore_protobuf_specs = "0.3.2" +sigstore_protobuf_specs = { version = "0.3.2", optional = true } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tokio-util = { version = "0.7.10", features = ["io-util"] } diff --git a/src/crypto/verification_key.rs b/src/crypto/verification_key.rs index e787f7e4c8..5d877750c8 100644 --- a/src/crypto/verification_key.rs +++ b/src/crypto/verification_key.rs @@ -331,7 +331,7 @@ impl CosignVerificationKey { /// Verify the signature provided has been actually generated by the given key /// when signing the provided prehashed message. - pub fn verify_prehash(&self, signature: Signature, msg: &[u8]) -> Result<()> { + pub(crate) fn verify_prehash(&self, signature: Signature, msg: &[u8]) -> Result<()> { let sig = match signature { Signature::Raw(data) => data.to_owned(), Signature::Base64Encoded(data) => BASE64_STD_ENGINE.decode(data)?, diff --git a/src/errors.rs b/src/errors.rs index b7de172b5f..5ba05393cb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -109,9 +109,6 @@ pub enum SigstoreError { #[error("Certificate pool error: {0}")] CertificatePoolError(String), - #[error("Certificate invalid: {0}")] - InvalidCertError(String), - #[error("Signing session expired")] ExpiredSigningSession(), diff --git a/src/trust/sigstore/constants.rs b/src/trust/sigstore/constants.rs index bb08b5b4e9..d4630aaf12 100644 --- a/src/trust/sigstore/constants.rs +++ b/src/trust/sigstore/constants.rs @@ -19,7 +19,7 @@ pub(crate) const SIGSTORE_TARGET_BASE: &str = "https://tuf-repo-cdn.sigstore.dev macro_rules! impl_static_resource { {$($name:literal,)+} => { #[inline] - pub(crate) fn static_resource(name: impl AsRef) -> Option<&'static [u8]> { + pub(crate) fn static_resource(name: N) -> Option<&'static [u8]> where N: AsRef { match name.as_ref() { $( $name => Some(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/trust_root/prod/", $name))) diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 4cb5a3915b..018b1227a8 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -38,7 +38,7 @@ /// ``` use futures_util::TryStreamExt; use sha2::{Digest, Sha256}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use tokio_util::bytes::BytesMut; use sigstore_protobuf_specs::dev::sigstore::{ @@ -62,7 +62,7 @@ pub struct SigstoreTrustRoot { impl SigstoreTrustRoot { /// Constructs a new trust repository established by a [tough::Repository]. - pub async fn new(checkout_dir: Option<&Path>) -> Result { + pub async fn new(checkout_dir: Option) -> Result { // These are statically defined and should always parse correctly. let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; @@ -77,8 +77,6 @@ impl SigstoreTrustRoot { .await .map_err(Box::new)?; - let checkout_dir = checkout_dir.map(ToOwned::to_owned); - let trusted_root = { let data = Self::fetch_target(&repository, &checkout_dir, "trusted_root.json").await?; serde_json::from_slice(&data[..])? @@ -105,12 +103,13 @@ impl SigstoreTrustRoot { } }; - // Try reading the target from disk cache. + // First, try reading the target from disk cache. let data = if let Some(Ok(local_data)) = local_path.as_ref().map(std::fs::read) { + debug!("{}: reading from embedded resources", name.raw()); local_data.to_vec() // Try reading the target embedded into the binary. } else if let Some(embedded_data) = constants::static_resource(name.raw()) { - debug!("read embedded target {}", name.raw()); + debug!("{}: reading from remote", name.raw()); embedded_data.to_vec() // If all else fails, read the data from the TUF repo. } else if let Ok(remote_data) = read_remote_target().await { @@ -128,15 +127,15 @@ impl SigstoreTrustRoot { }; let data = if Sha256::digest(&data)[..] != target.hashes.sha256[..] { + debug!("{}: out of date", name.raw()); read_remote_target().await?.to_vec() } else { data }; - // Write the up-to-date data back to the disk. This doesn't need to succeed, as we can - // always fetch the target again later. + // Write our updated data back to the disk. if let Some(local_path) = local_path { - let _ = std::fs::write(local_path, &data); + std::fs::write(local_path, &data)?; } Ok(data) diff --git a/src/verify/policy.rs b/src/verify/policy.rs index 67389c0290..c6c1806df8 100644 --- a/src/verify/policy.rs +++ b/src/verify/policy.rs @@ -109,7 +109,7 @@ impl VerificationPolicy for T // Parse raw string without DER encoding. let val = std::str::from_utf8(ext.extn_value.as_bytes()) - .expect("failed to parse constructed Extension!"); + .or(Err(PolicyError::ExtensionNotFound))?; if val != self.value() { return Err(PolicyError::ExtensionCheckFailed { @@ -172,7 +172,10 @@ pub struct AnyOf<'a> { } impl<'a> AnyOf<'a> { - pub fn new>(policies: I) -> Self { + pub fn new(policies: I) -> Self + where + I: IntoIterator, + { Self { children: policies.into_iter().collect(), } @@ -200,7 +203,10 @@ pub struct AllOf<'a> { } impl<'a> AllOf<'a> { - pub fn new>(policies: I) -> Option { + pub fn new(policies: I) -> Option + where + I: IntoIterator, + { let children: Vec<_> = policies.into_iter().collect(); // Without this, we'd be able to construct an `AllOf` containing an empty list of child diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 5d43e96f22..a461b910e7 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -62,13 +62,16 @@ impl AsyncVerifier { }) } - async fn verify_digest( + async fn verify_digest

( &self, input_digest: Sha256, bundle: Bundle, - policy: &impl VerificationPolicy, + policy: &P, offline: bool, - ) -> VerificationResult { + ) -> VerificationResult + where + P: VerificationPolicy, + { let input_digest = input_digest.finalize(); let materials: CheckedBundle = bundle.try_into()?; @@ -129,9 +132,9 @@ impl AsyncVerifier { // 4) Verify that the Rekor entry is consistent with the other signing // materials - let Some(log_entry) = materials.tlog_entry(offline, &input_digest) else { - return Err(SignatureErrorKind::Transparency)?; - }; + let log_entry = materials + .tlog_entry(offline, &input_digest) + .ok_or(SignatureErrorKind::Transparency)?; debug!("log entry is consistent with other materials"); // 5) Verify the inclusion proof supplied by Rekor for this artifact, @@ -166,13 +169,17 @@ impl AsyncVerifier { /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided /// [`VerificationPolicy`]. - pub async fn verify( + pub async fn verify( &self, mut input: R, bundle: Bundle, - policy: &impl VerificationPolicy, + policy: &P, offline: bool, - ) -> VerificationResult { + ) -> VerificationResult + where + R: AsyncRead + Unpin + Send, + P: VerificationPolicy, + { // arbitrary buffer size, chosen to be a multiple of the digest size. let mut buf = [0u8; 1024]; let mut hasher = Sha256::new(); @@ -228,13 +235,17 @@ impl Verifier { /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided /// [`VerificationPolicy`]. - pub fn verify( + pub fn verify( &self, mut input: R, bundle: Bundle, - policy: &impl VerificationPolicy, + policy: &P, offline: bool, - ) -> VerificationResult { + ) -> VerificationResult + where + R: Read, + P: VerificationPolicy, + { let mut hasher = Sha256::new(); io::copy(&mut input, &mut hasher).map_err(VerificationError::Input)?; From 11970d522f7bdfe8dfb3b202a81618952aa8a72b Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 14:21:44 -0500 Subject: [PATCH 115/161] verify: publicize `verify_digest` Signed-off-by: Andrew Pan --- src/verify/verifier.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index a461b910e7..45371e79af 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -62,7 +62,7 @@ impl AsyncVerifier { }) } - async fn verify_digest

( + pub async fn verify_digest

( &self, input_digest: Sha256, bundle: Bundle, @@ -233,6 +233,20 @@ impl Verifier { Ok(Self { rt, inner }) } + pub fn verify_digest

( + &self, + input_digest: Sha256, + bundle: Bundle, + policy: &P, + offline: bool, + ) -> VerificationResult + where + P: VerificationPolicy, + { + self.rt + .block_on(self.inner.verify_digest(input_digest, bundle, policy, offline)) + } + /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided /// [`VerificationPolicy`]. pub fn verify( @@ -249,8 +263,7 @@ impl Verifier { let mut hasher = Sha256::new(); io::copy(&mut input, &mut hasher).map_err(VerificationError::Input)?; - self.rt - .block_on(self.inner.verify_digest(hasher, bundle, policy, offline)) + self.verify_digest(hasher, bundle, policy, offline) } } From 39ea179c112685196ee657a30b5c940adaf7e92c Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 14:26:06 -0500 Subject: [PATCH 116/161] `cargo fmt` Signed-off-by: Andrew Pan --- src/verify/verifier.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 45371e79af..8cf68584f1 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -243,8 +243,10 @@ impl Verifier { where P: VerificationPolicy, { - self.rt - .block_on(self.inner.verify_digest(input_digest, bundle, policy, offline)) + self.rt.block_on( + self.inner + .verify_digest(input_digest, bundle, policy, offline), + ) } /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided From ecf8995354e2a739c96d4643aee421627fb451c8 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 18:21:37 -0500 Subject: [PATCH 117/161] adjust `bundle`, `sign`, `verify` feature flags Signed-off-by: Andrew Pan --- Cargo.toml | 26 +++++++++++++------------- src/bundle/mod.rs | 2 +- src/errors.rs | 3 ++- src/lib.rs | 7 ++----- src/trust/sigstore/mod.rs | 5 +++-- src/verify/models.rs | 3 +-- src/verify/verifier.rs | 4 ++++ tests/conformance/Cargo.toml | 3 ++- tests/conformance/conformance.rs | 4 ++-- 9 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 793ebf7a7f..e5e6640f62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" [features] -default = ["full-native-tls", "cached-client", "sigstore-trust-root", "sign", "verify"] +default = ["full-native-tls", "cached-client", "sigstore-trust-root", "bundle"] wasm = ["getrandom/js", "ring/wasm32_unknown_unknown_js"] full-native-tls = [ @@ -28,23 +28,23 @@ full-rustls-tls = [ # This features is used by tests that use docker to create a registry test-registry = [] -fulcio-native-tls = ["oauth-native-tls", "reqwest/native-tls", "fulcio"] -fulcio-rustls-tls = ["oauth-rustls-tls", "reqwest/rustls-tls", "fulcio"] -fulcio = ["serde_with"] - oauth-native-tls = ["openidconnect/native-tls", "oauth"] oauth-rustls-tls = ["openidconnect/rustls-tls", "oauth"] -oauth = [] +oauth = ["openidconnect"] + +fulcio-native-tls = ["oauth-native-tls", "reqwest/native-tls", "fulcio"] +fulcio-rustls-tls = ["oauth-rustls-tls", "reqwest/rustls-tls", "fulcio"] +fulcio = ["oauth", "serde_with"] rekor-native-tls = ["reqwest/native-tls", "rekor"] rekor-rustls-tls = ["reqwest/rustls-tls", "rekor"] rekor = ["reqwest"] -bundle = ["sigstore_protobuf_specs"] -sign = ["bundle"] -verify = ["bundle"] +sign = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"] +verify = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"] +bundle = ["sign", "verify"] -sigstore-trust-root = ["bundle", "futures-util", "tough", "regex", "tokio/sync"] +sigstore-trust-root = ["sigstore_protobuf_specs", "futures-util", "tough", "regex", "tokio/sync"] cosign-native-tls = [ "oci-distribution/native-tls", @@ -58,12 +58,12 @@ cosign-rustls-tls = [ "cosign", "registry-rustls-tls", ] -cosign = [] +cosign = ["olpc-cjson"] cert = [] registry-native-tls = ["oci-distribution/native-tls", "registry"] registry-rustls-tls = ["oci-distribution/rustls-tls", "registry"] -registry = [] +registry = ["olpc-cjson"] mock-client-native-tls = ["oci-distribution/native-tls", "mock-client"] mock-client-rustls-tls = ["oci-distribution/rustls-tls", "mock-client"] @@ -87,7 +87,7 @@ futures = "0.3" futures-util = { version = "0.3.30", optional = true } lazy_static = "1.4.0" oci-distribution = { version = "0.11", default-features = false, optional = true } -olpc-cjson = "0.1" +olpc-cjson = { version = "0.1", optional = true } openidconnect = { version = "3.0", default-features = false, features = [ "reqwest", ], optional = true } diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index c930c0d1c4..c8c8ea64b0 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Useful types for Sigstore bundles. +//! Common types for Sigstore Bundle support. use std::fmt::Display; use std::str::FromStr; diff --git a/src/errors.rs b/src/errors.rs index 5ba05393cb..56a3f93856 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -133,7 +133,8 @@ pub enum SigstoreError { #[error(transparent)] JoinError(#[from] tokio::task::JoinError), - #[cfg(feature = "sign")] + // HACK(tnytown): Remove when we rework the Fulcio V2 endpoint. + #[cfg(feature = "fulcio")] #[error(transparent)] ReqwestError(#[from] reqwest::Error), diff --git a/src/lib.rs b/src/lib.rs index 33c5f3eebb..1fee312ca5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,11 +282,8 @@ pub mod registry; #[cfg(feature = "rekor")] pub mod rekor; -// Don't export yet -- these types should only be useful internally. -#[cfg(feature = "bundle")] -mod bundle; -#[cfg(feature = "bundle")] -pub use bundle::Bundle; +#[cfg(any(feature = "sign", feature = "verify"))] +pub mod bundle; #[cfg(feature = "verify")] pub mod verify; diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 018b1227a8..b4a27d1712 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -105,14 +105,15 @@ impl SigstoreTrustRoot { // First, try reading the target from disk cache. let data = if let Some(Ok(local_data)) = local_path.as_ref().map(std::fs::read) { - debug!("{}: reading from embedded resources", name.raw()); + debug!("{}: reading from disk cache", name.raw()); local_data.to_vec() // Try reading the target embedded into the binary. } else if let Some(embedded_data) = constants::static_resource(name.raw()) { - debug!("{}: reading from remote", name.raw()); + debug!("{}: reading from embedded resources", name.raw()); embedded_data.to_vec() // If all else fails, read the data from the TUF repo. } else if let Ok(remote_data) = read_remote_target().await { + debug!("{}: reading from remote", name.raw()); remote_data.to_vec() } else { return Err(SigstoreError::TufTargetNotFoundError(name.raw().to_owned())); diff --git a/src/verify/models.rs b/src/verify/models.rs index 5a6777fca4..01fdc8b660 100644 --- a/src/verify/models.rs +++ b/src/verify/models.rs @@ -16,12 +16,11 @@ use std::str::FromStr; use crate::{ - bundle::Version as BundleVersion, + bundle::{Bundle, Version as BundleVersion}, crypto::certificate::{is_leaf, is_root_ca, CertificateValidationError}, rekor::models as rekor, }; -use crate::Bundle; use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use sigstore_protobuf_specs::dev::sigstore::{ bundle::v1::{bundle, verification_material}, diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 8cf68584f1..818888996a 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -62,6 +62,8 @@ impl AsyncVerifier { }) } + /// Verifies an input digest against the given Sigstore Bundle, ensuring conformance to the + /// provided [`VerificationPolicy`]. pub async fn verify_digest

( &self, input_digest: Sha256, @@ -233,6 +235,8 @@ impl Verifier { Ok(Self { rt, inner }) } + /// Verifies an input digest against the given Sigstore Bundle, ensuring conformance to the + /// provided [`VerificationPolicy`]. pub fn verify_digest

( &self, input_digest: Sha256, diff --git a/tests/conformance/Cargo.toml b/tests/conformance/Cargo.toml index b5d896c3e3..1f2cc3d92e 100644 --- a/tests/conformance/Cargo.toml +++ b/tests/conformance/Cargo.toml @@ -10,7 +10,8 @@ license = "Apache-2.0" clap = { version = "4.0.8", features = ["derive"] } anyhow = "1.0.75" serde_json = "1.0.107" -sigstore = { path = "../../" } +sigstore = { path = "../../", default-features = false, features = ["bundle", "sigstore-trust-root", "full-native-tls"] } +tracing-subscriber = "0.3" [[bin]] name = "sigstore" diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index 200d6c8545..e8ab9d3697 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -18,7 +18,6 @@ use std::{fs, process::exit}; -use anyhow::anyhow; use clap::{Parser, Subcommand}; use sigstore::{ oauth::IdentityToken, @@ -113,6 +112,7 @@ struct VerifyBundle { } fn main() { + tracing_subscriber::fmt::init(); let cli = Cli::parse(); let result = match cli.command { @@ -160,7 +160,7 @@ fn verify_bundle(args: VerifyBundle) -> anyhow::Result<()> { let bundle = fs::File::open(bundle)?; let mut artifact = fs::File::open(artifact)?; - let bundle: sigstore::Bundle = serde_json::from_reader(bundle)?; + let bundle: sigstore::bundle::Bundle = serde_json::from_reader(bundle)?; let verifier = Verifier::production()?; verifier.verify( From 3f150a207e8a1a8a546eb67472886f080ff0b299 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 18:38:42 -0500 Subject: [PATCH 118/161] fixup `cargo build` on features `bundle, wasm` Signed-off-by: Andrew Pan --- Cargo.toml | 4 ++-- src/crypto/certificate_pool.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5e6640f62..dd9c659f61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/sigstore/sigstore-rs" [features] default = ["full-native-tls", "cached-client", "sigstore-trust-root", "bundle"] -wasm = ["getrandom/js", "ring/wasm32_unknown_unknown_js"] +wasm = ["getrandom/js", "ring/wasm32_unknown_unknown_js", "chrono/wasmbind"] full-native-tls = [ "fulcio-native-tls", @@ -76,7 +76,7 @@ async-trait = "0.1.52" base64 = "0.22.0" cached = { version = "0.49.2", optional = true, features = ["async"] } cfg-if = "1.0.0" -chrono = { version = "0.4.27", default-features = false, features = ["serde"] } +chrono = { version = "0.4.27", default-features = false, features = ["now", "serde"] } const-oid = { version = "0.9.6", features = ["db"] } digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } diff --git a/src/crypto/certificate_pool.rs b/src/crypto/certificate_pool.rs index 9502fdc208..1fddead331 100644 --- a/src/crypto/certificate_pool.rs +++ b/src/crypto/certificate_pool.rs @@ -88,8 +88,12 @@ impl CertificatePool { ) -> SigstoreResult<()> { let der = CertificateDer::from(der); let cert = EndEntityCert::try_from(&der)?; + let time = std::time::Duration::from_secs(chrono::Utc::now().timestamp() as u64); - self.verify_cert_with_time(&cert, verification_time.unwrap_or(UnixTime::now()))?; + self.verify_cert_with_time( + &cert, + verification_time.unwrap_or(UnixTime::since_unix_epoch(time)), + )?; Ok(()) } From 81ae53920e78f3d39d1429b62e24a2800931ba86 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Wed, 10 Apr 2024 21:17:08 -0500 Subject: [PATCH 119/161] verifier: fixup time range check Signed-off-by: Andrew Pan --- src/verify/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/verify/verifier.rs b/src/verify/verifier.rs index 818888996a..ac4d4f62b3 100644 --- a/src/verify/verifier.rs +++ b/src/verify/verifier.rs @@ -160,7 +160,7 @@ impl AsyncVerifier { .not_after .to_unix_duration() .as_secs(); - if !(not_before <= integrated_time && integrated_time <= not_after) { + if integrated_time < not_before || integrated_time > not_after { return Err(CertificateErrorKind::Expired)?; } debug!("data signed during validity period"); From 71f5161a6b92ae9cf8276149f8bf1deb023c32ac Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 11 Apr 2024 16:38:58 -0500 Subject: [PATCH 120/161] trust/sigstore: test caching behavior Signed-off-by: Andrew Pan --- src/trust/sigstore/mod.rs | 151 +++++++++++++++++- .../data/repository/1.registry.npmjs.org.json | 23 --- tests/data/repository/1.root.json | 65 -------- tests/data/repository/1.snapshot.json | 32 ---- tests/data/repository/1.targets.json | 148 ----------------- .../data/repository/2.registry.npmjs.org.json | 23 --- tests/data/repository/2.root.json | 65 -------- tests/data/repository/2.snapshot.json | 32 ---- tests/data/repository/2.targets.json | 135 ---------------- tests/data/repository/registry.npmjs.org.json | 23 --- tests/data/repository/root.json | 65 -------- tests/data/repository/snapshot.json | 32 ---- tests/data/repository/targets.json | 135 ---------------- ...67072b6f89ddf1032273a78b.trusted_root.json | 86 ---------- ...40ca812d8ac47a128bf84963.trusted_root.json | 86 ---------- ...8f8df61bc7274189122c123446248426.keys.json | 26 --- ...2551fcaa870a30d4601ba1caf6f63699.keys.json | 26 --- tests/data/repository/timestamp.json | 24 --- 18 files changed, 143 insertions(+), 1034 deletions(-) delete mode 100644 tests/data/repository/1.registry.npmjs.org.json delete mode 100644 tests/data/repository/1.root.json delete mode 100644 tests/data/repository/1.snapshot.json delete mode 100644 tests/data/repository/1.targets.json delete mode 100644 tests/data/repository/2.registry.npmjs.org.json delete mode 100644 tests/data/repository/2.root.json delete mode 100644 tests/data/repository/2.snapshot.json delete mode 100644 tests/data/repository/2.targets.json delete mode 100644 tests/data/repository/registry.npmjs.org.json delete mode 100644 tests/data/repository/root.json delete mode 100644 tests/data/repository/snapshot.json delete mode 100644 tests/data/repository/targets.json delete mode 100644 tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json delete mode 100644 tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json delete mode 100644 tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json delete mode 100644 tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json delete mode 100644 tests/data/repository/timestamp.json diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index b4a27d1712..81954a9891 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -61,8 +61,21 @@ pub struct SigstoreTrustRoot { } impl SigstoreTrustRoot { - /// Constructs a new trust repository established by a [tough::Repository]. - pub async fn new(checkout_dir: Option) -> Result { + /// Constructs a new trust root from a [`tough::Repository`]. + async fn from_tough( + repository: &tough::Repository, + checkout_dir: Option, + ) -> Result { + let trusted_root = { + let data = Self::fetch_target(&repository, &checkout_dir, "trusted_root.json").await?; + serde_json::from_slice(&data[..])? + }; + + Ok(Self { trusted_root }) + } + + /// Constructs a new trust root backed by the Sigstore Public Good Instance. + pub async fn new(cache_dir: Option) -> Result { // These are statically defined and should always parse correctly. let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; @@ -77,12 +90,7 @@ impl SigstoreTrustRoot { .await .map_err(Box::new)?; - let trusted_root = { - let data = Self::fetch_target(&repository, &checkout_dir, "trusted_root.json").await?; - serde_json::from_slice(&data[..])? - }; - - Ok(Self { trusted_root }) + Self::from_tough(&repository, cache_dir).await } async fn fetch_target( @@ -242,3 +250,130 @@ fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { (_, Some(end)) => now <= end, } } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::path::Path; + use std::time::SystemTime; + use tempfile::TempDir; + + fn verify(root: &SigstoreTrustRoot, cache_dir: Option<&Path>) { + if let Some(cache_dir) = cache_dir { + assert!( + cache_dir.join("trusted_root.json").exists(), + "the trusted root was not cached" + ); + } + + assert!( + root.fulcio_certs().is_ok_and(|v| !v.is_empty()), + "no Fulcio certs established" + ); + assert!( + root.rekor_keys().is_ok_and(|v| !v.is_empty()), + "no Rekor keys established" + ); + assert!( + root.ctfe_keys().is_ok_and(|v| !v.is_empty()), + "no CTFE keys established" + ); + } + + macro_rules! impl_test { + ($name:ident, cache=$cache:literal $(,setup=$setup:expr)? $(,verify=$verify:expr)?) => { + #[test] + fn $name() -> Result<()> { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + + let cache_dir = if $cache { + let tmp = TempDir::new().expect("cannot create temp cache dir"); + Some(tmp.into_path()) + } else { + None + }; + let cache_dir_borrowed = cache_dir.as_ref().map(|p| p.as_path()); + + $( + $setup(cache_dir_borrowed); + )? + + let root = rt + .block_on(SigstoreTrustRoot::new(cache_dir.clone())) + .expect("failed to create trust root"); + + verify(&root, cache_dir_borrowed); + + $($verify(&root, cache_dir_borrowed);)? + + Ok(()) + } + } + } + + impl_test!(no_local_cache, cache = false); + impl_test!( + current_local_cache, + cache = true, + verify = |_, cache_dir: Option<&Path>| { + assert!( + cache_dir.unwrap().join("trusted_root.json").exists(), + "TUF cache was requested but not used" + ); + } + ); + impl_test!( + outdated_local_cache, + cache = true, + setup = |cache_dir: Option<&Path>| { + fs::write( + cache_dir.unwrap().join("trusted_root.json"), + b"fake trusted root", + ) + .expect("cannot write file to cache dir"); + }, + verify = |_, cache_dir: Option<&Path>| { + let data = fs::read(cache_dir.unwrap().join("trusted_root.json")) + .expect("cannot read trusted_root.json from cache"); + + assert_ne!( + data, b"fake trusted root", + "TUF cache was not properly updated" + ); + } + ); + + #[test] + fn test_is_timerange_valid() { + fn range_from(start: i64, end: i64) -> TimeRange { + let base = chrono::Utc::now(); + let start: SystemTime = (base + chrono::TimeDelta::seconds(start)).into(); + let end: SystemTime = (base + chrono::TimeDelta::seconds(end)).into(); + + TimeRange { + start: Some(start.into()), + end: Some(end.into()), + } + } + + assert!(is_timerange_valid(None, true)); + assert!(is_timerange_valid(None, false)); + + // Test lower bound conditions + + // Valid: 1 ago, 1 from now + assert!(is_timerange_valid(Some(&range_from(-1, 1)), false)); + // Invalid: 1 from now, 1 from now + assert!(!is_timerange_valid(Some(&range_from(1, 1)), false)); + + // Test upper bound conditions + + // Invalid: 1 ago, 1 ago + assert!(!is_timerange_valid(Some(&range_from(-1, -1)), false)); + // Valid: 1 ago, 1 ago + assert!(is_timerange_valid(Some(&range_from(-1, -1)), true)) + } +} diff --git a/tests/data/repository/1.registry.npmjs.org.json b/tests/data/repository/1.registry.npmjs.org.json deleted file mode 100644 index 1c8ec2165b..0000000000 --- a/tests/data/repository/1.registry.npmjs.org.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 1, - "expires": "2024-09-29T16:47:20Z", - "targets": { - "registry.npmjs.org/keys.json": { - "length": 1017, - "hashes": { - "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", - "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" - } - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3044022059bf01a64dd2793d5b630e26d7b6e455b0d6d8b47c23049ae856a122e5cec2ab022068b99b8bb39457e53d500f698cb43f9e640958ed26e5d3a47c29619df61889bc" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/1.root.json b/tests/data/repository/1.root.json deleted file mode 100644 index 4ca0da87fe..0000000000 --- a/tests/data/repository/1.root.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "signed": { - "_type": "root", - "spec_version": "1.0", - "version": 1, - "expires": "2024-09-29T16:47:17Z", - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - }, - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": { - "root": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "snapshot": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - }, - "targets": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "timestamp": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - } - }, - "consistent_snapshot": true - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "304602210085927cdb96e1d9d0876bfc26b6ceea7421a54f959e30b9af3e12d31f6c750543022100dde611b58a1f2b9fb26c43767138c68f4422cdeb898c8b63f3f0193791030d12" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/1.snapshot.json b/tests/data/repository/1.snapshot.json deleted file mode 100644 index fcb179878c..0000000000 --- a/tests/data/repository/1.snapshot.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "signed": { - "_type": "snapshot", - "spec_version": "1.0", - "version": 1, - "expires": "2024-04-19T16:47:48Z", - "meta": { - "registry.npmjs.org.json": { - "length": 713, - "hashes": { - "sha256": "17b361687dbb401c2d51d7ce21688d13547eae7f8e7b2183b7dd6d94fa675705", - "sha512": "3f60a08cdbab650ece48ded43b54943dc816580fdb2f5a2a20c30e878eb2489ab817f0308666cac80da03d75d6f5b71959431b1ba7794335fece8a4ed635eb4d" - }, - "version": 1 - }, - "targets.json": { - "length": 4518, - "hashes": { - "sha256": "cc62e5fb1644717c7429c82b6a1cbd085008f9a2e07aad38573f8fdf9d55386c", - "sha512": "5709bc76bc35da403a9a0a5ec96890db49e797c986eda9e5f7973938dbccad96838c8136617c91f5218cfd919d93745d3942ca6d50a52b5fd0e662e6876b395f" - }, - "version": 1 - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "304602210082d244d5dab0c20ee07b3229964beffaa8bb0bdf4c5107e2f764619878d124a2022100e7c50116ef636c41348ec49a7502f1c98037238b9c717ee781b62c5154f5a1f0" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/1.targets.json b/tests/data/repository/1.targets.json deleted file mode 100644 index 6844bad771..0000000000 --- a/tests/data/repository/1.targets.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 1, - "expires": "2024-09-29T16:47:20Z", - "targets": { - "artifact.pub": { - "length": 177, - "hashes": { - "sha256": "59ebf97a9850aecec4bc39c1f5c1dc46e6490a6b5fd2a6cacdcac0c3a6fc4cbf", - "sha512": "308fd1d1d95d7f80aa33b837795251cc3e886792982275e062409e13e4e236ffc34d676682aa96fdc751414de99c864bf132dde71581fa651c6343905e3bf988" - }, - "custom": { - "sigstore": { - "status": "Active", - "usage": "Unknown" - } - } - }, - "ctfe.pub": { - "length": 177, - "hashes": { - "sha256": "7fcb94a5d0ed541260473b990b99a6c39864c1fb16f3f3e594a5a3cebbfe138a", - "sha512": "4b20747d1afe2544238ad38cc0cc3010921b177d60ac743767e0ef675b915489bd01a36606c0ff83c06448622d7160f0d866c83d20f0c0f44653dcc3f9aa0bd4" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstore.dev/test", - "usage": "CTFE" - } - } - }, - "ctfe_2022.pub": { - "length": 178, - "hashes": { - "sha256": "270488a309d22e804eeb245493e87c667658d749006b9fee9cc614572d4fbbdc", - "sha512": "e83fa4f427b24ee7728637fad1b4aa45ebde2ba02751fa860694b1bb16059a490328f9985e51cc70e4d237545315a1bc866dc4fdeef2f6248d99cc7a6077bf85" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstore.dev/2022", - "usage": "CTFE" - } - } - }, - "fulcio.crt.pem": { - "length": 744, - "hashes": { - "sha256": "f360c53b2e13495a628b9b8096455badcb6d375b185c4816d95a5d746ff29908", - "sha512": "0713252a7fd17f7f3ab12f88a64accf2eb14b8ad40ca711d7fe8b4ecba3b24db9e9dffadb997b196d3867b8f9ff217faf930d80e4dab4e235c7fc3f07be69224" - }, - "custom": { - "sigstore": { - "status": "Expired", - "uri": "https://fulcio.sigstore.dev", - "usage": "Fulcio" - } - } - }, - "fulcio_intermediate_v1.crt.pem": { - "length": 789, - "hashes": { - "sha256": "f8cbecf186db7714624a5f4e99da31a917cbef70a94dd6921f5c3ca969dfe30a", - "sha512": "0f99f47dbc26c5f1e3cba0bfd9af4245a26e5cb735d6ef005792ec7e603f66fdb897de985973a6e50940ca7eff5e1849719e967b5ad2dac74a29115a41cf6f21" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstore.dev", - "usage": "Fulcio" - } - } - }, - "fulcio_v1.crt.pem": { - "length": 740, - "hashes": { - "sha256": "f989aa23def87c549404eadba767768d2a3c8d6d30a8b793f9f518a8eafd2cf5", - "sha512": "f2e33a6dc208cee1f51d33bbea675ab0f0ced269617497985f9a0680689ee7073e4b6f8fef64c91bda590d30c129b3070dddce824c05bc165ac9802f0705cab6" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstore.dev", - "usage": "Fulcio" - } - } - }, - "rekor.pub": { - "length": 178, - "hashes": { - "sha256": "dce5ef715502ec9f3cdfd11f8cc384b31a6141023d3e7595e9908a81cb6241bd", - "sha512": "0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://rekor.sigstore.dev", - "usage": "Rekor" - } - } - }, - "trusted_root.json": { - "length": 4567, - "hashes": { - "sha256": "cec894ad77f79b1cb324150f6363012bcef7492954f3ab9134f932e6aa2e2e20", - "sha512": "08be2fd75c19e654caad30852847c566f97e6245f2bbcc54d347d6bdec7e879135e3395b5633b9e3b85d739fdb9b4eb8c09ddc70495792bc2ea65c8caf770d27" - } - } - }, - "delegations": { - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": [ - { - "name": "registry.npmjs.org", - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1, - "terminating": true, - "paths": [ - "registry.npmjs.org/*" - ] - } - ] - } - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "304402201662b260e99e59f7271bd9e3fb01aa47a399bef8c5ec808bea6d40ae2d93625d022042fd2a275d84196dc50e17ca9c9408a34349372410febc7217415b11eb978bbb" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/2.registry.npmjs.org.json b/tests/data/repository/2.registry.npmjs.org.json deleted file mode 100644 index d53f15267b..0000000000 --- a/tests/data/repository/2.registry.npmjs.org.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:55Z", - "targets": { - "registry.npmjs.org/keys.json": { - "length": 1017, - "hashes": { - "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", - "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" - } - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3045022057b9fc8afd9feaf45cf3173d3420fdcd6b68c22e4ef7b47e80a6887e1f20246c0221009f39c42fac630ab354c5197288c9a82ab6d46a59b423f81fff719da57cff16ab" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/2.root.json b/tests/data/repository/2.root.json deleted file mode 100644 index f848d7d846..0000000000 --- a/tests/data/repository/2.root.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "signed": { - "_type": "root", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:11Z", - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - }, - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": { - "root": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "snapshot": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - }, - "targets": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "timestamp": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - } - }, - "consistent_snapshot": true - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "3045022057bbd23dd9f69f8280c5e5d2b0a0b1ace98d6d8efa0f59ef0a3190188f6e2c89022100b39e6c24091c4271d2b8b4cfa75e6120638b276fbffddda8da5bca1778c8f08c" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/2.snapshot.json b/tests/data/repository/2.snapshot.json deleted file mode 100644 index 6c1e4dd147..0000000000 --- a/tests/data/repository/2.snapshot.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "signed": { - "_type": "snapshot", - "spec_version": "1.0", - "version": 2, - "expires": "2028-04-19T21:11:16Z", - "meta": { - "registry.npmjs.org.json": { - "length": 715, - "hashes": { - "sha256": "4dc55b2b468b0d1c9629c457c5cfce2cc1c330c59c5a7cf71cb7549f1ef76f1d", - "sha512": "278f4b6112db9d4bd9366e1717cf710ad7eacf44605fd4f894c3374fc5dff850a1a03c24c4a885d050a4ac1a86fa6929537fae12d8c2864c8e0c239b382d5556" - }, - "version": 2 - }, - "targets.json": { - "length": 4120, - "hashes": { - "sha256": "095d093de09350cec021828f49361688b5dd692486ad7bfb03d4150b3269ef8a", - "sha512": "97b9c75f49fb41eaf2f33c5f58b125febc3bbecd4c97f6edd0901423a231e4d0c5760d4780bc180a364d7198b5e0710f07ee0abf84dcd163fe3348d6bce26fab" - }, - "version": 2 - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3044022013bf1032cf0a37d9f88ab9d33d0abb7a932efd95fadbc354fc21a807c7be29ef0220677e651c3e67e0728591faa20c5a09b8a16953038c3ceeffb7d9cfec766b3245" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/2.targets.json b/tests/data/repository/2.targets.json deleted file mode 100644 index dea42487f0..0000000000 --- a/tests/data/repository/2.targets.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:55Z", - "targets": { - "ctfe.pub": { - "length": 775, - "hashes": { - "sha256": "bd7a6812a1f239dfddbbb19d36c7423d21510da56d466ba5018401959cd66037", - "sha512": "b861189e48df51186a39612230fba6b02af951f7b35ad9375e8ca182d0e085d470e26d69f7cd4d7450a0f223991e8e5a4ddf8f1968caa15255de8e37035af43a" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/test", - "usage": "CTFE" - } - } - }, - "ctfe_2022.pub": { - "length": 178, - "hashes": { - "sha256": "910d899c7763563095a0fe684c8477573fedc19a78586de6ecfbfd8f289f5423", - "sha512": "ab975a75600fc366a837536d0dcba841b755552d21bb114498ff8ac9d2403f76643f5b91269bce5d124a365514719a3edee9dcc2b046cb173f51af659911fcd3" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/2022", - "usage": "CTFE" - } - } - }, - "ctfe_2022_2.pub": { - "length": 178, - "hashes": { - "sha256": "7054b4f15f969daca1c242bb9e77527abaf0b9acf9818a2a35144e4b32b20dc6", - "sha512": "3d035f94e1b14ac84627a28afdbed9a34861fb84239f76d73aa1a99f52262bfd95c4fa0ee71f1fd7e3bfb998d89cd5e0f0eafcff9fa7fa87c6e23484fc1e0cec" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/2022-2", - "usage": "CTFE" - } - } - }, - "fulcio.crt.pem": { - "length": 741, - "hashes": { - "sha256": "0e6b0442485ad552bea5f62f11c29e2acfda35307d7538430b4cc1dbef49bff1", - "sha512": "c69ae618883a0c89c282c0943a1ad0c16b0a7788f74e47a1adefc631dac48a0c4449d8c3de7455ae7d772e43c4a87e341f180b0614a46a86006969f8a7b84532" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstage.dev", - "usage": "Fulcio" - } - } - }, - "fulcio_intermediate.crt.pem": { - "length": 790, - "hashes": { - "sha256": "782868913fe13c385105ddf33e827191386f58da40a931f2075a7e27b1b6ac7b", - "sha512": "90659875a02f73d1026055427c6d857c556e410e23748ff88aeb493227610fd2f5fbdd95ef2a21565f91438dfb3e073f50c4c9dd06f9a601b5d9b064d5cb60b4" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstage.dev", - "usage": "Fulcio" - } - } - }, - "rekor.pub": { - "length": 178, - "hashes": { - "sha256": "1d80b8f72505a43e65e6e125247cd508f61b459dc457c1d1bcb78d96e1760959", - "sha512": "09ab08698a67354a95d3b8897d9ce7eaef05f06f5ed5f0202d79c228579858ecc5816b7e1b7cc6786abe7d6aaa758e1fcb05900cb749235186c3bf9522d6d7ce" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://rekor.sigstage.dev", - "usage": "Rekor" - } - } - }, - "trusted_root.json": { - "length": 4521, - "hashes": { - "sha256": "6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b", - "sha512": "fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963" - } - } - }, - "delegations": { - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": [ - { - "name": "registry.npmjs.org", - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1, - "terminating": true, - "paths": [ - "registry.npmjs.org/*" - ] - } - ] - } - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "304502210090b089087d1b17b2517c464b7774d76d3ea558ffca874eed63ccbee8f6bc3b76022022b56f551bcd0ac8a9c35cd0724ac5b00b4984544cbf812f47f276a9b48db8db" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/registry.npmjs.org.json b/tests/data/repository/registry.npmjs.org.json deleted file mode 100644 index d53f15267b..0000000000 --- a/tests/data/repository/registry.npmjs.org.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:55Z", - "targets": { - "registry.npmjs.org/keys.json": { - "length": 1017, - "hashes": { - "sha256": "7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426", - "sha512": "881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699" - } - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3045022057b9fc8afd9feaf45cf3173d3420fdcd6b68c22e4ef7b47e80a6887e1f20246c0221009f39c42fac630ab354c5197288c9a82ab6d46a59b423f81fff719da57cff16ab" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/root.json b/tests/data/repository/root.json deleted file mode 100644 index f848d7d846..0000000000 --- a/tests/data/repository/root.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "signed": { - "_type": "root", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:11Z", - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - }, - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": { - "root": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "snapshot": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - }, - "targets": { - "keyids": [ - "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" - ], - "threshold": 1 - }, - "timestamp": { - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1 - } - }, - "consistent_snapshot": true - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "3045022057bbd23dd9f69f8280c5e5d2b0a0b1ace98d6d8efa0f59ef0a3190188f6e2c89022100b39e6c24091c4271d2b8b4cfa75e6120638b276fbffddda8da5bca1778c8f08c" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/snapshot.json b/tests/data/repository/snapshot.json deleted file mode 100644 index 6c1e4dd147..0000000000 --- a/tests/data/repository/snapshot.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "signed": { - "_type": "snapshot", - "spec_version": "1.0", - "version": 2, - "expires": "2028-04-19T21:11:16Z", - "meta": { - "registry.npmjs.org.json": { - "length": 715, - "hashes": { - "sha256": "4dc55b2b468b0d1c9629c457c5cfce2cc1c330c59c5a7cf71cb7549f1ef76f1d", - "sha512": "278f4b6112db9d4bd9366e1717cf710ad7eacf44605fd4f894c3374fc5dff850a1a03c24c4a885d050a4ac1a86fa6929537fae12d8c2864c8e0c239b382d5556" - }, - "version": 2 - }, - "targets.json": { - "length": 4120, - "hashes": { - "sha256": "095d093de09350cec021828f49361688b5dd692486ad7bfb03d4150b3269ef8a", - "sha512": "97b9c75f49fb41eaf2f33c5f58b125febc3bbecd4c97f6edd0901423a231e4d0c5760d4780bc180a364d7198b5e0710f07ee0abf84dcd163fe3348d6bce26fab" - }, - "version": 2 - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3044022013bf1032cf0a37d9f88ab9d33d0abb7a932efd95fadbc354fc21a807c7be29ef0220677e651c3e67e0728591faa20c5a09b8a16953038c3ceeffb7d9cfec766b3245" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/targets.json b/tests/data/repository/targets.json deleted file mode 100644 index dea42487f0..0000000000 --- a/tests/data/repository/targets.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "signed": { - "_type": "targets", - "spec_version": "1.0", - "version": 2, - "expires": "2028-09-29T21:10:55Z", - "targets": { - "ctfe.pub": { - "length": 775, - "hashes": { - "sha256": "bd7a6812a1f239dfddbbb19d36c7423d21510da56d466ba5018401959cd66037", - "sha512": "b861189e48df51186a39612230fba6b02af951f7b35ad9375e8ca182d0e085d470e26d69f7cd4d7450a0f223991e8e5a4ddf8f1968caa15255de8e37035af43a" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/test", - "usage": "CTFE" - } - } - }, - "ctfe_2022.pub": { - "length": 178, - "hashes": { - "sha256": "910d899c7763563095a0fe684c8477573fedc19a78586de6ecfbfd8f289f5423", - "sha512": "ab975a75600fc366a837536d0dcba841b755552d21bb114498ff8ac9d2403f76643f5b91269bce5d124a365514719a3edee9dcc2b046cb173f51af659911fcd3" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/2022", - "usage": "CTFE" - } - } - }, - "ctfe_2022_2.pub": { - "length": 178, - "hashes": { - "sha256": "7054b4f15f969daca1c242bb9e77527abaf0b9acf9818a2a35144e4b32b20dc6", - "sha512": "3d035f94e1b14ac84627a28afdbed9a34861fb84239f76d73aa1a99f52262bfd95c4fa0ee71f1fd7e3bfb998d89cd5e0f0eafcff9fa7fa87c6e23484fc1e0cec" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://ctfe.sigstage.dev/2022-2", - "usage": "CTFE" - } - } - }, - "fulcio.crt.pem": { - "length": 741, - "hashes": { - "sha256": "0e6b0442485ad552bea5f62f11c29e2acfda35307d7538430b4cc1dbef49bff1", - "sha512": "c69ae618883a0c89c282c0943a1ad0c16b0a7788f74e47a1adefc631dac48a0c4449d8c3de7455ae7d772e43c4a87e341f180b0614a46a86006969f8a7b84532" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstage.dev", - "usage": "Fulcio" - } - } - }, - "fulcio_intermediate.crt.pem": { - "length": 790, - "hashes": { - "sha256": "782868913fe13c385105ddf33e827191386f58da40a931f2075a7e27b1b6ac7b", - "sha512": "90659875a02f73d1026055427c6d857c556e410e23748ff88aeb493227610fd2f5fbdd95ef2a21565f91438dfb3e073f50c4c9dd06f9a601b5d9b064d5cb60b4" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://fulcio.sigstage.dev", - "usage": "Fulcio" - } - } - }, - "rekor.pub": { - "length": 178, - "hashes": { - "sha256": "1d80b8f72505a43e65e6e125247cd508f61b459dc457c1d1bcb78d96e1760959", - "sha512": "09ab08698a67354a95d3b8897d9ce7eaef05f06f5ed5f0202d79c228579858ecc5816b7e1b7cc6786abe7d6aaa758e1fcb05900cb749235186c3bf9522d6d7ce" - }, - "custom": { - "sigstore": { - "status": "Active", - "uri": "https://rekor.sigstage.dev", - "usage": "Rekor" - } - } - }, - "trusted_root.json": { - "length": 4521, - "hashes": { - "sha256": "6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b", - "sha512": "fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963" - } - } - }, - "delegations": { - "keys": { - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { - "keytype": "ecdsa-sha2-nistp256", - "scheme": "ecdsa-sha2-nistp256", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keyval": { - "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" - } - } - }, - "roles": [ - { - "name": "registry.npmjs.org", - "keyids": [ - "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" - ], - "threshold": 1, - "terminating": true, - "paths": [ - "registry.npmjs.org/*" - ] - } - ] - } - }, - "signatures": [ - { - "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", - "sig": "304502210090b089087d1b17b2517c464b7774d76d3ea558ffca874eed63ccbee8f6bc3b76022022b56f551bcd0ac8a9c35cd0724ac5b00b4984544cbf812f47f276a9b48db8db" - } - ] -} \ No newline at end of file diff --git a/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json b/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json deleted file mode 100644 index 6a1c1f5a40..0000000000 --- a/tests/data/repository/targets/6494317303d0e04509a30b239bf8290057164fba67072b6f89ddf1032273a78b.trusted_root.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", - "tlogs": [ - { - "baseUrl": "https://rekor.sigstage.dev", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDODRU688UYGuy54mNUlaEBiQdTE9nYLr0lg6RXowI/QV/RE1azBn4Eg5/2uTOMbhB1/gfcHzijzFi9Tk+g1Prg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2021-01-12T11:53:27.000Z" - } - }, - "logId": { - "keyId": "0y8wo8MtY5wrdiIFohx7sHeI5oKDpK5vQhGHI6G+pJY=" - } - } - ], - "certificateAuthorities": [ - { - "subject": { - "organization": "sigstore.dev", - "commonName": "sigstore" - }, - "uri": "https://fulcio.sigstage.dev", - "certChain": { - "certificates": [ - { - "rawBytes": "MIIB9jCCAXugAwIBAgITDdEJvluliE0AzYaIE4jTMdnFTzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDMyNTE2NTA0NloXDTMyMDMyMjE2NTA0NVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMo9BUNk9QIYisYysC24+2OytoV72YiLonYcqR3yeVnYziPt7Xv++CYE8yoCTiwedUECCWKOcvQKRCJZb9ht4Hzy+VvBx36hK+C6sECCSR0x6pPSiz+cTk1f788ZjBlUZaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9CMrpofas6cK/cDNQa4j6Hj2ZlMB8GA1UdIwQYMBaAFP9CMrpofas6cK/cDNQa4j6Hj2ZlMAoGCCqGSM49BAMDA2kAMGYCMQD+kojuzMwztNay9Ibzjuk//ZL5m6T2OCsm45l1lY004pcb984L926BowodoirFMcMCMQDIJtFHhP/1D3a+M3dAGomOb6O4CmTry3TTPbPsAFnv22YA0Y+P21NVoxKDjdu0tkw=" - }, - { - "rawBytes": "MIICGTCCAaCgAwIBAgITJta/okfgHvjabGm1BOzuhrwA1TAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDQxNDIxMzg0MFoXDTMyMDMyMjE2NTA0NVowNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASosAySWJQ/tK5r8T5aHqavk0oI+BKQbnLLdmOMRXHQF/4Hx9KtNfpcdjH9hNKQSBxSlLFFN3tvFCco0qFBzWYwZtsYsBe1l91qYn/9VHFTaEVwYQWIJEEvrs0fvPuAqjajezB5MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRxhjCmFHxib/n31vQFGn9f/+tvrDAfBgNVHSMEGDAWgBT/QjK6aH2rOnCv3AzUGuI+h49mZTAKBggqhkjOPQQDAwNnADBkAjAM1lbKkcqQlE/UspMTbWNo1y2TaJ44tx3l/FJFceTSdDZ+0W1OHHeU4twie/lq8XgCMHQxgEv26xNNiAGyPXbkYgrDPvbOqp0UeWX4mJnLSrBr3aN/KX1SBrKQu220FmVL0Q==" - } - ] - }, - "validFor": { - "start": "2022-03-25T16:50:46.000Z" - } - } - ], - "ctlogs": [ - { - "baseUrl": "https://ctfe.sigstage.dev/test", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MIICCgKCAgEA27A2MPQXm0I0v7/Ly5BIauDjRZF5Jor9vU+QheoE2UIIsZHcyYq3slHzSSHy2lLj1ZD2d91CtJ492ZXqnBmsr4TwZ9jQ05tW2mGIRI8u2DqN8LpuNYZGz/f9SZrjhQQmUttqWmtu3UoLfKz6NbNXUnoo+NhZFcFRLXJ8VporVhuiAmL7zqT53cXR3yQfFPCUDeGnRksnlhVIAJc3AHZZSHQJ8DEXMhh35TVv2nYhTI3rID7GwjXXw4ocz7RGDD37ky6p39Tl5NB71gT1eSqhZhGHEYHIPXraEBd5+3w9qIuLWlp5Ej/K6Mu4ELioXKCUimCbwy+Cs8UhHFlqcyg4AysOHJwIadXIa8LsY51jnVSGrGOEBZevopmQPNPtyfFY3dmXSS+6Z3RD2Gd6oDnNGJzpSyEk410Ag5uvNDfYzJLCWX9tU8lIxNwdFYmIwpd89HijyRyoGnoJ3entd63cvKfuuix5r+GHyKp1Xm1L5j5AWM6P+z0xigwkiXnt+adexAl1J9wdDxv/pUFEESRF4DG8DFGVtbdH6aR1A5/vD4krO4tC1QYUSeyL5Mvsw8WRqIFHcXtgybtxylljvNcGMV1KXQC8UFDmpGZVDSHx6v3e/BHMrZ7gjoCCfVMZ/cFcQi0W2AIHPYEMH/C95J2r4XbHMRdYXpovpOoT5Ca78gsCAwEAAQ==", - "keyDetails": "PKCS1_RSA_PKCS1V5", - "validFor": { - "start": "2021-03-14T00:00:00.000Z" - } - }, - "logId": { - "keyId": "s9AOb93xWxr+a4ztxJnxxJCX7VZ0V3IF4jTu/OoL84A=" - } - }, - { - "baseUrl": "https://ctfe.sigstage.dev/2022", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh99xuRi6slBFd8VUJoK/rLigy4bYeSYWO/fE6Br7r0D8NpMI94+A63LR/WvLxpUUGBpY8IJA3iU2telag5CRpA==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-07-01T00:00:00.000Z" - } - }, - "logId": { - "keyId": "++JKOMQt7SJ3ynUHnCfnDhcKP8/58J4TueMqXuk3HmA=" - } - }, - { - "baseUrl": "https://ctfe.sigstage.dev/2022-2", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8gEDKNme8AnXuPBgHjrtXdS6miHqc24CRblNEOFpiJRngeq8Ko73Y+K18yRYVf1DXD4AVLwvKyzdNdl5n0jUSQ==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-07-01T00:00:00.000Z" - } - }, - "logId": { - "keyId": "KzC83GiIyeLh2CYpXnQfSDkxlgLynDPLXkNA/rKshno=" - } - } - ], - "timestampAuthorities": [] -} diff --git a/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json b/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json deleted file mode 100644 index 6a1c1f5a40..0000000000 --- a/tests/data/repository/targets/fa2ca05656176f993fd616fa8586f3deeaacfb891dfb6f58e02b26073cb0233a52b7e66338d0053c8549f551485581141094c2de40ca812d8ac47a128bf84963.trusted_root.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", - "tlogs": [ - { - "baseUrl": "https://rekor.sigstage.dev", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDODRU688UYGuy54mNUlaEBiQdTE9nYLr0lg6RXowI/QV/RE1azBn4Eg5/2uTOMbhB1/gfcHzijzFi9Tk+g1Prg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2021-01-12T11:53:27.000Z" - } - }, - "logId": { - "keyId": "0y8wo8MtY5wrdiIFohx7sHeI5oKDpK5vQhGHI6G+pJY=" - } - } - ], - "certificateAuthorities": [ - { - "subject": { - "organization": "sigstore.dev", - "commonName": "sigstore" - }, - "uri": "https://fulcio.sigstage.dev", - "certChain": { - "certificates": [ - { - "rawBytes": "MIIB9jCCAXugAwIBAgITDdEJvluliE0AzYaIE4jTMdnFTzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDMyNTE2NTA0NloXDTMyMDMyMjE2NTA0NVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMo9BUNk9QIYisYysC24+2OytoV72YiLonYcqR3yeVnYziPt7Xv++CYE8yoCTiwedUECCWKOcvQKRCJZb9ht4Hzy+VvBx36hK+C6sECCSR0x6pPSiz+cTk1f788ZjBlUZaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP9CMrpofas6cK/cDNQa4j6Hj2ZlMB8GA1UdIwQYMBaAFP9CMrpofas6cK/cDNQa4j6Hj2ZlMAoGCCqGSM49BAMDA2kAMGYCMQD+kojuzMwztNay9Ibzjuk//ZL5m6T2OCsm45l1lY004pcb984L926BowodoirFMcMCMQDIJtFHhP/1D3a+M3dAGomOb6O4CmTry3TTPbPsAFnv22YA0Y+P21NVoxKDjdu0tkw=" - }, - { - "rawBytes": "MIICGTCCAaCgAwIBAgITJta/okfgHvjabGm1BOzuhrwA1TAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIyMDQxNDIxMzg0MFoXDTMyMDMyMjE2NTA0NVowNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASosAySWJQ/tK5r8T5aHqavk0oI+BKQbnLLdmOMRXHQF/4Hx9KtNfpcdjH9hNKQSBxSlLFFN3tvFCco0qFBzWYwZtsYsBe1l91qYn/9VHFTaEVwYQWIJEEvrs0fvPuAqjajezB5MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRxhjCmFHxib/n31vQFGn9f/+tvrDAfBgNVHSMEGDAWgBT/QjK6aH2rOnCv3AzUGuI+h49mZTAKBggqhkjOPQQDAwNnADBkAjAM1lbKkcqQlE/UspMTbWNo1y2TaJ44tx3l/FJFceTSdDZ+0W1OHHeU4twie/lq8XgCMHQxgEv26xNNiAGyPXbkYgrDPvbOqp0UeWX4mJnLSrBr3aN/KX1SBrKQu220FmVL0Q==" - } - ] - }, - "validFor": { - "start": "2022-03-25T16:50:46.000Z" - } - } - ], - "ctlogs": [ - { - "baseUrl": "https://ctfe.sigstage.dev/test", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MIICCgKCAgEA27A2MPQXm0I0v7/Ly5BIauDjRZF5Jor9vU+QheoE2UIIsZHcyYq3slHzSSHy2lLj1ZD2d91CtJ492ZXqnBmsr4TwZ9jQ05tW2mGIRI8u2DqN8LpuNYZGz/f9SZrjhQQmUttqWmtu3UoLfKz6NbNXUnoo+NhZFcFRLXJ8VporVhuiAmL7zqT53cXR3yQfFPCUDeGnRksnlhVIAJc3AHZZSHQJ8DEXMhh35TVv2nYhTI3rID7GwjXXw4ocz7RGDD37ky6p39Tl5NB71gT1eSqhZhGHEYHIPXraEBd5+3w9qIuLWlp5Ej/K6Mu4ELioXKCUimCbwy+Cs8UhHFlqcyg4AysOHJwIadXIa8LsY51jnVSGrGOEBZevopmQPNPtyfFY3dmXSS+6Z3RD2Gd6oDnNGJzpSyEk410Ag5uvNDfYzJLCWX9tU8lIxNwdFYmIwpd89HijyRyoGnoJ3entd63cvKfuuix5r+GHyKp1Xm1L5j5AWM6P+z0xigwkiXnt+adexAl1J9wdDxv/pUFEESRF4DG8DFGVtbdH6aR1A5/vD4krO4tC1QYUSeyL5Mvsw8WRqIFHcXtgybtxylljvNcGMV1KXQC8UFDmpGZVDSHx6v3e/BHMrZ7gjoCCfVMZ/cFcQi0W2AIHPYEMH/C95J2r4XbHMRdYXpovpOoT5Ca78gsCAwEAAQ==", - "keyDetails": "PKCS1_RSA_PKCS1V5", - "validFor": { - "start": "2021-03-14T00:00:00.000Z" - } - }, - "logId": { - "keyId": "s9AOb93xWxr+a4ztxJnxxJCX7VZ0V3IF4jTu/OoL84A=" - } - }, - { - "baseUrl": "https://ctfe.sigstage.dev/2022", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh99xuRi6slBFd8VUJoK/rLigy4bYeSYWO/fE6Br7r0D8NpMI94+A63LR/WvLxpUUGBpY8IJA3iU2telag5CRpA==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-07-01T00:00:00.000Z" - } - }, - "logId": { - "keyId": "++JKOMQt7SJ3ynUHnCfnDhcKP8/58J4TueMqXuk3HmA=" - } - }, - { - "baseUrl": "https://ctfe.sigstage.dev/2022-2", - "hashAlgorithm": "SHA2_256", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8gEDKNme8AnXuPBgHjrtXdS6miHqc24CRblNEOFpiJRngeq8Ko73Y+K18yRYVf1DXD4AVLwvKyzdNdl5n0jUSQ==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-07-01T00:00:00.000Z" - } - }, - "logId": { - "keyId": "KzC83GiIyeLh2CYpXnQfSDkxlgLynDPLXkNA/rKshno=" - } - } - ], - "timestampAuthorities": [] -} diff --git a/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json b/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json deleted file mode 100644 index f5667a5f0e..0000000000 --- a/tests/data/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "keys": [ - { - "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", - "keyUsage": "npm:signatures", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "1999-01-01T00:00:00.000Z" - } - } - }, - { - "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", - "keyUsage": "npm:attestations", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-12-01T00:00:00.000Z" - } - } - } - ] -} diff --git a/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json b/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json deleted file mode 100644 index f5667a5f0e..0000000000 --- a/tests/data/repository/targets/registry.npmjs.org/881a853ee92d8cf513b07c164fea36b22a7305c256125bdfffdc5c65a4205c4c3fc2b5bcc98964349167ea68d40b8cd02551fcaa870a30d4601ba1caf6f63699.keys.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "keys": [ - { - "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", - "keyUsage": "npm:signatures", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "1999-01-01T00:00:00.000Z" - } - } - }, - { - "keyId": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", - "keyUsage": "npm:attestations", - "publicKey": { - "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==", - "keyDetails": "PKIX_ECDSA_P256_SHA_256", - "validFor": { - "start": "2022-12-01T00:00:00.000Z" - } - } - } - ] -} diff --git a/tests/data/repository/timestamp.json b/tests/data/repository/timestamp.json deleted file mode 100644 index 4b4a4dec2c..0000000000 --- a/tests/data/repository/timestamp.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "signed": { - "_type": "timestamp", - "spec_version": "1.0", - "version": 2, - "expires": "2028-04-12T21:11:28Z", - "meta": { - "snapshot.json": { - "length": 1039, - "hashes": { - "sha256": "b480856ab72c80fe10902ffac69ec10340e827e02b2bd114d6f141de910a96c5", - "sha512": "da06f65c1ee242d63820ba646fb1b4037fe355460309d89f98a923d1d009e7d46f11d4272a0d8e07829734baea655f7692d8c23383d6044b4f72263a4dbf3057" - }, - "version": 2 - } - } - }, - "signatures": [ - { - "keyid": "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600", - "sig": "3044022040e243b1bc8edb798df66803c2460471a4129704421d59f55c825dc549493f840220267e4684875d4803ae0948140af32fc9f560453efb84d9728ee66619e8767d8c" - } - ] -} \ No newline at end of file From 1e737836204e868e25f8ae58a20a00f22c6627cc Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Thu, 11 Apr 2024 18:05:33 -0500 Subject: [PATCH 121/161] `sign`, `verify`: reparent to `bundle`, doc fixups Signed-off-by: Andrew Pan --- src/bundle/mod.rs | 110 ++----------------- src/bundle/models.rs | 107 +++++++++++++++++++ src/{ => bundle}/sign.rs | 114 ++++++++++---------- src/{ => bundle}/verify/mod.rs | 6 +- src/{ => bundle}/verify/models.rs | 2 +- src/{ => bundle}/verify/policy.rs | 2 +- src/{ => bundle}/verify/verifier.rs | 158 ++++++++++++++-------------- src/cosign/client_builder.rs | 2 +- src/fulcio/mod.rs | 2 +- src/lib.rs | 6 -- src/trust/sigstore/mod.rs | 18 +--- tests/conformance/conformance.rs | 6 +- 12 files changed, 265 insertions(+), 268 deletions(-) create mode 100644 src/bundle/models.rs rename src/{ => bundle}/sign.rs (76%) rename src/{ => bundle}/verify/mod.rs (86%) rename src/{ => bundle}/verify/models.rs (99%) rename src/{ => bundle}/verify/policy.rs (99%) rename src/{ => bundle}/verify/verifier.rs (72%) diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index c8c8ea64b0..61ad0c48b3 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -12,112 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Common types for Sigstore Bundle support. +//! Sigstore bundle support. -use std::fmt::Display; -use std::str::FromStr; - -use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; -use json_syntax::Print; pub use sigstore_protobuf_specs::dev::sigstore::bundle::v1::Bundle; -use sigstore_protobuf_specs::dev::sigstore::{ - common::v1::LogId, - rekor::v1::{Checkpoint, InclusionPromise, InclusionProof, KindVersion, TransparencyLogEntry}, -}; - -use crate::rekor::models::{ - log_entry::InclusionProof as RekorInclusionProof, LogEntry as RekorLogEntry, -}; - -// Known Sigstore bundle media types. -#[derive(Clone, Copy, Debug)] -pub enum Version { - Bundle0_1, - Bundle0_2, -} - -impl Display for Version { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match &self { - Version::Bundle0_1 => "application/vnd.dev.sigstore.bundle+json;version=0.1", - Version::Bundle0_2 => "application/vnd.dev.sigstore.bundle+json;version=0.2", - }) - } -} - -impl FromStr for Version { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "application/vnd.dev.sigstore.bundle+json;version=0.1" => Ok(Version::Bundle0_1), - "application/vnd.dev.sigstore.bundle+json;version=0.2" => Ok(Version::Bundle0_2), - _ => Err(()), - } - } -} - -#[inline] -fn decode_hex>(hex: S) -> Result, ()> { - hex::decode(hex.as_ref()).or(Err(())) -} - -impl TryFrom for InclusionProof { - type Error = (); - - fn try_from(value: RekorInclusionProof) -> Result { - let hashes = value - .hashes - .iter() - .map(decode_hex) - .collect::, _>>()?; - - Ok(InclusionProof { - checkpoint: Some(Checkpoint { - envelope: value.checkpoint, - }), - hashes, - log_index: value.log_index, - root_hash: decode_hex(value.root_hash)?, - tree_size: value.tree_size, - }) - } -} -/// Convert log entries returned from Rekor into Sigstore Bundle format entries. -impl TryFrom for TransparencyLogEntry { - type Error = (); +mod models; - fn try_from(value: RekorLogEntry) -> Result { - let canonicalized_body = { - let mut body = json_syntax::to_value(value.body).or(Err(()))?; - body.canonicalize(); - body.compact_print().to_string().into_bytes() - }; - let inclusion_promise = Some(InclusionPromise { - signed_entry_timestamp: base64 - .decode(value.verification.signed_entry_timestamp) - .or(Err(()))?, - }); - let inclusion_proof = value - .verification - .inclusion_proof - .map(|p| p.try_into()) - .transpose()?; +#[cfg(feature = "sign")] +pub mod sign; - Ok(TransparencyLogEntry { - canonicalized_body, - inclusion_promise, - inclusion_proof, - integrated_time: value.integrated_time, - kind_version: Some(KindVersion { - kind: "hashedrekord".to_owned(), - version: "0.0.1".to_owned(), - }), - log_id: Some(LogId { - key_id: decode_hex(value.log_i_d)?, - }), - log_index: value.log_index, - }) - } -} +#[cfg(feature = "verify")] +pub mod verify; diff --git a/src/bundle/models.rs b/src/bundle/models.rs new file mode 100644 index 0000000000..79d27933dc --- /dev/null +++ b/src/bundle/models.rs @@ -0,0 +1,107 @@ +use std::fmt::Display; +use std::str::FromStr; + +use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use json_syntax::Print; + +use sigstore_protobuf_specs::dev::sigstore::{ + common::v1::LogId, + rekor::v1::{Checkpoint, InclusionPromise, InclusionProof, KindVersion, TransparencyLogEntry}, +}; + +use crate::rekor::models::{ + log_entry::InclusionProof as RekorInclusionProof, LogEntry as RekorLogEntry, +}; + +// Known Sigstore bundle media types. +#[derive(Clone, Copy, Debug)] +pub enum Version { + Bundle0_1, + Bundle0_2, +} + +impl Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match &self { + Version::Bundle0_1 => "application/vnd.dev.sigstore.bundle+json;version=0.1", + Version::Bundle0_2 => "application/vnd.dev.sigstore.bundle+json;version=0.2", + }) + } +} + +impl FromStr for Version { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "application/vnd.dev.sigstore.bundle+json;version=0.1" => Ok(Version::Bundle0_1), + "application/vnd.dev.sigstore.bundle+json;version=0.2" => Ok(Version::Bundle0_2), + _ => Err(()), + } + } +} + +#[inline] +fn decode_hex>(hex: S) -> Result, ()> { + hex::decode(hex.as_ref()).or(Err(())) +} + +impl TryFrom for InclusionProof { + type Error = (); + + fn try_from(value: RekorInclusionProof) -> Result { + let hashes = value + .hashes + .iter() + .map(decode_hex) + .collect::, _>>()?; + + Ok(InclusionProof { + checkpoint: Some(Checkpoint { + envelope: value.checkpoint, + }), + hashes, + log_index: value.log_index, + root_hash: decode_hex(value.root_hash)?, + tree_size: value.tree_size, + }) + } +} + +/// Convert log entries returned from Rekor into Sigstore Bundle format entries. +impl TryFrom for TransparencyLogEntry { + type Error = (); + + fn try_from(value: RekorLogEntry) -> Result { + let canonicalized_body = { + let mut body = json_syntax::to_value(value.body).or(Err(()))?; + body.canonicalize(); + body.compact_print().to_string().into_bytes() + }; + let inclusion_promise = Some(InclusionPromise { + signed_entry_timestamp: base64 + .decode(value.verification.signed_entry_timestamp) + .or(Err(()))?, + }); + let inclusion_proof = value + .verification + .inclusion_proof + .map(|p| p.try_into()) + .transpose()?; + + Ok(TransparencyLogEntry { + canonicalized_body, + inclusion_promise, + inclusion_proof, + integrated_time: value.integrated_time, + kind_version: Some(KindVersion { + kind: "hashedrekord".to_owned(), + version: "0.0.1".to_owned(), + }), + log_id: Some(LogId { + key_id: decode_hex(value.log_i_d)?, + }), + log_index: value.log_index, + }) + } +} diff --git a/src/sign.rs b/src/bundle/sign.rs similarity index 76% rename from src/sign.rs rename to src/bundle/sign.rs index 30f75737c7..b60472c568 100644 --- a/src/sign.rs +++ b/src/bundle/sign.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Types for signing artifacts and producing Sigstore Bundles. +//! Types for signing artifacts and producing Sigstore bundles. use std::io::{self, Read}; use std::time::SystemTime; @@ -38,7 +38,7 @@ use x509_cert::attr::{AttributeTypeAndValue, AttributeValue}; use x509_cert::builder::{Builder, RequestBuilder as CertRequestBuilder}; use x509_cert::ext::pkix as x509_ext; -use crate::bundle::Version; +use crate::bundle::models::Version; use crate::errors::{Result as SigstoreResult, SigstoreError}; use crate::fulcio::oauth::OauthTokenProvider; use crate::fulcio::{self, FulcioClient, FULCIO_ROOT}; @@ -50,22 +50,22 @@ use crate::rekor::models::{hashedrekord, proposed_entry::ProposedEntry as Propos /// An asynchronous Sigstore signing session. /// /// Sessions hold a provided user identity and key materials tied to that identity. A single -/// session may be used to sign multiple items. For more information, see [`AsyncSigningSession::sign`](Self::sign). +/// session may be used to sign multiple items. For more information, see [`SigningSession::sign`]. /// -/// This signing session operates asynchronously. To construct a synchronous [SigningSession], -/// use [`SigningContext::signer()`]. -pub struct AsyncSigningSession<'ctx> { +/// This signing session operates asynchronously. To construct a synchronous [`blocking::SigningSession`], +/// use [`SigningContext::blocking_signer()`]. +pub struct SigningSession<'ctx> { context: &'ctx SigningContext, identity_token: IdentityToken, private_key: ecdsa::SigningKey, certs: fulcio::CertificateResponse, } -impl<'ctx> AsyncSigningSession<'ctx> { +impl<'ctx> SigningSession<'ctx> { async fn new( context: &'ctx SigningContext, identity_token: IdentityToken, - ) -> SigstoreResult> { + ) -> SigstoreResult> { let (private_key, certs) = Self::materials(&context.fulcio, &identity_token).await?; Ok(Self { context, @@ -176,7 +176,7 @@ impl<'ctx> AsyncSigningSession<'ctx> { } /// Signs for the input with the session's identity. If the identity is expired, - /// [SigstoreError::ExpiredSigningSession] is returned. + /// [`SigstoreError::ExpiredSigningSession`] is returned. pub async fn sign( &self, input: R, @@ -197,48 +197,52 @@ impl<'ctx> AsyncSigningSession<'ctx> { } } -/// A synchronous Sigstore signing session. -/// -/// Sessions hold a provided user identity and key materials tied to that identity. A single -/// session may be used to sign multiple items. For more information, see [`SigningSession::sign`](Self::sign). -/// -/// This signing session operates synchronously, thus it cannot be used in an asynchronous context. -/// To construct an asynchronous [SigningSession], use [`SigningContext::async_signer()`]. -pub struct SigningSession<'ctx> { - inner: AsyncSigningSession<'ctx>, - rt: tokio::runtime::Runtime, -} +pub mod blocking { + use super::{SigningSession as AsyncSigningSession, *}; -impl<'ctx> SigningSession<'ctx> { - fn new(ctx: &'ctx SigningContext, token: IdentityToken) -> SigstoreResult { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()?; - let inner = rt.block_on(AsyncSigningSession::new(ctx, token))?; - Ok(Self { inner, rt }) - } - - /// Check if the session's identity token or key material is expired. + /// A synchronous Sigstore signing session. /// - /// If the session is expired, it cannot be used for signing operations, and a new session - /// must be created with a fresh identity token. - pub fn is_expired(&self) -> bool { - self.inner.is_expired() + /// Sessions hold a provided user identity and key materials tied to that identity. A single + /// session may be used to sign multiple items. For more information, see [`SigningSession::sign`]. + /// + /// This signing session operates synchronously, thus it cannot be used in an asynchronous context. + /// To construct an asynchronous [`SigningSession`], use [`SigningContext::signer()`]. + pub struct SigningSession<'ctx> { + inner: AsyncSigningSession<'ctx>, + rt: tokio::runtime::Runtime, } - /// Signs for the input with the session's identity. If the identity is expired, - /// [SigstoreError::ExpiredSigningSession] is returned. - pub fn sign(&self, mut input: R) -> SigstoreResult { - let mut hasher = Sha256::new(); - io::copy(&mut input, &mut hasher)?; - self.rt.block_on(self.inner.sign_digest(hasher)) + impl<'ctx> SigningSession<'ctx> { + pub(crate) fn new(ctx: &'ctx SigningContext, token: IdentityToken) -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let inner = rt.block_on(AsyncSigningSession::new(ctx, token))?; + Ok(Self { inner, rt }) + } + + /// Check if the session's identity token or key material is expired. + /// + /// If the session is expired, it cannot be used for signing operations, and a new session + /// must be created with a fresh identity token. + pub fn is_expired(&self) -> bool { + self.inner.is_expired() + } + + /// Signs for the input with the session's identity. If the identity is expired, + /// [`SigstoreError::ExpiredSigningSession`] is returned. + pub fn sign(&self, mut input: R) -> SigstoreResult { + let mut hasher = Sha256::new(); + io::copy(&mut input, &mut hasher)?; + self.rt.block_on(self.inner.sign_digest(hasher)) + } } } /// A Sigstore signing context. /// /// Contexts hold Fulcio (CA) and Rekor (CT) configurations which signing sessions can be -/// constructed against. Use [`SigningContext::production`](Self::production) to create a context against +/// constructed against. Use [`SigningContext::production`] to create a context against /// the public-good Sigstore infrastructure. pub struct SigningContext { fulcio: FulcioClient, @@ -246,7 +250,7 @@ pub struct SigningContext { } impl SigningContext { - /// Manually constructs a [SigningContext] from its constituent data. + /// Manually constructs a [`SigningContext`] from its constituent data. pub fn new(fulcio: FulcioClient, rekor_config: RekorConfiguration) -> Self { Self { fulcio, @@ -254,7 +258,7 @@ impl SigningContext { } } - /// Returns a [SigningContext] configured against the public-good production Sigstore + /// Returns a [`SigningContext`] configured against the public-good production Sigstore /// infrastructure. pub fn production() -> SigstoreResult { Ok(Self::new( @@ -266,19 +270,19 @@ impl SigningContext { )) } - /// Configures and returns an [AsyncSigningSession] with the held context. - pub async fn async_signer( - &self, - identity_token: IdentityToken, - ) -> SigstoreResult { - AsyncSigningSession::new(self, identity_token).await + /// Configures and returns a [`SigningSession`] with the held context. + pub async fn signer(&self, identity_token: IdentityToken) -> SigstoreResult { + SigningSession::new(self, identity_token).await } - /// Configures and returns a [SigningContext] with the held context. + /// Configures and returns a [`blocking::SigningSession`] with the held context. /// - /// Async contexts must use [`SigningContext::async_signer`](Self::async_signer). - pub fn signer(&self, identity_token: IdentityToken) -> SigstoreResult { - SigningSession::new(self, identity_token) + /// Async contexts must use [`SigningContext::signer`]. + pub fn blocking_signer( + &self, + identity_token: IdentityToken, + ) -> SigstoreResult { + blocking::SigningSession::new(self, identity_token) } } @@ -291,9 +295,9 @@ pub struct SigningArtifact { } impl SigningArtifact { - /// Consumes the signing artifact and produces a Sigstore [Bundle]. + /// Consumes the signing artifact and produces a Sigstore [`Bundle`]. /// - /// The resulting bundle can be serialized with [serde_json]. + /// The resulting bundle can be serialized with [`serde_json`]. pub fn to_bundle(self) -> Bundle { // NOTE: We explicitly only include the leaf certificate in the bundle's "chain" // here: the specs explicitly forbid the inclusion of the root certificate, diff --git a/src/verify/mod.rs b/src/bundle/verify/mod.rs similarity index 86% rename from src/verify/mod.rs rename to src/bundle/verify/mod.rs index 0504d10060..a6ba64330f 100644 --- a/src/verify/mod.rs +++ b/src/bundle/verify/mod.rs @@ -13,12 +13,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Verifier for Sigstore bundles and associated types and policies. +//! Types for verifying Sigstore bundles with policies. + mod models; + pub use models::{VerificationError, VerificationResult}; pub mod policy; pub use policy::{PolicyError, VerificationPolicy}; mod verifier; -pub use verifier::{AsyncVerifier, Verifier}; +pub use verifier::*; diff --git a/src/verify/models.rs b/src/bundle/verify/models.rs similarity index 99% rename from src/verify/models.rs rename to src/bundle/verify/models.rs index 01fdc8b660..198e9c05ee 100644 --- a/src/verify/models.rs +++ b/src/bundle/verify/models.rs @@ -16,7 +16,7 @@ use std::str::FromStr; use crate::{ - bundle::{Bundle, Version as BundleVersion}, + bundle::{models::Version as BundleVersion, Bundle}, crypto::certificate::{is_leaf, is_root_ca, CertificateValidationError}, rekor::models as rekor, }; diff --git a/src/verify/policy.rs b/src/bundle/verify/policy.rs similarity index 99% rename from src/verify/policy.rs rename to src/bundle/verify/policy.rs index c6c1806df8..267badc042 100644 --- a/src/verify/policy.rs +++ b/src/bundle/verify/policy.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Verifiers for certificate metadata. +//! Verification constraints for certificate metadata. //! //! diff --git a/src/verify/verifier.rs b/src/bundle/verify/verifier.rs similarity index 72% rename from src/verify/verifier.rs rename to src/bundle/verify/verifier.rs index ac4d4f62b3..0f4a4a5526 100644 --- a/src/verify/verifier.rs +++ b/src/bundle/verify/verifier.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Verifiers: async and blocking. + use std::io::{self, Read}; use sha2::{Digest, Sha256}; @@ -26,30 +28,30 @@ use crate::{ errors::Result as SigstoreResult, rekor::apis::configuration::Configuration as RekorConfiguration, trust::TrustRoot, - verify::{ - models::{CertificateErrorKind, SignatureErrorKind}, - VerificationError, - }, }; #[cfg(feature = "sigstore-trust-root")] use crate::trust::sigstore::SigstoreTrustRoot; -use super::{models::CheckedBundle, policy::VerificationPolicy, VerificationResult}; +use super::{ + models::{CertificateErrorKind, CheckedBundle, SignatureErrorKind}, + policy::VerificationPolicy, + VerificationError, VerificationResult, +}; /// An asynchronous Sigstore verifier. /// /// For synchronous usage, see [`Verifier`]. -pub struct AsyncVerifier { +pub struct Verifier { #[allow(dead_code)] rekor_config: RekorConfiguration, cert_pool: CertificatePool, } -impl AsyncVerifier { - /// Constructs an [`AsyncVerifier`]. +impl Verifier { + /// Constructs a [`Verifier`]. /// - /// For verifications against the public-good trust root, use [`AsyncVerifier::production`]. + /// For verifications against the public-good trust root, use [`Verifier::production()`]. pub fn new( rekor_config: RekorConfiguration, trust_repo: R, @@ -201,87 +203,89 @@ impl AsyncVerifier { } } -impl AsyncVerifier { - /// Constructs an [`AsyncVerifier`] against the public-good trust root. +impl Verifier { + /// Constructs an [`Verifier`] against the public-good trust root. #[cfg(feature = "sigstore-trust-root")] - pub async fn production() -> SigstoreResult { + pub async fn production() -> SigstoreResult { let updater = SigstoreTrustRoot::new(None).await?; - AsyncVerifier::new(Default::default(), updater) + Verifier::new(Default::default(), updater) } } -/// A synchronous Sigstore verifier. -/// -/// Async callers must use [`AsyncVerifier`]. Async usage of [`Verifier`] will result in a deadlock. -pub struct Verifier { - inner: AsyncVerifier, - rt: tokio::runtime::Runtime, -} +pub mod blocking { + use super::{Verifier as AsyncVerifier, *}; -impl Verifier { - /// Constructs a synchronous Sigstore verifier. - /// - /// For verifications against the public-good trust root, use [`Verifier::production`]. - pub fn new( - rekor_config: RekorConfiguration, - trust_repo: R, - ) -> SigstoreResult { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()?; - let inner = AsyncVerifier::new(rekor_config, trust_repo)?; - - Ok(Self { rt, inner }) + /// A synchronous Sigstore verifier. + pub struct Verifier { + inner: AsyncVerifier, + rt: tokio::runtime::Runtime, } - /// Verifies an input digest against the given Sigstore Bundle, ensuring conformance to the - /// provided [`VerificationPolicy`]. - pub fn verify_digest

( - &self, - input_digest: Sha256, - bundle: Bundle, - policy: &P, - offline: bool, - ) -> VerificationResult - where - P: VerificationPolicy, - { - self.rt.block_on( - self.inner - .verify_digest(input_digest, bundle, policy, offline), - ) - } + impl Verifier { + /// Constructs a synchronous Sigstore verifier. + /// + /// For verifications against the public-good trust root, use [`Verifier::production()`]. + pub fn new( + rekor_config: RekorConfiguration, + trust_repo: R, + ) -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let inner = AsyncVerifier::new(rekor_config, trust_repo)?; + + Ok(Self { rt, inner }) + } - /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided - /// [`VerificationPolicy`]. - pub fn verify( - &self, - mut input: R, - bundle: Bundle, - policy: &P, - offline: bool, - ) -> VerificationResult - where - R: Read, - P: VerificationPolicy, - { - let mut hasher = Sha256::new(); - io::copy(&mut input, &mut hasher).map_err(VerificationError::Input)?; + /// Verifies an input digest against the given Sigstore Bundle, ensuring conformance to the + /// provided [`VerificationPolicy`]. + pub fn verify_digest

( + &self, + input_digest: Sha256, + bundle: Bundle, + policy: &P, + offline: bool, + ) -> VerificationResult + where + P: VerificationPolicy, + { + self.rt.block_on( + self.inner + .verify_digest(input_digest, bundle, policy, offline), + ) + } - self.verify_digest(hasher, bundle, policy, offline) + /// Verifies an input against the given Sigstore Bundle, ensuring conformance to the provided + /// [`VerificationPolicy`]. + pub fn verify( + &self, + mut input: R, + bundle: Bundle, + policy: &P, + offline: bool, + ) -> VerificationResult + where + R: Read, + P: VerificationPolicy, + { + let mut hasher = Sha256::new(); + io::copy(&mut input, &mut hasher).map_err(VerificationError::Input)?; + + self.verify_digest(hasher, bundle, policy, offline) + } } -} -impl Verifier { - /// Constructs a synchronous [`Verifier`] against the public-good trust root. - #[cfg(feature = "sigstore-trust-root")] - pub fn production() -> SigstoreResult { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()?; - let inner = rt.block_on(AsyncVerifier::production())?; + impl Verifier { + /// Constructs a synchronous [`Verifier`] against the public-good trust root. + #[cfg(feature = "sigstore-trust-root")] + pub fn production() -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + let inner = rt.block_on(AsyncVerifier::production())?; - Ok(Verifier { inner, rt }) + Ok(Verifier { inner, rt }) + } } } diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 0ab7664728..1a688d9139 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -28,7 +28,7 @@ use crate::trust::TrustRoot; /// ## Rekor integration /// /// Rekor integration can be enabled by specifying Rekor's public key. -/// This can be provided via a [`crate::sigstore::ManualTrustRoot`]. +/// This can be provided via a [`crate::trust::ManualTrustRoot`]. /// /// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 13fd6c945e..9166fa0a1d 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -203,7 +203,7 @@ impl FulcioClient { /// /// TODO(tnytown): This (and other API clients) should be autogenerated. See sigstore-rs#209. /// - /// https://github.com/sigstore/fulcio/blob/main/fulcio.proto + /// /// /// Additionally, it might not be reasonable to expect callers to correctly construct and pass /// in an X509 CSR. diff --git a/src/lib.rs b/src/lib.rs index 1fee312ca5..fa336c1029 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,9 +284,3 @@ pub mod rekor; #[cfg(any(feature = "sign", feature = "verify"))] pub mod bundle; - -#[cfg(feature = "verify")] -pub mod verify; - -#[cfg(feature = "sign")] -pub mod sign; diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 81954a9891..0b26ba2af8 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -20,22 +20,6 @@ //! //! These can later be given to [`cosign::ClientBuilder`](crate::cosign::ClientBuilder) //! to enable Fulcio and Rekor integrations. -//! -//! # Example -//! -//! The `SigstoreRootTrust` instance can be created via the [`SigstoreTrustRoot::prefetch`] -//! 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::new(None).await; -/// // Now, get Fulcio and Rekor trust roots with the returned `SigstoreRootTrust` -/// # Ok(()) -/// # } -/// ``` use futures_util::TryStreamExt; use sha2::{Digest, Sha256}; use std::path::PathBuf; @@ -67,7 +51,7 @@ impl SigstoreTrustRoot { checkout_dir: Option, ) -> Result { let trusted_root = { - let data = Self::fetch_target(&repository, &checkout_dir, "trusted_root.json").await?; + let data = Self::fetch_target(repository, &checkout_dir, "trusted_root.json").await?; serde_json::from_slice(&data[..])? }; diff --git a/tests/conformance/conformance.rs b/tests/conformance/conformance.rs index e8ab9d3697..cccc0fcf7c 100644 --- a/tests/conformance/conformance.rs +++ b/tests/conformance/conformance.rs @@ -20,9 +20,9 @@ use std::{fs, process::exit}; use clap::{Parser, Subcommand}; use sigstore::{ + bundle::sign::SigningContext, + bundle::verify::{blocking::Verifier, policy}, oauth::IdentityToken, - sign::SigningContext, - verify::{policy, Verifier}, }; #[derive(Parser, Debug)] @@ -140,7 +140,7 @@ fn sign_bundle(args: SignBundle) -> anyhow::Result<()> { let mut artifact = fs::File::open(artifact)?; let context = SigningContext::production()?; - let signer = context.signer(identity_token); + let signer = context.blocking_signer(identity_token); let signing_artifact = signer?.sign(&mut artifact)?; let bundle_data = signing_artifact.to_bundle(); From c018346ecfec67c393c91a6911f10aa3f54b5a22 Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 12 Apr 2024 15:07:22 -0500 Subject: [PATCH 122/161] trust/sigstore: rework tests Signed-off-by: Andrew Pan --- src/trust/sigstore/mod.rs | 96 ++++++++++++++------------------------- 1 file changed, 34 insertions(+), 62 deletions(-) diff --git a/src/trust/sigstore/mod.rs b/src/trust/sigstore/mod.rs index 0b26ba2af8..9ee9f02cce 100644 --- a/src/trust/sigstore/mod.rs +++ b/src/trust/sigstore/mod.rs @@ -22,7 +22,7 @@ //! to enable Fulcio and Rekor integrations. use futures_util::TryStreamExt; use sha2::{Digest, Sha256}; -use std::path::PathBuf; +use std::path::Path; use tokio_util::bytes::BytesMut; use sigstore_protobuf_specs::dev::sigstore::{ @@ -48,10 +48,10 @@ impl SigstoreTrustRoot { /// Constructs a new trust root from a [`tough::Repository`]. async fn from_tough( repository: &tough::Repository, - checkout_dir: Option, + checkout_dir: Option<&Path>, ) -> Result { let trusted_root = { - let data = Self::fetch_target(repository, &checkout_dir, "trusted_root.json").await?; + let data = Self::fetch_target(repository, checkout_dir, "trusted_root.json").await?; serde_json::from_slice(&data[..])? }; @@ -59,7 +59,7 @@ impl SigstoreTrustRoot { } /// Constructs a new trust root backed by the Sigstore Public Good Instance. - pub async fn new(cache_dir: Option) -> Result { + pub async fn new(cache_dir: Option<&Path>) -> Result { // These are statically defined and should always parse correctly. let metadata_base = url::Url::parse(constants::SIGSTORE_METADATA_BASE)?; let target_base = url::Url::parse(constants::SIGSTORE_TARGET_BASE)?; @@ -79,7 +79,7 @@ impl SigstoreTrustRoot { async fn fetch_target( repository: &tough::Repository, - checkout_dir: &Option, + checkout_dir: Option<&Path>, name: N, ) -> Result> where @@ -238,6 +238,7 @@ fn is_timerange_valid(range: Option<&TimeRange>, allow_expired: bool) -> bool { #[cfg(test)] mod tests { use super::*; + use rstest::{fixture, rstest}; use std::fs; use std::path::Path; use std::time::SystemTime; @@ -265,71 +266,42 @@ mod tests { ); } - macro_rules! impl_test { - ($name:ident, cache=$cache:literal $(,setup=$setup:expr)? $(,verify=$verify:expr)?) => { - #[test] - fn $name() -> Result<()> { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()?; + #[fixture] + fn cache_dir() -> TempDir { + TempDir::new().expect("cannot create temp cache dir") + } - let cache_dir = if $cache { - let tmp = TempDir::new().expect("cannot create temp cache dir"); - Some(tmp.into_path()) - } else { - None - }; - let cache_dir_borrowed = cache_dir.as_ref().map(|p| p.as_path()); + async fn trust_root(cache: Option<&Path>) -> SigstoreTrustRoot { + SigstoreTrustRoot::new(cache) + .await + .expect("failed to construct SigstoreTrustRoot") + } - $( - $setup(cache_dir_borrowed); - )? + #[rstest] + #[tokio::test] + async fn trust_root_fetch(#[values(None, Some(cache_dir()))] cache: Option) { + let cache = cache.as_ref().map(|t| t.path()); + let root = trust_root(cache).await; - let root = rt - .block_on(SigstoreTrustRoot::new(cache_dir.clone())) - .expect("failed to create trust root"); + verify(&root, cache); + } - verify(&root, cache_dir_borrowed); + #[rstest] + #[tokio::test] + async fn trust_root_outdated(cache_dir: TempDir) { + let trusted_root_path = cache_dir.path().join("trusted_root.json"); + let outdated_data = b"fake trusted root"; + fs::write(&trusted_root_path, outdated_data) + .expect("failed to write to trusted root cache"); - $($verify(&root, cache_dir_borrowed);)? + let cache = Some(cache_dir.path()); + let root = trust_root(cache).await; + verify(&root, cache); - Ok(()) - } - } + let data = fs::read(&trusted_root_path).expect("failed to read from trusted root cache"); + assert_ne!(data, outdated_data, "TUF cache was not properly updated"); } - impl_test!(no_local_cache, cache = false); - impl_test!( - current_local_cache, - cache = true, - verify = |_, cache_dir: Option<&Path>| { - assert!( - cache_dir.unwrap().join("trusted_root.json").exists(), - "TUF cache was requested but not used" - ); - } - ); - impl_test!( - outdated_local_cache, - cache = true, - setup = |cache_dir: Option<&Path>| { - fs::write( - cache_dir.unwrap().join("trusted_root.json"), - b"fake trusted root", - ) - .expect("cannot write file to cache dir"); - }, - verify = |_, cache_dir: Option<&Path>| { - let data = fs::read(cache_dir.unwrap().join("trusted_root.json")) - .expect("cannot read trusted_root.json from cache"); - - assert_ne!( - data, b"fake trusted root", - "TUF cache was not properly updated" - ); - } - ); - #[test] fn test_is_timerange_valid() { fn range_from(start: i64, end: i64) -> TimeRange { From c69ec68b256c4142b8715032815f1d9024f98c22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:15:45 +0000 Subject: [PATCH 123/161] chore(deps): Update rstest requirement from 0.18.1 to 0.19.0 Updates the requirements on [rstest](https://github.com/la10736/rstest) to permit the latest version. - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/v0.18.1...v0.19.0) --- updated-dependencies: - dependency-name: rstest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dd9c659f61..39e9bf8833 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,7 +140,7 @@ chrono = "0.4.31" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" -rstest = "0.18.1" +rstest = "0.19.0" serial_test = "3.0.0" tempfile = "3.3.0" testcontainers = "0.15" From a153e36fd4df6d57701650f4423868ff3533d04e Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Mon, 4 Dec 2023 09:57:08 -0600 Subject: [PATCH 124/161] sign: Signed Certificate Timestamp validation Co-authored-by: Alex Cameron Signed-off-by: Andrew Pan --- src/bundle/sign.rs | 39 +++- src/bundle/verify/models.rs | 3 + src/bundle/verify/verifier.rs | 17 +- src/crypto/keyring.rs | 165 ++++++++++++++ src/crypto/mod.rs | 5 + src/crypto/transparency.rs | 404 ++++++++++++++++++++++++++++++++++ src/errors.rs | 8 + src/fulcio/mod.rs | 2 +- 8 files changed, 636 insertions(+), 7 deletions(-) create mode 100644 src/crypto/keyring.rs create mode 100644 src/crypto/transparency.rs diff --git a/src/bundle/sign.rs b/src/bundle/sign.rs index b60472c568..8e7b0afc4f 100644 --- a/src/bundle/sign.rs +++ b/src/bundle/sign.rs @@ -39,6 +39,8 @@ use x509_cert::builder::{Builder, RequestBuilder as CertRequestBuilder}; use x509_cert::ext::pkix as x509_ext; use crate::bundle::models::Version; +use crate::crypto::keyring::Keyring; +use crate::crypto::transparency::{verify_sct, CertificateEmbeddedSCT}; use crate::errors::{Result as SigstoreResult, SigstoreError}; use crate::fulcio::oauth::OauthTokenProvider; use crate::fulcio::{self, FulcioClient, FULCIO_ROOT}; @@ -46,6 +48,10 @@ use crate::oauth::IdentityToken; use crate::rekor::apis::configuration::Configuration as RekorConfiguration; use crate::rekor::apis::entries_api::create_log_entry; use crate::rekor::models::{hashedrekord, proposed_entry::ProposedEntry as ProposedLogEntry}; +use crate::trust::TrustRoot; + +#[cfg(feature = "sigstore-trust-root")] +use crate::trust::sigstore::SigstoreTrustRoot; /// An asynchronous Sigstore signing session. /// @@ -128,7 +134,12 @@ impl<'ctx> SigningSession<'ctx> { return Err(SigstoreError::ExpiredSigningSession()); } - // TODO(tnytown): verify SCT here, sigstore-rs#326 + if let Some(detached_sct) = &self.certs.detached_sct { + verify_sct(detached_sct, &self.context.ctfe_keyring)?; + } else { + let sct = CertificateEmbeddedSCT::new(&self.certs.cert, &self.certs.chain)?; + verify_sct(&sct, &self.context.ctfe_keyring)?; + } // Sign artifact. let input_hash: &[u8] = &hasher.clone().finalize(); @@ -247,29 +258,51 @@ pub mod blocking { pub struct SigningContext { fulcio: FulcioClient, rekor_config: RekorConfiguration, + ctfe_keyring: Keyring, } impl SigningContext { /// Manually constructs a [`SigningContext`] from its constituent data. - pub fn new(fulcio: FulcioClient, rekor_config: RekorConfiguration) -> Self { + pub fn new( + fulcio: FulcioClient, + rekor_config: RekorConfiguration, + ctfe_keyring: Keyring, + ) -> Self { Self { fulcio, rekor_config, + ctfe_keyring, } } /// Returns a [`SigningContext`] configured against the public-good production Sigstore /// infrastructure. - pub fn production() -> SigstoreResult { + #[cfg(feature = "sigstore-trust-root")] + pub async fn async_production() -> SigstoreResult { + let trust_root = SigstoreTrustRoot::new(None).await?; Ok(Self::new( FulcioClient::new( Url::parse(FULCIO_ROOT).expect("constant FULCIO root fails to parse!"), crate::fulcio::TokenProvider::Oauth(OauthTokenProvider::default()), ), Default::default(), + Keyring::new(trust_root.ctfe_keys()?)?, )) } + /// Returns a [`SigningContext`] configured against the public-good production Sigstore + /// infrastructure. + /// + /// Async callers should use [`SigningContext::async_production`]. + #[cfg(feature = "sigstore-trust-root")] + pub fn production() -> SigstoreResult { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + + rt.block_on(Self::async_production()) + } + /// Configures and returns a [`SigningSession`] with the held context. pub async fn signer(&self, identity_token: IdentityToken) -> SigstoreResult { SigningSession::new(self, identity_token).await diff --git a/src/bundle/verify/models.rs b/src/bundle/verify/models.rs index 198e9c05ee..f75ea8affb 100644 --- a/src/bundle/verify/models.rs +++ b/src/bundle/verify/models.rs @@ -102,6 +102,9 @@ pub enum CertificateErrorKind { #[error("certificate expired before time of signing")] Expired, + #[error("certificate SCT verification failed")] + Sct(#[source] crate::crypto::transparency::SCTError), + #[error("certificate verification failed")] VerificationFailed(#[source] webpki::Error), } diff --git a/src/bundle/verify/verifier.rs b/src/bundle/verify/verifier.rs index 0f4a4a5526..8635bcb7c0 100644 --- a/src/bundle/verify/verifier.rs +++ b/src/bundle/verify/verifier.rs @@ -24,7 +24,11 @@ use x509_cert::der::Encode; use crate::{ bundle::Bundle, - crypto::{CertificatePool, CosignVerificationKey, Signature}, + crypto::{ + keyring::Keyring, + transparency::{verify_sct, CertificateEmbeddedSCT}, + CertificatePool, CosignVerificationKey, Signature, + }, errors::Result as SigstoreResult, rekor::apis::configuration::Configuration as RekorConfiguration, trust::TrustRoot, @@ -46,6 +50,7 @@ pub struct Verifier { #[allow(dead_code)] rekor_config: RekorConfiguration, cert_pool: CertificatePool, + ctfe_keyring: Keyring, } impl Verifier { @@ -57,10 +62,12 @@ impl Verifier { trust_repo: R, ) -> SigstoreResult { let cert_pool = CertificatePool::from_certificates(trust_repo.fulcio_certs()?, [])?; + let ctfe_keyring = Keyring::new(trust_repo.ctfe_keys()?)?; Ok(Self { rekor_config, cert_pool, + ctfe_keyring, }) } @@ -110,14 +117,18 @@ impl Verifier { .try_into() .map_err(CertificateErrorKind::Malformed)?; - let _trusted_chain = self + let trusted_chain = self .cert_pool .verify_cert_with_time(&ee_cert, UnixTime::since_unix_epoch(issued_at)) .map_err(CertificateErrorKind::VerificationFailed)?; debug!("signing certificate chains back to trusted root"); - // TODO(tnytown): verify SCT here, sigstore-rs#326 + let sct_context = + CertificateEmbeddedSCT::new_with_verified_path(&materials.certificate, &trusted_chain) + .map_err(CertificateErrorKind::Sct)?; + verify_sct(&sct_context, &self.ctfe_keyring).map_err(CertificateErrorKind::Sct)?; + debug!("signing certificate's SCT is valid"); // 2) Verify that the signing certificate belongs to the signer. policy.verify(&materials.certificate)?; diff --git a/src/crypto/keyring.rs b/src/crypto/keyring.rs new file mode 100644 index 0000000000..d24e36496e --- /dev/null +++ b/src/crypto/keyring.rs @@ -0,0 +1,165 @@ +// Copyright 2023 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. + +use std::collections::HashMap; + +use const_oid::db::rfc5912::{ID_EC_PUBLIC_KEY, RSA_ENCRYPTION, SECP_256_R_1}; +use digest::Digest; +use ring::{signature as ring_signature, signature::UnparsedPublicKey}; +use thiserror::Error; +use x509_cert::{ + der, + der::{Decode, Encode}, + spki::SubjectPublicKeyInfoOwned, +}; + +#[derive(Error, Debug)] +pub enum KeyringError { + #[error("malformed key")] + KeyMalformed(#[from] x509_cert::der::Error), + #[error("unsupported algorithm")] + AlgoUnsupported, + + #[error("requested key not in keyring")] + KeyNotFound, + #[error("verification failed")] + VerificationFailed, +} +type Result = std::result::Result; + +/// A CT signing key. +struct Key { + inner: UnparsedPublicKey>, + /// The key's RFC 6962-style "key ID". + /// + fingerprint: [u8; 32], +} + +impl Key { + /// Creates a `Key` from a DER blob containing a SubjectPublicKeyInfo object. + pub fn new(spki_bytes: &[u8]) -> Result { + let spki = SubjectPublicKeyInfoOwned::from_der(spki_bytes)?; + let (algo, params) = if let Some(params) = &spki.algorithm.parameters { + // Special-case RSA keys, which don't have SPKI parameters. + if spki.algorithm.oid == RSA_ENCRYPTION && params == &der::Any::null() { + // TODO(tnytown): Do we need to support RSA keys? + return Err(KeyringError::AlgoUnsupported); + }; + + (spki.algorithm.oid, params.decode_as()?) + } else { + return Err(KeyringError::AlgoUnsupported); + }; + + match (algo, params) { + // TODO(tnytown): should we also accept ed25519, p384, ... ? + (ID_EC_PUBLIC_KEY, SECP_256_R_1) => Ok(Key { + inner: UnparsedPublicKey::new( + &ring_signature::ECDSA_P256_SHA256_ASN1, + spki.subject_public_key.raw_bytes().to_owned(), + ), + fingerprint: { + let mut hasher = sha2::Sha256::new(); + spki.encode(&mut hasher).expect("failed to hash key!"); + hasher.finalize().into() + }, + }), + _ => Err(KeyringError::AlgoUnsupported), + } + } +} + +/// Represents a set of CT signing keys, each of which is potentially a valid signer for +/// Signed Certificate Timestamps (SCTs) or Signed Tree Heads (STHs). +pub struct Keyring(HashMap<[u8; 32], Key>); + +impl Keyring { + /// Creates a `Keyring` from DER encoded SPKI-format public keys. + pub fn new<'a>(keys: impl IntoIterator) -> Result { + Ok(Self( + keys.into_iter() + .flat_map(Key::new) + .map(|k| Ok((k.fingerprint, k))) + .collect::>()?, + )) + } + + /// Verifies `data` against a `signature` with a public key identified by `key_id`. + pub fn verify(&self, key_id: &[u8; 32], signature: &[u8], data: &[u8]) -> Result<()> { + let key = self.0.get(key_id).ok_or(KeyringError::KeyNotFound)?; + + key.inner + .verify(data, signature) + .or(Err(KeyringError::VerificationFailed))?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::Keyring; + use crate::crypto::signing_key::ecdsa::{ECDSAKeys, EllipticCurve}; + use digest::Digest; + use std::io::Write; + + #[test] + fn verify_keyring() { + let message = b"some message"; + + // Create a key pair and a keyring containing the public key. + let key_pair = ECDSAKeys::new(EllipticCurve::P256).unwrap(); + let signer = key_pair.to_sigstore_signer().unwrap(); + let pub_key = key_pair.as_inner().public_key_to_der().unwrap(); + let keyring = Keyring::new([pub_key.as_slice()]).unwrap(); + + // Generate the signature. + let signature = signer.sign(message).unwrap(); + + // Generate the key id. + let mut hasher = sha2::Sha256::new(); + hasher.write(pub_key.as_slice()).unwrap(); + let key_id: [u8; 32] = hasher.finalize().into(); + + // Check for success. + assert!(keyring + .verify(&key_id, signature.as_slice(), message) + .is_ok()); + + // Check for failure with incorrect key id. + assert!(keyring + .verify(&[0; 32], signature.as_slice(), message) + .is_err()); + + // Check for failure with incorrect payload. + let incorrect_message = b"another message"; + + assert!(keyring + .verify(&key_id, signature.as_slice(), incorrect_message) + .is_err()); + + // Check for failure with incorrect keyring. + let incorrect_key_pair = ECDSAKeys::new(EllipticCurve::P256).unwrap(); + let incorrect_keyring = Keyring::new([incorrect_key_pair + .as_inner() + .public_key_to_der() + .unwrap() + .as_slice()]) + .unwrap(); + + assert!(incorrect_keyring + .verify(&key_id, signature.as_slice(), message) + .is_err()); + } +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 880088cbf9..92336ffa6d 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -177,6 +177,8 @@ pub(crate) mod certificate; pub(crate) mod certificate_pool; #[cfg(feature = "cert")] pub(crate) use certificate_pool::CertificatePool; +#[cfg(feature = "cert")] +pub(crate) mod keyring; pub mod verification_key; @@ -188,6 +190,9 @@ use self::signing_key::{ pub mod signing_key; +#[cfg(any(feature = "sign", feature = "verify"))] +pub(crate) mod transparency; + #[cfg(test)] pub(crate) mod tests { use chrono::{DateTime, TimeDelta, Utc}; diff --git a/src/crypto/transparency.rs b/src/crypto/transparency.rs new file mode 100644 index 0000000000..f4de034cb4 --- /dev/null +++ b/src/crypto/transparency.rs @@ -0,0 +1,404 @@ +// Copyright 2023 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. + +//! Types for Certificate Transparency validation. + +use const_oid::ObjectIdentifier; +use digest::Digest; +use thiserror::Error; +use tls_codec::{SerializeBytes, TlsByteVecU16, TlsByteVecU24, TlsSerializeBytes, TlsSize}; +use tracing::debug; +use x509_cert::{ + der, + der::{Decode, Encode}, + ext::pkix::{ + sct::Version, ExtendedKeyUsage, SignedCertificateTimestamp, SignedCertificateTimestampList, + }, + Certificate, +}; + +use super::{ + certificate, + keyring::{Keyring, KeyringError}, +}; +use crate::fulcio::SigningCertificateDetachedSCT; + +// TODO(tnytown): Migrate to const-oid's CT_PRECERT_SCTS when a new release is cut. +const CT_PRECERT_SCTS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.4.2"); +const PRECERTIFICATE_SIGNING_CERTIFICATE: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.4.4"); + +fn cert_is_preissuer(cert: &Certificate) -> bool { + let eku: ExtendedKeyUsage = match cert.tbs_certificate.get() { + Ok(Some((_, ext))) => ext, + _ => return false, + }; + + eku.0.contains(&PRECERTIFICATE_SIGNING_CERTIFICATE) +} + +// +fn find_issuer_cert(chain: &[Certificate]) -> Option<&Certificate> { + let cert = if cert_is_preissuer(&chain[0]) { + &chain[1] + } else { + &chain[0] + }; + + certificate::is_ca(cert).ok()?; + Some(cert) +} + +#[derive(Debug, Error)] +pub enum CertificateErrorKind { + #[error("SCT list extension missing from leaf certificate")] + LeafSCTMissing, + + #[error("cannot find leaf certificate's issuer")] + IssuerMissing, + + #[error("cannot decode leaf certificate's issuer")] + IssuerMalformed, + + #[error("cannot decode SCT")] + LeafSCTMalformed, + + #[error(transparent)] + Der(#[from] der::Error), + + #[error(transparent)] + Tls(#[from] tls_codec::Error), +} + +impl From for CertificateErrorKind { + fn from(value: x509_cert::ext::pkix::Error) -> Self { + match value { + x509_cert::ext::pkix::Error::Der(e) => CertificateErrorKind::Der(e), + x509_cert::ext::pkix::Error::Tls(e) => CertificateErrorKind::Tls(e), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum SCTError { + #[error("failed to extract SCT from certificate")] + Parsing(#[from] CertificateErrorKind), + + #[error("failed to reconstruct signed payload")] + Serialization(#[source] tls_codec::Error), + + #[error("failed to verify SCT")] + Verification(#[from] KeyringError), +} + +#[derive(PartialEq, Debug, TlsSerializeBytes, TlsSize)] +#[repr(u8)] +enum SignatureType { + CertificateTimestamp = 0, + TreeHash = 1, +} + +#[derive(PartialEq, Debug)] +#[repr(u16)] +enum LogEntryType { + X509Entry = 0, + PrecertEntry = 1, +} + +#[derive(PartialEq, Debug, TlsSerializeBytes, TlsSize)] +struct PreCert { + // opaque issuer_key_hash[32]; + issuer_key_hash: [u8; 32], + // opaque TBSCertificate<1..2^24-1>; + tbs_certificate: TlsByteVecU24, +} + +#[derive(PartialEq, Debug, TlsSerializeBytes, TlsSize)] +#[repr(u16)] +enum SignedEntry { + // opaque ASN.1Cert<1..2^24-1>; + #[tls_codec(discriminant = "LogEntryType::X509Entry")] + X509Entry(TlsByteVecU24), + #[tls_codec(discriminant = "LogEntryType::PrecertEntry")] + PrecertEntry(PreCert), +} + +#[derive(PartialEq, Debug, TlsSerializeBytes, TlsSize)] +pub struct DigitallySigned { + version: Version, + signature_type: SignatureType, + timestamp: u64, + signed_entry: SignedEntry, + // opaque CtExtensions<0..2^16-1>; + extensions: TlsByteVecU16, + + // XX(tnytown): pass in some useful context. These fields will not be encoded into the + // TLS DigitallySigned blob, but we need them to properly verify the reconstructed + // message. + #[tls_codec(skip)] + log_id: [u8; 32], + #[tls_codec(skip)] + signature: Vec, +} + +#[derive(Debug)] +pub struct CertificateEmbeddedSCT<'a> { + cert: &'a Certificate, + sct: SignedCertificateTimestamp, + issuer_id: [u8; 32], +} + +impl<'a> CertificateEmbeddedSCT<'a> { + fn new_with_spki(cert: &'a Certificate, spki: &[u8]) -> Result { + let scts: SignedCertificateTimestampList = match cert.tbs_certificate.get() { + Ok(Some((_, ext))) => ext, + _ => return Err(SCTError::Parsing(CertificateErrorKind::LeafSCTMissing))?, + }; + + // Parse SCT structures. + let sct = match scts + .parse_timestamps() + .map_err(CertificateErrorKind::from)? + .as_slice() + { + [e] => e, + // We expect exactly one element here. Fail if there are more or less. + _ => return Err(CertificateErrorKind::LeafSCTMissing)?, + } + .parse_timestamp() + .map_err(CertificateErrorKind::from)?; + + let issuer_id = { + let mut hasher = sha2::Sha256::new(); + hasher.update(spki); + hasher.finalize().into() + }; + + Ok(Self { + cert, + sct, + issuer_id, + }) + } + + pub fn new(leaf: &'a Certificate, chain: &[Certificate]) -> Result { + // Traverse chain to find the issuer we're verifying against. + let issuer = find_issuer_cert(chain); + let spki = issuer + .ok_or(CertificateErrorKind::IssuerMissing)? + .tbs_certificate + .subject_public_key_info + .to_der() + .map_err(CertificateErrorKind::from)?; + + Self::new_with_spki(leaf, &spki) + } + + pub fn new_with_verified_path( + leaf: &'a Certificate, + chain: &webpki::VerifiedPath, + ) -> Result { + let issuer_spki = if let Some(issuer) = chain.intermediate_certificates().next() { + debug!("intermediate is the leaf's issuer"); + + let issuer = Certificate::from_der(&issuer.der()) + .map_err(CertificateErrorKind::from)? + .tbs_certificate; + issuer + .subject_public_key_info + .to_der() + .map_err(CertificateErrorKind::from)? + } else { + debug!("anchor is the leaf's issuer"); + + // Prefix the SPKI with the DER SEQUENCE tag and a short definite-form length. + let body = &chain.anchor().subject_public_key_info[..]; + let body_len = body + .len() + .try_into() + .or(Err(CertificateErrorKind::IssuerMalformed))?; + let prefix = &[0x30u8, body_len]; + + [prefix, body].concat() + }; + + Self::new_with_spki(leaf, &issuer_spki) + } +} + +impl From<&CertificateEmbeddedSCT<'_>> for DigitallySigned { + fn from(value: &CertificateEmbeddedSCT) -> Self { + // Construct the precert by filtering out the SCT extension. + let mut tbs_precert = value.cert.tbs_certificate.clone(); + tbs_precert.extensions = tbs_precert.extensions.map(|exts| { + exts.iter() + .filter(|v| v.extn_id != CT_PRECERT_SCTS) + .cloned() + .collect() + }); + + let mut tbs_precert_der = Vec::new(); + tbs_precert + .encode_to_vec(&mut tbs_precert_der) + .expect("failed to re-encode Precertificate!"); + + DigitallySigned { + // XX(tnytown): This match is needed because `sct::Version` does not implement Copy. + version: match value.sct.version { + Version::V1 => Version::V1, + }, + signature_type: SignatureType::CertificateTimestamp, + timestamp: value.sct.timestamp, + signed_entry: SignedEntry::PrecertEntry(PreCert { + issuer_key_hash: value.issuer_id, + tbs_certificate: tbs_precert_der.as_slice().into(), + }), + extensions: value.sct.extensions.clone(), + + log_id: value.sct.log_id.key_id, + signature: value.sct.signature.signature.clone().into(), + } + } +} + +impl From<&SigningCertificateDetachedSCT> for DigitallySigned { + fn from(value: &SigningCertificateDetachedSCT) -> Self { + let sct = &value.signed_certificate_timestamp; + + DigitallySigned { + version: Version::V1, + signature_type: SignatureType::CertificateTimestamp, + timestamp: sct.timestamp, + signed_entry: SignedEntry::X509Entry(value.chain.certificates[0].contents().into()), + extensions: sct.extensions.clone().into(), + + log_id: sct.id, + signature: sct.signature.clone(), + } + } +} + +/// Verifies a given signing certificate's Signed Certificate Timestamp. +/// +/// SCT verification as defined by [RFC 6962] guarantees that a given certificate has been submitted +/// to a Certificate Transparency log. Verification should be performed on the signing certificate +/// in Sigstore verify and sign flows. Certificates that fail SCT verification are misissued and +/// MUST NOT be trusted. +/// +/// For more information on Certificate Transparency and the guarantees it provides, see . +/// +/// [RFC 6962]: https://datatracker.ietf.org/doc/html/rfc6962 +pub fn verify_sct(sct: S, keyring: &Keyring) -> Result<(), SCTError> +where + S: Into, +{ + let sct: DigitallySigned = sct.into(); + let serialized = sct.tls_serialize().map_err(SCTError::Serialization)?; + + keyring.verify(&sct.log_id, &sct.signature, &serialized)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{verify_sct, CertificateEmbeddedSCT}; + use crate::crypto::keyring::Keyring; + use crate::fulcio::SigningCertificateDetachedSCT; + use p256::ecdsa::VerifyingKey; + use std::str::FromStr; + use x509_cert::der::DecodePem; + use x509_cert::spki::EncodePublicKey; + use x509_cert::Certificate; + + #[test] + fn verify_embedded_sct() { + let cert_pem = r#"-----BEGIN CERTIFICATE----- +MIICzDCCAlGgAwIBAgIUF96OLbM9/tDVHKCJliXLTFvnfjAwCgYIKoZIzj0EAwMw +NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl +cm1lZGlhdGUwHhcNMjMxMjEzMDU1MDU1WhcNMjMxMjEzMDYwMDU1WjAAMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEmir+Lah2291zCsLkmREQNLzf99z571BNB+fa +rerSLGzcwLFK7GRLTGYcO0oStxCYavxRQPMo3JvB8vGtZbn/76OCAXAwggFsMA4G +A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU8U9M +t9GMrRm8+gifPtc63nlP3OIwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y +ZD8wGwYDVR0RAQH/BBEwD4ENYXNjQHRldHN1by5zaDAsBgorBgEEAYO/MAEBBB5o +dHRwczovL2dpdGh1Yi5jb20vbG9naW4vb2F1dGgwLgYKKwYBBAGDvzABCAQgDB5o +dHRwczovL2dpdGh1Yi5jb20vbG9naW4vb2F1dGgwgYkGCisGAQQB1nkCBAIEewR5 +AHcAdQDdPTBqxscRMmMZHhyZZzcCokpeuN48rf+HinKALynujgAAAYxhumYsAAAE +AwBGMEQCIHRRe20lRrNM4xd07mpjTtgaE6FGS3jjF++zW8ZMnth3AiAd6LVAAeVW +hSW4T0XJRw9lGU6/EK9+ELZpEjrY03dJ1zAKBggqhkjOPQQDAwNpADBmAjEAiHqK +W9PQ/5h7VROVIWPaxUo3LhrL2sZanw4bzTDBDY0dRR19ZFzjtAph1RzpQqppAjEA +plAvxwkAIR2jurboJZ4Zm9rNAx8KvA+A5yQFzNkGgKDLjTJrKmSKoIcWV3j7WfdL +-----END CERTIFICATE-----"#; + + let chain_pem = [ + r#"-----BEGIN CERTIFICATE----- +MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw +KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y +MjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl +LmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7 +7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS +0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB +BQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp +KFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI +zj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR +nZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP +mygUY7Ii2zbdCdliiow= +-----END CERTIFICATE-----"#, + r#"-----BEGIN CERTIFICATE----- +MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw +KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y +MTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl +LmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7 +XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex +X69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY +wB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ +KsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM +WP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9 +TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ +-----END CERTIFICATE-----"#, + ]; + + let ctfe_pem = r#"-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNK +AaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw== +-----END PUBLIC KEY-----"#; + + let cert = Certificate::from_pem(&cert_pem).unwrap(); + let chain = chain_pem.map(|c| Certificate::from_pem(&c).unwrap()); + let sct = CertificateEmbeddedSCT::new(&cert, &chain).unwrap(); + let ctfe_key: VerifyingKey = VerifyingKey::from_str(&ctfe_pem).unwrap(); + let keyring = Keyring::new([ctfe_key.to_public_key_der().unwrap().as_bytes()]).unwrap(); + + assert!(verify_sct(&sct, &keyring).is_ok()); + } + + #[test] + fn verify_detached_sct() { + let sct_json = r#"{"chain": {"certificates": ["-----BEGIN CERTIFICATE-----\nMIICUTCCAfigAwIBAgIUAafXe40Q5jthWJMo+JsJJCq09IAwCgYIKoZIzj0EAwIw\naDEMMAoGA1UEBhMDVVNBMQswCQYDVQQIEwJXQTERMA8GA1UEBxMIS2lya2xhbmQx\nFTATBgNVBAkTDDc2NyA2dGggU3QgUzEOMAwGA1UEERMFOTgwMzMxETAPBgNVBAoT\nCHNpZ3N0b3JlMB4XDTIzMTIxNDA3MDkzMFoXDTIzMTIxNDA3MTkzMFowADBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABDQT+qfW/VnHts0GSqI3kOc2z1lygSUWia3y\nIOx5qyWpXS1PwVcTbJnkcQEy1mnAES76NyfN5LsHHW2m53hF4WGjgecwgeQwDgYD\nVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBRpKUIe\nAqDxiw/GzKGRLFAvbaCnujAfBgNVHSMEGDAWgBTjGF7/fiITblnp3yIv3G1DETbS\ncTAbBgNVHREBAf8EETAPgQ1hc2NAdGV0c3VvLnNoMC4GCisGAQQBg78wAQEEIGh0\ndHBzOi8vb2F1dGgyLnNpZ3N0b3JlLmRldi9hdXRoMDAGCisGAQQBg78wAQgEIgwg\naHR0cHM6Ly9vYXV0aDIuc2lnc3RvcmUuZGV2L2F1dGgwCgYIKoZIzj0EAwIDRwAw\nRAIgOW+tCrt44rjWDCMSWhwC0zJRWpqH/qWRgSw2ndK7w3ICIGz0DDAXhvl6JFAz\nQp+40dnoUGKr+y0MF1zVaDOb1y+q\n-----END CERTIFICATE-----", "-----BEGIN CERTIFICATE-----\nMIICFzCCAb2gAwIBAgIUbPNC2sKGpw8cOQfpv8yJii7c7TEwCgYIKoZIzj0EAwIw\naDEMMAoGA1UEBhMDVVNBMQswCQYDVQQIEwJXQTERMA8GA1UEBxMIS2lya2xhbmQx\nFTATBgNVBAkTDDc2NyA2dGggU3QgUzEOMAwGA1UEERMFOTgwMzMxETAPBgNVBAoT\nCHNpZ3N0b3JlMB4XDTIzMTIxNDA2NDIzNloXDTMzMTIxNDA2NDIzNlowaDEMMAoG\nA1UEBhMDVVNBMQswCQYDVQQIEwJXQTERMA8GA1UEBxMIS2lya2xhbmQxFTATBgNV\nBAkTDDc2NyA2dGggU3QgUzEOMAwGA1UEERMFOTgwMzMxETAPBgNVBAoTCHNpZ3N0\nb3JlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfe1ZllZHky68F3jRhY4Hxx7o\nPBoBaD1i9UJtyE8xfIYGVpD1+jSHctZRmiv2ZsDEE6WN3k5lc2O2GyemHJwULqNF\nMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE\nFOMYXv9+IhNuWenfIi/cbUMRNtJxMAoGCCqGSM49BAMCA0gAMEUCIDj5wbYN3ym8\nwY+Uy+FkKASpBQodXdgF+JR9tWhNDlc/AiEAwqMTyLa6Yr+5t1DvnUsR4lQNoXD7\nz8XmxcUnJTenEh4=\n-----END CERTIFICATE-----"]}, "signedCertificateTimestamp": "eyJzY3RfdmVyc2lvbiI6MCwiaWQiOiJla0ppei9acEcrVUVuNXcvR2FJcjYrYXdJK1JLZmtwdC9WOVRldTd2YTFrPSIsInRpbWVzdGFtcCI6MTcwMjUzNzc3MDQyNiwiZXh0ZW5zaW9ucyI6IiIsInNpZ25hdHVyZSI6IkJBTUFSakJFQWlBT28vdDZ4RDY0RkV2TWpGcGFsMUhVVkZxQU5nOXJ3ZEttd3NQU2wxNm5FZ0lnZmFNTlJHMTBxQVY1Z280MzU1WkxVNVVvdHRvWTAwK0l0YXhZYjRkZmV0Zz0ifQ=="}"#; + + let ctfe_pem = r#"-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbbQiLx6GKy6ivhc11wJGbQjc2VX/ +mnuk5d670MTXR3p+LIAcxd5MhqIHpLmyYJ5mDKLEoZ/pC0nPuje3JueBcA== +-----END PUBLIC KEY-----"#; + + let sct: SigningCertificateDetachedSCT = serde_json::from_str(sct_json).unwrap(); + let ctfe_key: VerifyingKey = VerifyingKey::from_str(&ctfe_pem).unwrap(); + let keyring = Keyring::new([ctfe_key.to_public_key_der().unwrap().as_bytes()]).unwrap(); + + assert!(verify_sct(&sct, &keyring).is_ok()); + } +} diff --git a/src/errors.rs b/src/errors.rs index 56a3f93856..9cc6d620d4 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -133,6 +133,14 @@ pub enum SigstoreError { #[error(transparent)] JoinError(#[from] tokio::task::JoinError), + #[cfg(feature = "cert")] + #[error(transparent)] + KeyringError(#[from] crate::crypto::keyring::KeyringError), + + #[cfg(any(feature = "sign", feature = "verify"))] + #[error(transparent)] + SCTError(#[from] crate::crypto::transparency::SCTError), + // HACK(tnytown): Remove when we rework the Fulcio V2 endpoint. #[cfg(feature = "fulcio")] #[error(transparent)] diff --git a/src/fulcio/mod.rs b/src/fulcio/mod.rs index 9166fa0a1d..406dc5bea5 100644 --- a/src/fulcio/mod.rs +++ b/src/fulcio/mod.rs @@ -1,4 +1,4 @@ -mod models; +pub(crate) mod models; pub mod oauth; From 0d2fc87ef12d74c079498e78dbf17e81bcbf8b9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 19:24:20 +0000 Subject: [PATCH 125/161] chore(deps): Bump actions/checkout from 4.1.2 to 4.1.5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/9bb56186c3b09b4f86b1c65136769dd318469633...44c2b7a8a4ea60a981eaca3cf939b5f4305c123b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index b8746ea75a..3a3f6acb1b 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 09749783d1..2054a56ea3 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index 354dd71967..f4c34d2b96 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index 216975e78b..d30d252438 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 74e9e5c277..4f99b2f9f6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From a908d914155fcce75d0e22333109e97d10a6fc9a Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 9 May 2024 17:22:06 +0200 Subject: [PATCH 126/161] fix linter warning Remove unused code to address linter warning. Signed-off-by: Flavio Castelli --- src/bundle/verify/policy.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/bundle/verify/policy.rs b/src/bundle/verify/policy.rs index 267badc042..ffeaf7c22e 100644 --- a/src/bundle/verify/policy.rs +++ b/src/bundle/verify/policy.rs @@ -237,15 +237,6 @@ impl VerificationPolicy for AllOf<'_> { } } -pub(crate) struct UnsafeNoOp; - -impl VerificationPolicy for UnsafeNoOp { - fn verify(&self, _cert: &x509_cert::Certificate) -> PolicyResult { - warn!("unsafe (no-op) verification policy used! no verification performed!"); - Ok(()) - } -} - /// Verifies the certificate's "identity", corresponding to the X.509v3 SAN. /// Identities are verified modulo an OIDC issuer, so the issuer's URI /// is also required. From c01a1a2ae5bfba92dd0e57640dd83d1e05b93c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 08:58:52 +0000 Subject: [PATCH 127/161] chore(deps): Update webbrowser requirement from 0.8.12 to 1.0.1 Updates the requirements on [webbrowser](https://github.com/amodm/webbrowser-rs) to permit the latest version. - [Release notes](https://github.com/amodm/webbrowser-rs/releases) - [Changelog](https://github.com/amodm/webbrowser-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/amodm/webbrowser-rs/compare/v0.8.12...v1.0.1) --- updated-dependencies: - dependency-name: webbrowser dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 39e9bf8833..b856a7fe82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ openidconnect = { version = "3.0", default-features = false, features = [ ], optional = true } p256 = "0.13" p384 = "0.13" -webbrowser = "0.8.12" +webbrowser = "1.0.1" pem = { version = "3.0", features = ["serde"] } pkcs1 = { version = "0.7.5", features = ["std"] } pkcs8 = { version = "0.10.2", features = [ From 93f09d5997c5a4c5b38a436ece7404b5b5c3c1b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 08:58:59 +0000 Subject: [PATCH 128/161] chore(deps): Update cached requirement from 0.49.2 to 0.51.3 --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 39e9bf8833..ce0aa4f6ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.22.0" -cached = { version = "0.49.2", optional = true, features = ["async"] } +cached = { version = "0.51.3", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = ["now", "serde"] } const-oid = { version = "0.9.6", features = ["db"] } From b22548e047fbedaf2f0ba80173434b7bf15fae80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:05:53 +0000 Subject: [PATCH 129/161] chore(deps): Update testcontainers requirement from 0.15 to 0.16 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.15...0.16.3) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4e769fb756..5bce74875f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,7 +143,7 @@ openssl = "0.10.38" rstest = "0.19.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.15" +testcontainers = "0.16" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From 5639c1ed173b93adf73b952df4850eeab216d8a9 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 9 May 2024 19:19:27 +0200 Subject: [PATCH 130/161] test: fix usage of testcontainers Update the code to build with the latest version of the testcontainers crate. Signed-off-by: Flavio Castelli --- Makefile | 2 +- src/cosign/mod.rs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 1316c293ee..ded4560c9a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ doc: .PHONY: test test: fmt lint doc - cargo test --workspace + cargo test --workspace --features full-native-tls,test-registry .PHONY: clean clean: diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index f05e416899..b52e14175b 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -297,7 +297,7 @@ mod tests { use crate::crypto::SigningScheme; #[cfg(feature = "test-registry")] - use testcontainers::{clients, core::WaitFor}; + use testcontainers::{core::WaitFor, runners::AsyncRunner}; pub(crate) const REKOR_PUB_KEY: &str = r#"-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr @@ -566,10 +566,8 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ #[tokio::test] #[serial_test::serial] async fn sign_verify_image(#[case] signing_scheme: SigningScheme) { - let docker = clients::Cli::default(); - let image = registry_image(); - let test_container = docker.run(image); - let port = test_container.get_host_port_ipv4(5000); + let test_container = registry_image().start().await; + let port = test_container.get_host_port_ipv4(5000).await; let mut client = ClientBuilder::default() .enable_registry_caching() From 1cff32e17273f907077e2c98d2e44791ae813669 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 19:11:25 +0000 Subject: [PATCH 131/161] --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 3a3f6acb1b..28c2f89fd1 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 2054a56ea3..ec63c9bfed 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index f4c34d2b96..ec1b86fdd5 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index d30d252438..4bd32233e6 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -8,7 +8,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4f99b2f9f6..426a8ddab4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -68,7 +68,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 20908075ba5e663fe7675783f57e32e8a0020bf8 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 21 May 2024 19:04:59 +0200 Subject: [PATCH 132/161] chore(deps): change provider of cargo-audit GH action The GH action of cargo audit is now provides by the maintainers of the project. The previous actions we were using seems to not be maintained anymore. Signed-off-by: Flavio Castelli --- .github/workflows/security-audit-cron.yml | 12 ++++++++++-- .github/workflows/security-audit-reactive.yml | 14 +++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index ec1b86fdd5..3dd7184f2e 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -1,12 +1,20 @@ name: Security audit cron job on: schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" + +permissions: + contents: read + jobs: audit: + permissions: + checks: write # for rustsec/audit-check to create check + contents: read # for actions/checkout to fetch code + issues: write # for rustsec/audit-check to create issues runs-on: ubuntu-latest steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 + - uses: rustsec/audit-check@dd51754d4e59da7395a4cd9b593f0ff2d61a9b95 # v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index 4bd32233e6..a642acfa45 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -2,13 +2,21 @@ name: Security audit on: push: paths: - - '**/Cargo.toml' - - '**/Cargo.lock' + - "**/Cargo.toml" + - "**/Cargo.lock" + +permissions: + contents: read + jobs: security_audit: + permissions: + checks: write # for rustsec/audit-check to create check + contents: read # for actions/checkout to fetch code + issues: write # for rustsec/audit-check to create issues runs-on: ubuntu-latest steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # v1.2.0 + - uses: rustsec/audit-check@dd51754d4e59da7395a4cd9b593f0ff2d61a9b95 # v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} From 4076349271b02fbb9481860779dc59fd73ac5e10 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 23 May 2024 19:13:22 +0200 Subject: [PATCH 133/161] docs: handle feature flags When building the docs, provide additional information about which feature makes a module/struct/function/method available. On top of that, fix some warnings returned by `cargo doc`. The warnings were caused by broken internal references. Signed-off-by: Flavio Castelli --- Cargo.toml | 21 ++++++++++++++++++--- src/bundle/mod.rs | 2 ++ src/bundle/verify/verifier.rs | 2 ++ src/cosign/client_builder.rs | 9 +++++---- src/cosign/mod.rs | 6 +++--- src/errors.rs | 4 ++++ src/lib.rs | 8 ++++++++ src/oauth/mod.rs | 2 +- src/registry/config.rs | 7 ++++--- src/trust/mod.rs | 1 + 10 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5bce74875f..3c8317cae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ license = "Apache-2.0" readme = "README.md" repository = "https://github.com/sigstore/sigstore-rs" +[package.metadata.docs.rs] +all-features = true + [features] default = ["full-native-tls", "cached-client", "sigstore-trust-root", "bundle"] wasm = ["getrandom/js", "ring/wasm32_unknown_unknown_js", "chrono/wasmbind"] @@ -44,7 +47,13 @@ sign = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"] verify = ["sigstore_protobuf_specs", "fulcio", "rekor", "cert"] bundle = ["sign", "verify"] -sigstore-trust-root = ["sigstore_protobuf_specs", "futures-util", "tough", "regex", "tokio/sync"] +sigstore-trust-root = [ + "sigstore_protobuf_specs", + "futures-util", + "tough", + "regex", + "tokio/sync", +] cosign-native-tls = [ "oci-distribution/native-tls", @@ -76,7 +85,10 @@ async-trait = "0.1.52" base64 = "0.22.0" cached = { version = "0.51.3", optional = true, features = ["async"] } cfg-if = "1.0.0" -chrono = { version = "0.4.27", default-features = false, features = ["now", "serde"] } +chrono = { version = "0.4.27", default-features = false, features = [ + "now", + "serde", +] } const-oid = { version = "0.9.6", features = ["db"] } digest = { version = "0.10.3", default-features = false } ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } @@ -113,7 +125,10 @@ rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" -serde_with = { version = "3.4.0", features = ["base64", "json"], optional = true } +serde_with = { version = "3.4.0", features = [ + "base64", + "json", +], optional = true } sha2 = { version = "0.10.6", features = ["oid"] } signature = { version = "2.0" } sigstore_protobuf_specs = { version = "0.3.2", optional = true } diff --git a/src/bundle/mod.rs b/src/bundle/mod.rs index 61ad0c48b3..e89d34f7e8 100644 --- a/src/bundle/mod.rs +++ b/src/bundle/mod.rs @@ -18,8 +18,10 @@ pub use sigstore_protobuf_specs::dev::sigstore::bundle::v1::Bundle; mod models; +#[cfg_attr(docsrs, doc(cfg(feature = "sign")))] #[cfg(feature = "sign")] pub mod sign; +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] #[cfg(feature = "verify")] pub mod verify; diff --git a/src/bundle/verify/verifier.rs b/src/bundle/verify/verifier.rs index 0f4a4a5526..9005d835a2 100644 --- a/src/bundle/verify/verifier.rs +++ b/src/bundle/verify/verifier.rs @@ -206,6 +206,7 @@ impl Verifier { impl Verifier { /// Constructs an [`Verifier`] against the public-good trust root. #[cfg(feature = "sigstore-trust-root")] + #[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] pub async fn production() -> SigstoreResult { let updater = SigstoreTrustRoot::new(None).await?; @@ -279,6 +280,7 @@ pub mod blocking { impl Verifier { /// Constructs a synchronous [`Verifier`] against the public-good trust root. #[cfg(feature = "sigstore-trust-root")] + #[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] pub fn production() -> SigstoreResult { let rt = tokio::runtime::Builder::new_current_thread() .enable_all() diff --git a/src/cosign/client_builder.rs b/src/cosign/client_builder.rs index 1a688d9139..66239e65b2 100644 --- a/src/cosign/client_builder.rs +++ b/src/cosign/client_builder.rs @@ -30,15 +30,15 @@ use crate::trust::TrustRoot; /// Rekor integration can be enabled by specifying Rekor's public key. /// This can be provided via a [`crate::trust::ManualTrustRoot`]. /// -/// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods +/// > Note well: the [`trust::sigstore`](crate::trust::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// ## Fulcio integration /// /// Fulcio integration can be enabled by specifying Fulcio's certificate. -/// This can be provided via a [`crate::sigstore::ManualTrustRoot`]. +/// This can be provided via a [`crate::trust::sigstore::ManualTrustRoot`]. /// -/// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods +/// > Note well: the [`trust::sigstore`](crate::trust::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// ## Registry caching @@ -63,6 +63,7 @@ pub struct ClientBuilder<'a> { impl<'a> ClientBuilder<'a> { /// Enable caching of data returned from remote OCI registries #[cfg(feature = "cached-client")] + #[cfg_attr(docsrs, doc(cfg(feature = "cached-registry")))] pub fn enable_registry_caching(mut self) -> Self { self.enable_registry_caching = true; self @@ -71,7 +72,7 @@ impl<'a> ClientBuilder<'a> { /// Optional - Configures the roots of trust. /// /// Enables Fulcio and Rekor integration with the given trust repository. - /// See [crate::sigstore::TrustRoot] for more details on trust repositories. + /// See [crate::trust::sigstore::TrustRoot] for more details on trust repositories. pub fn with_trust_repository(mut self, repo: &'a R) -> Result { let rekor_keys = repo.rekor_keys()?; if !rekor_keys.is_empty() { diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index b52e14175b..7d3795b427 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -101,12 +101,12 @@ pub trait CosignCapabilities { /// must be satisfied: /// /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Rekor integration enabled (see [`crate::sigstore::ManualTrustRoot`]) + /// have been created with Rekor integration enabled (see [`crate::trust::sigstore::ManualTrustRoot`]) /// * The [`sigstore::cosign::Client`](crate::cosign::client::Client) must - /// have been created with Fulcio integration enabled (see [`crate::sigstore::ManualTrustRoot]) + /// have been created with Fulcio integration enabled (see [`crate::trust::sigstore::ManualTrustRoot]) /// * The layer must include a bundle produced by Rekor /// - /// > Note well: the [`sigstore`](crate::sigstore) module provides helper structs and methods + /// > Note well: the [`trust::sigstore`](crate::trust::sigstore) module provides helper structs and methods /// > to obtain this data from the official TUF repository of the Sigstore project. /// /// When the embedded certificate cannot be verified, [`SignatureLayer::certificate_signature`] diff --git a/src/errors.rs b/src/errors.rs index 56a3f93856..7403d24e3a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -23,6 +23,7 @@ use crate::cosign::{ }; #[cfg(feature = "cosign")] +#[cfg_attr(docsrs, doc(cfg(feature = "cosign")))] #[derive(Error, Debug)] #[error("Several Signature Layers failed verification")] pub struct SigstoreVerifyConstraintsError<'a> { @@ -30,6 +31,7 @@ pub struct SigstoreVerifyConstraintsError<'a> { } #[cfg(feature = "cosign")] +#[cfg_attr(docsrs, doc(cfg(feature = "cosign")))] #[derive(Error, Debug)] #[error("Several Constraints failed to apply on the SignatureLayer")] pub struct SigstoreApplicationConstraintsError<'a> { @@ -135,6 +137,7 @@ pub enum SigstoreError { // HACK(tnytown): Remove when we rework the Fulcio V2 endpoint. #[cfg(feature = "fulcio")] + #[cfg_attr(docsrs, doc(cfg(feature = "fulcio")))] #[error(transparent)] ReqwestError(#[from] reqwest::Error), @@ -163,6 +166,7 @@ pub enum SigstoreError { SigstoreNoVerifiedLayer, #[cfg(feature = "sigstore-trust-root")] + #[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] #[error(transparent)] TufError(#[from] Box), diff --git a/src/lib.rs b/src/lib.rs index fa336c1029..ad2586060c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,29 +258,37 @@ #![forbid(unsafe_code)] #![warn(clippy::unwrap_used, clippy::panic)] +#![cfg_attr(docsrs, feature(doc_cfg))] pub mod crypto; pub mod trust; +#[cfg_attr(docsrs, doc(cfg(feature = "mock-client")))] #[cfg(feature = "mock-client")] mod mock_client; +#[cfg_attr(docsrs, doc(cfg(feature = "cosign")))] #[cfg(feature = "cosign")] pub mod cosign; pub mod errors; +#[cfg_attr(docsrs, doc(cfg(feature = "fulcio")))] #[cfg(feature = "fulcio")] pub mod fulcio; +#[cfg_attr(docsrs, doc(cfg(feature = "oauth")))] #[cfg(feature = "oauth")] pub mod oauth; +#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] #[cfg(feature = "registry")] pub mod registry; +#[cfg_attr(docsrs, doc(cfg(feature = "rekor")))] #[cfg(feature = "rekor")] pub mod rekor; +#[cfg_attr(docsrs, doc(cfg(any(feature = "sign", feature = "verify"))))] #[cfg(any(feature = "sign", feature = "verify"))] pub mod bundle; diff --git a/src/oauth/mod.rs b/src/oauth/mod.rs index a5419e7fce..b78bd78d6c 100644 --- a/src/oauth/mod.rs +++ b/src/oauth/mod.rs @@ -15,5 +15,5 @@ pub mod openidflow; -mod token; +pub mod token; pub use token::IdentityToken; diff --git a/src/registry/config.rs b/src/registry/config.rs index b7dd3d2da1..9bfd9c34db 100644 --- a/src/registry/config.rs +++ b/src/registry/config.rs @@ -147,7 +147,8 @@ pub struct ClientConfig { pub protocol: ClientProtocol, /// Accept invalid hostname. Defaults to false - #[cfg(feature = "native-tls")] + #[cfg_attr(docsrs, doc(cfg(feature = "full-native-tls")))] + #[cfg(feature = "full-native-tls")] pub accept_invalid_hostnames: bool, /// Accept invalid certificates. Defaults to false @@ -162,7 +163,7 @@ impl Default for ClientConfig { fn default() -> Self { ClientConfig { protocol: ClientProtocol::Https, - #[cfg(feature = "native-tls")] + #[cfg(feature = "full-native-tls")] accept_invalid_hostnames: false, accept_invalid_certificates: false, extra_root_certificates: Vec::new(), @@ -175,7 +176,7 @@ impl From for oci_distribution::client::ClientConfig { oci_distribution::client::ClientConfig { protocol: config.protocol.into(), accept_invalid_certificates: config.accept_invalid_certificates, - #[cfg(feature = "native-tls")] + #[cfg(feature = "full-native-tls")] accept_invalid_hostnames: config.accept_invalid_hostnames, extra_root_certificates: config .extra_root_certificates diff --git a/src/trust/mod.rs b/src/trust/mod.rs index 966bb238fc..e0fe98b13f 100644 --- a/src/trust/mod.rs +++ b/src/trust/mod.rs @@ -15,6 +15,7 @@ use webpki::types::CertificateDer; +#[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] #[cfg(feature = "sigstore-trust-root")] pub mod sigstore; From 906b5f6249be367221e808e1971e24e37760dab2 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 9 Apr 2024 18:58:24 +0200 Subject: [PATCH 134/161] ci: require docs to be fine Build the documentation as part of PRs. Fail when something wrong is found while building the docs. Signed-off-by: Flavio Castelli --- .github/workflows/tests.yml | 13 +++++++++++++ Makefile | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 426a8ddab4..234472691c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,19 @@ jobs: command: test args: --workspace --features full-native-tls,test-registry + doc: + name: Build Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 + with: + profile: minimal + toolchain: nightly + override: true + - run: | + make doc + fmt: name: Rustfmt runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index ded4560c9a..c05cbf9a0c 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ lint: .PHONY: doc doc: - cargo doc + RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo +nightly doc --all-features --no-deps .PHONY: test test: fmt lint doc From 1686be894a8d152ce6915afbee9cce45269a6195 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 23 May 2024 17:18:49 +0200 Subject: [PATCH 135/161] fix: allow ManualTrustRoot to have multiple rekor keys `ManualTrustRoot` implements the `TrustRoot` trait, which requires the implemented to have multiple rekor keys. The `ManualTrustRoot` struct has now been updated to handle that, prior to that only one Rekor key was stored inside of a `ManualTrustRoot` instance. Signed-off-by: Flavio Castelli --- examples/cosign/verify/main.rs | 14 ++++++-------- src/lib.rs | 4 ++-- src/trust/mod.rs | 14 ++++---------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/examples/cosign/verify/main.rs b/examples/cosign/verify/main.rs index ad8b1555d9..91112ec694 100644 --- a/examples/cosign/verify/main.rs +++ b/examples/cosign/verify/main.rs @@ -63,11 +63,11 @@ struct Cli { /// File containing Rekor's public key (e.g.: ~/.sigstore/root/targets/rekor.pub) #[clap(long, required(false))] - rekor_pub_key: Option, + rekor_pub_keys: Vec, /// File containing Fulcio's certificate (e.g.: ~/.sigstore/root/targets/fulcio.crt.pem) #[clap(long, required(false))] - fulcio_cert: Option, + fulcio_certs: Vec, /// The issuer of the OIDC token used by the user to authenticate against Fulcio #[clap(long, required(false))] @@ -235,14 +235,14 @@ async fn fulcio_and_rekor_data(cli: &Cli) -> anyhow::Result anyhow::Result { - pub fulcio_certs: Option>>, - pub rekor_key: Option>, + pub fulcio_certs: Vec>, + pub rekor_keys: Vec>, pub ctfe_keys: Vec>, } impl TrustRoot for ManualTrustRoot<'_> { fn fulcio_certs(&self) -> crate::errors::Result> { - Ok(match &self.fulcio_certs { - Some(certs) => certs.clone(), - None => Vec::new(), - }) + Ok(self.fulcio_certs.clone()) } fn rekor_keys(&self) -> crate::errors::Result> { - Ok(match &self.rekor_key { - Some(key) => vec![&key[..]], - None => Vec::new(), - }) + Ok(self.rekor_keys.iter().map(|key| &key[..]).collect()) } fn ctfe_keys(&self) -> crate::errors::Result> { From b9c86c4f72e3ca47bca3ff17330867dee82b48f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 19:31:46 +0000 Subject: [PATCH 136/161] build(deps): update testcontainers requirement from 0.16 to 0.17 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.16.0...0.17.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3c8317cae6..5c6bd1aa65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.19.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.16" +testcontainers = "0.17" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From e83644b6e9c4ac4e1adf37c2727b6eafb64e863e Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Thu, 30 May 2024 17:57:45 +0200 Subject: [PATCH 137/161] chore(deps): update code to consume latest testcontainers release Change the code to consume latest API of testcontainers Signed-off-by: Flavio Castelli --- src/cosign/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cosign/mod.rs b/src/cosign/mod.rs index 7d3795b427..379d0de366 100644 --- a/src/cosign/mod.rs +++ b/src/cosign/mod.rs @@ -566,8 +566,14 @@ TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ #[tokio::test] #[serial_test::serial] async fn sign_verify_image(#[case] signing_scheme: SigningScheme) { - let test_container = registry_image().start().await; - let port = test_container.get_host_port_ipv4(5000).await; + let test_container = registry_image() + .start() + .await + .expect("failed to start registry"); + let port = test_container + .get_host_port_ipv4(5000) + .await + .expect("failed to get port"); let mut client = ClientBuilder::default() .enable_registry_caching() From 927ec22a159ba5f116f65d47a98a0f9b62c65300 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:40:53 +0000 Subject: [PATCH 138/161] build(deps): update rstest requirement from 0.19.0 to 0.21.0 Updates the requirements on [rstest](https://github.com/la10736/rstest) to permit the latest version. - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/v0.19.0...v0.21.0) --- updated-dependencies: - dependency-name: rstest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5c6bd1aa65..4a30e731f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,7 +155,7 @@ chrono = "0.4.31" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" -rstest = "0.19.0" +rstest = "0.21.0" serial_test = "3.0.0" tempfile = "3.3.0" testcontainers = "0.17" From 669c315a489b0ec0ea9d7908e8272239992058e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:03:46 +0000 Subject: [PATCH 139/161] build(deps): update testcontainers requirement from 0.17 to 0.18 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.17.0...0.18.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4a30e731f7..b750f5b401 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.21.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.17" +testcontainers = "0.18" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From 63f3dc11cc825b2e729ebee46d4812aeeb19870f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:20:56 +0000 Subject: [PATCH 140/161] build(deps): bump actions/checkout from 4.1.6 to 4.1.7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.6 to 4.1.7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/a5ac7e51b41094c92402da3b24376905380afc29...692973e3d937129bcbf40652eb9f2f61becf3332) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../workflows/auto-publish-crates-upon-release.yml | 2 +- .github/workflows/conformance.yml | 2 +- .github/workflows/security-audit-cron.yml | 2 +- .github/workflows/security-audit-reactive.yml | 2 +- .github/workflows/tests.yml | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/auto-publish-crates-upon-release.yml b/.github/workflows/auto-publish-crates-upon-release.yml index 28c2f89fd1..67bc563593 100644 --- a/.github/workflows/auto-publish-crates-upon-release.yml +++ b/.github/workflows/auto-publish-crates-upon-release.yml @@ -7,7 +7,7 @@ jobs: publish-automatically: runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index ec63c9bfed..77bf5fda35 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -6,7 +6,7 @@ jobs: conformance: runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal diff --git a/.github/workflows/security-audit-cron.yml b/.github/workflows/security-audit-cron.yml index 3dd7184f2e..62161fe504 100644 --- a/.github/workflows/security-audit-cron.yml +++ b/.github/workflows/security-audit-cron.yml @@ -14,7 +14,7 @@ jobs: issues: write # for rustsec/audit-check to create issues runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: rustsec/audit-check@dd51754d4e59da7395a4cd9b593f0ff2d61a9b95 # v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-audit-reactive.yml b/.github/workflows/security-audit-reactive.yml index a642acfa45..4cd50993d4 100644 --- a/.github/workflows/security-audit-reactive.yml +++ b/.github/workflows/security-audit-reactive.yml @@ -16,7 +16,7 @@ jobs: issues: write # for rustsec/audit-check to create issues runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: rustsec/audit-check@dd51754d4e59da7395a4cd9b593f0ff2d61a9b95 # v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 234472691c..f53412d34c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Check WASM runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -37,7 +37,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -52,7 +52,7 @@ jobs: name: Build Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -65,7 +65,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal @@ -81,7 +81,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: profile: minimal From 6249cc531f3a5308e3d6be109ffa68a512bbce71 Mon Sep 17 00:00:00 2001 From: Andrew Pan <3821575+tnytown@users.noreply.github.com> Date: Thu, 20 Jun 2024 10:17:35 -0500 Subject: [PATCH 141/161] Apply suggestions from code review Co-authored-by: Flavio Castelli Signed-off-by: Andrew Pan <3821575+tnytown@users.noreply.github.com> --- src/bundle/sign.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bundle/sign.rs b/src/bundle/sign.rs index 8e7b0afc4f..0c6e4c82af 100644 --- a/src/bundle/sign.rs +++ b/src/bundle/sign.rs @@ -277,6 +277,7 @@ impl SigningContext { /// Returns a [`SigningContext`] configured against the public-good production Sigstore /// infrastructure. + #[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] #[cfg(feature = "sigstore-trust-root")] pub async fn async_production() -> SigstoreResult { let trust_root = SigstoreTrustRoot::new(None).await?; @@ -294,6 +295,7 @@ impl SigningContext { /// infrastructure. /// /// Async callers should use [`SigningContext::async_production`]. + #[cfg_attr(docsrs, doc(cfg(feature = "sigstore-trust-root")))] #[cfg(feature = "sigstore-trust-root")] pub fn production() -> SigstoreResult { let rt = tokio::runtime::Builder::new_current_thread() From 049ae7a1db74593bb34becfe5d78e95501b066ae Mon Sep 17 00:00:00 2001 From: Andrew Pan Date: Fri, 21 Jun 2024 16:20:07 -0500 Subject: [PATCH 142/161] transparency: pull OID constants from `const-oid` Signed-off-by: Andrew Pan --- src/crypto/transparency.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/crypto/transparency.rs b/src/crypto/transparency.rs index f4de034cb4..1a6cceeaa3 100644 --- a/src/crypto/transparency.rs +++ b/src/crypto/transparency.rs @@ -14,7 +14,7 @@ //! Types for Certificate Transparency validation. -use const_oid::ObjectIdentifier; +use const_oid::db::rfc6962::{CT_PRECERT_SCTS, CT_PRECERT_SIGNING_CERT}; use digest::Digest; use thiserror::Error; use tls_codec::{SerializeBytes, TlsByteVecU16, TlsByteVecU24, TlsSerializeBytes, TlsSize}; @@ -34,18 +34,13 @@ use super::{ }; use crate::fulcio::SigningCertificateDetachedSCT; -// TODO(tnytown): Migrate to const-oid's CT_PRECERT_SCTS when a new release is cut. -const CT_PRECERT_SCTS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.4.2"); -const PRECERTIFICATE_SIGNING_CERTIFICATE: ObjectIdentifier = - ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.4.4"); - fn cert_is_preissuer(cert: &Certificate) -> bool { let eku: ExtendedKeyUsage = match cert.tbs_certificate.get() { Ok(Some((_, ext))) => ext, _ => return false, }; - eku.0.contains(&PRECERTIFICATE_SIGNING_CERTIFICATE) + eku.0.contains(&CT_PRECERT_SIGNING_CERT) } // From b72415a82edb4e7ab06c5bffb373d9a052a8159a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 19:46:07 +0000 Subject: [PATCH 143/161] build(deps): update testcontainers requirement from 0.18 to 0.19 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.18.0...0.19.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b750f5b401..c1e4b55a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.21.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.18" +testcontainers = "0.19" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From 95803294b93ab78bf5352538f8e5b787b136046a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:57:22 +0000 Subject: [PATCH 144/161] build(deps): update testcontainers requirement from 0.19 to 0.20 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.19.0...0.20.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c1e4b55a12..ce738be3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.21.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.19" +testcontainers = "0.20" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From c0837e4b77404193109c08f17783eb3d30d04781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:57:40 +0000 Subject: [PATCH 145/161] build(deps): update cached requirement from 0.51.3 to 0.52.0 --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c1e4b55a12..b555d24c82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.22.0" -cached = { version = "0.51.3", optional = true, features = ["async"] } +cached = { version = "0.52.0", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = [ "now", From c6cfa03aa9533c7b102e79340356821ccd7c58f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:20:40 +0000 Subject: [PATCH 146/161] build(deps): update cached requirement from 0.52.0 to 0.53.1 --- updated-dependencies: - dependency-name: cached dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 71a29154cc..83c4ddff76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ cached-client = ["cached"] [dependencies] async-trait = "0.1.52" base64 = "0.22.0" -cached = { version = "0.52.0", optional = true, features = ["async"] } +cached = { version = "0.53.1", optional = true, features = ["async"] } cfg-if = "1.0.0" chrono = { version = "0.4.27", default-features = false, features = [ "now", From e84fb647ece71a038791d927aebdae3fdeaf39dc Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Mon, 5 Aug 2024 12:18:17 +0200 Subject: [PATCH 147/161] test: address clippy warnings Address the clippu warnings produced by Rust 1.80 update Signed-off-by: Flavio Castelli --- examples/openidflow/openidconnect/main.rs | 4 ++-- src/cosign/payload/mod.rs | 3 +-- src/crypto/mod.rs | 26 +++++++++++------------ src/crypto/signing_key/ecdsa/ec.rs | 3 ++- src/crypto/signing_key/mod.rs | 6 +++--- src/crypto/signing_key/rsa/mod.rs | 2 +- src/crypto/transparency.rs | 8 +++---- src/lib.rs | 10 ++++----- src/oauth/openidflow.rs | 8 +++---- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/examples/openidflow/openidconnect/main.rs b/examples/openidflow/openidconnect/main.rs index b2f9384139..167bfc45c6 100644 --- a/examples/openidflow/openidconnect/main.rs +++ b/examples/openidflow/openidconnect/main.rs @@ -47,10 +47,10 @@ fn main() -> Result<(), anyhow::Error> { match result { Ok((token_response, id_token)) => { - println!("Email {:?}", token_response.email().unwrap().to_string()); + println!("Email {:?}", token_response.email().unwrap()); println!( "Access Token:{:?}", - token_response.access_token_hash().unwrap().to_string() + token_response.access_token_hash().unwrap() ); println!("id_token: {:?}", id_token.to_string()); } diff --git a/src/cosign/payload/mod.rs b/src/cosign/payload/mod.rs index f4eb61e453..1245fb4117 100644 --- a/src/cosign/payload/mod.rs +++ b/src/cosign/payload/mod.rs @@ -15,8 +15,7 @@ //! This module defines different kinds of payload to be signed //! in cosign. Now it supports: -//! * `SimpleSigning`: Refer to -//! +//! * `SimpleSigning`: Refer to pub mod simple_signing; pub use simple_signing::SimpleSigning; diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 92336ffa6d..acab80ab07 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -24,27 +24,27 @@ pub use verification_key::CosignVerificationKey; /// Different digital signature algorithms. /// * `RSA_PSS_SHA256`: RSA PSS padding using SHA-256 -/// for RSA signatures. All the `usize` member inside -/// an RSA enum represents the key size of the RSA key. +/// for RSA signatures. All the `usize` member inside +/// an RSA enum represents the key size of the RSA key. /// * `RSA_PSS_SHA384`: RSA PSS padding using SHA-384 -/// for RSA signatures. +/// for RSA signatures. /// * `RSA_PSS_SHA512`: RSA PSS padding using SHA-512 -/// for RSA signatures. +/// for RSA signatures. /// * `RSA_PKCS1_SHA256`: PKCS#1 1.5 padding using -/// SHA-256 for RSA signatures. +/// SHA-256 for RSA signatures. /// * `RSA_PKCS1_SHA384`: PKCS#1 1.5 padding using -/// SHA-384 for RSA signatures. +/// SHA-384 for RSA signatures. /// * `RSA_PKCS1_SHA512`: PKCS#1 1.5 padding using -/// SHA-512 for RSA signatures. +/// SHA-512 for RSA signatures. /// * `ECDSA_P256_SHA256_ASN1`: ASN.1 DER-encoded ECDSA -/// signatures using the P-256 curve and SHA-256. It -/// is the default signing scheme. +/// signatures using the P-256 curve and SHA-256. It +/// is the default signing scheme. /// * `ECDSA_P384_SHA384_ASN1`: ASN.1 DER-encoded ECDSA -/// signatures using the P-384 curve and SHA-384. +/// signatures using the P-384 curve and SHA-384. /// * `ED25519`: ECDSA signature using SHA2-512 -/// as the digest function and curve edwards25519. The -/// signature format please refer -/// to [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.6). +/// as the digest function and curve edwards25519. The +/// signature format please refer +/// to [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.6). #[allow(non_camel_case_types)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum SigningScheme { diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index a9cd15b5d3..a144bb4ccf 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -26,6 +26,7 @@ //! The [`ECDSAKeys`] has two enums due to their underlying elliptic curves, s.t. //! * `P256` //! * `P384` +//! //! To have an uniform interface for all kinds of asymmetric keys, [`ECDSAKeys`] //! is also wrapped in [`super::super::SigStoreKeyPair`] enum. //! @@ -483,7 +484,7 @@ mod tests { /// This test will do the following things: /// * Generate a ecdsa-P256 keypair. /// * Sign the MESSAGE with the private key and digest algorithm SHA256, - /// then generate a signature. + /// then generate a signature. /// * Verify the signature using the public key. #[test] fn ecdsa_sign_and_verify() { diff --git a/src/crypto/signing_key/mod.rs b/src/crypto/signing_key/mod.rs index f209ba2a60..3c037d218b 100644 --- a/src/crypto/signing_key/mod.rs +++ b/src/crypto/signing_key/mod.rs @@ -34,11 +34,11 @@ //! * [`SigStoreSigner::RSA_PKCS1_SHA384`]: RSA signatures using PKCS#1v1.5 padding and SHA-384. //! * [`SigStoreSigner::RSA_PKCS1_SHA512`]: RSA signatures using PKCS#1v1.5 padding and SHA-512. //! * [`SigStoreSigner::ECDSA_P256_SHA256_ASN1`]: ASN.1 DER-encoded ECDSA -//! signatures using the P-256 curve and SHA-256. +//! signatures using the P-256 curve and SHA-256. //! * [`SigStoreSigner::ECDSA_P384_SHA384_ASN1`]: ASN.1 DER-encoded ECDSA -//! signatures using the P-384 curve and SHA-384. +//! signatures using the P-384 curve and SHA-384. //! * [`SigStoreSigner::ED25519`]: ECDSA signature using SHA2-512 -//! as the digest function and curve edwards25519. +//! as the digest function and curve edwards25519. //! //! # Simple Usages //! diff --git a/src/crypto/signing_key/rsa/mod.rs b/src/crypto/signing_key/rsa/mod.rs index c04ed83917..876d1edb25 100644 --- a/src/crypto/signing_key/rsa/mod.rs +++ b/src/crypto/signing_key/rsa/mod.rs @@ -78,7 +78,7 @@ pub enum DigestAlgorithm { /// RSA-based signing algorithm. /// * `PSS`: Probabilistic Signature Scheme, more secure than `PKCS1v15`. /// * `PKCS1v15`: also known as simply PKCS1, is a simple padding -/// scheme developed for use with RSA keys. +/// scheme developed for use with RSA keys. pub enum PaddingScheme { PSS, PKCS1v15, diff --git a/src/crypto/transparency.rs b/src/crypto/transparency.rs index 1a6cceeaa3..7bec068f33 100644 --- a/src/crypto/transparency.rs +++ b/src/crypto/transparency.rs @@ -372,10 +372,10 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNK AaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw== -----END PUBLIC KEY-----"#; - let cert = Certificate::from_pem(&cert_pem).unwrap(); - let chain = chain_pem.map(|c| Certificate::from_pem(&c).unwrap()); + let cert = Certificate::from_pem(cert_pem).unwrap(); + let chain = chain_pem.map(|c| Certificate::from_pem(c).unwrap()); let sct = CertificateEmbeddedSCT::new(&cert, &chain).unwrap(); - let ctfe_key: VerifyingKey = VerifyingKey::from_str(&ctfe_pem).unwrap(); + let ctfe_key: VerifyingKey = VerifyingKey::from_str(ctfe_pem).unwrap(); let keyring = Keyring::new([ctfe_key.to_public_key_der().unwrap().as_bytes()]).unwrap(); assert!(verify_sct(&sct, &keyring).is_ok()); @@ -391,7 +391,7 @@ mnuk5d670MTXR3p+LIAcxd5MhqIHpLmyYJ5mDKLEoZ/pC0nPuje3JueBcA== -----END PUBLIC KEY-----"#; let sct: SigningCertificateDetachedSCT = serde_json::from_str(sct_json).unwrap(); - let ctfe_key: VerifyingKey = VerifyingKey::from_str(&ctfe_pem).unwrap(); + let ctfe_key: VerifyingKey = VerifyingKey::from_str(ctfe_pem).unwrap(); let keyring = Keyring::new([ctfe_key.to_public_key_der().unwrap().as_bytes()]).unwrap(); assert!(verify_sct(&sct, &keyring).is_ok()); diff --git a/src/lib.rs b/src/lib.rs index 2e52e61d4d..265731f453 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,18 +238,18 @@ //! //! - `default`: Enables `full-native-tls`, `cached-client` and `test-registry` features. //! - `full-native-tls`: Enables support for `fulcio`, `rekor` and `cosign`. All the underlying -//! tls uses `native-tls`. This feature will not enable `test-registry.` +//! tls uses `native-tls`. This feature will not enable `test-registry.` //! - `full-rustls-tls`: Enables support for `fulcio`, `rekor` and `cosign`. All the underlying -//! tls uses `rustls-tls`. This feature will not enable `test-registry.` +//! tls uses `rustls-tls`. This feature will not enable `test-registry.` //! //! - `fulcio-native-tls` and `fulcio-rustls-tls`: Enables support for `fulcio`, but one uses -//! `native-tls` as underlying tls and the other uses `rustls-tls`. +//! `native-tls` as underlying tls and the other uses `rustls-tls`. //! //! - `rekor-native-tls` and `rekor-rustls-tls`: Enables support for `rekor`, but one uses -//! `native-tls` as underlying tls and the other uses `rustls-tls`. +//! `native-tls` as underlying tls and the other uses `rustls-tls`. //! //! - `cosign-native-tls` and `cosign-rustls-tls`: Enables support for `cosign`, but one uses -//! `native-tls` as underlying tls and the other uses `rustls-tls`. +//! `native-tls` as underlying tls and the other uses `rustls-tls`. //! //! - `cached-client`: Enables support for OCI registry client caching. //! diff --git a/src/oauth/openidflow.rs b/src/oauth/openidflow.rs index fccaf8e42e..84466cc255 100644 --- a/src/oauth/openidflow.rs +++ b/src/oauth/openidflow.rs @@ -25,16 +25,16 @@ //! The `auth_url` function returns the following: //! //! - `authorize_url` is a URL that can be opened in a browser. The user will be -//! prompted to login and authorize the application. The user will be redirected to -//! the `redirect_uri` URL with a code parameter. +//! prompted to login and authorize the application. The user will be redirected to +//! the `redirect_uri` URL with a code parameter. //! //! - `client` is a client object that can be used to make requests to the OpenID -//! Connect server. +//! Connect server. //! //! - `nonce` is a random value that is used to prevent replay attacks. //! //! - `pkce_verifier` is a PKCE verifier that can be used to generate the code_verifier -//! value. +//! value. //! //! Once you have recieved the above tuple, you can use the [`RedirectListener::redirect_listener`](RedirectListener::redirect_listener) //! function to get the ID Token and scope. From f3a54448ee3ca2fd38e0000b89e95e73737d0bfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:21:35 +0000 Subject: [PATCH 148/161] build(deps): update testcontainers requirement from 0.20 to 0.21 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.20.0...0.21.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 83c4ddff76..d53441f9e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.21.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.20" +testcontainers = "0.21" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From 3a3c00d5d2ca5fd2021905bb86dcc36c6329c998 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:22:11 +0000 Subject: [PATCH 149/161] build(deps): update rstest requirement from 0.21.0 to 0.22.0 Updates the requirements on [rstest](https://github.com/la10736/rstest) to permit the latest version. - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: rstest dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 83c4ddff76..9a4f9b62c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,7 +155,7 @@ chrono = "0.4.31" clap = { version = "4.0.8", features = ["derive"] } docker_credential = "1.1.0" openssl = "0.10.38" -rstest = "0.21.0" +rstest = "0.22.0" serial_test = "3.0.0" tempfile = "3.3.0" testcontainers = "0.20" From c80a715e2cf5413d8db5d049336242cf1683c27c Mon Sep 17 00:00:00 2001 From: Gerald Pinder Date: Tue, 6 Aug 2024 15:08:29 -0400 Subject: [PATCH 150/161] fix: Allow empty passwords for encrypted pem files Signed-off-by: Gerald Pinder --- src/crypto/signing_key/ecdsa/ec.rs | 53 +++++++++++++++++++++++---- src/crypto/signing_key/ed25519.rs | 50 +++++++++++++++++++++---- src/crypto/signing_key/rsa/keypair.rs | 51 ++++++++++++++++++++++---- 3 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index a9cd15b5d3..8e68f892d1 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -248,13 +248,10 @@ where /// Return the encrypted private key in PEM-encoded format. fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; - let pem = match password.len() { - 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), - _ => pem::Pem::new( - SIGSTORE_PRIVATE_KEY_PEM_LABEL, - kdf::encrypt(&der, password)?, - ), - }; + let pem = pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ); let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) } @@ -378,6 +375,7 @@ mod tests { use super::{EcdsaKeys, EcdsaSigner}; const PASSWORD: &[u8] = b"123"; + const EMPTY_PASSWORD: &[u8] = b""; /// This test will try to read an unencrypted ecdsa /// private key file, which is generated by `sigstore`. @@ -418,6 +416,19 @@ mod tests { ); } + /// This test will try to encrypt a ecdsa keypair and + /// return the pem-encoded contents. + #[test] + fn ecdsa_to_encrypted_pem_empty_password() { + let key = + EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); + let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + assert!( + key.is_ok(), + "can not export private key in encrypted PEM format." + ); + } + /// This test will generate a EcdsaKeys, encode the private key /// it into pem, and decode a new key from the generated pem-encoded /// private key. @@ -432,6 +443,34 @@ mod tests { assert!(key.is_ok(), "can not create EcdsaKeys from PEM string."); } + /// This test will generate a EcdsaKeys, encode the private key + /// it into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn ecdsa_to_and_from_encrypted_pem() { + let key = + EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); + let key = key + .private_key_to_encrypted_pem(PASSWORD) + .expect("export private key to PEM format failed."); + let key = EcdsaKeys::::from_encrypted_pem(key.as_bytes(), PASSWORD); + assert!(key.is_ok(), "can not create EcdsaKeys from PEM string."); + } + + /// This test will generate a EcdsaKeys, encode the private key + /// it into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn ecdsa_to_and_from_encrypted_pem_empty_password() { + let key = + EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); + let key = key + .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .expect("export private key to PEM format failed."); + let key = EcdsaKeys::::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + assert!(key.is_ok(), "can not create EcdsaKeys from PEM string."); + } + /// This test will generate a EcdsaKeys, encode the private key /// it into der, and decode a new key from the generated der-encoded /// private key. diff --git a/src/crypto/signing_key/ed25519.rs b/src/crypto/signing_key/ed25519.rs index f1292c12a7..24d01ba261 100644 --- a/src/crypto/signing_key/ed25519.rs +++ b/src/crypto/signing_key/ed25519.rs @@ -207,13 +207,10 @@ impl KeyPair for Ed25519Keys { /// Return the encrypted asn.1 pkcs8 private key. fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; - let pem = match password.len() { - 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), - _ => pem::Pem::new( - SIGSTORE_PRIVATE_KEY_PEM_LABEL, - kdf::encrypt(&der, password)?, - ), - }; + let pem = pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ); let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) } @@ -291,6 +288,7 @@ mod tests { use super::{Ed25519Keys, Ed25519Signer}; const PASSWORD: &[u8] = b"123"; + const EMPTY_PASSWORD: &[u8] = b""; /// This test will try to read an unencrypted ed25519 /// private key file, which is generated by `sigstore`. @@ -330,6 +328,18 @@ mod tests { ); } + /// This test will try to encrypt a ed25519 keypair and + /// return the pem-encoded contents. + #[test] + fn ed25519_to_encrypted_pem_empty_password() { + let key = Ed25519Keys::new().expect("create Ed25519 keys failed."); + let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + assert!( + key.is_ok(), + "can not export private key in encrypted PEM format." + ); + } + /// This test will generate a Ed25519Keys, encode the private key /// into pem, and decode a new key from the generated pem-encoded /// private key. @@ -343,6 +353,32 @@ mod tests { assert!(key.is_ok(), "can not create Ed25519Keys from PEM string."); } + /// This test will generate a Ed25519Keys, encode the private key + /// into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn ed25519_to_and_from_encrypted_pem() { + let key = Ed25519Keys::new().expect("create ed25519 keys failed."); + let key = key + .private_key_to_encrypted_pem(PASSWORD) + .expect("export private key to PEM format failed."); + let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), PASSWORD); + assert!(key.is_ok(), "can not create Ed25519Keys from PEM string."); + } + + /// This test will generate a Ed25519Keys, encode the private key + /// into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn ed25519_to_and_from_encrypted_pem_empty_password() { + let key = Ed25519Keys::new().expect("create ed25519 keys failed."); + let key = key + .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .expect("export private key to PEM format failed."); + let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + assert!(key.is_ok(), "can not create Ed25519Keys from PEM string."); + } + /// This test will generate a Ed25519Keys, encode the private key /// it into der, and decode a new key from the generated der-encoded /// private key. diff --git a/src/crypto/signing_key/rsa/keypair.rs b/src/crypto/signing_key/rsa/keypair.rs index f54294f598..9f02468adc 100644 --- a/src/crypto/signing_key/rsa/keypair.rs +++ b/src/crypto/signing_key/rsa/keypair.rs @@ -231,13 +231,10 @@ impl KeyPair for RSAKeys { /// Return the encrypted asn.1 pkcs8 private key. fn private_key_to_encrypted_pem(&self, password: &[u8]) -> Result> { let der = self.private_key_to_der()?; - let pem = match password.len() { - 0 => pem::Pem::new(PRIVATE_KEY_PEM_LABEL, der.to_vec()), - _ => pem::Pem::new( - SIGSTORE_PRIVATE_KEY_PEM_LABEL, - kdf::encrypt(&der, password)?, - ), - }; + let pem = pem::Pem::new( + SIGSTORE_PRIVATE_KEY_PEM_LABEL, + kdf::encrypt(&der, password)?, + ); let pem = pem::encode(&pem); Ok(zeroize::Zeroizing::new(pem)) } @@ -283,6 +280,7 @@ mod tests { use super::RSAKeys; const PASSWORD: &[u8] = b"123"; + const EMPTY_PASSWORD: &[u8] = b""; const KEY_SIZE: usize = 2048; /// This test will try to read an unencrypted rsa @@ -324,6 +322,19 @@ mod tests { ); } + /// This test will try to encrypt a rsa keypair and + /// return the pem-encoded contents. The bit size + /// of the rsa key is [`KEY_SIZE`]. + #[test] + fn rsa_to_encrypted_pem_empty_password() { + let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); + let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + assert!( + key.is_ok(), + "can not export private key in encrypted PEM format." + ); + } + /// This test will generate a RSAKeys, encode the private key /// it into pem, and decode a new key from the generated pem-encoded /// private key. @@ -337,6 +348,32 @@ mod tests { assert!(key.is_ok(), "can not create RSAKeys from PEM string."); } + /// This test will generate a RSAKeys, encode the private key + /// it into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn rsa_to_and_from_encrypted_pem() { + let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); + let key = key + .private_key_to_encrypted_pem(PASSWORD) + .expect("export private key to PEM format failed."); + let key = RSAKeys::from_encrypted_pem(key.as_bytes(), PASSWORD); + assert!(key.is_ok(), "can not create RSAKeys from PEM string."); + } + + /// This test will generate a RSAKeys, encode the private key + /// it into pem, and decode a new key from the generated pem-encoded + /// private key. + #[test] + fn rsa_to_and_from_encrypted_pem_empty_password() { + let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); + let key = key + .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .expect("export private key to PEM format failed."); + let key = RSAKeys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + assert!(key.is_ok(), "can not create RSAKeys from PEM string."); + } + /// This test will generate a RSAKeys, encode the private key /// it into der, and decode a new key from the generated der-encoded /// private key. From 48857ffaf1a8aa3bdb03d3d6bf6b93e7a9802472 Mon Sep 17 00:00:00 2001 From: Gerald Pinder Date: Tue, 6 Aug 2024 15:11:18 -0400 Subject: [PATCH 151/161] fix: Allow reading cosign generated encrypted PEM files Signed-off-by: Gerald Pinder --- src/crypto/signing_key/ecdsa/ec.rs | 13 +++++++++++++ src/crypto/signing_key/kdf.rs | 11 ++++++++--- .../cosign_generated_encrypted_empty_private.key | 11 +++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 tests/data/keys/cosign_generated_encrypted_empty_private.key diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index 8e68f892d1..353379b92c 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -403,6 +403,19 @@ mod tests { ); } + /// This test will try to read an encrypted ecdsa + /// private key file, which is generated by `sigstore`. + #[test] + fn ecdsa_from_encrypted_pem_cosign_empty_password() { + let content = fs::read("tests/data/keys/cosign_generated_encrypted_empty_private.key") + .expect("read tests/data/keys/cosign_generated_encrypted_empty_private.key failed."); + let key = EcdsaKeys::::from_encrypted_pem(&content, EMPTY_PASSWORD); + assert!( + key.is_ok(), + "can not create EcdsaKeys from encrypted PEM file" + ); + } + /// This test will try to encrypt a ecdsa keypair and /// return the pem-encoded contents. #[test] diff --git a/src/crypto/signing_key/kdf.rs b/src/crypto/signing_key/kdf.rs index 4fb5a7f9db..b991b4eae6 100644 --- a/src/crypto/signing_key/kdf.rs +++ b/src/crypto/signing_key/kdf.rs @@ -33,7 +33,9 @@ pub const SALT_SIZE: u32 = 32; pub const NAME_SCRYPT: &str = "scrypt"; /// Scrypt algorithm parameter log2(n) -pub const SCRYPT_N: u32 = 32768; +pub const SCRYPT_N_LOW: u32 = 32768; + +pub const SCRYPT_N_HIGH: u32 = 65536; /// Scrypt algorithm parameter r pub const SCRYPT_R: u32 = 8; @@ -95,7 +97,7 @@ impl Default for ScryptKDF { Self { name: NAME_SCRYPT.into(), params: ScryptParams { - n: SCRYPT_N, + n: SCRYPT_N_LOW, r: SCRYPT_R, p: SCRYPT_P, }, @@ -122,7 +124,10 @@ impl ScryptKDF { /// Check whether the given params is as the default, /// to avoid a DoS attack. fn check_params(&self) -> Result<()> { - match self.params.n == SCRYPT_N && self.params.r == SCRYPT_R && self.params.p == SCRYPT_P { + match (self.params.n == SCRYPT_N_LOW || self.params.n == SCRYPT_N_HIGH) + && self.params.r == SCRYPT_R + && self.params.p == SCRYPT_P + { true => Ok(()), false => Err(SigstoreError::PrivateKeyDecryptError( "Unexpected kdf parameters".into(), diff --git a/tests/data/keys/cosign_generated_encrypted_empty_private.key b/tests/data/keys/cosign_generated_encrypted_empty_private.key new file mode 100644 index 0000000000..44246c12ff --- /dev/null +++ b/tests/data/keys/cosign_generated_encrypted_empty_private.key @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjo2NTUzNiwiciI6 +OCwicCI6MX0sInNhbHQiOiJFY3pJR3Z0bnpFcCsxaEpudERnbGI1UnoxN3gwQVMy +YklvWGRNcDQrU1NJPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJtSVVKbG1OMzNINExvZ0dhaG5MamdmK3R4SmJwci93MCJ9LCJj +aXBoZXJ0ZXh0IjoiM3FtS2FidXRuYm1WT1IvTkZGM2NDaUErTXp1WWQ5L0VERDVI +MVlUSGhQVzZsSzAvdjVqdDZCQzlsME12NGV5am9qZkl0d3B6a2JJOXQrZGx5VXZ3 +VUgvMWZjbkFZT2dEdXRLQzkvSkNiOE02SVY2VHpVaDF5c0ZFWDFzeG9xb1FzeUpL +bjM0UVlldFNlaVdDaGtmUHUyZXByQjFEV0pwekdzTGZIakxNVTkzOEdmNk1xM1A0 +N0ZSd2syZzY1cFZnSGMwVWV1L1N1OUs0Zmc9PSJ9 +-----END ENCRYPTED SIGSTORE PRIVATE KEY----- From cd0d10429fc09ddb08a8d1c7244a1623772113af Mon Sep 17 00:00:00 2001 From: Gerald Pinder Date: Sat, 10 Aug 2024 13:04:48 -0400 Subject: [PATCH 152/161] fix: Allow using from_encrypted_pem for unencrypted pems if an empty password is given Signed-off-by: Gerald Pinder --- src/crypto/signing_key/ecdsa/ec.rs | 86 ++++++++++++--------------- src/crypto/signing_key/ed25519.rs | 67 +++++++++++---------- src/crypto/signing_key/rsa/keypair.rs | 72 +++++++++++----------- 3 files changed, 112 insertions(+), 113 deletions(-) diff --git a/src/crypto/signing_key/ecdsa/ec.rs b/src/crypto/signing_key/ecdsa/ec.rs index 353379b92c..94577afba5 100644 --- a/src/crypto/signing_key/ecdsa/ec.rs +++ b/src/crypto/signing_key/ecdsa/ec.rs @@ -153,6 +153,12 @@ where let ec_seckey = SecretKey::::from_sec1_der(pkcs8.private_key)?; Self::from_private_key(ec_seckey) } + PRIVATE_KEY_PEM_LABEL if password.is_empty() => Self::from_pem(private_key), + PRIVATE_KEY_PEM_LABEL if !password.is_empty() => { + Err(SigstoreError::PrivateKeyDecryptError( + "Unencrypted private key but password provided".into(), + )) + } tag => Err(SigstoreError::PrivateKeyDecryptError(format!( "Unsupported pem tag {tag}" ))), @@ -366,6 +372,8 @@ where mod tests { use std::fs; + use rstest::rstest; + use crate::crypto::{ signing_key::{tests::MESSAGE, KeyPair, Signer}, verification_key::CosignVerificationKey, @@ -392,24 +400,16 @@ mod tests { /// This test will try to read an encrypted ecdsa /// private key file, which is generated by `sigstore`. - #[test] - fn ecdsa_from_encrypted_pem() { - let content = fs::read("tests/data/keys/ecdsa_encrypted_private.key") - .expect("read tests/data/keys/ecdsa_encrypted_private.key failed."); - let key = EcdsaKeys::::from_encrypted_pem(&content, PASSWORD); - assert!( - key.is_ok(), - "can not create EcdsaKeys from encrypted PEM file" - ); - } - - /// This test will try to read an encrypted ecdsa - /// private key file, which is generated by `sigstore`. - #[test] - fn ecdsa_from_encrypted_pem_cosign_empty_password() { - let content = fs::read("tests/data/keys/cosign_generated_encrypted_empty_private.key") - .expect("read tests/data/keys/cosign_generated_encrypted_empty_private.key failed."); - let key = EcdsaKeys::::from_encrypted_pem(&content, EMPTY_PASSWORD); + #[rstest] + #[case("tests/data/keys/ecdsa_encrypted_private.key", PASSWORD)] + #[case::empty_password( + "tests/data/keys/cosign_generated_encrypted_empty_private.key", + EMPTY_PASSWORD + )] + #[case::empty_password_unencrypted("tests/data/keys/ecdsa_private.key", EMPTY_PASSWORD)] + fn ecdsa_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) { + let content = fs::read(keypath).expect("read key failed."); + let key = EcdsaKeys::::from_encrypted_pem(&content, password); assert!( key.is_ok(), "can not create EcdsaKeys from encrypted PEM file" @@ -418,27 +418,31 @@ mod tests { /// This test will try to encrypt a ecdsa keypair and /// return the pem-encoded contents. - #[test] - fn ecdsa_to_encrypted_pem() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(EMPTY_PASSWORD)] + fn ecdsa_to_encrypted_pem(#[case] password: &[u8]) { let key = EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); - let key = key.private_key_to_encrypted_pem(PASSWORD); + let key = key.private_key_to_encrypted_pem(password); assert!( key.is_ok(), "can not export private key in encrypted PEM format." ); } - /// This test will try to encrypt a ecdsa keypair and - /// return the pem-encoded contents. + /// This test will ensure that an unencrypted + /// keypair will fail to read if a non-empty + /// password is given. #[test] - fn ecdsa_to_encrypted_pem_empty_password() { - let key = - EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); - let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + fn ecdsa_error_unencrypted_pem_password() { + let content = fs::read("tests/data/keys/ecdsa_private.key").expect("read key failed."); + let key = EcdsaKeys::::from_encrypted_pem(&content, PASSWORD); assert!( - key.is_ok(), - "can not export private key in encrypted PEM format." + key.is_err_and(|e| e + .to_string() + .contains("Unencrypted private key but password provided")), + "read unencrypted key with password" ); } @@ -459,28 +463,16 @@ mod tests { /// This test will generate a EcdsaKeys, encode the private key /// it into pem, and decode a new key from the generated pem-encoded /// private key. - #[test] - fn ecdsa_to_and_from_encrypted_pem() { - let key = - EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); - let key = key - .private_key_to_encrypted_pem(PASSWORD) - .expect("export private key to PEM format failed."); - let key = EcdsaKeys::::from_encrypted_pem(key.as_bytes(), PASSWORD); - assert!(key.is_ok(), "can not create EcdsaKeys from PEM string."); - } - - /// This test will generate a EcdsaKeys, encode the private key - /// it into pem, and decode a new key from the generated pem-encoded - /// private key. - #[test] - fn ecdsa_to_and_from_encrypted_pem_empty_password() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(EMPTY_PASSWORD)] + fn ecdsa_to_and_from_encrypted_pem(#[case] password: &[u8]) { let key = EcdsaKeys::::new().expect("create ecdsa keys with P256 curve failed."); let key = key - .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .private_key_to_encrypted_pem(password) .expect("export private key to PEM format failed."); - let key = EcdsaKeys::::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + let key = EcdsaKeys::::from_encrypted_pem(key.as_bytes(), password); assert!(key.is_ok(), "can not create EcdsaKeys from PEM string."); } diff --git a/src/crypto/signing_key/ed25519.rs b/src/crypto/signing_key/ed25519.rs index 24d01ba261..0946e0fbe9 100644 --- a/src/crypto/signing_key/ed25519.rs +++ b/src/crypto/signing_key/ed25519.rs @@ -120,6 +120,12 @@ impl Ed25519Keys { })?; Self::from_key_pair_bytes(key_pair_bytes) } + PRIVATE_KEY_PEM_LABEL if password.is_empty() => Self::from_pem(encrypted_pem), + PRIVATE_KEY_PEM_LABEL if !password.is_empty() => { + Err(SigstoreError::PrivateKeyDecryptError( + "Unencrypted private key but password provided".into(), + )) + } tag => Err(SigstoreError::PrivateKeyDecryptError(format!( "Unsupported pem tag {tag}" ))), @@ -279,6 +285,8 @@ impl Signer for Ed25519Signer { mod tests { use std::fs; + use rstest::rstest; + use crate::crypto::{ signing_key::{tests::MESSAGE, KeyPair, Signer}, verification_key::CosignVerificationKey, @@ -305,11 +313,12 @@ mod tests { /// This test will try to read an encrypted ed25519 /// private key file, which is generated by `sigstore`. - #[test] - fn ed25519_from_encrypted_pem() { - let content = fs::read("tests/data/keys/ed25519_encrypted_private.key") - .expect("read tests/data/keys/ed25519_encrypted_private.key failed."); - let key = Ed25519Keys::from_encrypted_pem(&content, PASSWORD); + #[rstest] + #[case("tests/data/keys/ed25519_encrypted_private.key", PASSWORD)] + #[case::empty_password("tests/data/keys/ed25519_private.key", EMPTY_PASSWORD)] + fn ed25519_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) { + let content = fs::read(keypath).expect("read key failed."); + let key = Ed25519Keys::from_encrypted_pem(&content, password); assert!( key.is_ok(), "can not create Ed25519Keys from encrypted PEM file" @@ -318,25 +327,30 @@ mod tests { /// This test will try to encrypt a ed25519 keypair and /// return the pem-encoded contents. - #[test] - fn ed25519_to_encrypted_pem() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(EMPTY_PASSWORD)] + fn ed25519_to_encrypted_pem(#[case] password: &[u8]) { let key = Ed25519Keys::new().expect("create Ed25519 keys failed."); - let key = key.private_key_to_encrypted_pem(PASSWORD); + let key = key.private_key_to_encrypted_pem(password); assert!( key.is_ok(), "can not export private key in encrypted PEM format." ); } - /// This test will try to encrypt a ed25519 keypair and - /// return the pem-encoded contents. + /// This test will ensure that an unencrypted + /// keypair will fail to read if a non-empty + /// password is given. #[test] - fn ed25519_to_encrypted_pem_empty_password() { - let key = Ed25519Keys::new().expect("create Ed25519 keys failed."); - let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + fn ed25519_error_unencrypted_pem_password() { + let content = fs::read("tests/data/keys/ed25519_private.key").expect("read key failed."); + let key = Ed25519Keys::from_encrypted_pem(&content, PASSWORD); assert!( - key.is_ok(), - "can not export private key in encrypted PEM format." + key.is_err_and(|e| e + .to_string() + .contains("Unencrypted private key but password provided")), + "read unencrypted key with password" ); } @@ -356,26 +370,15 @@ mod tests { /// This test will generate a Ed25519Keys, encode the private key /// into pem, and decode a new key from the generated pem-encoded /// private key. - #[test] - fn ed25519_to_and_from_encrypted_pem() { - let key = Ed25519Keys::new().expect("create ed25519 keys failed."); - let key = key - .private_key_to_encrypted_pem(PASSWORD) - .expect("export private key to PEM format failed."); - let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), PASSWORD); - assert!(key.is_ok(), "can not create Ed25519Keys from PEM string."); - } - - /// This test will generate a Ed25519Keys, encode the private key - /// into pem, and decode a new key from the generated pem-encoded - /// private key. - #[test] - fn ed25519_to_and_from_encrypted_pem_empty_password() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(EMPTY_PASSWORD)] + fn ed25519_to_and_from_encrypted_pem(#[case] password: &[u8]) { let key = Ed25519Keys::new().expect("create ed25519 keys failed."); let key = key - .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .private_key_to_encrypted_pem(password) .expect("export private key to PEM format failed."); - let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), password); assert!(key.is_ok(), "can not create Ed25519Keys from PEM string."); } diff --git a/src/crypto/signing_key/rsa/keypair.rs b/src/crypto/signing_key/rsa/keypair.rs index 9f02468adc..868b2a3b8c 100644 --- a/src/crypto/signing_key/rsa/keypair.rs +++ b/src/crypto/signing_key/rsa/keypair.rs @@ -101,7 +101,14 @@ impl RSAKeys { })?; Ok(Self::from(private_key)) } - + RSA_PRIVATE_KEY_PEM_LABEL | PRIVATE_KEY_PEM_LABEL if password.is_empty() => { + Self::from_pem(encrypted_pem) + } + RSA_PRIVATE_KEY_PEM_LABEL | PRIVATE_KEY_PEM_LABEL if !password.is_empty() => { + Err(SigstoreError::PrivateKeyDecryptError( + "Unencrypted private key but password provided".into(), + )) + } tag => Err(SigstoreError::PrivateKeyDecryptError(format!( "Unsupported pem tag {tag}" ))), @@ -267,6 +274,8 @@ impl KeyPair for RSAKeys { mod tests { use std::fs; + use rstest::rstest; + use crate::crypto::{ signing_key::{ rsa::{DigestAlgorithm, PaddingScheme, RSASigner}, @@ -298,11 +307,13 @@ mod tests { /// This test will try to read an encrypted rsa /// private key file, which is generated by `sigstore`. - #[test] - fn rsa_from_encrypted_pem() { - let content = fs::read("tests/data/keys/rsa_encrypted_private.key") - .expect("read tests/data/keys/rsa_encrypted_private.key failed."); - let key = RSAKeys::from_encrypted_pem(&content, PASSWORD); + #[rstest] + #[case("tests/data/keys/rsa_encrypted_private.key", PASSWORD)] + #[case("tests/data/keys/rsa_private.key", EMPTY_PASSWORD)] + fn rsa_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) { + let content = + fs::read(keypath).expect("read tests/data/keys/rsa_encrypted_private.key failed."); + let key = RSAKeys::from_encrypted_pem(&content, password); assert!( key.is_ok(), "can not create RSAKeys from encrypted PEM file" @@ -312,26 +323,30 @@ mod tests { /// This test will try to encrypt a rsa keypair and /// return the pem-encoded contents. The bit size /// of the rsa key is [`KEY_SIZE`]. - #[test] - fn rsa_to_encrypted_pem() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(PASSWORD)] + fn rsa_to_encrypted_pem(#[case] password: &[u8]) { let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); - let key = key.private_key_to_encrypted_pem(PASSWORD); + let key = key.private_key_to_encrypted_pem(password); assert!( key.is_ok(), "can not export private key in encrypted PEM format." ); } - /// This test will try to encrypt a rsa keypair and - /// return the pem-encoded contents. The bit size - /// of the rsa key is [`KEY_SIZE`]. + /// This test will ensure that an unencrypted + /// keypair will fail to read if a non-empty + /// password is given. #[test] - fn rsa_to_encrypted_pem_empty_password() { - let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); - let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD); + fn rsa_error_unencrypted_pem_password() { + let content = fs::read("tests/data/keys/rsa_private.key").expect("read key failed."); + let key = RSAKeys::from_encrypted_pem(&content, PASSWORD); assert!( - key.is_ok(), - "can not export private key in encrypted PEM format." + key.is_err_and(|e| e + .to_string() + .contains("Unencrypted private key but password provided")), + "read unencrypted key with password" ); } @@ -351,26 +366,15 @@ mod tests { /// This test will generate a RSAKeys, encode the private key /// it into pem, and decode a new key from the generated pem-encoded /// private key. - #[test] - fn rsa_to_and_from_encrypted_pem() { - let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); - let key = key - .private_key_to_encrypted_pem(PASSWORD) - .expect("export private key to PEM format failed."); - let key = RSAKeys::from_encrypted_pem(key.as_bytes(), PASSWORD); - assert!(key.is_ok(), "can not create RSAKeys from PEM string."); - } - - /// This test will generate a RSAKeys, encode the private key - /// it into pem, and decode a new key from the generated pem-encoded - /// private key. - #[test] - fn rsa_to_and_from_encrypted_pem_empty_password() { + #[rstest] + #[case(PASSWORD)] + #[case::empty_password(EMPTY_PASSWORD)] + fn rsa_to_and_from_encrypted_pem(#[case] password: &[u8]) { let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed."); let key = key - .private_key_to_encrypted_pem(EMPTY_PASSWORD) + .private_key_to_encrypted_pem(password) .expect("export private key to PEM format failed."); - let key = RSAKeys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD); + let key = RSAKeys::from_encrypted_pem(key.as_bytes(), password); assert!(key.is_ok(), "can not create RSAKeys from PEM string."); } From d6a3d1ff9f4e8f6522665fb8e53280311712eb27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:24:12 +0000 Subject: [PATCH 153/161] build(deps): update testcontainers requirement from 0.21 to 0.22 Updates the requirements on [testcontainers](https://github.com/testcontainers/testcontainers-rs) to permit the latest version. - [Release notes](https://github.com/testcontainers/testcontainers-rs/releases) - [Changelog](https://github.com/testcontainers/testcontainers-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-rs/compare/0.21.0...0.22.0) --- updated-dependencies: - dependency-name: testcontainers dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3195a29d95..791e80ccff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,7 @@ openssl = "0.10.38" rstest = "0.22.0" serial_test = "3.0.0" tempfile = "3.3.0" -testcontainers = "0.21" +testcontainers = "0.22" tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } From f59afceea9bc4d4340f42889e912307da49c4799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:28:26 +0000 Subject: [PATCH 154/161] build(deps): update tough requirement from 0.17.1 to 0.18.0 Updates the requirements on [tough](https://github.com/awslabs/tough) to permit the latest version. - [Release notes](https://github.com/awslabs/tough/releases) - [Commits](https://github.com/awslabs/tough/compare/tough-v0.17.1...tough-v0.18.0) --- updated-dependencies: - dependency-name: tough dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 791e80ccff..54a8bf2376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,7 +135,7 @@ sigstore_protobuf_specs = { version = "0.3.2", optional = true } thiserror = "1.0.30" tokio = { version = "1.17.0", features = ["rt"] } tokio-util = { version = "0.7.10", features = ["io-util"] } -tough = { version = "0.17.1", features = ["http"], optional = true } +tough = { version = "0.18.0", features = ["http"], optional = true } tracing = "0.1.31" url = "2.2.2" x509-cert = { version = "0.2.5", features = ["builder", "pem", "std", "sct"] } From d80b800b17fc5cda5aed84a8435aa7e8aac41add Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 10 Sep 2024 11:23:34 +0200 Subject: [PATCH 155/161] fix: fetch Sigstore's TUF repository when default flags are disabled Fetching Sigstore's TUF repository is done using the `aws/tough` crate. This crate is currently using an older version of `reqwest`, which in turn uses an older version of `hyper`. When building the `sigstore` crate with the default features turned off and a limited set of "verification-related" features enabled (like `sigstore-trust-root` and `cosign-rustls-tls`), fetching the TUF repository causes a runtime panic. The panic happens because the old `reqwest` crate is built without TLS support. This was not a problem before, since both sigstore and `tough` used the same version of `reqwest`, hence enabling `cosign-rustls-tls` led to TLS support being enabled also for tough. This commit introduces two new feature flags: `sigstore-trust-root-native-tls` and `sigstore-trust-root-rustl-tls` which enable TLS support also for the old version of reqwest being currently used by tough. Worth of interest, building with the default flags doesn't currently expose this issue. That happens because of a happy coincidence. The "feature chain reaction" is the following one: - The "enable default features" cause the `full-native-tls` feature to be enabled - The `full-native-tls` feature enables `fulcio-native-tls` feature - The `fulcio-native-tls` features enables the `oauth-native-tls` feature - The `oauth-native-tls` features causes the `openidconnect` crate to be required, moreover it enables TLS support of `openidconnect` by enabling the `openidconnect/native-tls` feature. Currently the `openidconnect` crate depends on the same old version of `reqwest` used by aws/tough. Hence, enabling TLS support for `openidconnect` leads to TLS support being enabled also by tough. This coincidental fix is going to disappear as soon as `openidconnect` moves to the latest version of `reqwest`. Signed-off-by: Flavio Castelli --- Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 54a8bf2376..89e2971b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,12 +20,14 @@ full-native-tls = [ "rekor-native-tls", "cosign-native-tls", "mock-client-native-tls", + "sigstore-trust-root-native-tls", ] full-rustls-tls = [ "fulcio-rustls-tls", "rekor-rustls-tls", "cosign-rustls-tls", "mock-client-rustls-tls", + "sigstore-trust-root-rustls-tls", ] # This features is used by tests that use docker to create a registry @@ -51,9 +53,18 @@ sigstore-trust-root = [ "sigstore_protobuf_specs", "futures-util", "tough", + "reqwest_0_11", "regex", "tokio/sync", ] +sigstore-trust-root-native-tls = [ + "reqwest_0_11/native-tls", + "sigstore-trust-root", +] +sigstore-trust-root-rustls-tls = [ + "reqwest_0_11/rustls-tls", + "sigstore-trust-root", +] cosign-native-tls = [ "oci-distribution/native-tls", @@ -121,6 +132,11 @@ reqwest = { version = "0.12", default-features = false, features = [ "json", "multipart", ], optional = true } +# We have to include this old version of reqwest because tough is currently using it. +# By including it, we can configure which TLS backend it's going to use, otherwise fetching the +# TUF sigstore repository will fail at runtime because the old version of reqwest +# will be compiled withtout TLS support. +reqwest_0_11 = { package = "reqwest", version = "0.11", default-features = false, optional = true } rsa = "0.9.2" scrypt = "0.11.0" serde = { version = "1.0.136", features = ["derive"] } From 6fefd4f3e10a651b2b6d1715e167dd9de81a245c Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Fri, 24 May 2024 11:14:37 +0200 Subject: [PATCH 156/161] chore: update cargo audit ignore list A bunch of warnings have popped up. There's nothing we can do about them and they basically no impact on us. - "RUSTSEC-2024-0370": This is a warning about `proc-macro-errors` being unmaintained. It's a transitive dependency of `sigstore` and `oci-spec`. - "RUSTSEC-2023-0055": This is a warning about `lexical` having multiple soundness issues. It's a transitive dependency of `sigstore`. Also, stop ignoring "RUSTSEC-2020-0071" since the issue has been fixed by our transitive dependency. Signed-off-by: Flavio Castelli --- .cargo/audit.toml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index d905f8bcd1..11c1ec0b22 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -1,13 +1,8 @@ [advisories] ignore = [ - "RUSTSEC-2020-0071", # `time` localtime_r segfault -- https://rustsec.org/advisories/RUSTSEC-2020-0071 - # This vulnerability is currently not affecting chrono 0.4.20+ - # See https://github.com/chronotope/chrono/issues/602 - # Chrono 0.5 will upgrade this depependency, but this will lead - # to API breakages. - # - # This is a transitive depependency of tough - "RUSTSEC-2023-0071" # "Classic" RSA timing sidechannel attack from non-constant-time implementation. - # Okay for local use. - # https://rustsec.org/advisories/RUSTSEC-2023-0071.html + "RUSTSEC-2023-0071", # "Classic" RSA timing sidechannel attack from non-constant-time implementation. + # Okay for local use. + # https://rustsec.org/advisories/RUSTSEC-2023-0071.html + "RUSTSEC-2024-0370", # This is a warning about `proc-macro-errors` being unmaintained. It's a transitive dependency of `sigstore` and `oci-spec`. + "RUSTSEC-2023-0055", # This is a warning about `lexical` having multiple soundness issues. It's a transitive dependency of `sigstore`. ] From d2d92174b5bc709eeb00dd20403fc8527484d956 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 10 Sep 2024 15:43:13 +0200 Subject: [PATCH 157/161] chore(deps): relax dependency versions We're a library, unless strictly needed, we should not require major.minor.patch level versions of dependencies. This is especially true for dependencies like `tokio`, where we should limit ourselves to the `major` version only. Signed-off-by: Flavio Castelli --- Cargo.toml | 104 +++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89e2971b98..4cf0cf7dd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,23 +92,23 @@ mock-client = [] cached-client = ["cached"] [dependencies] -async-trait = "0.1.52" -base64 = "0.22.0" -cached = { version = "0.53.1", optional = true, features = ["async"] } +async-trait = "0.1" +base64 = "0.22" +cached = { version = "0.53", optional = true, features = ["async"] } cfg-if = "1.0.0" -chrono = { version = "0.4.27", default-features = false, features = [ +chrono = { version = "0.4", default-features = false, features = [ "now", "serde", ] } -const-oid = { version = "0.9.6", features = ["db"] } -digest = { version = "0.10.3", default-features = false } -ecdsa = { version = "0.16.7", features = ["pkcs8", "digest", "der", "signing"] } -ed25519 = { version = "2.2.1", features = ["alloc"] } +const-oid = { version = "0.9", features = ["db"] } +digest = { version = "0.10", default-features = false } +ecdsa = { version = "0.16", features = ["pkcs8", "digest", "der", "signing"] } +ed25519 = { version = "2.2", features = ["alloc"] } ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } -elliptic-curve = { version = "0.13.5", features = ["arithmetic", "pem"] } +elliptic-curve = { version = "0.13", features = ["arithmetic", "pem"] } futures = "0.3" -futures-util = { version = "0.3.30", optional = true } -lazy_static = "1.4.0" +futures-util = { version = "0.3", optional = true } +lazy_static = "1.4" oci-distribution = { version = "0.11", default-features = false, optional = true } olpc-cjson = { version = "0.1", optional = true } openidconnect = { version = "3.0", default-features = false, features = [ @@ -116,17 +116,12 @@ openidconnect = { version = "3.0", default-features = false, features = [ ], optional = true } p256 = "0.13" p384 = "0.13" -webbrowser = "1.0.1" +webbrowser = "1.0" pem = { version = "3.0", features = ["serde"] } -pkcs1 = { version = "0.7.5", features = ["std"] } -pkcs8 = { version = "0.10.2", features = [ - "pem", - "alloc", - "pkcs5", - "encryption", -] } -rand = { version = "0.8.5", features = ["getrandom", "std"] } -getrandom = "0.2.8" +pkcs1 = { version = "0.7", features = ["std"] } +pkcs8 = { version = "0.10", features = ["pem", "alloc", "pkcs5", "encryption"] } +rand = { version = "0.8", features = ["getrandom", "std"] } +getrandom = "0.2" regex = { version = "1.5.5", optional = true } reqwest = { version = "0.12", default-features = false, features = [ "json", @@ -137,46 +132,43 @@ reqwest = { version = "0.12", default-features = false, features = [ # TUF sigstore repository will fail at runtime because the old version of reqwest # will be compiled withtout TLS support. reqwest_0_11 = { package = "reqwest", version = "0.11", default-features = false, optional = true } -rsa = "0.9.2" -scrypt = "0.11.0" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -serde_with = { version = "3.4.0", features = [ - "base64", - "json", -], optional = true } -sha2 = { version = "0.10.6", features = ["oid"] } +rsa = "0.9" +scrypt = "0.11" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_with = { version = "3.4", features = ["base64", "json"], optional = true } +sha2 = { version = "0.10", features = ["oid"] } signature = { version = "2.0" } -sigstore_protobuf_specs = { version = "0.3.2", optional = true } -thiserror = "1.0.30" -tokio = { version = "1.17.0", features = ["rt"] } -tokio-util = { version = "0.7.10", features = ["io-util"] } -tough = { version = "0.18.0", features = ["http"], optional = true } -tracing = "0.1.31" -url = "2.2.2" -x509-cert = { version = "0.2.5", features = ["builder", "pem", "std", "sct"] } -crypto_secretbox = "0.1.1" -zeroize = "1.5.7" -rustls-webpki = { version = "0.102.1", features = ["alloc"] } -serde_repr = "0.1.16" -hex = "0.4.3" -json-syntax = { version = "0.12.2", features = ["canonicalize", "serde"] } -tls_codec = { version = "0.4.1", features = ["derive"] } -ring = "0.17.6" +sigstore_protobuf_specs = { version = "0.3", optional = true } +thiserror = "1.0" +tokio = { version = "1", features = ["rt"] } +tokio-util = { version = "0.7", features = ["io-util"] } +tough = { version = "0.18", features = ["http"], optional = true } +tracing = "0.1" +url = "2.2" +x509-cert = { version = "0.2", features = ["builder", "pem", "std", "sct"] } +crypto_secretbox = "0.1" +zeroize = "1.5" +rustls-webpki = { version = "0.102", features = ["alloc"] } +serde_repr = "0.1" +hex = "0.4" +json-syntax = { version = "0.12", features = ["canonicalize", "serde"] } +tls_codec = { version = "0.4", features = ["derive"] } +ring = "0.17" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } -assert-json-diff = "2.0.2" -chrono = "0.4.31" -clap = { version = "4.0.8", features = ["derive"] } -docker_credential = "1.1.0" -openssl = "0.10.38" -rstest = "0.22.0" -serial_test = "3.0.0" -tempfile = "3.3.0" +assert-json-diff = "2.0" +chrono = "0.4" +clap = { version = "4.0", features = ["derive"] } +docker_credential = "1.1" +openssl = "0.10" +rstest = "0.22" +serial_test = "3.0" +tempfile = "3.3" testcontainers = "0.22" -tokio = { version = "1.17.0", features = ["rt", "rt-multi-thread"] } -tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } # cosign example mappings From 68fa22abbe1176f22d1a41433b9eb4a8a15ac22e Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Tue, 10 Sep 2024 15:44:38 +0200 Subject: [PATCH 158/161] chore(deps): remove duplicated entry Remove the `chrono` dev-dependency, it's already a top level dependency. Signed-off-by: Flavio Castelli --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4cf0cf7dd1..ee30021b53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,7 +159,6 @@ ring = "0.17" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } assert-json-diff = "2.0" -chrono = "0.4" clap = { version = "4.0", features = ["derive"] } docker_credential = "1.1" openssl = "0.10" From c62204d4a80f9efb0487d6d1c1e688346cd14612 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 11 Sep 2024 10:54:29 +0200 Subject: [PATCH 159/161] chore(deps): bump dev-dependencies Update dev-dependencies to latest versions Signed-off-by: Flavio Castelli --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee30021b53..1cfa53f69b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,12 +159,12 @@ ring = "0.17" [dev-dependencies] anyhow = { version = "1.0", features = ["backtrace"] } assert-json-diff = "2.0" -clap = { version = "4.0", features = ["derive"] } -docker_credential = "1.1" +clap = { version = "4.5", features = ["derive"] } +docker_credential = "1.3" openssl = "0.10" rstest = "0.22" -serial_test = "3.0" -tempfile = "3.3" +serial_test = "3.1" +tempfile = "3.12" testcontainers = "0.22" tokio = { version = "1", features = ["rt", "rt-multi-thread"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } From d02fd1de954295da3a5e9f95236b1aaa3180141e Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 11 Sep 2024 11:20:15 +0200 Subject: [PATCH 160/161] chore(deps): bump dependencies Bump dependencies to latest version. Signed-off-by: Flavio Castelli --- Cargo.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1cfa53f69b..2b1f272870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,14 +104,14 @@ const-oid = { version = "0.9", features = ["db"] } digest = { version = "0.10", default-features = false } ecdsa = { version = "0.16", features = ["pkcs8", "digest", "der", "signing"] } ed25519 = { version = "2.2", features = ["alloc"] } -ed25519-dalek = { version = "2.0.0-rc.2", features = ["pkcs8", "rand_core"] } +ed25519-dalek = { version = "2.1", features = ["pkcs8", "rand_core"] } elliptic-curve = { version = "0.13", features = ["arithmetic", "pem"] } futures = "0.3" futures-util = { version = "0.3", optional = true } -lazy_static = "1.4" +lazy_static = "1.5" oci-distribution = { version = "0.11", default-features = false, optional = true } olpc-cjson = { version = "0.1", optional = true } -openidconnect = { version = "3.0", default-features = false, features = [ +openidconnect = { version = "3.5", default-features = false, features = [ "reqwest", ], optional = true } p256 = "0.13" @@ -122,7 +122,7 @@ pkcs1 = { version = "0.7", features = ["std"] } pkcs8 = { version = "0.10", features = ["pem", "alloc", "pkcs5", "encryption"] } rand = { version = "0.8", features = ["getrandom", "std"] } getrandom = "0.2" -regex = { version = "1.5.5", optional = true } +regex = { version = "1.10", optional = true } reqwest = { version = "0.12", default-features = false, features = [ "json", "multipart", @@ -136,19 +136,19 @@ rsa = "0.9" scrypt = "0.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -serde_with = { version = "3.4", features = ["base64", "json"], optional = true } +serde_with = { version = "3.9", features = ["base64", "json"], optional = true } sha2 = { version = "0.10", features = ["oid"] } -signature = { version = "2.0" } +signature = { version = "2.2" } sigstore_protobuf_specs = { version = "0.3", optional = true } thiserror = "1.0" tokio = { version = "1", features = ["rt"] } tokio-util = { version = "0.7", features = ["io-util"] } tough = { version = "0.18", features = ["http"], optional = true } tracing = "0.1" -url = "2.2" +url = "2.5" x509-cert = { version = "0.2", features = ["builder", "pem", "std", "sct"] } crypto_secretbox = "0.1" -zeroize = "1.5" +zeroize = "1.8" rustls-webpki = { version = "0.102", features = ["alloc"] } serde_repr = "0.1" hex = "0.4" From ecb7df56052c00aba0f1f078c42816edeb74f100 Mon Sep 17 00:00:00 2001 From: Flavio Castelli Date: Wed, 11 Sep 2024 12:26:06 +0200 Subject: [PATCH 161/161] Tag 0.10.0 relase Lots of changes happened, some of them breaking the API. Checkout the changelog for a detailed overview. Signed-off-by: Flavio Castelli --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a77aba2c5..7acc45a682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,73 @@ +# v0.10.0 + +## What's Changed + +- chore(deps): Update oci-distribution requirement from 0.10 to 0.11 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/343 +- verify: init by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/311 +- chore(deps): Update rstest requirement from 0.18.1 to 0.19.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/351 +- chore(deps): Bump actions/checkout from 4.1.2 to 4.1.5 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/360 +- fix linter warning by @flavio in https://github.com/sigstore/sigstore-rs/pull/361 +- chore(deps): Update cached requirement from 0.49.2 to 0.51.3 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/362 +- chore(deps): Update webbrowser requirement from 0.8.12 to 1.0.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/359 +- chore(deps): Bump actions/checkout from 4.1.5 to 4.1.6 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/363 +- chore(deps): Update testcontainers requirement from 0.15 to 0.16 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/355 +- chore(deps): change provider of cargo-audit GH action by @flavio in https://github.com/sigstore/sigstore-rs/pull/364 +- fix docs by @flavio in https://github.com/sigstore/sigstore-rs/pull/366 +- fix: allow ManualTrustRoot to have multiple rekor keys by @flavio in https://github.com/sigstore/sigstore-rs/pull/365 +- build(deps): update testcontainers requirement from 0.16 to 0.17 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/368 +- build(deps): update rstest requirement from 0.19.0 to 0.21.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/370 +- build(deps): bump actions/checkout from 4.1.6 to 4.1.7 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/372 +- build(deps): update testcontainers requirement from 0.17 to 0.18 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/371 +- Signed Certificate Timestamp verification by @tnytown in https://github.com/sigstore/sigstore-rs/pull/326 +- transparency: pull OID constants from `const-oid` by @tnytown in https://github.com/sigstore/sigstore-rs/pull/374 +- build(deps): update testcontainers requirement from 0.18 to 0.19 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/375 +- build(deps): update cached requirement from 0.51.3 to 0.52.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/377 +- build(deps): update testcontainers requirement from 0.19 to 0.20 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/376 +- build(deps): update cached requirement from 0.52.0 to 0.53.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/379 +- build(deps): update rstest requirement from 0.21.0 to 0.22.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/383 +- build(deps): update testcontainers requirement from 0.20 to 0.21 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/382 +- build(deps): update testcontainers requirement from 0.21 to 0.22 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/386 +- fix: Allow empty passwords for encrypted pem files by @gmpinder in https://github.com/sigstore/sigstore-rs/pull/381 +- build(deps): update tough requirement from 0.17.1 to 0.18.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/389 +- dependency cleanup by @flavio in https://github.com/sigstore/sigstore-rs/pull/390 +- chore: update cargo audit ignore list by @flavio in https://github.com/sigstore/sigstore-rs/pull/387 + +## New Contributors + +- @tnytown made their first contribution in https://github.com/sigstore/sigstore-rs/pull/326 +- @gmpinder made their first contribution in https://github.com/sigstore/sigstore-rs/pull/381 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.9.0...v0.10.0 + +# v0.9.0 + +## What's Changed + +- sign: init by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/310 +- cargo audit: ignore RUSTSEC-2023-0071 by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/321 +- chore(deps): Update json-syntax requirement from 0.9.6 to 0.10.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/319 +- chore(deps): Update cached requirement from 0.46.0 to 0.47.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/323 +- chore(deps): Update serial_test requirement from 2.0.0 to 3.0.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/322 +- dep: update rustls-webpki, fold in pki_types by @jleightcap in https://github.com/sigstore/sigstore-rs/pull/324 +- chore(deps): Update cached requirement from 0.47.0 to 0.48.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/325 +- chore(deps): Update json-syntax requirement from 0.10.0 to 0.11.1 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/327 +- chore(deps): Update cached requirement from 0.48.0 to 0.49.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/329 +- chore(deps): Update json-syntax requirement from 0.11.1 to 0.12.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/330 +- lint: fix lint error of chrono and tokio by @Xynnn007 in https://github.com/sigstore/sigstore-rs/pull/334 +- chore(deps): Update base64 requirement from 0.21.0 to 0.22.0 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/332 +- The `Repository` trait and `ManualRepository` struct no longer require a feature flag by @tannaurus in https://github.com/sigstore/sigstore-rs/pull/331 +- chore(deps): Bump actions/checkout from 4.1.1 to 4.1.2 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/336 +- chore(deps): Update reqwest requirement from 0.11 to 0.12 by @dependabot in https://github.com/sigstore/sigstore-rs/pull/341 +- update tough dep by @astoycos in https://github.com/sigstore/sigstore-rs/pull/340 +- Tag the 0.9.0 release by @flavio in https://github.com/sigstore/sigstore-rs/pull/342 + +## New Contributors + +- @tannaurus made their first contribution in https://github.com/sigstore/sigstore-rs/pull/331 +- @astoycos made their first contribution in https://github.com/sigstore/sigstore-rs/pull/340 + +**Full Changelog**: https://github.com/sigstore/sigstore-rs/compare/v0.8.0...v0.9.0 + # v0.8.0 ## What's Changed diff --git a/Cargo.toml b/Cargo.toml index 2b1f272870..7cac6568d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sigstore" description = "An experimental crate to interact with sigstore" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = ["sigstore-rs developers"] license = "Apache-2.0"