From e39d67eb6fae0934dcde0066ec68d56f4a9351d2 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 2 May 2022 10:34:37 -0600 Subject: [PATCH 1/4] sigstore/{cli, sign}: add logging, fix output abstraction Signed-off-by: William Woodruff --- sigstore/_cli.py | 23 +++++++++++++------- sigstore/_sign.py | 53 +++++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/sigstore/_cli.py b/sigstore/_cli.py index d5f17e3e3..4902ae7bc 100644 --- a/sigstore/_cli.py +++ b/sigstore/_cli.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging from importlib import resources import click @@ -21,6 +22,8 @@ from sigstore._sign import sign from sigstore._verify import verify +logger = logging.getLogger(__name__) + @click.group() def main(): @@ -51,13 +54,17 @@ def _sign(files, identity_token, ctfe_pem): ctfe_pem = ctfe_pem.read() for file in files: + result = sign( + file=file, + identity_token=identity_token, + ctfe_pem=ctfe_pem, + ) + + click.echo(f"Signature: {result.b64_signature}") + click.echo("Using ephemeral certificate:") + click.echo(result.cert_pem) click.echo( - sign( - file=file, - identity_token=identity_token, - ctfe_pem=ctfe_pem, - output=click.echo, - ) + f"Transparency log entry created at index: {result.log_entry.log_index}" ) @@ -70,11 +77,11 @@ def _sign(files, identity_token, ctfe_pem): ) def _verify(files, certificate_path, signature_path, cert_email): # Load the signing certificate - click.echo(f"Using certificate from: {certificate_path.name}") + logger.debug(f"Using certificate from: {certificate_path.name}") certificate = certificate_path.read() # Load the signature - click.echo(f"Using signature from: {signature_path.name}") + logger.debug(f"Using signature from: {signature_path.name}") signature = signature_path.read() for file in files: diff --git a/sigstore/_sign.py b/sigstore/_sign.py index d47a1deb9..a352a624d 100644 --- a/sigstore/_sign.py +++ b/sigstore/_sign.py @@ -14,37 +14,58 @@ import base64 import hashlib +import logging from typing import BinaryIO from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.serialization import load_pem_public_key +from pydantic import BaseModel from sigstore._internal.fulcio import ( FulcioCertificateSigningRequest, FulcioClient, ) from sigstore._internal.oidc import Identity -from sigstore._internal.rekor import RekorClient +from sigstore._internal.rekor import RekorClient, RekorEntry from sigstore._internal.sct import verify_sct +logger = logging.getLogger(__name__) -def _no_output(*a, **kw): - pass +class SigningResult(BaseModel): + """ + Represents the artifacts of a signing operation. + """ -def sign(file: BinaryIO, identity_token: str, ctfe_pem: bytes, output=_no_output): + cert_pem: str + """ + The PEM-encoded public half of the certificate used for signing. + """ + + b64_signature: str + """ + The base64-encoded signature. + """ + + log_entry: RekorEntry + """ + A record of the Rekor log entry for the signing operation. + """ + + +def sign(file: BinaryIO, identity_token: str, ctfe_pem: bytes) -> SigningResult: """Public API for signing blobs""" - output(f"Using payload from: {file.name}") + logger.debug(f"Using payload from: {file.name}") artifact_contents = file.read() sha256_artifact_hash = hashlib.sha256(artifact_contents).hexdigest() - output("Generating ephemeral keys...") + logger.debug("Generating ephemeral keys...") private_key = ec.generate_private_key(ec.SECP384R1()) public_key = private_key.public_key() - output("Retrieving signed certificate...") + logger.debug("Retrieving signed certificate...") fulcio = FulcioClient() oidc_identity = Identity(identity_token) @@ -82,11 +103,7 @@ def sign(file: BinaryIO, identity_token: str, ctfe_pem: bytes, output=_no_output verify_sct(sct, cert, ctfe_key) - output("Successfully verified SCT...") - - # Output the ephemeral certificate - output("Using ephemeral certificate:") - output(cert.public_bytes(encoding=serialization.Encoding.PEM)) + logger.debug("Successfully verified SCT...") # Sign artifact artifact_signature = private_key.sign(artifact_contents, ec.ECDSA(hashes.SHA256())) @@ -108,10 +125,10 @@ def sign(file: BinaryIO, identity_token: str, ctfe_pem: bytes, output=_no_output encoded_public_key=pub_b64.decode(), ) - output(f"Transparency log entry created with index: {entry.log_index}") - - # Output the signature - output(f"Signature: {b64_artifact_signature}") + logger.debug(f"Transparency log entry created with index: {entry.log_index}") - # Determine what to return here - return None + return SigningResult( + cert_pem=cert.public_bytes(encoding=serialization.Encoding.PEM).decode(), + b64_signature=b64_artifact_signature, + log_entry=entry, + ) From 3d3fbb3527ba0fe576c935d3075d8421e1af5c5c Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 2 May 2022 10:35:35 -0600 Subject: [PATCH 2/4] test: update sign test Signed-off-by: William Woodruff --- test/test_sign.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_sign.py b/test/test_sign.py index 72555276f..de2cd86bf 100644 --- a/test/test_sign.py +++ b/test/test_sign.py @@ -22,6 +22,5 @@ def test_sign(): file_ = pretend.stub() identity_token = pretend.stub() - output = pretend.call_recorder(lambda s: None) - assert sign(file_, identity_token, output) == "Nothing here yet" + assert sign(file_, identity_token) == "Nothing here yet" From 629f66e325eed18fa2e03a21b507a6d1cf1ad6c9 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 2 May 2022 10:46:57 -0600 Subject: [PATCH 3/4] test: fix lint Signed-off-by: William Woodruff --- test/test_sign.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_sign.py b/test/test_sign.py index de2cd86bf..5e893e3e7 100644 --- a/test/test_sign.py +++ b/test/test_sign.py @@ -22,5 +22,6 @@ def test_sign(): file_ = pretend.stub() identity_token = pretend.stub() + ctfe_pem = pretend.stub() - assert sign(file_, identity_token) == "Nothing here yet" + assert sign(file_, identity_token, ctfe_pem) == "Nothing here yet" From b82970c22b25a5771082087eea8767a967ef8000 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 2 May 2022 12:09:04 -0600 Subject: [PATCH 4/4] cli: make output consistent with cosign Signed-off-by: William Woodruff --- sigstore/_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sigstore/_cli.py b/sigstore/_cli.py index 4902ae7bc..e2c6cd4cf 100644 --- a/sigstore/_cli.py +++ b/sigstore/_cli.py @@ -60,12 +60,12 @@ def _sign(files, identity_token, ctfe_pem): ctfe_pem=ctfe_pem, ) - click.echo(f"Signature: {result.b64_signature}") click.echo("Using ephemeral certificate:") click.echo(result.cert_pem) click.echo( f"Transparency log entry created at index: {result.log_entry.log_index}" ) + click.echo(f"Signature: {result.b64_signature}") @main.command("verify")