From: Boojin Kim Date: Wed, 16 May 2018 02:49:42 +0000 (+0900) Subject: [COMMON] fmp: support integrity check and clang X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=87b8fd131870f585821f14c65b39695198db14e9;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] fmp: support integrity check and clang fips: fix compile error with clag add fips init status fix build error for gcc compiler fix build error on clang support integrity check Change-Id: I3fd3255b5e14b3166980b6a2789febcfd98a7f7f Signed-off-by: Boojin Kim --- diff --git a/Makefile b/Makefile index 25be45fdd620..ce838fbd97e5 100644 --- a/Makefile +++ b/Makefile @@ -392,6 +392,11 @@ PERL = perl 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 = diff --git a/drivers/crypto/fmp/Makefile b/drivers/crypto/fmp/Makefile index 3e14d208343f..07a53490b835 100644 --- a/drivers/crypto/fmp/Makefile +++ b/drivers/crypto/fmp/Makefile @@ -1,9 +1,16 @@ # 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 diff --git a/drivers/crypto/fmp/first_file.c b/drivers/crypto/fmp/first_file.c index acb91db114d3..447d1cef8d61 100644 --- a/drivers/crypto/fmp/first_file.c +++ b/drivers/crypto/fmp/first_file.c @@ -15,5 +15,9 @@ const unsigned char first_fmp_rodata = 0x10; __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){}; diff --git a/drivers/crypto/fmp/fmp.c b/drivers/crypto/fmp/fmp.c index 706c4bffc21c..e09900ace080 100644 --- a/drivers/crypto/fmp/fmp.c +++ b/drivers/crypto/fmp/fmp.c @@ -289,8 +289,17 @@ int exynos_fmp_crypt(struct fmp_crypto_info *ci, void *priv) } 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; } diff --git a/drivers/crypto/fmp/fmp_fips_integrity.c b/drivers/crypto/fmp/fmp_fips_integrity.c index 9840f4378ade..5d291d82e2fd 100644 --- a/drivers/crypto/fmp/fmp_fips_integrity.c +++ b/drivers/crypto/fmp/fmp_fips_integrity.c @@ -29,7 +29,7 @@ #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 */ diff --git a/drivers/crypto/fmp/fmp_fips_main.c b/drivers/crypto/fmp/fmp_fips_main.c index 5cfd4ddf2066..9f6f32b5c771 100644 --- a/drivers/crypto/fmp/fmp_fips_main.c +++ b/drivers/crypto/fmp/fmp_fips_main.c @@ -45,6 +45,13 @@ bool in_fmp_fips_err(void) 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; diff --git a/drivers/crypto/fmp/fmp_fips_main.h b/drivers/crypto/fmp/fmp_fips_main.h index fd8cbbc9730b..912972b7aa5e 100644 --- a/drivers/crypto/fmp/fmp_fips_main.h +++ b/drivers/crypto/fmp/fmp_fips_main.h @@ -14,6 +14,7 @@ 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) { @@ -28,5 +29,10 @@ inline bool in_fmp_fips_err(void) { return 0; } + +inline bool in_fmp_fips_init(void) +{ + return 0; +} #endif /* CONFIG_EXYNOS_FMP_FIPS */ #endif /* _FMP_FIPS_MAIN_H_ */ diff --git a/drivers/crypto/fmp/fmp_test.c b/drivers/crypto/fmp/fmp_test.c index 8ca62d9fe8e0..d21a174d2419 100644 --- a/drivers/crypto/fmp/fmp_test.c +++ b/drivers/crypto/fmp/fmp_test.c @@ -39,11 +39,6 @@ static dev_t find_devt_for_test(struct exynos_fmp *fmp, 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); diff --git a/drivers/crypto/fmp/last_file.c b/drivers/crypto/fmp/last_file.c index c0d405d5d829..3baede3aa5e7 100644 --- a/drivers/crypto/fmp/last_file.c +++ b/drivers/crypto/fmp/last_file.c @@ -17,5 +17,9 @@ const unsigned char last_fmp_rodata = 0x20; __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){}; diff --git a/scripts/fmp/ELF.py b/scripts/fmp/ELF.py new file mode 100644 index 000000000000..e333905a2bd7 --- /dev/null +++ b/scripts/fmp/ELF.py @@ -0,0 +1,255 @@ +#!/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)) diff --git a/scripts/fmp/IntegrityRoutine.py b/scripts/fmp/IntegrityRoutine.py new file mode 100644 index 000000000000..702060a358d7 --- /dev/null +++ b/scripts/fmp/IntegrityRoutine.py @@ -0,0 +1,305 @@ +#!/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)) diff --git a/scripts/fmp/Utils.py b/scripts/fmp/Utils.py new file mode 100644 index 000000000000..e22d92b92e32 --- /dev/null +++ b/scripts/fmp/Utils.py @@ -0,0 +1,100 @@ +#!/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]) diff --git a/scripts/fmp/fips_fmp_integrity.py b/scripts/fmp/fips_fmp_integrity.py new file mode 100644 index 000000000000..29ab8a73e033 --- /dev/null +++ b/scripts/fmp/fips_fmp_integrity.py @@ -0,0 +1,44 @@ +#!/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) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh old mode 100755 new mode 100644 index e6818b8e7141..ea3338d22e85 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -333,5 +333,10 @@ if [ -n "${CONFIG_KALLSYMS}" ]; then 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