1717import subprocess
1818import sys
1919import tarfile
20- import tempfile
2120import zipfile
2221from pathlib import Path
2322
@@ -334,12 +333,22 @@ def validate_archive_contents(archive: Path) -> list[str]:
334333 raise ReadinessError (f"unsupported archive extension: { archive .name } " )
335334
336335
337- def verify_signature (archive : Path , signature : Path , public_key : Path ) -> None :
338- if not public_key .is_file ():
336+ def verify_signature (archive : Path , signature : Path , public_key : Path | None ) -> None :
337+ """Verify ``archive`` against ``public_key`` via ``cargo xtask verify``.
338+
339+ When ``public_key`` is ``None`` the ``--public-key`` flag is omitted and
340+ ``cargo xtask verify`` reads the key from ``ROCM_CLI_SIGNING_PUBLIC_KEY_PEM``
341+ (the inline PEM release/nightly CI wire from the signing-key secret), so no
342+ temporary key file has to be materialized here.
343+ """
344+ if public_key is not None and not public_key .is_file ():
339345 raise ReadinessError (f"public key does not exist: { public_key } " )
340346 cargo = shutil .which ("cargo" )
341347 if cargo is None :
342348 raise ReadinessError ("cargo is required for signature verification" )
349+ key_args = (
350+ ["--public-key" , str (public_key .resolve ())] if public_key is not None else []
351+ )
343352 # Resolve to absolute paths because the subprocess runs from the repo root
344353 # (so the `cargo xtask` alias resolves); relative paths would otherwise be
345354 # interpreted against the repo root rather than the caller's working directory.
@@ -348,8 +357,7 @@ def verify_signature(archive: Path, signature: Path, public_key: Path) -> None:
348357 cargo ,
349358 "xtask" ,
350359 "verify" ,
351- "--public-key" ,
352- str (public_key .resolve ()),
360+ * key_args ,
353361 "--in" ,
354362 str (archive .resolve ()),
355363 "--signature" ,
@@ -374,6 +382,7 @@ def validate_archive(
374382 * ,
375383 require_signatures : bool ,
376384 public_key : Path | None ,
385+ verify_with_env_key : bool = False ,
377386 require_rocm_asset_names : bool ,
378387) -> list [str ]:
379388 messages : list [str ] = []
@@ -400,15 +409,16 @@ def validate_archive(
400409 )
401410 messages .append (f"checksum ok: { archive .name } " )
402411
412+ verify = public_key is not None or verify_with_env_key
403413 signature = Path (f"{ archive } .sig" )
404- if require_signatures or public_key is not None :
414+ if require_signatures or verify :
405415 if not signature .is_file ():
406416 raise ReadinessError (f"missing signature sidecar: { signature } " )
407417 if signature .stat ().st_size <= 0 :
408418 raise ReadinessError (f"signature sidecar is empty: { signature } " )
409419 messages .append (f"signature present: { archive .name } .sig" )
410420
411- if public_key is not None :
421+ if verify :
412422 verify_signature (archive , signature , public_key )
413423 messages .append (f"signature verified: { archive .name } " )
414424
@@ -464,33 +474,6 @@ def require_existing_env_path(name: str) -> Path:
464474 return path
465475
466476
467- def resolve_release_public_key () -> Path :
468- """Materialize the release signing public key from ROCM_CLI_SIGNING_PUBLIC_KEY_PEM
469- into a temp file so archives can be verified against it. Raises if it is not set.
470-
471- Verifying against this key proves the artifact CI is about to publish was signed
472- by the private half of the key that installers will pin as a trust root, instead
473- of merely confirming a key input exists — a mismatched pair would otherwise pass
474- the gate and ship releases that installers reject once default-on verification
475- lands. The env value MUST be the same public key pinned in install.sh/.ps1.
476-
477- Only the inline PEM form is accepted (that is what release/nightly CI wire from
478- the signing-key secret); to verify against a key *file* on disk, pass its path
479- with the --public-key flag instead.
480- """
481- pem = env_text ("ROCM_CLI_SIGNING_PUBLIC_KEY_PEM" )
482- if pem is None :
483- raise ReadinessError (
484- "production trust requires the release signing public key: set "
485- "ROCM_CLI_SIGNING_PUBLIC_KEY_PEM (or pass --public-key)"
486- )
487- with tempfile .NamedTemporaryFile (
488- "w" , suffix = ".pem" , delete = False , encoding = "ascii"
489- ) as handle :
490- handle .write (pem if pem .endswith ("\n " ) else f"{ pem } \n " )
491- return Path (handle .name )
492-
493-
494477def validate_production_trust () -> list [str ]:
495478 """Validate only explicit owner-provided production trust inputs."""
496479
@@ -539,11 +522,13 @@ def validate_release(
539522 assets : list [str ],
540523 require_signatures : bool ,
541524 public_key : Path | None ,
525+ verify_with_env_key : bool = False ,
542526 require_production_trust : bool ,
543527 require_rocm_asset_names : bool ,
544528 require_exact_assets : bool ,
545529) -> list [str ]:
546530 archives = discover_archives (dist , assets )
531+ verify = public_key is not None or verify_with_env_key
547532 messages : list [str ] = []
548533 if require_exact_assets :
549534 if not assets :
@@ -552,7 +537,7 @@ def validate_release(
552537 validate_exact_dist_assets (
553538 dist ,
554539 archives ,
555- expect_signatures = require_signatures or public_key is not None ,
540+ expect_signatures = require_signatures or verify ,
556541 )
557542 )
558543 for archive in archives :
@@ -561,6 +546,7 @@ def validate_release(
561546 archive ,
562547 require_signatures = require_signatures ,
563548 public_key = public_key ,
549+ verify_with_env_key = verify_with_env_key ,
564550 require_rocm_asset_names = require_rocm_asset_names ,
565551 )
566552 )
@@ -869,29 +855,6 @@ def run_self_test(root: Path) -> None:
869855 )
870856 print ("release readiness self-test: valid production trust inputs accepted" )
871857
872- # resolve_release_public_key(): the PEM env input is materialized to a temp
873- # file, and a missing input is an error. This is the key CI verifies release
874- # archives against under production trust.
875- pem_text = "-----BEGIN PUBLIC KEY-----\n self-test\n -----END PUBLIC KEY-----"
876- resolved_pem = run_with_env (
877- {"ROCM_CLI_SIGNING_PUBLIC_KEY_PEM" : pem_text },
878- resolve_release_public_key ,
879- )
880- if resolved_pem .read_text (encoding = "ascii" ) != f"{ pem_text } \n " :
881- raise ReadinessError (
882- "resolve_release_public_key did not materialize the PEM input"
883- )
884- resolved_pem .unlink ()
885- print ("release readiness self-test: release public key resolution ok" )
886-
887- expect_failure (
888- "production trust without a release public key" ,
889- lambda : run_with_env (
890- {"ROCM_CLI_SIGNING_PUBLIC_KEY_PEM" : None },
891- resolve_release_public_key ,
892- ),
893- )
894-
895858 expect_failure (
896859 "missing explicit asset" ,
897860 lambda : validate_release (
@@ -1011,18 +974,28 @@ def main() -> None:
1011974 # trust; otherwise opportunistic: if a signing public key is present in the
1012975 # environment (release/nightly wire it from the secret), verify against it.
1013976 # An explicit --public-key still wins; with neither, behavior is unchanged.
977+ #
978+ # When relying on the environment key, `cargo xtask verify` reads it directly
979+ # from ROCM_CLI_SIGNING_PUBLIC_KEY_PEM, so no temp key file is materialized here.
1014980 public_key = args .public_key
981+ verify_with_env_key = False
1015982 try :
1016983 if public_key is None and (
1017984 require_production_trust
1018985 or env_text ("ROCM_CLI_SIGNING_PUBLIC_KEY_PEM" ) is not None
1019986 ):
1020- public_key = resolve_release_public_key ()
987+ if env_text ("ROCM_CLI_SIGNING_PUBLIC_KEY_PEM" ) is None :
988+ raise ReadinessError (
989+ "production trust requires the release signing public key: set "
990+ "ROCM_CLI_SIGNING_PUBLIC_KEY_PEM (or pass --public-key)"
991+ )
992+ verify_with_env_key = True
1021993 messages = validate_release (
1022994 Path (args .dist ),
1023995 assets = args .asset ,
1024996 require_signatures = require_signatures ,
1025997 public_key = public_key ,
998+ verify_with_env_key = verify_with_env_key ,
1026999 require_production_trust = require_production_trust ,
10271000 require_rocm_asset_names = args .require_rocm_asset_names ,
10281001 require_exact_assets = args .require_exact_assets ,
0 commit comments