| 
					
				 | 
			
			
				@@ -72,6 +72,15 @@ class TAMRequiredError(IntegrityError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     traceback = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ArchiveTAMRequiredError(TAMRequiredError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __doc__ = textwrap.dedent( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Archive '{}' is unauthenticated, but it is required for this repository. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ).strip() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    traceback = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class TAMInvalid(IntegrityError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     __doc__ = IntegrityError.__doc__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     traceback = False 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -81,6 +90,15 @@ class TAMInvalid(IntegrityError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         super().__init__("Manifest authentication did not verify") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ArchiveTAMInvalid(IntegrityError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __doc__ = IntegrityError.__doc__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    traceback = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Error message becomes: "Data integrity error: Archive authentication did not verify" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        super().__init__("Archive authentication did not verify") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class TAMUnsupportedSuiteError(IntegrityError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Could not verify manifest: Unsupported suite {!r}; a newer version is needed.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -279,6 +297,46 @@ class KeyBase: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         logger.debug("TAM-verified manifest") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return unpacked, True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def unpack_and_verify_archive(self, data, force_tam_not_required=False): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Unpack msgpacked *data* and return (object, did_verify).""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam_required = self.tam_required 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if force_tam_not_required and tam_required: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logger.warning("Archive authentication DISABLED.") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            tam_required = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data = bytearray(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        unpacker = get_limited_unpacker("archive") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        unpacker.feed(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        unpacked = unpacker.unpack() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if b"tam" not in unpacked: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if tam_required: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                archive_name = unpacked.get(b"name", b"<unknown>").decode("ascii", "replace") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                raise ArchiveTAMRequiredError(archive_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logger.debug("TAM not found and not required") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return unpacked, False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam = unpacked.pop(b"tam", None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not isinstance(tam, dict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise ArchiveTAMInvalid() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam_type = tam.get(b"type", b"<none>").decode("ascii", "replace") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if tam_type != "HKDF_HMAC_SHA512": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if tam_required: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                raise TAMUnsupportedSuiteError(repr(tam_type)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logger.debug("Ignoring TAM made with unsupported suite, since TAM is not required: %r", tam_type) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return unpacked, False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam_hmac = tam.get(b"hmac") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam_salt = tam.get(b"salt") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not isinstance(tam_salt, bytes) or not isinstance(tam_hmac, bytes): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise ArchiveTAMInvalid() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        offset = data.index(tam_hmac) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data[offset : offset + 64] = bytes(64) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tam_key = self._tam_key(tam_salt, context=b"archive") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        calculated_hmac = hmac.digest(tam_key, data, "sha512") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not hmac.compare_digest(calculated_hmac, tam_hmac): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise ArchiveTAMInvalid() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logger.debug("TAM-verified archive") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return unpacked, True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class PlaintextKey(KeyBase): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     TYPE = KeyType.PLAINTEXT 
			 |