fips140_gen_hmac.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2021 - Google LLC
  4. * Author: Ard Biesheuvel <ardb@google.com>
  5. *
  6. * This is a host tool that is intended to be used to take the HMAC digest of
  7. * the .text and .rodata sections of the fips140.ko module, and store it inside
  8. * the module. The module will perform an integrity selfcheck at module_init()
  9. * time, by recalculating the digest and comparing it with the value calculated
  10. * here.
  11. *
  12. * Note that the peculiar way an HMAC is being used as a digest with a public
  13. * key rather than as a symmetric key signature is mandated by FIPS 140-2.
  14. */
  15. #include <elf.h>
  16. #include <fcntl.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sys/mman.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <unistd.h>
  24. #include <openssl/hmac.h>
  25. static Elf64_Ehdr *ehdr;
  26. static Elf64_Shdr *shdr;
  27. static int num_shdr;
  28. static const char *strtab, *shstrtab;
  29. static Elf64_Sym *syms;
  30. static int num_syms;
  31. static Elf64_Shdr *find_symtab_section(void)
  32. {
  33. int i;
  34. for (i = 0; i < num_shdr; i++)
  35. if (shdr[i].sh_type == SHT_SYMTAB)
  36. return &shdr[i];
  37. return NULL;
  38. }
  39. static int get_section_idx(const char *name)
  40. {
  41. int i;
  42. for (i = 0; i < num_shdr; i++)
  43. if (!strcmp(shstrtab + shdr[i].sh_name, name))
  44. return i;
  45. return -1;
  46. }
  47. static int get_sym_idx(const char *sym_name)
  48. {
  49. int i;
  50. for (i = 0; i < num_syms; i++)
  51. if (!strcmp(strtab + syms[i].st_name, sym_name))
  52. return i;
  53. return -1;
  54. }
  55. static void *get_sym_addr(const char *sym_name)
  56. {
  57. int i = get_sym_idx(sym_name);
  58. if (i >= 0)
  59. return (void *)ehdr + shdr[syms[i].st_shndx].sh_offset +
  60. syms[i].st_value;
  61. return NULL;
  62. }
  63. static int update_rela_ref(const char *name)
  64. {
  65. /*
  66. * We need to do a couple of things to ensure that the copied RELA data
  67. * is accessible to the module itself at module init time:
  68. * - the associated entry in the symbol table needs to refer to the
  69. * correct section index, and have SECTION type and GLOBAL linkage.
  70. * - the 'count' global variable in the module need to be set to the
  71. * right value based on the size of the RELA section.
  72. */
  73. unsigned int *size_var;
  74. int sec_idx, sym_idx;
  75. char str[32];
  76. sprintf(str, "fips140_rela_%s", name);
  77. size_var = get_sym_addr(str);
  78. if (!size_var) {
  79. printf("variable '%s' not found, disregarding .%s section\n",
  80. str, name);
  81. return 1;
  82. }
  83. sprintf(str, "__sec_rela_%s", name);
  84. sym_idx = get_sym_idx(str);
  85. sprintf(str, ".init.rela.%s", name);
  86. sec_idx = get_section_idx(str);
  87. if (sec_idx < 0 || sym_idx < 0) {
  88. fprintf(stderr, "failed to locate metadata for .%s section in binary\n",
  89. name);
  90. return 0;
  91. }
  92. syms[sym_idx].st_shndx = sec_idx;
  93. syms[sym_idx].st_info = (STB_GLOBAL << 4) | STT_SECTION;
  94. size_var[1] = shdr[sec_idx].sh_size / sizeof(Elf64_Rela);
  95. return 1;
  96. }
  97. static void hmac_section(HMAC_CTX *hmac, const char *start, const char *end)
  98. {
  99. void *start_addr = get_sym_addr(start);
  100. void *end_addr = get_sym_addr(end);
  101. HMAC_Update(hmac, start_addr, end_addr - start_addr);
  102. }
  103. int main(int argc, char **argv)
  104. {
  105. Elf64_Shdr *symtab_shdr;
  106. const char *hmac_key;
  107. unsigned char *dg;
  108. unsigned int dglen;
  109. struct stat stat;
  110. HMAC_CTX *hmac;
  111. int fd, ret;
  112. if (argc < 2) {
  113. fprintf(stderr, "file argument missing\n");
  114. exit(EXIT_FAILURE);
  115. }
  116. fd = open(argv[1], O_RDWR);
  117. if (fd < 0) {
  118. fprintf(stderr, "failed to open %s\n", argv[1]);
  119. exit(EXIT_FAILURE);
  120. }
  121. ret = fstat(fd, &stat);
  122. if (ret < 0) {
  123. fprintf(stderr, "failed to stat() %s\n", argv[1]);
  124. exit(EXIT_FAILURE);
  125. }
  126. ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  127. if (ehdr == MAP_FAILED) {
  128. fprintf(stderr, "failed to mmap() %s\n", argv[1]);
  129. exit(EXIT_FAILURE);
  130. }
  131. shdr = (void *)ehdr + ehdr->e_shoff;
  132. num_shdr = ehdr->e_shnum;
  133. symtab_shdr = find_symtab_section();
  134. syms = (void *)ehdr + symtab_shdr->sh_offset;
  135. num_syms = symtab_shdr->sh_size / sizeof(Elf64_Sym);
  136. strtab = (void *)ehdr + shdr[symtab_shdr->sh_link].sh_offset;
  137. shstrtab = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
  138. if (!update_rela_ref("text") || !update_rela_ref("rodata"))
  139. exit(EXIT_FAILURE);
  140. hmac_key = get_sym_addr("fips140_integ_hmac_key");
  141. if (!hmac_key) {
  142. fprintf(stderr, "failed to locate HMAC key in binary\n");
  143. exit(EXIT_FAILURE);
  144. }
  145. dg = get_sym_addr("fips140_integ_hmac_digest");
  146. if (!dg) {
  147. fprintf(stderr, "failed to locate HMAC digest in binary\n");
  148. exit(EXIT_FAILURE);
  149. }
  150. hmac = HMAC_CTX_new();
  151. HMAC_Init_ex(hmac, hmac_key, strlen(hmac_key), EVP_sha256(), NULL);
  152. hmac_section(hmac, "__fips140_text_start", "__fips140_text_end");
  153. hmac_section(hmac, "__fips140_rodata_start", "__fips140_rodata_end");
  154. HMAC_Final(hmac, dg, &dglen);
  155. close(fd);
  156. return 0;
  157. }