@@ -64,7 +64,10 @@ def test_kms_urls_are_sent_to_upgrade_app(self) -> None:
6464
6565
6666class SealedEnvTests (unittest .TestCase ):
67+ """Exercise signed recipient selection and SealedEnv compatibility."""
68+
6769 def setUp (self ):
70+ """Create synthetic keys, a signed response and an isolated client."""
6871 self .app_id = "11" * 20
6972 self .epoch = 3
7073 self .timestamp = 1700000000
@@ -102,9 +105,12 @@ def setUp(self):
102105 self .envs = {"SECRET" : "synthetic test value" , "UNICODE" : "测试" }
103106
104107 def decrypt (self , payload ):
108+ """Check plaintext recovery and rejection under a different epoch context."""
105109 self .assertNotIn ("encrypted_env" , payload )
106110 envelope = json .loads (bytes .fromhex (payload ["sealed_env" ]))
107- self .assertEqual (set (envelope ), {"version" , "root_epoch" , "app_epoch" , "enc" , "ciphertext" })
111+ self .assertEqual (
112+ set (envelope ), {"version" , "root_epoch" , "app_epoch" , "enc" , "ciphertext" }
113+ )
108114 self .assertEqual (envelope ["version" ], 1 )
109115 self .assertEqual (envelope ["root_epoch" ], self .epoch )
110116 enc = base64 .b64decode (envelope ["enc" ], validate = True )
@@ -125,19 +131,25 @@ def decrypt(self, payload):
125131 wrong_epoch .open (ciphertext , aad = b"" )
126132
127133 def test_sealed_payload_round_trip_and_normalized_pins (self ):
134+ """Accept equivalent pin encodings without using legacy encryption."""
128135 for pin in [self .pin , "0x" + self .pin .upper ()]:
129136 with (
130137 self .subTest (pin = pin ),
131138 patch .object (VMM_CLI , "load_whitelist" , return_value = [pin ]),
132139 ):
133- self .decrypt (self .cli .encrypt_env_payload (self .envs , self .app_id , None , True ))
140+ self .decrypt (
141+ self .cli .encrypt_env_payload (self .envs , self .app_id , None , True )
142+ )
134143 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
135144
136145 def test_app_epoch_is_signed_and_used_for_sealing (self ):
146+ """Bind the app epoch in both the signature and HPKE context."""
137147 self .response ["app_epoch" ] = 2
138148 digest = keccak (
139- b"dstack-sealed-env-pubkey-v1:" + bytes .fromhex (self .app_id )
140- + self .epoch .to_bytes (4 , "big" ) + (2 ).to_bytes (4 , "big" )
149+ b"dstack-sealed-env-pubkey-v1:"
150+ + bytes .fromhex (self .app_id )
151+ + self .epoch .to_bytes (4 , "big" )
152+ + (2 ).to_bytes (4 , "big" )
141153 + bytes .fromhex (self .response ["public_key" ])
142154 + self .timestamp .to_bytes (8 , "big" )
143155 )
@@ -147,16 +159,20 @@ def test_app_epoch_is_signed_and_used_for_sealing(self):
147159 self .assertEqual (envelope ["version" ], 1 )
148160 self .assertEqual (envelope ["app_epoch" ], 2 )
149161 receiver = self .suite .create_recipient_context (
150- base64 .b64decode (envelope ["enc" ]), self .recipient .private_key ,
162+ base64 .b64decode (envelope ["enc" ]),
163+ self .recipient .private_key ,
151164 info = f"dstack-sealed-env-v1:{ self .epoch } :2" .encode (),
152165 )
153166 plaintext = json .loads (receiver .open (base64 .b64decode (envelope ["ciphertext" ])))
154- self .assertEqual (plaintext , {"env" : [{"key" : k , "value" : v } for k , v in self .envs .items ()]})
167+ self .assertEqual (
168+ plaintext , {"env" : [{"key" : k , "value" : v } for k , v in self .envs .items ()]}
169+ )
155170 self .response ["app_epoch" ] = 1
156171 with self .assertRaises (ValueError ):
157172 self .cli .encrypt_env_payload (self .envs , self .app_id , None , True )
158173
159174 def test_unpinned_signer_never_becomes_trusted (self ):
175+ """Reject recipients without an independently trusted signer."""
160176 for pins in [
161177 [],
162178 [keys .PrivateKey (b"\x33 " * 32 ).public_key .to_compressed_bytes ().hex ()],
@@ -172,6 +188,7 @@ def test_unpinned_signer_never_becomes_trusted(self):
172188 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
173189
174190 def test_signature_binds_every_field_and_app (self ):
191+ """Reject mutations of the signed claim or application identity."""
175192 cases = [
176193 ("public_key" , "44" * 32 ),
177194 ("root_epoch" , 4 ),
@@ -201,20 +218,26 @@ def test_signature_binds_every_field_and_app(self):
201218 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
202219
203220 def test_pinned_signed_key_timestamp_window (self ):
221+ """Enforce the freshness window for a trusted signer."""
204222 for age in [- 60 , 0 , 300 ]:
205- with self .subTest (age = age ), patch .object (
206- VMM_CLI .time , "time" , return_value = self .timestamp + age
223+ with (
224+ self .subTest (age = age ),
225+ patch .object (VMM_CLI .time , "time" , return_value = self .timestamp + age ),
207226 ):
208- self .decrypt (self .cli .encrypt_env_payload (self .envs , self .app_id , None , True ))
227+ self .decrypt (
228+ self .cli .encrypt_env_payload (self .envs , self .app_id , None , True )
229+ )
209230 for age , reason in [(- 61 , "future" ), (301 , "stale" )]:
210- with self .subTest (age = age ), patch .object (
211- VMM_CLI .time , "time" , return_value = self .timestamp + age
231+ with (
232+ self .subTest (age = age ),
233+ patch .object (VMM_CLI .time , "time" , return_value = self .timestamp + age ),
212234 ):
213235 with self .assertRaisesRegex (ValueError , reason ):
214236 self .cli .encrypt_env_payload (self .envs , self .app_id , None , True )
215237 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
216238
217239 def test_public_key_request_contains_only_app_id (self ):
240+ """Keep epoch selection on the KMS rather than in the lookup request."""
218241 response = self .cli .get_sealed_env_pub_key (self .app_id )
219242 self .assertEqual (
220243 self .cli .client .request .call_args .kwargs ["body" ],
@@ -223,8 +246,10 @@ def test_public_key_request_contains_only_app_id(self):
223246 self .assertEqual (response ["root_epoch" ], self .epoch )
224247
225248 def test_missing_rpc_cannot_replay_a_legacy_recipient (self ):
249+ """Reject capability downgrade attempts using a captured legacy key."""
226250 legacy_digest = keccak (
227- b"dstack-env-encrypt-pubkey:" + bytes .fromhex (self .app_id )
251+ b"dstack-env-encrypt-pubkey:"
252+ + bytes .fromhex (self .app_id )
228253 + bytes .fromhex (self .response ["public_key" ])
229254 )
230255 legacy = {
@@ -234,27 +259,48 @@ def test_missing_rpc_cannot_replay_a_legacy_recipient(self):
234259 missing = {"error" : "Service not found: GetSealedEnvPubKey" }
235260 errors = [(status , missing ) for status in [400 , 404 ]]
236261 errors += [
237- (400 , {"error" : f"Request failed with status={ status } , error={ json .dumps (missing )} " })
262+ (
263+ 400 ,
264+ {
265+ "error" : f"Request failed with status={ status } , error={ json .dumps (missing )} "
266+ },
267+ )
238268 for status in ["400 Bad Request" , "404 Not Found" ]
239269 ]
240270 for error in errors :
241- with self .subTest (error = error ), patch .object (VMM_CLI , "VmmClient" ) as client :
271+ with (
272+ self .subTest (error = error ),
273+ patch .object (VMM_CLI , "VmmClient" ) as client ,
274+ ):
242275 client .return_value .request .side_effect = [error , (200 , legacy )]
243276 with self .assertRaises (RuntimeError ):
244- self .cli .encrypt_env_payload (self .envs , self .app_id , "https://kms.example" , True )
277+ self .cli .encrypt_env_payload (
278+ self .envs , self .app_id , "https://kms.example" , True
279+ )
245280 client .return_value .request .assert_called_once ()
246281 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
247282
248283 def test_errors_and_bad_responses_do_not_downgrade (self ):
284+ """Fail closed on lookup errors and malformed key responses."""
249285 for status , body in [
250286 (401 , {"error" : "Service not found: GetSealedEnvPubKey" }),
251287 (403 , {"error" : "forbidden" }),
252288 (404 , "Not Found" ),
253289 (400 , {"error" : "invalid app" }),
254290 (500 , {"error" : "unavailable" }),
255- (400 , {"error" : 'Request failed with status=500 Internal Server Error, error={"error":"Service not found: GetSealedEnvPubKey"}' }),
256- (400 , {"error" : 'Request failed with status=404 Not Found, error={"error":"other service"}' }),
257- (400 , {"error" : 'Request failed with status=404 Not Found, error=invalid' }),
291+ (
292+ 400 ,
293+ {
294+ "error" : 'Request failed with status=500 Internal Server Error, error={"error":"Service not found: GetSealedEnvPubKey"}'
295+ },
296+ ),
297+ (
298+ 400 ,
299+ {
300+ "error" : 'Request failed with status=404 Not Found, error={"error":"other service"}'
301+ },
302+ ),
303+ (400 , {"error" : "Request failed with status=404 Not Found, error=invalid" }),
258304 (200 , {}),
259305 (200 , "invalid JSON" ),
260306 ]:
@@ -268,6 +314,7 @@ def test_errors_and_bad_responses_do_not_downgrade(self):
268314 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
269315
270316 def test_direct_kms_verifies_pin_and_does_not_forward_vmm_credentials (self ):
317+ """Authenticate direct KMS keys without forwarding VMM credentials."""
271318 with patch .object (VMM_CLI , "VmmClient" ) as client :
272319 client .return_value .request .return_value = (200 , self .response )
273320 self .decrypt (
@@ -279,17 +326,34 @@ def test_direct_kms_verifies_pin_and_does_not_forward_vmm_credentials(self):
279326 self .cli .get_app_env_encrypt_pub_key .assert_not_called ()
280327
281328 def test_image_downgrade_and_env_replacement_share_one_request (self ):
329+ """Replace the image and environment format in the same update."""
330+
282331 def rpc (method , params = None ):
283332 if method == "GetInfo" :
284- return {"found" : True , "info" : {"app_id" : self .app_id , "configuration" : {
285- "image" : "new" , "compose_file" : '{"kms_enabled":true}' ,
286- }}}
333+ return {
334+ "found" : True ,
335+ "info" : {
336+ "app_id" : self .app_id ,
337+ "configuration" : {
338+ "image" : "new" ,
339+ "compose_file" : '{"kms_enabled":true}' ,
340+ },
341+ },
342+ }
287343 if method == "ListImages" :
288- return {"images" : [{"name" : "new" , "sealed_env" : True }, {"name" : "old" }]}
344+ return {
345+ "images" : [{"name" : "new" , "sealed_env" : True }, {"name" : "old" }]
346+ }
289347 return {}
348+
290349 self .cli .rpc_call = Mock (side_effect = rpc )
291- with patch .object (VMM_CLI , "parse_env_file" , return_value = self .envs ), contextlib .redirect_stdout (io .StringIO ()):
292- self .cli .update_vm ("vm" , image = "old" , env_file = "env" , kms_urls = ["https://kms.example" ])
350+ with (
351+ patch .object (VMM_CLI , "parse_env_file" , return_value = self .envs ),
352+ contextlib .redirect_stdout (io .StringIO ()),
353+ ):
354+ self .cli .update_vm (
355+ "vm" , image = "old" , env_file = "env" , kms_urls = ["https://kms.example" ]
356+ )
293357 calls = self .cli .rpc_call .call_args_list
294358 self .assertNotIn ("ResizeVm" , [call .args [0 ] for call in calls ])
295359 method , payload = calls [- 1 ].args
@@ -300,6 +364,7 @@ def rpc(method, params=None):
300364 self .cli .client .request .assert_not_called ()
301365
302366 def test_create_update_and_update_env_send_only_the_negotiated_field (self ):
367+ """Use the selected ciphertext field consistently across CLI commands."""
303368 compose = json .dumps ({"kms_enabled" : True })
304369 args = argparse .Namespace (
305370 compose = "compose.json" ,
0 commit comments