|
20 | 20 | import logging
|
21 | 21 | import os
|
22 | 22 | import sys
|
| 23 | +from concurrent import futures |
23 | 24 | from dataclasses import dataclass
|
24 | 25 | from pathlib import Path
|
25 | 26 | from typing import Any, NoReturn, TextIO, Union
|
@@ -667,63 +668,72 @@ def _sign_common(
|
667 | 668 | _invalid_arguments(args, "No identity token supplied or detected!")
|
668 | 669 |
|
669 | 670 | with signing_ctx.signer(identity) as signer:
|
670 |
| - 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 |
| - ) |
| 671 | + with futures.ThreadPoolExecutor(max_workers=10) as executor: |
| 672 | + |
| 673 | + def _sign_file(file: Path, outputs: SigningOutputs) -> None: |
| 674 | + _logger.debug(f"signing for {file.name}") |
| 675 | + with file.open(mode="rb") as io: |
| 676 | + # The input can be indefinitely large, so we perform a streaming |
| 677 | + # digest and sign the prehash rather than buffering it fully. |
| 678 | + digest = sha256_digest(io) |
| 679 | + try: |
| 680 | + if predicate is None: |
| 681 | + result = signer.sign_artifact(input_=digest) |
| 682 | + else: |
| 683 | + subject = Subject( |
| 684 | + name=file.name, digest={"sha256": digest.digest.hex()} |
| 685 | + ) |
| 686 | + predicate_type = args.predicate_type |
| 687 | + statement_builder = StatementBuilder( |
| 688 | + subjects=[subject], |
| 689 | + predicate_type=predicate_type, |
| 690 | + predicate=predicate, |
| 691 | + ) |
| 692 | + result = signer.sign_dsse(statement_builder.build()) |
| 693 | + except ExpiredIdentity as exp_identity: |
| 694 | + print("Signature failed: identity token has expired") |
| 695 | + raise exp_identity |
| 696 | + |
| 697 | + except ExpiredCertificate as exp_certificate: |
| 698 | + print("Signature failed: Fulcio signing certificate has expired") |
| 699 | + raise exp_certificate |
| 700 | + |
| 701 | + print("Using ephemeral certificate:") |
| 702 | + cert = result.signing_certificate |
| 703 | + cert_pem = cert.public_bytes(Encoding.PEM).decode() |
| 704 | + print(cert_pem) |
| 705 | + |
| 706 | + print( |
| 707 | + f"Transparency log entry created at index: {result.log_entry.log_index}" |
| 708 | + ) |
706 | 709 |
|
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) |
715 |
| - if outputs.signature is not None: |
716 |
| - print(f"Signature written to {outputs.signature}") |
717 |
| - |
718 |
| - if outputs.certificate is not None: |
719 |
| - with outputs.certificate.open(mode="w") as io: |
720 |
| - print(cert_pem, file=io) |
721 |
| - print(f"Certificate written to {outputs.certificate}") |
722 |
| - |
723 |
| - if outputs.bundle is not None: |
724 |
| - with outputs.bundle.open(mode="w") as io: |
725 |
| - print(result.to_json(), file=io) |
726 |
| - print(f"Sigstore bundle written to {outputs.bundle}") |
| 710 | + sig_output: TextIO |
| 711 | + if outputs.signature is not None: |
| 712 | + sig_output = outputs.signature.open("w") |
| 713 | + else: |
| 714 | + sig_output = sys.stdout |
| 715 | + |
| 716 | + signature = base64.b64encode(result.signature).decode() |
| 717 | + print(signature, file=sig_output) |
| 718 | + if outputs.signature is not None: |
| 719 | + print(f"Signature written to {outputs.signature}") |
| 720 | + |
| 721 | + if outputs.certificate is not None: |
| 722 | + with outputs.certificate.open(mode="w") as io: |
| 723 | + print(cert_pem, file=io) |
| 724 | + print(f"Certificate written to {outputs.certificate}") |
| 725 | + |
| 726 | + if outputs.bundle is not None: |
| 727 | + with outputs.bundle.open(mode="w") as io: |
| 728 | + print(result.to_json(), file=io) |
| 729 | + print(f"Sigstore bundle written to {outputs.bundle}") |
| 730 | + |
| 731 | + jobs = [ |
| 732 | + executor.submit(_sign_file, file, outputs) |
| 733 | + for file, outputs in output_map.items() |
| 734 | + ] |
| 735 | + for job in futures.as_completed(jobs): |
| 736 | + job.result() |
727 | 737 |
|
728 | 738 |
|
729 | 739 | def _attest(args: argparse.Namespace) -> None:
|
|
0 commit comments