Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cosign verify-bundle example #186

Merged
merged 2 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ serial_test = "0.10.0"
name = "verify"
path = "examples/cosign/verify/main.rs"

[[example]]
name = "verify-bundle"
path = "examples/cosign/verify-bundle/main.rs"

[[example]]
name = "sign"
path = "examples/cosign/sign/main.rs"
Expand Down
2 changes: 2 additions & 0 deletions examples/cosign/verify-bundle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
artifact.bundle
artifact.txt
20 changes: 20 additions & 0 deletions examples/cosign/verify-bundle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This example shows how to verify a blob, using a bundle that was created by the
`cosign sign-blob` command.

### Create the artifact to be signed.
```console
echo something > artifact.txt
```

### Sign the artifact.txt file using cosign
```
COSIGN_EXPERIMENTAL=1 cosign sign-blob --bundle=artifact.bundle artifact.txt
```

### Verify using sigstore-rs:
```console
cargo run --example verify-bundle -- \
--rekor-pub-key ~/.sigstore/root/targets/rekor.pub \
--bundle artifact.bundle \
artifact.txt
```
68 changes: 68 additions & 0 deletions examples/cosign/verify-bundle/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap::Parser;
use sigstore::cosign::bundle::SignedArtifactBundle;
use sigstore::crypto::{CosignVerificationKey, SigningScheme};
use std::fs;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Cli {
/// Path to bundle file
#[clap(short, long)]
bundle: String,

/// Path to artifact to be verified
blob: String,

/// File containing Rekor's public key (e.g.: ~/.sigstore/root/targets/rekor.pub)
#[clap(long, required(false))]
rekor_pub_key: String,

/// Enable verbose mode
#[clap(short, long)]
verbose: bool,
}

#[tokio::main]
pub async fn main() {
let cli = Cli::parse();

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

let rekor_pub_pem =
fs::read_to_string(&cli.rekor_pub_key).expect("error reading rekor's public key");
let rekor_pub_key =
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 bundle = SignedArtifactBundle::new_verified(&bundle_json, &rekor_pub_key).unwrap();
if bundle.verify_blob(&blob).is_ok() {
println!("Verification succeeded");
} else {
eprintln!("Verification failed");
}
}
17 changes: 17 additions & 0 deletions examples/cosign/verify-bundle/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
BLOB="artifact.txt"
BUNDLE="artifact.bundle"

echo -e "\nGenerate the blob to be signed"
echo something > $BLOB

echo -e "\nSign the artifact.txt file using sign-blob"
COSIGN_EXPERIMENTAL=1 cosign sign-blob --bundle=$BUNDLE $BLOB

echo -e "\nVerify using cosign. TODO: remove this later"
cosign verify-blob --bundle=$BUNDLE $BLOB

echo -e "\nRun examples/cosign/verify-bundle"
cargo run --example verify-bundle -- \
--rekor-pub-key ~/.sigstore/root/targets/rekor.pub \
--bundle $BUNDLE \
$BLOB
19 changes: 17 additions & 2 deletions src/cosign/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _};
use olpc_cjson::CanonicalFormatter;
use serde::{Deserialize, Serialize};
use std::cmp::PartialEq;
use std::convert::TryFrom;

use crate::crypto::{CosignVerificationKey, Signature};
use crate::errors::{Result, SigstoreError};
Expand Down Expand Up @@ -43,13 +45,26 @@ impl SignedArtifactBundle {
///
/// **Note well:** The bundle will be returned only if it can be verified
/// using the supplied `rekor_pub_key` public key.
#[allow(dead_code)]
pub(crate) fn new_verified(raw: &str, rekor_pub_key: &CosignVerificationKey) -> Result<Self> {
pub fn new_verified(raw: &str, rekor_pub_key: &CosignVerificationKey) -> Result<Self> {
let bundle: SignedArtifactBundle = serde_json::from_str(raw).map_err(|e| {
SigstoreError::UnexpectedError(format!("Cannot parse bundle |{}|: {:?}", raw, e))
})?;
Bundle::verify_bundle(&bundle.rekor_bundle, rekor_pub_key).map(|_| bundle)
}

/// Verifies the passed-in blob against the signature in this
/// SignedArtifactBundle.
pub fn verify_blob(&self, blob: &[u8]) -> Result<()> {
danbev marked this conversation as resolved.
Show resolved Hide resolved
let cert = BASE64_STD_ENGINE.decode(&self.cert)?;
let (_, pem) = x509_parser::pem::parse_x509_pem(&cert)?;
let (_, cert) = x509_parser::parse_x509_certificate(&pem.contents)?;
let subject_public_key = cert.public_key();
let ver_key =
CosignVerificationKey::try_from(subject_public_key).expect("conversion failed");
let signature = Signature::Base64Encoded(self.base64_signature.as_bytes());
ver_key.verify_signature(signature, blob)?;
Ok(())
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
Expand Down
2 changes: 1 addition & 1 deletion src/cosign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use tracing::warn;
use crate::errors::{Result, SigstoreApplicationConstraintsError, SigstoreVerifyConstraintsError};
use crate::registry::{Auth, PushResponse};

mod bundle;
pub mod bundle;
pub(crate) mod constants;
pub mod signature_layers;
pub use signature_layers::SignatureLayer;
Expand Down