Skip to content

Commit

Permalink
verify: init
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Pan <andrew.pan@trailofbits.com>
Signed-off-by: Jack Leightcap <jack.leightcap@trailofbits.com>
Co-authored-by: Andrew Pan <a@tny.town>
  • Loading branch information
tnytown authored and jleightcap committed Nov 16, 2023
1 parent 4573f76 commit 7cdc30a
Show file tree
Hide file tree
Showing 48 changed files with 2,588 additions and 555 deletions.
18 changes: 10 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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", "verify"]
wasm = ["getrandom/js"]

full-native-tls = [
Expand Down Expand Up @@ -42,6 +42,9 @@ rekor = ["reqwest"]

tuf = ["tough", "regex"]

sign = []
verify = []

cosign-native-tls = [
"oci-distribution/native-tls",
"cert",
Expand Down Expand Up @@ -72,7 +75,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"] }
Expand All @@ -88,10 +91,7 @@ openidconnect = { version = "3.0", default-features = false, features = [
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",
] }
pem = { version = "3.0", features = ["serde"] }
pkcs1 = { version = "0.7.5", features = ["std"] }
pkcs8 = { version = "0.10.2", features = [
"pem",
Expand All @@ -113,17 +113,20 @@ 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"] }
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"
hex = "0.4.3"
json-syntax = { version = "0.9.6", features = ["canonicalize", "serde"] }

[dev-dependencies]
anyhow = { version = "1.0", features = ["backtrace"] }
Expand All @@ -137,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

Expand Down
11 changes: 11 additions & 0 deletions src/bundle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ use std::fmt::Display;

pub use sigstore_protobuf_specs::Bundle;

macro_rules! required {

Check warning on line 21 in src/bundle/mod.rs

View workflow job for this annotation

GitHub Actions / Check WASM

unused macro definition: `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;

Check warning on line 30 in src/bundle/mod.rs

View workflow job for this annotation

GitHub Actions / Check WASM

unused import: `required`

// Known Sigstore bundle media types.
#[derive(Clone, Copy, Debug)]
pub enum Version {
Expand Down
28 changes: 28 additions & 0 deletions src/crypto/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ 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<()> {
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);
}

// TODO(tnytown): cert_is_ca

verify_key_usages(certificate)?;

Ok(())
}

pub(crate) fn is_root_ca(_certificate: &Certificate) -> Result<()> {
// TODO(tnytown)
todo!()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 2 additions & 0 deletions src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
12 changes: 12 additions & 0 deletions src/crypto/verification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use rustls_pki_types::CertificateDer;
use webpki::TrustAnchor;

/// Machinery for Sigstore end entity certificate verification.
struct CertificateVerificationContext<'a> {
pub trust_anchors: Vec<TrustAnchor<'a>>,
pub intermediate_certs: Vec<CertificateDer<'a>>,
}

impl CertificateVerificationContext<'_> {
pub fn new() {}
}
64 changes: 63 additions & 1 deletion src/crypto/verification_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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)]
Expand Down
30 changes: 30 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub enum SigstoreError {
#[error("invalid key format: {error}")]
InvalidKeyFormat { error: String },

#[error("Unable to parse identity token: {0}")]
IdentityTokenError(&'static str),

#[error("unmatched key type {key_typ} and signing scheme {scheme}")]
UnmatchedKeyAndSigningScheme { key_typ: String, scheme: String },

Expand All @@ -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),

Expand Down Expand Up @@ -103,6 +109,12 @@ pub enum SigstoreError {
#[error("Certificate pool error: {0}")]
CertificatePoolError(&'static str),

#[error("Signing session expired")]
ExpiredSigningSession(),

#[error("Fulcio request unsuccessful: {0}")]
FulcioClientError(&'static str),

#[error("Cannot fetch manifest of {image}: {error}")]
RegistryFetchManifestError { image: String, error: String },

Expand All @@ -115,9 +127,18 @@ pub enum SigstoreError {
#[error("Cannot push {image}: {error}")]
RegistryPushError { image: String, error: String },

#[error("Rekor request unsuccessful: {0}")]
RekorClientError(String),

#[error(transparent)]
ReqwestError(#[from] reqwest::Error),

Check failure on line 134 in src/errors.rs

View workflow job for this annotation

GitHub Actions / Check WASM

failed to resolve: use of undeclared crate or module `reqwest`

#[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,

Expand Down Expand Up @@ -155,6 +176,9 @@ pub enum SigstoreError {
#[error("{0}")]
VerificationConstraintError(String),

#[error("{0}")]
VerificationMaterialError(String),

#[error("{0}")]
ApplyConstraintError(String),

Expand Down Expand Up @@ -214,4 +238,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),
}
Loading

0 comments on commit 7cdc30a

Please sign in to comment.