Skip to content

Commit

Permalink
fix!: use all Fulcio's certificates
Browse files Browse the repository at this point in the history
Fulcio has recently introduced a new root CA to issue certificates.
Prior to this commit, the code was making use only of the initial
root CA.
Because of that, recent keyless signatures were considered not valid.

Now the code is taking into account of the root CAs that Fulcio used
over the time.

This fixes sigstore#32

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
  • Loading branch information
flavio committed Mar 8, 2022
1 parent bfb71f0 commit 8e65da1
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 186 deletions.
2 changes: 1 addition & 1 deletion examples/verify/main.rs
Expand Up @@ -145,7 +145,7 @@ async fn run_app() -> anyhow::Result<Vec<SignatureLayer>> {

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

// Set Rekor public key. Give higher precendece to the key specified by the user over the
Expand Down
24 changes: 9 additions & 15 deletions src/cosign/client.rs
Expand Up @@ -14,24 +14,26 @@
// limitations under the License.

use async_trait::async_trait;
use x509_parser::{traits::FromDer, x509::SubjectPublicKeyInfo};

use super::{
constants::SIGSTORE_OCI_MEDIA_TYPE,
signature_layers::{build_signature_layers, SignatureLayer},
CosignCapabilities,
};
use crate::crypto::CosignVerificationKey;
use crate::errors::{Result, SigstoreError};
use crate::registry::Auth;
use crate::{
crypto::certificate_pool::CertificatePool,
errors::{Result, SigstoreError},
};

/// Cosign Client
///
/// Instances of `Client` can be built via [`sigstore::cosign::ClientBuilder`](crate::cosign::ClientBuilder).
pub struct Client {
pub(crate) registry_client: Box<dyn crate::registry::ClientCapabilities>,
pub(crate) rekor_pub_key: Option<CosignVerificationKey>,
pub(crate) fulcio_pub_key_der: Option<Vec<u8>>,
pub(crate) fulcio_cert_pool: Option<CertificatePool>,
}

#[async_trait]
Expand All @@ -53,7 +55,7 @@ impl CosignCapabilities for Client {
"{}/{}:{}.sig",
image_reference.registry(),
image_reference.repository(),
manifest_digest.replace(":", "-")
manifest_digest.replace(':', "-")
);
let reference = sign
.parse()
Expand Down Expand Up @@ -81,20 +83,12 @@ impl CosignCapabilities for Client {
}
};

let fulcio_pub_key = match &self.fulcio_pub_key_der {
None => None,
Some(der) => {
let (_, key) = SubjectPublicKeyInfo::from_der(der)?;
Some(key)
}
};

build_signature_layers(
&image_manifest,
source_image_digest,
&layers,
self.rekor_pub_key.as_ref(),
fulcio_pub_key.as_ref(),
self.fulcio_cert_pool.as_ref(),
)
}
}
Expand Down Expand Up @@ -138,7 +132,7 @@ impl Client {
#[cfg(test)]
mod tests {
use super::*;
use crate::cosign::tests::{FULCIO_CRT_PEM, REKOR_PUB_KEY};
use crate::cosign::tests::{get_fulcio_cert_pool, REKOR_PUB_KEY};
use crate::{crypto::SignatureDigestAlgorithm, mock_client::test::MockOciClient};

fn build_test_client(mock_client: MockOciClient) -> Client {
Expand All @@ -151,7 +145,7 @@ mod tests {
Client {
registry_client: Box::new(mock_client),
rekor_pub_key: Some(rekor_pub_key),
fulcio_pub_key_der: Some(FULCIO_CRT_PEM.as_bytes().to_vec()),
fulcio_cert_pool: Some(get_fulcio_cert_pool()),
}
}

Expand Down
71 changes: 44 additions & 27 deletions src/cosign/client_builder.rs
Expand Up @@ -17,10 +17,10 @@ use tracing::info;

use super::client::Client;
use crate::crypto::{
certificate::extract_public_key_from_pem_cert, CosignVerificationKey, SignatureDigestAlgorithm,
certificate_pool::CertificatePool, CosignVerificationKey, SignatureDigestAlgorithm,
};
use crate::errors::Result;
use crate::registry::ClientConfig;
use crate::registry::{Certificate, ClientConfig};

/// A builder that generates Client objects.
///
Expand All @@ -29,47 +29,64 @@ use crate::registry::ClientConfig;
/// Rekor integration can be enabled by specifying Rekor's public key.
/// This can be provided via the [`ClientBuilder::with_rekor_pub_key`] method.
///
/// > Note well: currently this library is not able to retrieve the key from Sigstore's
/// > TUF repository like `cosign` does. This will be done in the near future.
/// > 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.
///
/// ## Fulcio integration
///
/// Fulcio integration can be enabled by specifying Fulcio's certificate.
/// This can be provided via the [`ClientBuilder::with_fulcio_cert`] method.
///
/// > Note well: currently this library is not able to retrieve the certificate from Sigstore's
/// > TUF repository like `cosign` does. This will be done in the near future.
/// > 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.
#[derive(Default)]
pub struct ClientBuilder {
oci_client_config: ClientConfig,
rekor_pub_key: Option<String>,
fulcio_cert: Option<Vec<u8>>,
fulcio_certs: Vec<Certificate>,
}

impl ClientBuilder {
/// Specify the public key used by Rekor.
///
/// Currently this library is not able to retrieve the key from Sigstore's
/// TUF repository like `cosign` does. This will be done in the near future.
/// The public key can be obtained by using the helper methods under the
/// [`tuf`](crate::tuf) module.
///
/// In the meantime, end users of the library can fetch the key in a secure
/// way by using `cosign initialize`.
/// This will place the key under `~/.sigstore/root/targets/rekor.pub`.
/// `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.
/// 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
///
/// Currently this library is not able to retrieve the certificate from Sigstore's
/// TUF repository like `cosign` does. This will be done in the near future.
/// The certificates can be obtained by using the helper methods under the
/// [`tuf`](crate::tuf) module.
///
/// In the meantime, end users of the library can fetch the certificate in a secure
/// way by using `cosign initialize`.
/// This will place the key under `~/.sigstore/root/targets/fulcio.crt.pem`.
/// When provided, this enables Fulcio's integration.
pub fn with_fulcio_cert(mut self, cert: &[u8]) -> Self {
self.fulcio_cert = Some(cert.to_owned());
let certificate = Certificate {
encoding: crate::registry::CertificateEncoding::Pem,
data: cert.to_owned(),
};
self.fulcio_certs.push(certificate);
self
}

/// 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
}

Expand All @@ -85,7 +102,7 @@ impl ClientBuilder {
pub fn build(self) -> Result<Client> {
let rekor_pub_key = match self.rekor_pub_key {
None => {
info!("rekor public key not provided");
info!("Rekor public key not provided. Rekor integration disabled");
None
}
Some(data) => Some(CosignVerificationKey::from_pem(
Expand All @@ -94,12 +111,12 @@ impl ClientBuilder {
)?),
};

let fulcio_pub_key_der = match self.fulcio_cert {
None => {
info!("The fulcio cert has not been provided");
None
}
Some(cert) => Some(extract_public_key_from_pem_cert(&cert)?),
let fulcio_cert_pool = if self.fulcio_certs.is_empty() {
info!("No Fulcio cert has been provided. Fulcio integration disabled");
None
} else {
let cert_pool = CertificatePool::from_certificates(&self.fulcio_certs)?;
Some(cert_pool)
};

let oci_client =
Expand All @@ -109,7 +126,7 @@ impl ClientBuilder {
registry_client: oci_client,
}),
rekor_pub_key,
fulcio_pub_key_der,
fulcio_cert_pool,
})
}
}
37 changes: 29 additions & 8 deletions src/cosign/mod.rs
Expand Up @@ -133,18 +133,16 @@ mod tests {
use crate::cosign::signature_layers::tests::build_correct_signature_layer_with_certificate;
use crate::cosign::signature_layers::CertificateSubject;
use crate::cosign::verification_constraint::{AnnotationVerifier, CertSubjectEmailVerifier};
use crate::crypto::{
certificate::extract_public_key_from_pem_cert, CosignVerificationKey,
SignatureDigestAlgorithm,
};
use crate::crypto::certificate_pool::CertificatePool;
use crate::crypto::{CosignVerificationKey, SignatureDigestAlgorithm};
use crate::simple_signing::Optional;

pub(crate) const REKOR_PUB_KEY: &str = r#"-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr
kBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==
-----END PUBLIC KEY-----"#;

pub(crate) const FULCIO_CRT_PEM: &str = r#"-----BEGIN CERTIFICATE-----
const FULCIO_CRT_1_PEM: &str = r#"-----BEGIN CERTIFICATE-----
MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu
Expand All @@ -158,9 +156,32 @@ Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup
Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==
-----END CERTIFICATE-----"#;

pub(crate) fn get_fulcio_public_key() -> Vec<u8> {
extract_public_key_from_pem_cert(FULCIO_CRT_PEM.as_bytes())
.expect("Cannot extract public key from Fulcio hard-coded cert")
const FULCIO_CRT_2_PEM: &str = 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-----"#;

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_rekor_public_key() -> CosignVerificationKey {
Expand Down

0 comments on commit 8e65da1

Please sign in to comment.