20
20
Metadata ,
21
21
MetaFile ,
22
22
Root ,
23
+ RootVerificationResult ,
24
+ Signed ,
23
25
Snapshot ,
24
26
TargetFile ,
25
27
Targets ,
26
28
Timestamp ,
29
+ VerificationResult ,
27
30
)
28
31
from tuf .repository import Repository
29
32
@@ -89,6 +92,27 @@ def targets_infos(self) -> Dict[str, MetaFile]:
89
92
def snapshot_info (self ) -> MetaFile :
90
93
return self ._snapshot_info
91
94
95
+ def _get_verification_result (
96
+ self , role : str , md : Metadata
97
+ ) -> VerificationResult | RootVerificationResult :
98
+ """Verify roles metadata using the existing repository metadata"""
99
+ if role == Root .type :
100
+ assert isinstance (md .signed , Root )
101
+ root = self .root ()
102
+ if root .version == 0 :
103
+ # special case first root
104
+ root = md .signed
105
+ return md .signed .get_root_verification_result (
106
+ root , md .signed_bytes , md .signatures
107
+ )
108
+ if role in [Timestamp .type , Snapshot .type , Targets .type ]:
109
+ delegator : Signed = self .root ()
110
+ else :
111
+ delegator = self .targets ()
112
+ return delegator .get_verification_result (
113
+ role , md .signed_bytes , md .signatures
114
+ )
115
+
92
116
def open (self , role : str ) -> Metadata :
93
117
"""Return current Metadata for role from 'storage' (or create a new one)"""
94
118
@@ -112,6 +136,14 @@ def close(self, role: str, md: Metadata) -> None:
112
136
for signer in self .signer_cache [role ]:
113
137
md .sign (signer , append = True )
114
138
139
+ # Double check that we only write verified metadata
140
+ vr = self ._get_verification_result (role , md )
141
+ if not vr :
142
+ raise ValueError (f"Role { role } failed to verify" )
143
+ keyids = [keyid [:7 ] for keyid in vr .signed ]
144
+ verify_str = f"verified with keys [{ ', ' .join (keyids )} ]"
145
+ logger .debug ("Role %s v%d: %s" , role , md .signed .version , verify_str )
146
+
115
147
# store new metadata version, update version caches
116
148
self .role_cache [role ].append (md )
117
149
if role == "snapshot" :
@@ -130,8 +162,6 @@ def add_target(self, path: str, content: str) -> None:
130
162
with self .edit_targets () as targets :
131
163
targets .targets [path ] = TargetFile .from_data (path , data )
132
164
133
- logger .debug ("Targets v%d" , targets .version )
134
-
135
165
# update snapshot, timestamp
136
166
self .do_snapshot ()
137
167
self .do_timestamp ()
@@ -157,8 +187,6 @@ def submit_delegation(self, rolename: str, data: bytes) -> bool:
157
187
logger .info ("Failed to add delegation for %s: %s" , rolename , e )
158
188
return False
159
189
160
- logger .debug ("Targets v%d" , targets .version )
161
-
162
190
# update snapshot, timestamp
163
191
self .do_snapshot ()
164
192
self .do_timestamp ()
@@ -177,19 +205,26 @@ def submit_role(self, role: str, data: bytes) -> bool:
177
205
if not targetpath .startswith (f"{ role } /" ):
178
206
raise ValueError (f"targets allowed under { role } / only" )
179
207
180
- self .targets ().verify_delegate (role , md .signed_bytes , md .signatures )
181
-
182
208
if md .signed .version != self .targets (role ).version + 1 :
183
209
raise ValueError ("Invalid version {md.signed.version}" )
184
210
185
211
except (RepositoryError , ValueError ) as e :
186
212
logger .info ("Failed to add new version for %s: %s" , role , e )
187
213
return False
188
214
215
+ # Check that we only write verified metadata
216
+ vr = self ._get_verification_result (role , md )
217
+ if not vr :
218
+ logger .info ("Role %s failed to verify" , role )
219
+ return False
220
+
221
+ keyids = [keyid [:7 ] for keyid in vr .signed ]
222
+ verify_str = f"verified with keys [{ ', ' .join (keyids )} ]"
223
+ logger .debug ("Role %s v%d: %s" , role , md .signed .version , verify_str )
224
+
189
225
# Checks passed: Add new delegated role version
190
226
self .role_cache [role ].append (md )
191
227
self ._targets_infos [f"{ role } .json" ].version = md .signed .version
192
- logger .debug ("%s v%d" , role , md .signed .version )
193
228
194
229
# To keep it simple, target content is generated from targetpath
195
230
for targetpath in md .signed .targets :
0 commit comments