mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-18 11:21:11 +00:00
fix(core): store translation signatures directly into history section
without messing with the "current" entry at all
This commit is contained in:
parent
63d1311509
commit
229a06d3a2
@ -31,16 +31,22 @@ VERSION_H = HERE.parent / "embed" / "firmware" / "version.h"
|
|||||||
SIGNATURES_JSON = HERE / "signatures.json"
|
SIGNATURES_JSON = HERE / "signatures.json"
|
||||||
|
|
||||||
|
|
||||||
class SignatureInfo(t.TypedDict):
|
class SignedInfo(t.TypedDict):
|
||||||
|
merkle_root: str
|
||||||
|
signature: str
|
||||||
|
datetime: str
|
||||||
|
commit: str
|
||||||
|
|
||||||
|
|
||||||
|
class UnsignedInfo(t.TypedDict):
|
||||||
merkle_root: str
|
merkle_root: str
|
||||||
signature: str | None
|
|
||||||
datetime: str
|
datetime: str
|
||||||
commit: str
|
commit: str
|
||||||
|
|
||||||
|
|
||||||
class SignatureFile(t.TypedDict):
|
class SignatureFile(t.TypedDict):
|
||||||
current: SignatureInfo
|
current: UnsignedInfo
|
||||||
history: list[SignatureInfo]
|
history: list[SignedInfo]
|
||||||
|
|
||||||
|
|
||||||
def _version_from_version_h() -> translations.VersionTuple:
|
def _version_from_version_h() -> translations.VersionTuple:
|
||||||
@ -63,21 +69,22 @@ def _version_from_version_h() -> translations.VersionTuple:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def make_signature_info(merkle_root: bytes, signature: bytes | None) -> SignatureInfo:
|
def make_tree_info(merkle_root: bytes) -> UnsignedInfo:
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.utcnow()
|
||||||
commit = (
|
commit = (
|
||||||
subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=HERE)
|
subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=HERE)
|
||||||
.decode("ascii")
|
.decode("ascii")
|
||||||
.strip()
|
.strip()
|
||||||
)
|
)
|
||||||
return SignatureInfo(
|
return UnsignedInfo(
|
||||||
merkle_root=merkle_root.hex(),
|
merkle_root=merkle_root.hex(), datetime=now.isoformat(), commit=commit
|
||||||
signature=signature.hex() if signature is not None else None,
|
|
||||||
datetime=now.isoformat(),
|
|
||||||
commit=commit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sign_info(info: UnsignedInfo, signature: bytes) -> SignedInfo:
|
||||||
|
return SignedInfo(signature=signature.hex(), **info)
|
||||||
|
|
||||||
|
|
||||||
def update_merkle_root(signature_file: SignatureFile, merkle_root: bytes) -> bool:
|
def update_merkle_root(signature_file: SignatureFile, merkle_root: bytes) -> bool:
|
||||||
"""Update signatures.json with the new Merkle root.
|
"""Update signatures.json with the new Merkle root.
|
||||||
|
|
||||||
@ -89,18 +96,11 @@ def update_merkle_root(signature_file: SignatureFile, merkle_root: bytes) -> boo
|
|||||||
# Merkle root is already up to date
|
# Merkle root is already up to date
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if current["signature"] is None:
|
# overwrite with a new one
|
||||||
# current content is not signed. just overwrite with a new one
|
signature_file["current"] = make_tree_info(merkle_root)
|
||||||
signature_file["current"] = make_signature_info(merkle_root, None)
|
|
||||||
SIGNATURES_JSON.write_text(json.dumps(signature_file, indent=2))
|
SIGNATURES_JSON.write_text(json.dumps(signature_file, indent=2))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# move current to history
|
|
||||||
signature_file["history"].insert(0, current)
|
|
||||||
# create new current
|
|
||||||
signature_file["current"] = make_signature_info(merkle_root, None)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def generate_all_blobs(rewrite_version: bool) -> list[translations.TranslationsBlob]:
|
def generate_all_blobs(rewrite_version: bool) -> list[translations.TranslationsBlob]:
|
||||||
order = translations.order_from_json(json.loads((HERE / "order.json").read_text()))
|
order = translations.order_from_json(json.loads((HERE / "order.json").read_text()))
|
||||||
@ -188,7 +188,8 @@ def cli() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
def gen() -> None:
|
@click.option("--signed", is_flag=True, help="Generate signed blobs.")
|
||||||
|
def gen(signed: bool | None) -> None:
|
||||||
"""Generate all language blobs for all models.
|
"""Generate all language blobs for all models.
|
||||||
|
|
||||||
The generated blobs will be signed with the development keys.
|
The generated blobs will be signed with the development keys.
|
||||||
@ -196,11 +197,25 @@ def gen() -> None:
|
|||||||
all_blobs = generate_all_blobs(rewrite_version=True)
|
all_blobs = generate_all_blobs(rewrite_version=True)
|
||||||
tree = merkle_tree.MerkleTree(b.header_bytes for b in all_blobs)
|
tree = merkle_tree.MerkleTree(b.header_bytes for b in all_blobs)
|
||||||
root = tree.get_root_hash()
|
root = tree.get_root_hash()
|
||||||
|
|
||||||
|
signature_file: SignatureFile = json.loads(SIGNATURES_JSON.read_text())
|
||||||
|
|
||||||
|
if signed:
|
||||||
|
for entry in signature_file["history"]:
|
||||||
|
if entry["merkle_root"] == root.hex():
|
||||||
|
signature_hex = entry["signature"]
|
||||||
|
signature_bytes = bytes.fromhex(signature_hex)
|
||||||
|
sigmask, signature = signature_bytes[0], signature_bytes[1:]
|
||||||
|
build_all_blobs(all_blobs, tree, sigmask, signature, production=True)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise click.ClickException(
|
||||||
|
"No matching signature found in signatures.json. Run `cli.py sign` first."
|
||||||
|
)
|
||||||
|
|
||||||
signature = cosi.sign_with_privkeys(root, PRIVATE_KEYS_DEV)
|
signature = cosi.sign_with_privkeys(root, PRIVATE_KEYS_DEV)
|
||||||
sigmask = 0b111
|
sigmask = 0b111
|
||||||
build_all_blobs(all_blobs, tree, sigmask, signature)
|
build_all_blobs(all_blobs, tree, sigmask, signature)
|
||||||
|
|
||||||
signature_file = json.loads(SIGNATURES_JSON.read_text())
|
|
||||||
if update_merkle_root(signature_file, root):
|
if update_merkle_root(signature_file, root):
|
||||||
SIGNATURES_JSON.write_text(json.dumps(signature_file, indent=2) + "\n")
|
SIGNATURES_JSON.write_text(json.dumps(signature_file, indent=2) + "\n")
|
||||||
click.echo("Updated signatures.json")
|
click.echo("Updated signatures.json")
|
||||||
@ -228,13 +243,10 @@ def merkle_root() -> None:
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.argument("signature_hex", required=False)
|
@click.argument("signature_hex")
|
||||||
@click.option("--force", is_flag=True)
|
@click.option("--force", is_flag=True, help="Write even if the signature is invalid.")
|
||||||
def sign(signature_hex: str | None, force: bool) -> None:
|
def sign(signature_hex: str, force: bool | None) -> None:
|
||||||
"""Insert a signature into language blobs.
|
"""Insert a signature into language blobs."""
|
||||||
|
|
||||||
If signature_hex is not provided, the signature will be located in signatures.json.
|
|
||||||
"""
|
|
||||||
all_blobs = generate_all_blobs(rewrite_version=False)
|
all_blobs = generate_all_blobs(rewrite_version=False)
|
||||||
tree = merkle_tree.MerkleTree(b.header_bytes for b in all_blobs)
|
tree = merkle_tree.MerkleTree(b.header_bytes for b in all_blobs)
|
||||||
root = tree.get_root_hash()
|
root = tree.get_root_hash()
|
||||||
@ -247,23 +259,10 @@ def sign(signature_hex: str | None, force: bool) -> None:
|
|||||||
f"Stored in signatures.json: {signature_file['current']['merkle_root']}"
|
f"Stored in signatures.json: {signature_file['current']['merkle_root']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if signature_hex is None:
|
|
||||||
if signature_file["current"]["signature"] is None:
|
|
||||||
raise click.ClickException("Please provide a signature.")
|
|
||||||
signature_hex = signature_file["current"]["signature"]
|
|
||||||
elif (
|
|
||||||
not force
|
|
||||||
and signature_file["current"]["signature"] is not None
|
|
||||||
and signature_file["current"]["signature"] != signature_hex
|
|
||||||
):
|
|
||||||
raise click.ClickException(
|
|
||||||
"A different signature is already present in signatures.json\n"
|
|
||||||
"Use --force to overwrite it."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Update signature file data. It will be written only if the signature verifies.
|
# Update signature file data. It will be written only if the signature verifies.
|
||||||
signature_file["current"]["signature"] = signature_hex
|
tree_info = make_tree_info(root)
|
||||||
signature_file["current"]["datetime"] = datetime.datetime.utcnow().isoformat()
|
signed_info = sign_info(tree_info, bytes.fromhex(signature_hex))
|
||||||
|
signature_file["history"].insert(0, signed_info)
|
||||||
|
|
||||||
signature_bytes = bytes.fromhex(signature_hex)
|
signature_bytes = bytes.fromhex(signature_hex)
|
||||||
sigmask, signature = signature_bytes[0], signature_bytes[1:]
|
sigmask, signature = signature_bytes[0], signature_bytes[1:]
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"merkle_root": "d349550e9ad7be0b63b06fc43c6b764d008bf4497a8e1d0374cb92119b242160",
|
"merkle_root": "d349550e9ad7be0b63b06fc43c6b764d008bf4497a8e1d0374cb92119b242160",
|
||||||
"signature": null,
|
|
||||||
"datetime": "2024-03-25T14:23:51.859562",
|
"datetime": "2024-03-25T14:23:51.859562",
|
||||||
"commit": "ef11039a818bb0941191af49e0c9a0f7aae9324a"
|
"commit": "ef11039a818bb0941191af49e0c9a0f7aae9324a"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user