Skip to content

Commit bf5164b

Browse files
committed
sigstore: stream input into signing
Closes #158. Signed-off-by: William Woodruff <[email protected]>
1 parent c5ab2ce commit bf5164b

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

sigstore/_cli.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,11 @@ def _sign(args: argparse.Namespace) -> None:
447447

448448
for file, outputs in output_map.items():
449449
logger.debug(f"signing for {file.name}")
450-
result = signer.sign(
451-
input_=file.read_bytes(),
452-
identity_token=args.identity_token,
453-
)
450+
with file.open(mode="rb", buffering=0) as io:
451+
result = signer.sign(
452+
input_=io,
453+
identity_token=args.identity_token,
454+
)
454455

455456
print("Using ephemeral certificate:")
456457
print(result.cert_pem)

sigstore/_sign.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@
1515
from __future__ import annotations
1616

1717
import base64
18-
import hashlib
1918
import logging
19+
from typing import IO
2020

2121
import cryptography.x509 as x509
2222
from cryptography.hazmat.primitives import hashes, serialization
2323
from cryptography.hazmat.primitives.asymmetric import ec
24+
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
2425
from cryptography.x509.oid import NameOID
2526
from pydantic import BaseModel
2627

2728
from sigstore._internal.fulcio import FulcioClient
2829
from sigstore._internal.oidc import Identity
2930
from sigstore._internal.rekor import RekorClient, RekorEntry
3031
from sigstore._internal.sct import verify_sct
32+
from sigstore._utils import sha256_streaming
3133

3234
logger = logging.getLogger(__name__)
3335

@@ -56,11 +58,11 @@ def staging(cls) -> Signer:
5658

5759
def sign(
5860
self,
59-
input_: bytes,
61+
input_: IO[bytes],
6062
identity_token: str,
6163
) -> SigningResult:
6264
"""Public API for signing blobs"""
63-
sha256_artifact_hash = hashlib.sha256(input_).hexdigest()
65+
input_digest = sha256_streaming(input_)
6466

6567
logger.debug("Generating ephemeral keys...")
6668
private_key = ec.generate_private_key(ec.SECP384R1())
@@ -102,7 +104,9 @@ def sign(
102104
logger.debug("Successfully verified SCT...")
103105

104106
# Sign artifact
105-
artifact_signature = private_key.sign(input_, ec.ECDSA(hashes.SHA256()))
107+
artifact_signature = private_key.sign(
108+
input_digest, ec.ECDSA(Prehashed(hashes.SHA256()))
109+
)
106110
b64_artifact_signature = base64.b64encode(artifact_signature).decode()
107111

108112
# Prepare inputs
@@ -113,7 +117,7 @@ def sign(
113117
# Create the transparency log entry
114118
entry = self._rekor.log.entries.post(
115119
b64_artifact_signature=b64_artifact_signature,
116-
sha256_artifact_hash=sha256_artifact_hash,
120+
sha256_artifact_hash=input_digest.hex(),
117121
b64_cert=b64_cert.decode(),
118122
)
119123

sigstore/_utils.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import base64
2222
import hashlib
23-
from typing import Union
23+
from typing import IO, Union
2424

2525
from cryptography.hazmat.primitives import serialization
2626
from cryptography.hazmat.primitives.asymmetric import ec, rsa
@@ -104,3 +104,24 @@ def split_certificate_chain(chain_pem: str) -> list[bytes]:
104104
certificate_chain = [(pem_header + c).encode() for c in certificate_chain]
105105

106106
return certificate_chain
107+
108+
109+
def sha256_streaming(io: IO[bytes]) -> bytes:
110+
"""
111+
Compute the SHA256 of a stream.
112+
113+
This function does its own internal buffering, so an unbuffered stream
114+
should be supplied for optimal performance.
115+
"""
116+
117+
sha256 = hashlib.sha256()
118+
# Per coreutils' ioblksize.h: 128KB performs optimally across a range
119+
# of systems in terms of minimizing syscall overhead.
120+
view = memoryview(bytearray(128 * 1024))
121+
122+
nbytes = io.readinto(view)
123+
while nbytes:
124+
sha256.update(view[:nbytes])
125+
nbytes = io.readinto(view)
126+
127+
return sha256.digest()

0 commit comments

Comments
 (0)