From 8213d77b509e4bf5580b87c8b87c3457919898f1 Mon Sep 17 00:00:00 2001 From: Martin Vrachev Date: Wed, 9 Feb 2022 16:16:30 +0200 Subject: [PATCH] Add unrecognized fields support for Signature The TUF specification says the following regarding unknown fields: "All of the formats described below include the ability to add more attribute-value fields to objects for backward-compatible format changes. Implementers who encounter undefined attribute-value pairs in the format must include the data when calculating hashes or verifying signatures and must preserve the data when re-serializing." From: https://theupdateframework.github.io/specification/latest/#document-formats This section is the reason ADR0008 was accepted inside python-tuf (see here: https://github.com/theupdateframework/python-tuf/blob/develop/docs/adr/0008-accept-unrecognised-fields.md) and we have added support for unrecognized fields for all fields inside the SIGNED portion of the metadata. However, this limits what the citation implies or that everywhere there inside the metadata files including signatures we should accept unrecognized fields. That's why I made these changes. These changes have the approval of the community see: - https://github.com/theupdateframework/specification/issues/203 - https://github.com/theupdateframework/python-tuf/issues/1802 Another change I had to do, so I can add unrecognized fields support inside Signature is to make "Signature.from_dict()" to behave the same way as the rest of the "from_dict()" functions inside TUF or destroy the input dictionary. This was necessary, as that provides me with an easy way to pass the unrecognized fields to the constructor. Signed-off-by: Martin Vrachev --- securesystemslib/signer.py | 31 ++++++++++++++++++++++++++----- tests/test_signer.py | 8 ++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/securesystemslib/signer.py b/securesystemslib/signer.py index 59ad9999..bfe87d77 100644 --- a/securesystemslib/signer.py +++ b/securesystemslib/signer.py @@ -7,7 +7,7 @@ import abc import securesystemslib.keys as sslib_keys -from typing import Any, Dict +from typing import Any, Dict, Optional, Mapping class Signature: @@ -22,18 +22,30 @@ class Signature: Attributes: keyid: HEX string used as a unique identifier of the key. signature: HEX string representing the signature. + unrecognized_fields: Dictionary of all attributes that are not managed + by securesystemslib. """ - def __init__(self, keyid: str, sig: str): + def __init__( + self, + keyid: str, + sig: str, + unrecognized_fields: Optional[Mapping[str, Any]] = None + ): self.keyid = keyid self.signature = sig + self.unrecognized_fields: Mapping[str, Any] = unrecognized_fields or {} def __eq__(self, other: Any) -> bool: if not isinstance(other, Signature): return False - return self.keyid == other.keyid and self.signature == other.signature + return ( + self.keyid == other.keyid + and self.signature == other.signature + and self.unrecognized_fields == other.unrecognized_fields + ) @classmethod @@ -50,11 +62,19 @@ def from_dict(cls, signature_dict: Dict) -> "Signature": KeyError: If any of the "keyid" and "sig" fields are missing from the signature_dict. + Side Effect: + Destroys the metadata dict passed by reference. + Returns: A "Signature" instance. """ - return cls(signature_dict["keyid"], signature_dict["sig"]) + keyid = signature_dict.pop("keyid") + sig = signature_dict.pop("sig") + # All fields left in the signature_dict are unrecognized. + return cls( + keyid, sig, signature_dict + ) def to_dict(self) -> Dict: @@ -62,7 +82,8 @@ def to_dict(self) -> Dict: return { "keyid": self.keyid, - "sig": self.signature + "sig": self.signature, + **self.unrecognized_fields, } diff --git a/tests/test_signer.py b/tests/test_signer.py index 81006ab6..b4f79bcb 100644 --- a/tests/test_signer.py +++ b/tests/test_signer.py @@ -61,9 +61,13 @@ def test_sslib_sign(self): def test_signature_from_to_dict(self): signature_dict = { "sig": "30460221009342e4566528fcecf6a7a5d53ebacdb1df151e242f55f8775883469cb01dbc6602210086b426cc826709acfa2c3f9214610cb0a832db94bbd266fd7c5939a48064a851", - "keyid": "11fa391a0ed7a447cbfeb4b2667e286fc248f64d5e6d0eeed2e5e23f97f9f714" + "keyid": "11fa391a0ed7a447cbfeb4b2667e286fc248f64d5e6d0eeed2e5e23f97f9f714", + "foo": "bar" # unrecognized_field } - sig_obj = Signature.from_dict(signature_dict) + sig_obj = Signature.from_dict(copy.copy(signature_dict)) + + # Verify that unrecognized fields are stored correctly. + self.assertEqual(sig_obj.unrecognized_fields, {"foo": "bar"}) self.assertDictEqual(signature_dict, sig_obj.to_dict())