Skip to content

Commit

Permalink
Update create_log_entry example to create key pair.
Browse files Browse the repository at this point in the history
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 <jguilhermevanz@suse.com>
  • Loading branch information
jvanz committed May 5, 2023
1 parent d0744c8 commit b7c0930
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 24 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ tempfile = "3.3.0"
tracing-subscriber = { version = "0.3.9", features = ["env-filter"] }
testcontainers = "0.14"
serial_test = "1.0.0"
hex = "0.4.3"

# cosign example mappings

Expand Down
130 changes: 106 additions & 24 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,37 +26,60 @@ use sigstore::rekor::models::{

use clap::{Arg, Command};

async fn create_signer() -> SigStoreSigner {
SigningScheme::ECDSA_P256_SHA256_ASN1
.create_signer()
.unwrap()
}

// 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 pram will generate a key pair, download the file available at [TODO], 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:
Data: b"This file will be used to test create_log_entry\nChange 1\n"
Data hash: c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413
Public key base 64: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFbjNJeUh4SDVzbDFFczVXcmFQNSs3aFUxejNLbgpGUzlOL3FQam80YVpPeW1ob29IUnBscUxCS0JQS3QvN2RoS0ZEZHBnUkJhb0ZaZXVHQ3JnUllNVU9BPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
Signature: 30460221008b1b96b32f8b9d6c2ae789cb692a95cd72fb59502e79899360b23d379e23c8cd02210098a045432a057a671ec21038b65be7b1e29188ccd37950abc904b832268dfcf5
Signature base64: MEYCIQCLG5azL4udbCrnictpKpXNcvtZUC55iZNgsj03niPIzQIhAJigRUMqBXpnHsIQOLZb57HikYjM03lQq8kEuDImjfz1
Err(
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 +89,64 @@ 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:
Data: b"This file will be used to test create_log_entry\nChange 1\n"
Data hash: c7ead87fa5c82d2b17feece1c2ee1bda8e94788f4b208de5057b3617a42b7413
Public key base 64: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFb3BFbFNReldSMC9JNkhBYnorWmlValVXVGNuZQovU0RwVWVNUHFGN0QyYmNqWkJFV0xhbjNWN0wreG5ncTRGampDemtyK0Y0bW9lSDhRSmhMaEtJT3ZRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
Signature: 304502200871cd755fb6bd63aca23bb489d89db199334eab957882613d78225584935c41022100e0789ede2c6ae2bf289945f4ad30968fb4609ea5e33c060d89d87d3d562402a5
Signature base64: MEUCIAhxzXVftr1jrKI7tInYnbGZM06rlXiCYT14IlWEk1xBAiEA4Hie3ixq4r8omUX0rTCWj7RgnqXjPAYNidh9PVYkAqU=
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 keys_job = generate_keys();
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 +160,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,33 +173,38 @@ 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 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 (key, public_key_base64) = keys_job.await.unwrap();
//
// I want to panic if something went wrong. Therefore, I'll use unwrap() to
// get the job output
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

0 comments on commit b7c0930

Please sign in to comment.