[COMMON] fmp: support integrity check and clang
authorBoojin Kim <boojin.kim@samsung.com>
Wed, 16 May 2018 02:49:42 +0000 (11:49 +0900)
committerJunho Choi <junhosj.choi@samsung.com>
Thu, 24 May 2018 00:02:02 +0000 (09:02 +0900)
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 <boojin.kim@samsung.com>
14 files changed:
Makefile
drivers/crypto/fmp/Makefile
drivers/crypto/fmp/first_file.c
drivers/crypto/fmp/fmp.c
drivers/crypto/fmp/fmp_fips_integrity.c
drivers/crypto/fmp/fmp_fips_main.c
drivers/crypto/fmp/fmp_fips_main.h
drivers/crypto/fmp/fmp_test.c
drivers/crypto/fmp/last_file.c
scripts/fmp/ELF.py [new file with mode: 0644]
scripts/fmp/IntegrityRoutine.py [new file with mode: 0644]
scripts/fmp/Utils.py [new file with mode: 0644]
scripts/fmp/fips_fmp_integrity.py [new file with mode: 0644]
scripts/link-vmlinux.sh [changed mode: 0755->0644]

index 25be45fdd6208ece0b62a3cdb2a7d10296a22d4b..ce838fbd97e5fa50d6f0e5688957a6e027fda5da 100644 (file)
--- 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  =
index 3e14d208343f4964caba2cb98b4612824c15f2c3..07a53490b83559a5c801e1477c7eb50273842c3d 100644 (file)
@@ -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
index acb91db114d3831b5057caf8936bfff511098d73..447d1cef8d61704232ef3f853e573b3314476947 100644 (file)
@@ -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){};
index 706c4bffc21ccad977728a759eac6d6705bb9c94..e09900ace080b452bd9ec40ad9344cf2c8a72c46 100644 (file)
@@ -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;
        }
 
index 9840f4378aded1c904fd4243dd0544fe809f8b59..5d291d82e2fdff7d245930e069bbd16f9a5d1eb1 100644 (file)
@@ -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 */
index 5cfd4ddf20663c906aa4407005fed677f64c8738..9f6f32b5c771e7f8c169d1f2b1a7d618289397cb 100644 (file)
@@ -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;
index fd8cbbc9730b17a27e5df18bdaec3f41c8b0ba29..912972b7aa5e2a2db56083c1d754a629f0a36cac 100644 (file)
@@ -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_ */
index 8ca62d9fe8e03efe6ed8dfb2d3c07d745ed9bfb2..d21a174d2419f081884fc669727c7d184f4e8601 100644 (file)
@@ -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);
index c0d405d5d82944714d72ad00636dedb30f515932..3baede3aa5e7d254f9d4441eea2a2799c979a5b0 100644 (file)
@@ -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 (file)
index 0000000..e333905
--- /dev/null
@@ -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 (file)
index 0000000..702060a
--- /dev/null
@@ -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 (file)
index 0000000..e22d92b
--- /dev/null
@@ -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 (file)
index 0000000..29ab8a7
--- /dev/null
@@ -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)
old mode 100755 (executable)
new mode 100644 (file)
index e6818b8..ea3338d
@@ -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