PYTHON = python
CHECK = sparse
+ifeq ($(CONFIG_EXYNOS_FMP_FIPS),)
+READELF = $(CROSS_COMPILE)readelf
+export READELF
+endif
+
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void $(CF)
NOSTDINC_FLAGS =
# Exynos FMP/SMU makefile
#ccflags-y := -DCONFIG_EXYNOS_FMP_FIPS_FUNC_TEST
obj-$(CONFIG_EXYNOS_SMU) += smu.o
+CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?)
+ifeq ($(CC_NO_CLANG), 0)
+ccflags-y += -DCC_USE_CLANG
+CFLAGS_fmp_fips_selftest.o = -fno-merge-all-constants
+endif
+ifeq ($(CC_NO_CLANG), 1)
+CFLAGS_fmp_fips_selftest.o = -fno-merge-constants
+endif
obj-$(CONFIG_EXYNOS_FMP_FIPS) += first_file.o
obj-$(CONFIG_EXYNOS_FMP) += fmp.o fmp_test.o
-CFLAGS_fmp_fips_selftest.o = -fno-merge-constants
obj-$(CONFIG_EXYNOS_FMP_FIPS) += fmp_fips_main.o fmp_fips_fops.o fmp_fips_selftest.o \
fmp_fips_integrity.o hmac-sha256.o \
fmp_fips_cipher.o
__attribute__ ((section(".text"), unused))
void first_fmp_text(void){}
+#ifdef CC_USE_CLANG
+__attribute__ ((section(".init.text"), unused))
+#else
__attribute__ ((section(".init.text"), optimize("-O0"), unused))
-static void first_fmp_init(void){};
+#endif
+void first_fmp_init(void){};
}
if (unlikely(in_fmp_fips_err())) {
+#if defined(CONFIG_NODE_FOR_SELFTEST_FAIL)
pr_err("%s: Fail to work fmp config due to fips in error.\n",
__func__);
+#else
+ if (in_fmp_fips_init())
+ pr_err("%s: Fail to work fmp config due to fips in init.\n",
+ __func__);
+ else
+ panic("%s: Fail to work fmp config due to fips in error\n",
+ __func__);
+#endif
return -EINVAL;
}
#include "fmp_fips_info.h"
#include "fmp_fips_integrity.h"
-#undef FIPS_CHECK_INTEGRITY
+#define FIPS_CHECK_INTEGRITY
#undef FIPS_DEBUG_INTEGRITY
/* Same as build time */
return false;
}
+bool in_fmp_fips_init(void)
+{
+ if (fmp_fips_state == FMP_FIPS_INIT_STATE)
+ return true;
+ return false;
+}
+
static void set_fmp_fips_state(uint32_t val)
{
fmp_fips_state = val;
int exynos_fmp_fips_init(struct exynos_fmp *fmp);
void exynos_fmp_fips_exit(struct exynos_fmp *fmp);
bool in_fmp_fips_err(void);
+bool in_fmp_fips_init(void);
#else
inline int exynos_fmp_fips_init(struct exynos_fmp *fmp)
{
{
return 0;
}
+
+inline bool in_fmp_fips_init(void)
+{
+ return 0;
+}
#endif /* CONFIG_EXYNOS_FMP_FIPS */
#endif /* _FMP_FIPS_MAIN_H_ */
memset(size_list, 0, sizeof(size_list));
memset(devt_list, 0, sizeof(devt_list));
- if (!data || !data->block_type) {
- dev_err(dev, "Invalid fmp test data\n");
- return (dev_t) 0;
- }
-
do {
for (i = 1; i < MAX_SCAN_PART; i++) {
devt_scan = blk_lookup_devt(data->block_type, i);
__attribute__ ((section(".text"), unused))
void last_fmp_text(void){}
+#ifdef CC_USE_CLANG
+__attribute__ ((section(".init.text"), unused))
+#else
__attribute__ ((section(".init.text"), optimize("-O0"), unused))
-static void last_fmp_init(void){};
+#endif
+void last_fmp_init(void){};
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Module ELF contains ELF, Symbol, Section classes for manipulation over ELF files.
+It can parse, and change ELF file. This version works only with vmlinux and doesn't properly work with ELF that contains
+UND symbols
+"""
+
+import subprocess
+import re
+import os
+from Utils import Utils
+from collections import OrderedDict
+
+__author__ = "Vadym Stupakov"
+__copyright__ = "Copyright (c) 2017 Samsung Electronics"
+__credits__ = ["Vadym Stupakov"]
+__version__ = "1.0"
+__maintainer__ = "Vadym Stupakov"
+__email__ = "v.stupakov@samsung.com"
+__status__ = "Production"
+
+
+class Symbol:
+ def __init__(self, name=str(), sym_type=str(), bind=str(), visibility=str(), addr=int(), size=int(), ndx=str()):
+ self.utils = Utils()
+ self.name = str(name)
+ self.type = str(sym_type)
+ self.bind = str(bind)
+ self.ndx = str(ndx)
+ self.visibility = str(visibility)
+ self.addr = self.utils.to_int(addr)
+ self.size = self.utils.to_int(size)
+
+ def __str__(self):
+ return "name: '{}', type: '{}', bind: '{}', ndx: '{}', visibility: '{}', address: '{}', size: '{}'".format(
+ self.name, self.type, self.bind, self.ndx, self.visibility, hex(self.addr), hex(self.size)
+ )
+
+ def __lt__(self, other):
+ return self.addr <= other.addr
+
+
+class Section:
+ def __init__(self, name=str(), sec_type=str(), addr=int(), offset=int(), size=int()):
+ self.utils = Utils()
+ self.name = str(name)
+ self.type = str(sec_type)
+ self.addr = self.utils.to_int(addr)
+ self.offset = self.utils.to_int(offset)
+ self.size = self.utils.to_int(size)
+
+ def __str__(self):
+ return "name: '{}', type: '{}', address: '{}', offset: '{}', size: '{}'".format(
+ self.name, self.type, hex(self.addr), hex(self.offset), hex(self.size)
+ )
+
+ def __lt__(self, other):
+ return self.addr <= other.addr
+
+
+class ELF:
+ """
+ Utils for manipulating over ELF
+ """
+ def __init__(self, elf_file, readelf_path="readelf"):
+ self.__elf_file = elf_file
+ self.utils = Utils()
+ self.__readelf_path = readelf_path
+ self.__sections = OrderedDict()
+ self.__symbols = OrderedDict()
+ self.__relocs = list()
+ self.__re_hexdecimal = "\s*[0-9A-Fa-f]+\s*"
+ self.__re_sec_name = "\s*[._a-zA-Z]+\s*"
+ self.__re_type = "\s*[A-Z]+\s*"
+
+ def __readelf_raw(self, options):
+ """
+ Execute readelf with options and print raw output
+ :param options readelf options: ["opt1", "opt2", "opt3", ..., "optN"]
+ :returns raw output
+ """
+ ret = subprocess.Popen(args=[self.__readelf_path] + options,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = ret.communicate()
+ if "readelf: Error: the PHDR segment is not covered by a LOAD segment" in stderr.decode("utf-8").strip():
+ ret.returncode = 0
+ if ret.returncode != 0:
+ raise ChildProcessError(stderr.decode("utf-8") + stdout.decode("utf-8"))
+ return stdout.decode("utf-8")
+
+ def set_elf_file(self, elf_file):
+ if os.path.abspath(self.__elf_file) != os.path.abspath(elf_file):
+ self.__elf_file = os.path.abspath(elf_file)
+ self.__sections.clear()
+ self.__symbols.clear()
+ self.__relocs.clear()
+
+ def get_elf_file(self):
+ return os.path.abspath(self.__elf_file)
+
+ def get_sections(self):
+ """"
+ Execute -> parse -> transform to dict() readelf output
+ :returns dict: {sec_addr : Section()}
+ """
+ if len(self.__sections) == 0:
+ sec_header = self.__readelf_raw(["-SW", self.__elf_file]).strip()
+ secs = re.compile("^.*\[.*\](" + self.__re_sec_name + self.__re_type + self.__re_hexdecimal +
+ self.__re_hexdecimal + self.__re_hexdecimal + ")", re.MULTILINE)
+ found = secs.findall(sec_header)
+ for line in found:
+ line = line.split()
+ if len(line) == 5:
+ self.__sections[int(line[2], 16)] = Section(name=line[0], sec_type=line[1], addr=int(line[2], 16),
+ offset=int(line[3], 16), size=int(line[4], 16))
+ self.__sections = OrderedDict(sorted(self.__sections.items()))
+ return self.__sections
+
+ def get_symbols(self):
+ """"
+ Execute -> parse -> transform to dict() readelf output
+ :returns dict: {sym_addr : Symbol()}
+ """
+ if len(self.__symbols) == 0:
+ sym_tab = self.__readelf_raw(["-sW", self.__elf_file])
+ syms = re.compile(r"^.*\d+:\s(.*$)", re.MULTILINE)
+ found = syms.findall(sym_tab.strip())
+ for line in found:
+ line = line.split()
+ if len(line) == 7:
+ size = line[1]
+ # This needs, because readelf prints sizes in hex if size is large
+ if size[:2].upper() == "0X":
+ size = int(size, 16)
+ else:
+ size = int(size, 10)
+ self.__symbols[int(line[0], 16)] = Symbol(addr=int(line[0], 16), size=size, sym_type=line[2],
+ bind=line[3], visibility=line[4], ndx=line[5],
+ name=line[6])
+ self.__symbols = OrderedDict(sorted(self.__symbols.items()))
+ return self.__symbols
+
+ def get_relocs(self, start_addr=None, end_addr=None):
+ """"
+ :param start_addr: start address :int
+ :param end_addr: end address: int
+ :returns list: [reloc1, reloc2, reloc3, ..., relocN]
+ """
+ if len(self.__relocs) == 0:
+ relocs = self.__readelf_raw(["-rW", self.__elf_file])
+ rel = re.compile(r"^(" + self.__re_hexdecimal + ")\s*", re.MULTILINE)
+ self.__relocs = [self.utils.to_int(el) for el in rel.findall(relocs.strip())]
+
+ if start_addr and end_addr is not None:
+ ranged_rela = list()
+ for el in self.__relocs:
+ if self.utils.to_int(start_addr) <= self.utils.to_int(el) <= self.utils.to_int(end_addr):
+ ranged_rela.append(el)
+ return ranged_rela
+ return self.__relocs
+
+ def get_symbol_by_name(self, sym_names):
+ """
+ Get symbol by_name
+ :param sym_names: "sym_name" : str or list
+ :return: Symbol() or [Symbol()]
+ """
+ if isinstance(sym_names, str):
+ for addr, symbol_obj in self.get_symbols().items():
+ if symbol_obj.name == sym_names:
+ return symbol_obj
+ elif isinstance(sym_names, list):
+ symbols = [self.get_symbol_by_name(sym_name) for sym_name in sym_names]
+ return symbols
+ else:
+ raise ValueError
+ return None
+
+ def get_symbol_by_vaddr(self, vaddrs=None):
+ """
+ Get symbol by virtual address
+ :param vaddrs: vaddr : int or list
+ :return: Symbol() or [Symbol()]
+ """
+ if isinstance(vaddrs, int):
+ if vaddrs in self.get_symbols():
+ return self.get_symbols()[vaddrs]
+ for addr, symbol_obj in self.get_symbols().items():
+ if (addr + symbol_obj.size) >= vaddrs >= addr:
+ return symbol_obj
+ elif isinstance(vaddrs, list):
+ symbol = [self.get_symbol_by_vaddr(vaddr) for vaddr in vaddrs]
+ return symbol
+ else:
+ raise ValueError
+ return None
+
+ def get_section_by_name(self, sec_names=None):
+ """
+ Get section by_name
+ :param sec_names: "sec_name" : str or list
+ :return: Section() or [Section()]
+ """
+ if isinstance(sec_names, str):
+ for addr, section_obj in self.get_sections().items():
+ if section_obj.name == sec_names:
+ return section_obj
+ elif isinstance(sec_names, list):
+ sections = [self.get_section_by_name(sec_name) for sec_name in sec_names]
+ return sections
+ else:
+ raise ValueError
+ return None
+
+ def get_section_by_vaddr(self, vaddrs=None):
+ """
+ Get section by virtual address
+ :param vaddrs: vaddr : int or list
+ :return: Section() or [Section()]
+ """
+ if isinstance(vaddrs, int):
+ if vaddrs in self.get_sections():
+ return self.get_sections()[vaddrs]
+ for addr, section_obj in self.get_sections().items():
+ if (addr + section_obj.size) >= vaddrs >= addr:
+ return section_obj
+ elif isinstance(vaddrs, list):
+ sections = [self.get_symbol_by_vaddr(vaddr) for vaddr in vaddrs]
+ return sections
+ else:
+ raise ValueError
+ return None
+
+ def vaddr_to_file_offset(self, vaddrs):
+ """
+ Transform virtual address to file offset
+ :param vaddrs: addr string or int or list
+ :returns file offset or list
+ """
+ if isinstance(vaddrs, str) or isinstance(vaddrs, int):
+ section = self.get_section_by_vaddr(vaddrs)
+ return self.utils.to_int(vaddrs, 16) - section.addr + section.offset
+ elif isinstance(vaddrs, list):
+ return [self.vaddr_to_file_offset(vaddr) for vaddr in vaddrs]
+ else:
+ raise ValueError
+
+ def read_data_from_vaddr(self, vaddr, size, out_file):
+ with open(self.__elf_file, "rb") as elf_fp:
+ elf_fp.seek(self.vaddr_to_file_offset(vaddr))
+ with open(out_file, "wb") as out_fp:
+ out_fp.write(elf_fp.read(size))
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Module IntegrityRoutine Contains IntegrityRoutine class helps with FIPS 140-2 build time integrity routine.
+This module is needed to calculate HMAC and embed other needed stuff.
+"""
+
+import hmac
+import hashlib
+import bisect
+import itertools
+import binascii
+from ELF import *
+
+__author__ = "Vadym Stupakov"
+__copyright__ = "Copyright (c) 2017 Samsung Electronics"
+__credits__ = ["Vadym Stupakov"]
+__version__ = "1.0"
+__maintainer__ = "Vadym Stupakov"
+__email__ = "v.stupakov@samsung.com"
+__status__ = "Production"
+
+
+class IntegrityRoutine(ELF):
+ """
+ Utils for fips-integrity process
+ """
+ def __init__(self, elf_file, readelf_path="readelf"):
+ ELF.__init__(self, elf_file, readelf_path)
+
+ @staticmethod
+ def __remove_all_dublicates(lst):
+ """
+ Removes all occurrences of tha same value. For instance: transforms [1, 2, 3, 1] -> [2, 3]
+ :param lst: input list
+ :return: lst w/o duplicates
+ """
+ to_remove = list()
+ for i in range(len(lst)):
+ it = itertools.islice(lst, i + 1, len(lst) - 1, None)
+ for j, val in enumerate(it, start=i+1):
+ if val == lst[i]:
+ to_remove.extend([lst[i], lst[j]])
+
+ for el in to_remove:
+ lst.remove(el)
+
+ def get_reloc_gaps(self, start_addr, end_addr):
+ """
+ :param start_addr: start address :int
+ :param end_addr: end address: int
+ :returns list of relocation gaps like [[gap_start, gap_end], [gap_start, gap_end], ...]
+ """
+ all_relocs = self.get_relocs(start_addr, end_addr)
+ relocs_gaps = list()
+ for addr in all_relocs:
+ relocs_gaps.append(addr)
+ relocs_gaps.append(addr + 8)
+ self.__remove_all_dublicates(relocs_gaps)
+ relocs_gaps.sort()
+ relocs_gaps = [[addr1, addr2] for addr1, addr2 in self.utils.pairwise(relocs_gaps)]
+ return relocs_gaps
+
+ def get_addrs_for_hmac(self, sec_sym_sequence, relocs_gaps=None):
+ """
+ Generate addresses for calculating HMAC
+ :param sec_sym_sequence: [addr_start1, addr_end1, ..., addr_startN, addr_endN],
+ :param relocs_gaps: [[start_gap_addr, end_gap_addr], [start_gap_addr, end_gap_addr]]
+ :return: addresses for calculating HMAC: [[addr_start, addr_end], [addr_start, addr_end], ...]
+ """
+ addrs_for_hmac = list()
+ for section_name, sym_names in sec_sym_sequence.items():
+ if relocs_gaps is not None and section_name == ".rodata":
+ for symbol in self.get_symbol_by_name(sym_names):
+ addrs_for_hmac.append(symbol.addr)
+ else:
+ for symbol in self.get_symbol_by_name(sym_names):
+ addrs_for_hmac.append(symbol.addr)
+ addrs_for_hmac.extend(self.utils.flatten(relocs_gaps))
+ addrs_for_hmac.sort()
+ return [[item1, item2] for item1, item2 in self.utils.pairwise(addrs_for_hmac)]
+
+ def embed_bytes(self, vaddr, in_bytes):
+ """
+ Write bytes to ELF file
+ :param vaddr: virtual address in ELF
+ :param in_bytes: byte array to write
+ """
+ offset = self.vaddr_to_file_offset(vaddr)
+ with open(self.get_elf_file(), "rb+") as elf_file:
+ elf_file.seek(offset)
+ elf_file.write(in_bytes)
+
+ def __update_hmac(self, hmac_obj, file_obj, file_offset_start, file_offset_end):
+ """
+ Update hmac from addrstart tp addr_end
+ FIXMI: it needs to implement this function via fixed block size
+ :param file_offset_start: could be string or int
+ :param file_offset_end: could be string or int
+ """
+ file_offset_start = self.utils.to_int(file_offset_start)
+ file_offset_end = self.utils.to_int(file_offset_end)
+ file_obj.seek(self.vaddr_to_file_offset(file_offset_start))
+ block_size = file_offset_end - file_offset_start
+ msg = file_obj.read(block_size)
+ hmac_obj.update(msg)
+
+ def get_hmac(self, offset_sequence, key, output_type="byte"):
+ """
+ Calculate HMAC
+ :param offset_sequence: start and end addresses sequence [addr_start, addr_end], [addr_start, addr_end], ...]
+ :param key HMAC key: string value
+ :param output_type string value. Could be "hex" or "byte"
+ :return: bytearray or hex string
+ """
+ digest = hmac.new(bytearray(key.encode("utf-8")), digestmod=hashlib.sha256)
+ with open(self.get_elf_file(), "rb") as file:
+ for addr_start, addr_end in offset_sequence:
+ self.__update_hmac(digest, file, addr_start, addr_end)
+ if output_type == "byte":
+ return digest.digest()
+ if output_type == "hex":
+ return digest.hexdigest()
+
+ def __find_nearest_symbol_by_vaddr(self, vaddr, method):
+ """
+ Find nearest symbol near vaddr
+ :param vaddr:
+ :return: idx of symbol from self.get_symbols()
+ """
+ symbol = self.get_symbol_by_vaddr(vaddr)
+ if symbol is None:
+ raise ValueError("Can't find symbol by vaddr")
+ idx = method(list(self.get_symbols()), vaddr)
+ return idx
+
+ def find_rnearest_symbol_by_vaddr(self, vaddr):
+ """
+ Find right nearest symbol near vaddr
+ :param vaddr:
+ :return: idx of symbol from self.get_symbols()
+ """
+ return self.__find_nearest_symbol_by_vaddr(vaddr, bisect.bisect_right)
+
+ def find_lnearest_symbol_by_vaddr(self, vaddr):
+ """
+ Find left nearest symbol near vaddr
+ :param vaddr:
+ :return: idx of symbol from self.get_symbols()
+ """
+ return self.__find_nearest_symbol_by_vaddr(vaddr, bisect.bisect_left)
+
+ def find_symbols_between_vaddrs(self, vaddr_start, vaddr_end):
+ """
+ Returns list of symbols between two virtual addresses
+ :param vaddr_start:
+ :param vaddr_end:
+ :return: [(Symbol(), Section)]
+ """
+ symbol_start = self.get_symbol_by_vaddr(vaddr_start)
+ symbol_end = self.get_symbol_by_vaddr(vaddr_end)
+ if symbol_start is None or symbol_end is None:
+ raise ValueError("Error: Cannot find symbol by vaddr. vaddr should coincide with symbol address!")
+
+ idx_start = self.find_lnearest_symbol_by_vaddr(vaddr_start)
+ idx_end = self.find_lnearest_symbol_by_vaddr(vaddr_end)
+
+ sym_sec = list()
+ for idx in range(idx_start, idx_end):
+ symbol_addr = list(self.get_symbols())[idx]
+ symbol = self.get_symbol_by_vaddr(symbol_addr)
+ section = self.get_section_by_vaddr(symbol_addr)
+ sym_sec.append((symbol, section))
+
+ sym_sec.sort(key=lambda x: x[0])
+ return sym_sec
+
+ @staticmethod
+ def __get_skipped_bytes(symbol, relocs):
+ """
+ :param symbol: Symbol()
+ :param relocs: [[start1, end1], [start2, end2]]
+ :return: Returns skipped bytes and [[start, end]] addresses that show which bytes were skipped
+ """
+ symbol_start_addr = symbol.addr
+ symbol_end_addr = symbol.addr + symbol.size
+ skipped_bytes = 0
+ reloc_addrs = list()
+ for reloc_start, reloc_end in relocs:
+ if reloc_start >= symbol_start_addr and reloc_end <= symbol_end_addr:
+ skipped_bytes += reloc_end - reloc_start
+ reloc_addrs.append([reloc_start, reloc_end])
+ if reloc_start > symbol_end_addr:
+ break
+
+ return skipped_bytes, reloc_addrs
+
+ def print_covered_info(self, sec_sym, relocs, print_reloc_addrs=False, sort_by="address", reverse=False):
+ """
+ Prints information about covered symbols in detailed table:
+ |N| symbol name | symbol address | symbol section | bytes skipped | skipped bytes address range |
+ |1| symbol | 0xXXXXXXXXXXXXXXXX | .rodata | 8 | [[addr1, addr2], [addr1, addr2]] |
+ :param sec_sym: {section_name : [sym_name1, sym_name2]}
+ :param relocs: [[start1, end1], [start2, end2]]
+ :param print_reloc_addrs: print or not skipped bytes address range
+ :param sort_by: method for sorting table. Could be: "address", "name", "section"
+ :param reverse: sort order
+ """
+ if sort_by.lower() == "address":
+ def sort_method(x): return x[0].addr
+ elif sort_by.lower() == "name":
+ def sort_method(x): return x[0].name
+ elif sort_by.lower() == "section":
+ def sort_method(x): return x[1].name
+ else:
+ raise ValueError("Invalid sort type!")
+ table_format = "|{:4}| {:50} | {:18} | {:20} | {:15} |"
+ if print_reloc_addrs is True:
+ table_format += "{:32} |"
+
+ print(table_format.format("N", "symbol name", "symbol address", "symbol section", "bytes skipped",
+ "skipped bytes address range"))
+ data_to_print = list()
+ for sec_name, sym_names in sec_sym.items():
+ for symbol_start, symbol_end in self.utils.pairwise(self.get_symbol_by_name(sym_names)):
+ symbol_sec_in_range = self.find_symbols_between_vaddrs(symbol_start.addr, symbol_end.addr)
+ for symbol, section in symbol_sec_in_range:
+ skipped_bytes, reloc_addrs = self.__get_skipped_bytes(symbol, relocs)
+ reloc_addrs_str = "["
+ for start_addr, end_addr in reloc_addrs:
+ reloc_addrs_str += "[{}, {}], ".format(hex(start_addr), hex(end_addr))
+ reloc_addrs_str += "]"
+ if symbol.size > 0:
+ data_to_print.append((symbol, section, skipped_bytes, reloc_addrs_str))
+
+ skipped_bytes_size = 0
+ symbol_covered_size = 0
+ cnt = 0
+ data_to_print.sort(key=sort_method, reverse=reverse)
+ for symbol, section, skipped_bytes, reloc_addrs_str in data_to_print:
+ cnt += 1
+ symbol_covered_size += symbol.size
+ skipped_bytes_size += skipped_bytes
+ if print_reloc_addrs is True:
+ print(table_format.format(cnt, symbol.name, hex(symbol.addr), section.name,
+ self.utils.human_size(skipped_bytes), reloc_addrs_str))
+ else:
+ print(table_format.format(cnt, symbol.name, hex(symbol.addr), section.name,
+ self.utils.human_size(skipped_bytes)))
+ addrs_for_hmac = self.get_addrs_for_hmac(sec_sym, relocs)
+ all_covered_size = 0
+ for addr_start, addr_end in addrs_for_hmac:
+ all_covered_size += addr_end - addr_start
+ print("Symbol covered bytes len: {} ".format(self.utils.human_size(symbol_covered_size - skipped_bytes_size)))
+ print("All covered bytes len : {} ".format(self.utils.human_size(all_covered_size)))
+ print("Skipped bytes len : {} ".format(self.utils.human_size(skipped_bytes_size)))
+
+ def dump_covered_bytes(self, vaddr_seq, out_file):
+ """
+ Dumps covered bytes
+ :param vaddr_seq: [[start1, end1], [start2, end2]] start - end sequence of covered bytes
+ :param out_file: file where will be stored dumped bytes
+ """
+ with open(self.get_elf_file(), "rb") as elf_fp:
+ with open(out_file, "wb") as out_fp:
+ for vaddr_start, vaddr_end, in vaddr_seq:
+ elf_fp.seek(self.vaddr_to_file_offset(vaddr_start))
+ out_fp.write(elf_fp.read(vaddr_end - vaddr_start))
+
+ def make_integrity(self, sec_sym, module_name, debug=False, print_reloc_addrs=False, sort_by="address",
+ reverse=False):
+ """
+ Calculate HMAC and embed needed info
+ :param sec_sym: {sec_name: [addr1, addr2, ..., addrN]}
+ :param module_name: module name that you want to make integrity. See Makefile targets
+ :param debug: If True prints debug information
+ :param print_reloc_addrs: If True, print relocation addresses that are skipped
+ :param sort_by: sort method
+ :param reverse: sort order
+ """
+ rel_addr_start = self.get_symbol_by_name("first_" + module_name + "_rodata")
+ rel_addr_end = self.get_symbol_by_name("last_" + module_name + "_rodata")
+
+ reloc_gaps = self.get_reloc_gaps(rel_addr_start.addr, rel_addr_end.addr)
+ addrs_for_hmac = self.get_addrs_for_hmac(sec_sym, reloc_gaps)
+
+ digest = self.get_hmac(addrs_for_hmac, "The quick brown fox jumps over the lazy dog")
+
+ self.embed_bytes(self.get_symbol_by_name("builtime_" + module_name + "_hmac").addr,
+ self.utils.to_bytearray(digest))
+
+ self.embed_bytes(self.get_symbol_by_name("integrity_" + module_name + "_addrs").addr,
+ self.utils.to_bytearray(addrs_for_hmac))
+
+ self.embed_bytes(self.get_symbol_by_name(module_name + "_buildtime_address").addr,
+ self.utils.to_bytearray(self.get_symbol_by_name(module_name + "_buildtime_address").addr))
+
+ print("HMAC for \"{}\" module is: {}".format(module_name, binascii.hexlify(digest)))
+ if debug:
+ self.print_covered_info(sec_sym, reloc_gaps, print_reloc_addrs=print_reloc_addrs, sort_by=sort_by,
+ reverse=reverse)
+ self.dump_covered_bytes(addrs_for_hmac, "covered_dump_for_" + module_name + ".bin")
+
+ print("FIPS integrity procedure has been finished for {}".format(module_name))
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Module Utils contains Utils class with general purpose helper functions.
+"""
+
+import struct
+import os
+from itertools import chain
+
+__author__ = "Vadym Stupakov"
+__copyright__ = "Copyright (c) 2017 Samsung Electronics"
+__credits__ = ["Vadym Stupakov"]
+__version__ = "1.0"
+__maintainer__ = "Vadym Stupakov"
+__email__ = "v.stupakov@samsung.com"
+__status__ = "Production"
+
+
+class Utils:
+ """
+ Utils class with general purpose helper functions.
+ """
+ @staticmethod
+ def flatten(alist):
+ """
+ Make list from sub lists
+ :param alist: any list: [[item1, item2], [item3, item4], ..., [itemN, itemN+1]]
+ :return: [item1, item2, item3, item4, ..., itemN, itemN+1]
+ """
+ if alist is []:
+ return []
+ elif type(alist) is not list:
+ return [alist]
+ else:
+ return [el for el in chain.from_iterable(alist)]
+
+ @staticmethod
+ def pairwise(iterable):
+ """
+ Iter over two elements: [s0, s1, s2, s3, ..., sN] -> (s0, s1), (s2, s3), ..., (sN, sN+1)
+ :param iterable:
+ :return: (s0, s1), (s2, s3), ..., (sN, sN+1)
+ """
+ a = iter(iterable)
+ return zip(a, a)
+
+ @staticmethod
+ def paths_exists(path_list):
+ """
+ Check if path exist, otherwise raise FileNotFoundError exception
+ :param path_list: list of paths
+ """
+ for path in path_list:
+ if not os.path.exists(path):
+ raise FileNotFoundError("File: \"" + path + "\" doesn't exist!\n")
+
+ @staticmethod
+ def to_int(value, base=16):
+ """
+ Converts string to int
+ :param value: string or int
+ :param base: string base int
+ :return: integer value
+ """
+ if isinstance(value, int):
+ return value
+ elif isinstance(value, str):
+ return int(value.strip(), base)
+
+ def to_bytearray(self, value):
+ """
+ Converts list to bytearray with block size 8 byte
+ :param value: list of integers or bytearray or int
+ :return: bytes
+ """
+ if isinstance(value, bytearray) or isinstance(value, bytes):
+ return value
+ elif isinstance(value, list):
+ value = self.flatten(value)
+ return struct.pack("%sQ" % len(value), *value)
+ elif isinstance(value, int):
+ return struct.pack("Q", value)
+
+ @staticmethod
+ def human_size(nbytes):
+ """
+ Print in human readable
+ :param nbytes: number of bytes
+ :return: human readable string. For instance: 0x26a5d (154.6 K)
+ """
+ raw = nbytes
+ suffixes = ("B", "K", "M")
+ i = 0
+ while nbytes >= 1024 and i < len(suffixes) - 1:
+ nbytes /= 1024.
+ i += 1
+ f = "{:.1f}".format(nbytes).rstrip("0").rstrip(".")
+ return "{} ({} {})".format(hex(raw), f, suffixes[i])
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+This script is needed for buildtime integrity routine.
+It calculates and embeds HMAC and other needed stuff for in terms of FIPS 140-2
+"""
+
+import os
+import sys
+from IntegrityRoutine import IntegrityRoutine
+from Utils import Utils
+
+
+__author__ = "Vadym Stupakov"
+__copyright__ = "Copyright (c) 2017 Samsung Electronics"
+__credits__ = ["Vadym Stupakov"]
+__version__ = "1.0"
+__maintainer__ = "Vadym Stupakov"
+__email__ = "v.stupakov@samsung.com"
+__status__ = "Production"
+
+
+sec_sym = {".text": ["first_fmp_text", "last_fmp_text"],
+ ".rodata": ["first_fmp_rodata", "last_fmp_rodata"],
+ "init.text": ["first_fmp_init", "last_fmp_init"]
+ }
+
+module_name = "fmp"
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("Usage " + sys.argv[0] + " elf_file")
+ sys.exit(-1)
+
+ elf_file = os.path.abspath(sys.argv[1])
+ modules = sys.argv[2:]
+
+ utils = Utils()
+ utils.paths_exists([elf_file])
+
+ integrity = IntegrityRoutine(elf_file)
+ integrity.make_integrity(sec_sym=sec_sym, module_name=module_name, debug=False, print_reloc_addrs=False,
+ sort_by="address", reverse=False)
fi
fi
+if [ -n "${CONFIG_EXYNOS_FMP_FIPS}" ]; then
+ echo ' FIPS : Generating hmac of fmp and updating vmlinux... '
+ PYTHONDONTWRITEBYTECODE=0 "${srctree}/scripts/fmp/fips_fmp_integrity.py" "${objtree}/vmlinux"
+fi
+
# We made a new kernel - delete old version file
rm -f .old_version