def test_sign_ec(self): try: signing.verify_signature_from_file( f"{TEST_FILES}/allowlist-ec-key.pem", f"{TEST_FILES}/allowlist.json", f"{TEST_FILES}/allowlist-ec-sig.bin", "Testing Allowlist") except Exception as e: self.fail(f"Signing raised exception: {e}!")
def test_sign_bad_sig(self): try: signing.verify_signature_from_file( f"{TEST_FILES}/allowlist-pgp-key.pgp", f"{TEST_FILES}/allowlist.json", f"{TEST_FILES}/allowlist-invalid-sig.sig", "Testing Allowlist") self.fail("Signing passed with invalid signature!") except Exception: pass
def test_sign_gpg(self): try: signing.verify_signature_from_file( f"{TEST_FILES}/allowlist-pgp-key.pgp", f"{TEST_FILES}/allowlist.json", f"{TEST_FILES}/allowlist-pgp-sig.sig", "Testing Allowlist", ) except Exception as e: self.fail(f"Signing raised exception: {e}!")
def read_allowlist(al_path=None, checksum="", gpg_sig_file=None, gpg_key_file=None): if al_path is None: al_path = config.get("tenant", "ima_allowlist") # If user only wants signatures then an allowlist is not required if al_path is None or al_path == "": return copy.deepcopy(empty_allowlist) # Purposefully die if path doesn't exist with open(al_path, "rb") as f: pass # verify GPG signature if needed if gpg_sig_file and gpg_key_file: signing.verify_signature_from_file(gpg_key_file, al_path, gpg_sig_file, "allowlist") # Purposefully die if path doesn't exist with open(al_path, "rb") as f: logger.debug("Loading allowlist from %s", al_path) alist_bytes = f.read() sha256 = hashlib.sha256() sha256.update(alist_bytes) calculated_checksum = sha256.hexdigest() alist_raw = alist_bytes.decode("utf-8") logger.debug("Loaded allowlist from %s with checksum %s", al_path, calculated_checksum) if checksum: if checksum == calculated_checksum: logger.debug("Allowlist passed checksum validation") else: raise Exception( f"Checksum of allowlist does not match! Expected {checksum}, Calculated {calculated_checksum}" ) # if the first non-whitespace character in the file is '{' treat it as the new JSON format p = re.compile(r"^\s*{") if p.match(alist_raw): logger.debug("Reading allow list as JSON format") alist = json.loads(alist_raw) # verify it's the current version if "meta" in alist and "version" in alist["meta"]: version = alist["meta"]["version"] if int(version) <= ALLOWLIST_CURRENT_VERSION: logger.debug("Allowlist has compatible version %s", version) else: # in the future we will support multiple versions and convert between them, # but for now there is only one raise Exception("Allowlist has unsupported version {version}") else: logger.debug("Allowlist does not specify a version. Assuming current version %s", ALLOWLIST_CURRENT_VERSION) else: # convert legacy format into new structured format logger.debug("Converting legacy allowlist format to JSON") alist = copy.deepcopy(empty_allowlist) alist["meta"]["timestamp"] = str(datetime.datetime.now()) alist["meta"]["generator"] = "keylime-legacy-format-upgrade" if checksum: alist["meta"]["checksum"] = checksum for line in alist_raw.splitlines(): line = line.strip() if len(line) == 0: continue pieces = line.split(None, 1) if not len(pieces) == 2: logger.warning("Line in AllowList does not consist of hash and file path: %s", line) continue (checksum_hash, path) = pieces if path.startswith("%keyring:"): entrytype = "keyrings" path = path[len("%keyring:") :] # remove leading '%keyring:' from path to get keyring name else: entrytype = "hashes" if path in alist[entrytype]: alist[entrytype][path].append(checksum_hash) else: alist[entrytype][path] = [checksum_hash] alist = update_allowlist(alist) return alist