diff --git a/Makefile b/Makefile index a42a89b4..e683f481 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,7 @@ SRC := hypervisor.ld $(sort $(notdir $(foreach dir,$(SRC_DIR),$(wildcard $(dir)/ OBJ := $(patsubst %.ld,$(PAT_OBJ), $(patsubst %.S,$(PAT_OBJ), $(patsubst %.cpp,$(PAT_OBJ), $(SRC)))) OBJ_DEP := $(OBJ:%.o=%.d) +DIG := $(BLD_DIR)/digest HYP := $(BLD_DIR)/$(ARCH)-nova ELF := $(HYP).elf BIN := $(HYP).bin @@ -165,6 +166,7 @@ Makefile.conf: $(call message,CFG,$@) @cp $@.example $@ +$(DIG): $(MFL) | $(BLD_DIR) tool_hst_cc $(OBJ): $(MFL) | $(BLD_DIR) tool_tgt_cc # Zap old-fashioned suffixes @@ -174,12 +176,19 @@ $(OBJ): $(MFL) | $(BLD_DIR) tool_tgt_cc clean: $(call message,CLN,$@) - $(RM) $(OBJ) $(HYP) $(ELF) $(BIN) $(OBJ_DEP) + $(RM) $(DIG) $(OBJ) $(HYP) $(ELF) $(BIN) $(OBJ_DEP) -install: $(HYP) +install: $(HYP) | $(DIG) $(call message,INS,$^ =\> $(INS_DIR)) $(INSTALL) $^ $(INS_DIR) @$(TGT_SZ) $< +ifeq ($(ARCH),x86_64) + @echo Reference Integrity Measurements for $^ + @echo $(shell $(DIG) $^ | sha1sum) "SHA1-160" + @echo $(shell $(DIG) $^ | sha256sum) "SHA2-256" + @echo $(shell $(DIG) $^ | sha384sum) "SHA2-384" + @echo $(shell $(DIG) $^ | sha512sum) "SHA2-512" +endif run: $(ELF) $(RUN) $< diff --git a/cmd/digest.cpp b/cmd/digest.cpp new file mode 100644 index 00000000..4306c47e --- /dev/null +++ b/cmd/digest.cpp @@ -0,0 +1,140 @@ +/* + * NOVA Integrity Measurement Tool for Intel TXT + * + * Copyright (C) 2019-2025 Udo Steinberg, BlueRock Security, Inc. + * + * This file is part of the NOVA microhypervisor. + * + * NOVA is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * NOVA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include +#include +#include +#include +#include +#include + +struct Mh final +{ + uint64_t uuid[2]; + uint32_t size, version, entry, first, mle_start, mle_end, mle_caps, cmd_start, cmd_end; + + [[nodiscard]] bool valid() const + { + return uuid[0] == 0x74a7476f9082ac5a && uuid[1] == 0x42b651cba2555c0f && size == 52 && version == 0x20003; + } +}; + +struct Eh final +{ + uint32_t ei_magic; + uint8_t ei_class, ei_data, ei_version, ei_osabi, ei_abiversion, ei_pad[7]; + uint16_t type, machine; + uint32_t version; + uint64_t entry, ph_offset, sh_offset; + uint32_t flags; + uint16_t eh_size, ph_size, ph_count, sh_size, sh_count, strtab; + + [[nodiscard]] bool valid() const + { + return ei_magic == 0x464c457f && ei_class == 2 && ei_data == 1 && ei_version == 1 && type == 2 && machine == 0x3e; + } +}; + +struct Ph final +{ + uint32_t type, flags; + uint64_t f_offs, v_addr, p_addr, f_size, m_size, align; +}; + +int main (int argc, char **argv) +{ + struct stat st; + + if (argc < 2) { + std::cerr << "You must specify a file.\n"; + return 1; + } + + if (fstat (fileno (stdout), &st) == -1) { + perror ("fstat"); + return 2; + } + + if (!S_ISFIFO (st.st_mode)) { + std::cerr << "You must pipe the output through a hash program.\n"; + return 3; + } + + auto const fd { open (argv[1], O_RDONLY) }; + + if (fd == -1) { + perror ("open"); + return 4; + } + + if (fstat (fd, &st) == -1) { + perror ("fstat"); + return 5; + } + + auto const ptr { mmap (nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0) }; + + if (ptr == MAP_FAILED) { + perror ("mmap"); + return 6; + } + + close (fd); + + auto const addr { reinterpret_cast(ptr) }; + + uint64_t size { 0 }; + + for (auto x { addr }; x < addr + st.st_size; x += sizeof (uint64_t)) { + auto const mh { reinterpret_cast(x) }; + if (mh->valid()) { + size = mh->mle_end - mh->mle_start; + break; + } + } + + if (!size) { + std::cerr << "File is not an MLE.\n"; + return 7; + } + + auto const eh { reinterpret_cast(addr) }; + + if (!eh->valid()) { + std::cerr << "File is not an ELF.\n"; + return 8; + } + + auto ph { reinterpret_cast(addr + eh->ph_offset) }; + + for (auto c { eh->ph_count }; size && c--; ph++) { + + if (ph->type != 1 || ph->f_size == 0) + continue; + + auto const s { std::min (size, ph->f_size) }; + + if (fwrite (reinterpret_cast(addr + ph->f_offs), s, 1, stdout) != 1) { + perror ("fwrite"); + return 9; + } + + size -= s; + } + + return 0; +}