Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tuf/api/dsse.py: 31%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Low-level TUF DSSE API. (experimental!)"""
3from __future__ import annotations
5import json
6from typing import Generic, cast
8from securesystemslib.dsse import Envelope as BaseSimpleEnvelope
10# Expose all payload classes to use API independently of ``tuf.api.metadata``.
11from tuf.api._payload import ( # noqa: F401
12 _ROOT,
13 _SNAPSHOT,
14 _TARGETS,
15 _TIMESTAMP,
16 SPECIFICATION_VERSION,
17 TOP_LEVEL_ROLE_NAMES,
18 BaseFile,
19 DelegatedRole,
20 Delegations,
21 MetaFile,
22 Role,
23 Root,
24 RootVerificationResult,
25 Signed,
26 Snapshot,
27 SuccinctRoles,
28 T,
29 TargetFile,
30 Targets,
31 Timestamp,
32 VerificationResult,
33)
34from tuf.api.serialization import DeserializationError, SerializationError
37class SimpleEnvelope(Generic[T], BaseSimpleEnvelope):
38 """Dead Simple Signing Envelope (DSSE) for TUF payloads.
40 * Sign with ``self.sign()`` (inherited).
41 * Verify with ``verify_delegate`` on a ``Root`` or ``Targets``
42 object::
44 delegator.verify_delegate(
45 role_name,
46 envelope.pae(), # Note, how we don't pass ``envelope.payload``!
47 envelope.signatures,
48 )
50 Attributes:
51 payload: Serialized payload bytes.
52 payload_type: Payload string identifier.
53 signatures: Ordered dictionary of keyids to ``Signature`` objects.
55 """
57 DEFAULT_PAYLOAD_TYPE = "application/vnd.tuf+json"
59 @classmethod
60 def from_bytes(cls, data: bytes) -> SimpleEnvelope[T]:
61 """Load envelope from JSON bytes.
63 NOTE: Unlike ``tuf.api.metadata.Metadata.from_bytes``, this method
64 does not deserialize the contained payload. Use ``self.get_signed`` to
65 deserialize the payload into a ``Signed`` object.
67 Args:
68 data: envelope JSON bytes.
70 Raises:
71 tuf.api.serialization.DeserializationError:
72 data cannot be deserialized.
74 Returns:
75 TUF ``SimpleEnvelope`` object.
76 """
77 try:
78 envelope_dict = json.loads(data.decode())
79 envelope = SimpleEnvelope.from_dict(envelope_dict)
81 except Exception as e:
82 raise DeserializationError from e
84 return cast(SimpleEnvelope[T], envelope)
86 def to_bytes(self) -> bytes:
87 """Return envelope as JSON bytes.
89 NOTE: Unlike ``tuf.api.metadata.Metadata.to_bytes``, this method does
90 not serialize the payload. Use ``SimpleEnvelope.from_signed`` to
91 serialize a ``Signed`` object and wrap it in an SimpleEnvelope.
93 Raises:
94 tuf.api.serialization.SerializationError:
95 self cannot be serialized.
96 """
97 try:
98 envelope_dict = self.to_dict()
99 json_bytes = json.dumps(envelope_dict).encode()
101 except Exception as e:
102 raise SerializationError from e
104 return json_bytes
106 @classmethod
107 def from_signed(cls, signed: T) -> SimpleEnvelope[T]:
108 """Serialize payload as JSON bytes and wrap in envelope.
110 Args:
111 signed: ``Signed`` object.
113 Raises:
114 tuf.api.serialization.SerializationError:
115 The signed object cannot be serialized.
116 """
117 try:
118 signed_dict = signed.to_dict()
119 json_bytes = json.dumps(signed_dict).encode()
121 except Exception as e:
122 raise SerializationError from e
124 return cls(json_bytes, cls.DEFAULT_PAYLOAD_TYPE, {})
126 def get_signed(self) -> T:
127 """Extract and deserialize payload JSON bytes from envelope.
129 Raises:
130 tuf.api.serialization.DeserializationError:
131 The signed object cannot be deserialized.
132 """
134 try:
135 payload_dict = json.loads(self.payload.decode())
137 # TODO: can we move this to tuf.api._payload?
138 _type = payload_dict["_type"]
139 if _type == _TARGETS:
140 inner_cls: type[Signed] = Targets
141 elif _type == _SNAPSHOT:
142 inner_cls = Snapshot
143 elif _type == _TIMESTAMP:
144 inner_cls = Timestamp
145 elif _type == _ROOT:
146 inner_cls = Root
147 else:
148 raise ValueError(f'unrecognized role type "{_type}"')
150 except Exception as e:
151 raise DeserializationError from e
153 return cast(T, inner_cls.from_dict(payload_dict))