Skip to content
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
23 changes: 15 additions & 8 deletions sigstore/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -21,6 +22,8 @@
from sigstore._sign import sign
from sigstore._verify import verify

logger = logging.getLogger(__name__)


@click.group()
def main():
Expand Down Expand Up @@ -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")
Expand All @@ -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:
Expand Down
53 changes: 35 additions & 18 deletions sigstore/_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()))
Expand All @@ -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,
)
4 changes: 2 additions & 2 deletions test/test_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"