Skip to content

Commit a7352f4

Browse files
authored
Revert "Parallel signing (#1468)" (#1478)
This reverts commit 3a26b7d. The windows tests started to fail with this one: Reverting until there is a fix Signed-off-by: Jussi Kukkonen <[email protected]>
1 parent 3a26b7d commit a7352f4

File tree

7 files changed

+98
-210
lines changed

7 files changed

+98
-210
lines changed

sigstore/_cli.py

Lines changed: 52 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@
2020
import logging
2121
import os
2222
import sys
23-
from concurrent import futures
2423
from dataclasses import dataclass
2524
from pathlib import Path
26-
from typing import Any, NoReturn, Union
25+
from typing import Any, NoReturn, TextIO, Union
2726

2827
from cryptography.hazmat.primitives.serialization import Encoding
2928
from cryptography.x509 import load_pem_x509_certificate
@@ -57,7 +56,7 @@
5756
Issuer,
5857
detect_credential,
5958
)
60-
from sigstore.sign import Signer, SigningContext
59+
from sigstore.sign import SigningContext
6160
from sigstore.verify import (
6261
Verifier,
6362
policy,
@@ -637,57 +636,6 @@ def _get_identity_token(args: argparse.Namespace) -> None:
637636
_invalid_arguments(args, "No identity token supplied or detected!")
638637

639638

640-
def _sign_file_threaded(
641-
signer: Signer,
642-
predicate_type: str | None,
643-
predicate: dict[str, Any] | None,
644-
file: Path,
645-
outputs: SigningOutputs,
646-
) -> None:
647-
"""sign method to be called from signing thread"""
648-
_logger.debug(f"signing for {file.name}")
649-
with file.open(mode="rb") as io:
650-
# The input can be indefinitely large, so we perform a streaming
651-
# digest and sign the prehash rather than buffering it fully.
652-
digest = sha256_digest(io)
653-
try:
654-
if predicate is None:
655-
result = signer.sign_artifact(input_=digest)
656-
else:
657-
subject = Subject(name=file.name, digest={"sha256": digest.digest.hex()})
658-
statement_builder = StatementBuilder(
659-
subjects=[subject],
660-
predicate_type=predicate_type,
661-
predicate=predicate,
662-
)
663-
result = signer.sign_dsse(statement_builder.build())
664-
except ExpiredIdentity as exp_identity:
665-
_logger.error("Signature failed: identity token has expired")
666-
raise exp_identity
667-
668-
except ExpiredCertificate as exp_certificate:
669-
_logger.error("Signature failed: Fulcio signing certificate has expired")
670-
raise exp_certificate
671-
672-
_logger.info(
673-
f"Transparency log entry created at index: {result.log_entry.log_index}"
674-
)
675-
676-
if outputs.signature is not None:
677-
signature = base64.b64encode(result.signature).decode()
678-
with outputs.signature.open(mode="w") as io:
679-
print(signature, file=io)
680-
681-
if outputs.certificate is not None:
682-
cert_pem = signer._signing_cert().public_bytes(Encoding.PEM).decode()
683-
with outputs.certificate.open(mode="w") as io:
684-
print(cert_pem, file=io)
685-
686-
if outputs.bundle is not None:
687-
with outputs.bundle.open(mode="w") as io:
688-
print(result.to_json(), file=io)
689-
690-
691639
def _sign_common(
692640
args: argparse.Namespace, output_map: OutputMap, predicate: dict[str, Any] | None
693641
) -> None:
@@ -718,37 +666,63 @@ def _sign_common(
718666
if not identity:
719667
_invalid_arguments(args, "No identity token supplied or detected!")
720668

721-
# Not all commands provide --predicate-type
722-
predicate_type = getattr(args, "predicate_type", None)
723-
724669
with signing_ctx.signer(identity) as signer:
725-
print("Using ephemeral certificate:")
726-
cert_pem = signer._signing_cert().public_bytes(Encoding.PEM).decode()
727-
print(cert_pem)
728-
729-
# sign in threads: this is relevant for especially Rekor v2 as otherwise we wait
730-
# for log inclusion for each signature separately
731-
with futures.ThreadPoolExecutor() as executor:
732-
jobs = [
733-
executor.submit(
734-
_sign_file_threaded,
735-
signer,
736-
predicate_type,
737-
predicate,
738-
file,
739-
outputs,
740-
)
741-
for file, outputs in output_map.items()
742-
]
743-
for job in futures.as_completed(jobs):
744-
job.result()
745-
746670
for file, outputs in output_map.items():
671+
_logger.debug(f"signing for {file.name}")
672+
with file.open(mode="rb") as io:
673+
# The input can be indefinitely large, so we perform a streaming
674+
# digest and sign the prehash rather than buffering it fully.
675+
digest = sha256_digest(io)
676+
try:
677+
if predicate is None:
678+
result = signer.sign_artifact(input_=digest)
679+
else:
680+
subject = Subject(
681+
name=file.name, digest={"sha256": digest.digest.hex()}
682+
)
683+
predicate_type = args.predicate_type
684+
statement_builder = StatementBuilder(
685+
subjects=[subject],
686+
predicate_type=predicate_type,
687+
predicate=predicate,
688+
)
689+
result = signer.sign_dsse(statement_builder.build())
690+
except ExpiredIdentity as exp_identity:
691+
print("Signature failed: identity token has expired")
692+
raise exp_identity
693+
694+
except ExpiredCertificate as exp_certificate:
695+
print("Signature failed: Fulcio signing certificate has expired")
696+
raise exp_certificate
697+
698+
print("Using ephemeral certificate:")
699+
cert = result.signing_certificate
700+
cert_pem = cert.public_bytes(Encoding.PEM).decode()
701+
print(cert_pem)
702+
703+
print(
704+
f"Transparency log entry created at index: {result.log_entry.log_index}"
705+
)
706+
707+
sig_output: TextIO
708+
if outputs.signature is not None:
709+
sig_output = outputs.signature.open("w")
710+
else:
711+
sig_output = sys.stdout
712+
713+
signature = base64.b64encode(result.signature).decode()
714+
print(signature, file=sig_output)
747715
if outputs.signature is not None:
748716
print(f"Signature written to {outputs.signature}")
717+
749718
if outputs.certificate is not None:
719+
with outputs.certificate.open(mode="w") as io:
720+
print(cert_pem, file=io)
750721
print(f"Certificate written to {outputs.certificate}")
722+
751723
if outputs.bundle is not None:
724+
with outputs.bundle.open(mode="w") as io:
725+
print(result.to_json(), file=io)
752726
print(f"Sigstore bundle written to {outputs.bundle}")
753727

754728

sigstore/_internal/rekor/client.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,8 @@ def from_response(cls, dict_: dict[str, Any]) -> RekorLogInfo:
7373

7474

7575
class _Endpoint(ABC):
76-
def __init__(self, url: str, session: requests.Session | None = None) -> None:
77-
# Note that _Endpoint may not be thread be safe if the same Session is provided
78-
# to an _Endpoint in multiple threads
76+
def __init__(self, url: str, session: requests.Session) -> None:
7977
self.url = url
80-
if session is None:
81-
session = requests.Session()
82-
session.headers.update(
83-
{
84-
"Content-Type": "application/json",
85-
"Accept": "application/json",
86-
"User-Agent": USER_AGENT,
87-
}
88-
)
89-
9078
self.session = session
9179

9280

@@ -222,6 +210,20 @@ def __init__(self, url: str) -> None:
222210
Create a new `RekorClient` from the given URL.
223211
"""
224212
self.url = f"{url}/api/v1"
213+
self.session = requests.Session()
214+
self.session.headers.update(
215+
{
216+
"Content-Type": "application/json",
217+
"Accept": "application/json",
218+
"User-Agent": USER_AGENT,
219+
}
220+
)
221+
222+
def __del__(self) -> None:
223+
"""
224+
Terminates the underlying network session.
225+
"""
226+
self.session.close()
225227

226228
@classmethod
227229
def production(cls) -> RekorClient:
@@ -244,8 +246,7 @@ def log(self) -> RekorLog:
244246
"""
245247
Returns a `RekorLog` adapter for making requests to a Rekor log.
246248
"""
247-
248-
return RekorLog(f"{self.url}/log")
249+
return RekorLog(f"{self.url}/log", session=self.session)
249250

250251
def create_entry(self, request: EntryRequestBody) -> LogEntry:
251252
"""

sigstore/_internal/rekor/client_v2.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ def __init__(self, base_url: str) -> None:
5454
Create a new `RekorV2Client` from the given URL.
5555
"""
5656
self.url = f"{base_url}/api/v2"
57+
self.session = requests.Session()
58+
self.session.headers.update(
59+
{
60+
"Content-Type": "application/json",
61+
"Accept": "application/json",
62+
"User-Agent": USER_AGENT,
63+
}
64+
)
65+
66+
def __del__(self) -> None:
67+
"""
68+
Terminates the underlying network session.
69+
"""
70+
self.session.close()
5771

5872
def create_entry(self, payload: EntryRequestBody) -> LogEntry:
5973
"""
@@ -64,19 +78,7 @@ def create_entry(self, payload: EntryRequestBody) -> LogEntry:
6478
https://github.com/sigstore/rekor-tiles/blob/main/CLIENTS.md#handling-longer-requests
6579
"""
6680
_logger.debug(f"proposed: {json.dumps(payload)}")
67-
68-
# Use a short lived session to avoid potential issues with multi-threading:
69-
# Session thread-safety is ambiguous
70-
session = requests.Session()
71-
session.headers.update(
72-
{
73-
"Content-Type": "application/json",
74-
"Accept": "application/json",
75-
"User-Agent": USER_AGENT,
76-
}
77-
)
78-
79-
resp = session.post(
81+
resp = self.session.post(
8082
f"{self.url}/log/entries",
8183
json=payload,
8284
)

sigstore/_internal/timestamp.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ def __init__(self, url: str) -> None:
6868
Create a new `TimestampAuthorityClient` from the given URL.
6969
"""
7070
self.url = url
71+
self.session = requests.Session()
72+
self.session.headers.update(
73+
{
74+
"Content-Type": "application/timestamp-query",
75+
"User-Agent": USER_AGENT,
76+
}
77+
)
78+
79+
def __del__(self) -> None:
80+
"""
81+
Terminates the underlying network session.
82+
"""
83+
self.session.close()
7184

7285
def request_timestamp(self, signature: bytes) -> TimeStampResponse:
7386
"""
@@ -91,18 +104,9 @@ def request_timestamp(self, signature: bytes) -> TimeStampResponse:
91104
msg = f"invalid request: {error}"
92105
raise TimestampError(msg)
93106

94-
# Use single use session to avoid potential Session thread safety issues
95-
session = requests.Session()
96-
session.headers.update(
97-
{
98-
"Content-Type": "application/timestamp-query",
99-
"User-Agent": USER_AGENT,
100-
}
101-
)
102-
103107
# Send it to the TSA for signing
104108
try:
105-
response = session.post(
109+
response = self.session.post(
106110
self.url,
107111
data=timestamp_request.as_bytes(),
108112
timeout=CLIENT_TIMEOUT,

test/assets/integration/b.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/assets/integration/c.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)