2929
3030
3131RETAINED_ENCRYPTED_ARTIFACT_NAME = "dashboard-data.enc"
32+ RETAINED_ARTIFACT_LEGACY_VERSION = 1
33+ RETAINED_ARTIFACT_VERSION = 2
34+ RETAINED_ARTIFACT_AAD_LABEL_V2 = "reponomics:retained-artifact:v2:dashboard-data"
35+ RETAINED_ARTIFACT_AAD_V2 = RETAINED_ARTIFACT_AAD_LABEL_V2 .encode ("utf-8" )
3236
3337
3438def _retained_encrypted_candidates (retained_data_dir : Path | None ) -> list [Path ]:
@@ -148,17 +152,36 @@ def _object_dict(value: Any) -> dict[str, Any]:
148152 return {}
149153
150154
151- def _load_retained_encrypted_payload (path : Path ) -> tuple [list [DoctorStage ], dict [str , Any ] | None ]:
155+ def _load_retained_encrypted_payload (
156+ path : Path ,
157+ ) -> tuple [list [DoctorStage ], dict [str , Any ] | None ]:
152158 try :
153159 payload = json .loads (path .read_text (encoding = "utf-8" ))
154160 except Exception as exc :
155- return [_stage ("retained_artifact_readable" , "failed" , f"encrypted artifact was not readable JSON: { exc } " )], None
161+ return [
162+ _stage (
163+ "retained_artifact_readable" ,
164+ "failed" ,
165+ f"encrypted artifact was not readable JSON: { exc } " ,
166+ )
167+ ], None
156168 if not isinstance (payload , dict ):
157- return [_stage ("retained_artifact_readable" , "failed" , "encrypted artifact payload was not a JSON object" )], None
169+ return [
170+ _stage (
171+ "retained_artifact_readable" ,
172+ "failed" ,
173+ "encrypted artifact payload was not a JSON object" ,
174+ )
175+ ], None
158176
159177 errors : list [str ] = []
160- if payload .get ("version" ) != 1 :
178+ if payload .get ("version" ) not in { RETAINED_ARTIFACT_LEGACY_VERSION , RETAINED_ARTIFACT_VERSION } :
161179 errors .append ("unsupported encrypted artifact version" )
180+ if (
181+ payload .get ("version" ) == RETAINED_ARTIFACT_VERSION
182+ and payload .get ("aad" ) != RETAINED_ARTIFACT_AAD_LABEL_V2
183+ ):
184+ errors .append ("unsupported AAD" )
162185 if payload .get ("kdf" ) != "PBKDF2-SHA256" :
163186 errors .append ("unsupported KDF" )
164187 if payload .get ("iterations" ) != EXPECTED_KDF_ITERATIONS :
@@ -173,6 +196,12 @@ def _load_retained_encrypted_payload(path: Path) -> tuple[list[DoctorStage], dic
173196 return [_stage ("retained_artifact_readable" , "passed" , "encrypted artifact payload is readable" )], payload
174197
175198
199+ def _retained_artifact_aad (payload : dict [str , Any ]) -> bytes | None :
200+ if payload .get ("version" ) == RETAINED_ARTIFACT_LEGACY_VERSION :
201+ return None
202+ return RETAINED_ARTIFACT_AAD_V2
203+
204+
176205def _diagnose_encrypted_retained_artifact (
177206 path : Path ,
178207 * ,
@@ -223,7 +252,7 @@ def _diagnose_encrypted_retained_artifact(
223252 for label , secret in accepted_secrets :
224253 key = _derive_key (secret , salt )
225254 try :
226- plaintext = AESGCM (key ).decrypt (iv , ciphertext , None )
255+ plaintext = AESGCM (key ).decrypt (iv , ciphertext , _retained_artifact_aad ( payload ) )
227256 except InvalidTag :
228257 stages .append (_stage ("retained_artifact_decrypts" , "failed" , "AES-GCM authentication failed" , label ))
229258 continue
0 commit comments