Sign Sparkle appcasts in Python
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Successful in 3m4s
Build Rust / Cargo Test (push) Successful in 4m5s
Build Site / Next.js Build (push) Failing after 4s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 4s
Lint Governance / BEP Metadata (push) Successful in 1s
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Successful in 3m4s
Build Rust / Cargo Test (push) Successful in 4m5s
Build Site / Next.js Build (push) Failing after 4s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 4s
Lint Governance / BEP Metadata (push) Successful in 1s
This commit is contained in:
parent
1ef6fedeaf
commit
640d4693ac
5 changed files with 35 additions and 26 deletions
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
Google Cloud KMS Ed25519 signing can only sign small payloads directly. Sparkle
|
||||
requires a standard EdDSA signature over the full update archive, so normal
|
||||
release artifacts use Sparkle's signing tool with the decrypted release key.
|
||||
release artifacts sign directly from the decrypted release seed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -58,32 +58,39 @@ def kms_sign(args: argparse.Namespace, payload: Path) -> str:
|
|||
return base64.b64encode(signature_path.read_bytes()).decode("ascii")
|
||||
|
||||
|
||||
def sign_update(args: argparse.Namespace, payload: Path) -> str:
|
||||
def read_sparkle_seed(key_path: Path) -> bytes:
|
||||
key_data = key_path.read_bytes().strip()
|
||||
if len(key_data) == 32:
|
||||
return key_data
|
||||
try:
|
||||
decoded = base64.b64decode(key_data, validate=True)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"Sparkle key is not raw or base64 Ed25519 seed data: {key_path}") from exc
|
||||
if len(decoded) != 32:
|
||||
raise ValueError(f"Sparkle key must decode to a 32-byte Ed25519 seed: {key_path}")
|
||||
return decoded
|
||||
|
||||
|
||||
def sign_with_release_seed(args: argparse.Namespace, payload: Path) -> str:
|
||||
key_path = Path(args.ed_key_path) if args.ed_key_path else None
|
||||
if key_path is None or not key_path.is_file():
|
||||
raise FileNotFoundError(
|
||||
"Sparkle artifact is too large for direct Google KMS Ed25519 signing "
|
||||
"and SPARKLE_EDDSA_KEY_PATH does not point to a key file"
|
||||
)
|
||||
result = run([
|
||||
args.sign_update,
|
||||
"--ed-key-file",
|
||||
str(key_path),
|
||||
"-p",
|
||||
str(payload),
|
||||
])
|
||||
signature = result.stdout.strip().splitlines()[-1].strip()
|
||||
if not signature:
|
||||
raise ValueError(f"Sparkle sign_update produced no signature for {payload}")
|
||||
return signature
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
|
||||
seed = read_sparkle_seed(key_path)
|
||||
signature = Ed25519PrivateKey.from_private_bytes(seed).sign(payload.read_bytes())
|
||||
return base64.b64encode(signature).decode("ascii")
|
||||
|
||||
|
||||
def sparkle_signature(args: argparse.Namespace, payload: Path) -> str:
|
||||
if args.ed_key_path:
|
||||
return sign_update(args, payload)
|
||||
return sign_with_release_seed(args, payload)
|
||||
if payload.stat().st_size <= args.kms_max_input_bytes:
|
||||
return kms_sign(args, payload)
|
||||
return sign_update(args, payload)
|
||||
return sign_with_release_seed(args, payload)
|
||||
|
||||
|
||||
def enclosure_path(enclosure: ET.Element, artifact_dir: Path) -> Path:
|
||||
|
|
@ -130,7 +137,6 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
|
|||
parser.add_argument("--kms-max-input-bytes", type=int, default=DEFAULT_KMS_MAX_INPUT_BYTES)
|
||||
parser.add_argument("--gcloud", default=os.environ.get("GCLOUD", "gcloud"))
|
||||
parser.add_argument("--ed-key-path", default=os.environ.get("SPARKLE_EDDSA_KEY_PATH"))
|
||||
parser.add_argument("--sign-update", default=os.environ.get("SPARKLE_SIGN_UPDATE", "sign_update"))
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue