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

Update create_log_entry example to create key pair. #206

Merged
merged 2 commits into from
May 30, 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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
113 changes: 91 additions & 22 deletions examples/rekor/create_log_entry/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -21,20 +26,38 @@ 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<u8>, 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 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
program can print an error. See an example:

Example command :
cargo run --example create_log_entry -- \
--hash c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413\
--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:
Expand All @@ -43,15 +66,15 @@ 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 {
code: Some(
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",
),
},
),
Expand All @@ -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 leaving 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")
Expand All @@ -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")
Expand All @@ -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::<String>("hash")
.unwrap_or(&HASH.to_string())
.unwrap_or(&digest)
.to_owned(),
);
let data = Data::new(hash);
let public_key = PublicKey::new(
flags
.get_one::<String>("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);
Expand Down