diff --git a/sigstore/_cli.py b/sigstore/_cli.py index d5f17e3e3..e2c6cd4cf 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,14 +54,18 @@ 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("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}" ) + click.echo(f"Signature: {result.b64_signature}") @main.command("verify") @@ -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, + ) diff --git a/test/test_sign.py b/test/test_sign.py index 72555276f..5e893e3e7 100644 --- a/test/test_sign.py +++ b/test/test_sign.py @@ -22,6 +22,6 @@ def test_sign(): file_ = pretend.stub() identity_token = pretend.stub() - output = pretend.call_recorder(lambda s: None) + ctfe_pem = pretend.stub() - assert sign(file_, identity_token, output) == "Nothing here yet" + assert sign(file_, identity_token, ctfe_pem) == "Nothing here yet"