test-keys.c.in 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2020 Red Hat, Inc.
  4. * Author: Sergio Correia <scorreia@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "keys.c"
  20. #include "test-util.h"
  21. const char* jwkdir = "@testjwkdir@";
  22. struct thp_result {
  23. const char* thp;
  24. int valid;
  25. };
  26. struct test_result_int {
  27. const char* data;
  28. int expected;
  29. };
  30. static void
  31. verify_keys_permissions(const char* targetdir)
  32. {
  33. struct stat st;
  34. struct dirent* d;
  35. DIR* dir = opendir(targetdir);
  36. ASSERT(dir);
  37. char filepath[PATH_MAX];
  38. const char* pattern = ".jwk";
  39. while ((d = readdir(dir)) != NULL) {
  40. if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
  41. continue;
  42. }
  43. char* dot = strrchr(d->d_name, '.');
  44. if (!dot) {
  45. continue;
  46. }
  47. if (strcmp(dot, pattern) == 0) {
  48. /* Found a file with .jwk extension. */
  49. if (snprintf(filepath, PATH_MAX, "%s/%s", targetdir, d->d_name) < 0) {
  50. fprintf(stderr, "Unable to prepare variable with file full path (%s); skipping\n", d->d_name);
  51. continue;
  52. }
  53. filepath[sizeof(filepath) - 1] = '\0';
  54. ASSERT(stat(filepath, &st) == 0);
  55. ASSERT_WITH_MSG(st.st_mode & (S_IRUSR | S_IRGRP), "key = %s, missing perm (0%o)", filepath, (S_IRUSR | S_IRGRP));
  56. int unexpected_perms[] = {
  57. S_ISUID, /* 04000 set-user-ID */
  58. S_ISGID, /* 02000 set-group-ID */
  59. S_IWUSR, /* 00200 write by owner */
  60. S_IXUSR, /* 00100 execute/search by owner */
  61. S_IWGRP, /* 00020 write by group */
  62. S_IXGRP, /* 00010 execute/search by group */
  63. S_IROTH, /* 00004 read by others */
  64. S_IWOTH, /* 00002 write by others */
  65. S_IXOTH, /* 00001 execute/search by others */
  66. 0
  67. };
  68. for (int i = 0; unexpected_perms[i] != 0; i++) {
  69. ASSERT_WITH_MSG((st.st_mode & unexpected_perms[i]) == 0, "key = %s, i = %d, unexpected perm (0%o)", filepath, i, unexpected_perms[i]);
  70. }
  71. }
  72. }
  73. closedir(dir);
  74. }
  75. static void
  76. test_create_new_keys(void)
  77. {
  78. __attribute__((cleanup(cleanup_str))) char* newdir = create_tempdir();
  79. ASSERT(newdir);
  80. __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(newdir);
  81. ASSERT(tki);
  82. ASSERT(tki->m_keys_count == 2);
  83. /* Make sure keys have proper permissions. */
  84. verify_keys_permissions(newdir);
  85. remove_tempdir(newdir);
  86. }
  87. static void
  88. test_is_hash(void)
  89. {
  90. const struct test_result_int test_data[] = {
  91. {NULL, 0},
  92. {"", 0},
  93. {"ES512", 0},
  94. {"ECMR", 0},
  95. {"foobar", 0},
  96. {"{", 0},
  97. {"[}", 0},
  98. {"[]", 0},
  99. {"S1", 1},
  100. {"S224", 1},
  101. {"S256", 1},
  102. {"S384", 1},
  103. {"S512", 1},
  104. {"S42", 0}
  105. };
  106. for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
  107. int ret = is_hash(test_data[i].data);
  108. ASSERT_WITH_MSG(ret == test_data[i].expected, "i = %d, alg = %s", i, test_data[i].data);
  109. };
  110. }
  111. static void
  112. test_jwk_generate(void)
  113. {
  114. const struct test_result_int test_data[] = {
  115. {NULL, 0},
  116. {"", 0},
  117. {"ES512", 1},
  118. {"ECMR", 1},
  119. {"foobar", 0},
  120. {"{", 0},
  121. {"[}", 0},
  122. {"[]", 0}
  123. };
  124. for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
  125. json_auto_t* jwk = jwk_generate(test_data[i].data);
  126. ASSERT_WITH_MSG(!!jwk == test_data[i].expected, "i = %d, alg = %s", i, test_data[i].data);
  127. };
  128. }
  129. static void
  130. test_find_jws(void)
  131. {
  132. const struct thp_result test_data[] = {
  133. {"00BUQM4A7NYxbOrBR9QDfkzGVGj3k57Fs4jCbJxcLYAgRFHu5B7jtbL97x1T7stQ", 1},
  134. {"dd5qbN1lQ6UWdZszbfx2oIcH34ShklzFL1SUQg", 1},
  135. {"dOZkUtZ_gLDUP53GIlyAxHMNuyrk8vdY-XXND32GccqNbT_MKpqGC-13-GNEye48", 1},
  136. {"DZrlBQvfvlwPQlvH_IieBdc_KpesEramLygVL_rFr7g", 1},
  137. {"FL_Zt5fFadUL4syeMMpUnss8aKdCrPGFy3102JGR3EE", 1},
  138. {"qgmqJSo6AEEuVQY7zVlklqdTMqY", 1},
  139. {"r4E2wG1u_YyKUo0N0rIK7jJF5Xg", 1},
  140. {"ugJ4Ula-YABQIiJ-0g3B_jpFpF2nl3W-DNpfLdXArhTusV0QCcd1vtgDeGHEPzpm7jEsyC7VYYSSOkZicK22mw", 1},
  141. {"up0Z4fRhpd4O5QwBaMCXDTlrvxCmZacU0MD8kw", 1},
  142. {"vllHS-M0aQFCo2yUCcAahMU4TAtXACyeuRf-zbmmTPBg7V0Pb-RRFGo5C6MnpzdirK8B3ORLOsN8RyXClvtjxA", 1},
  143. {"-bWkGaJi0Zdvxaj4DCp28umLcRA", 0},
  144. {"WEpfFyeoNKkE2-TosN_bP-gd9UgRvQCZpVasZQ", 0},
  145. {"L4xg2tZXTEVbsK39bzOZM1jGWn3HtOxF5gh6F9YVf5Q", 0},
  146. {"9U8qgy_YjyY6Isuq6QuiKEiYZgNJShcGgJx5FJzCu6m3N6zFaIPy_HDkxkVqAZ9E", 0},
  147. {"Cy73glFjs6B6RU7wy6vWxAc-2bJy5VJOT9LyK80eKgZ8k27wXZ-3rjsuNU5tua_yHWtluyoSYtjoKXfI0E8ESw", 0},
  148. {NULL, 1},
  149. {"a", 0},
  150. {"foo", 0},
  151. {"bar", 0},
  152. {"XXXXXXXXXXXXXXXXXX", 0}
  153. };
  154. __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
  155. for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
  156. json_auto_t* jws = find_jws(tki, test_data[i].thp);
  157. ASSERT_WITH_MSG(!!jws == test_data[i].valid, "i = %d, thp = %s", i, test_data[i].thp);
  158. }
  159. /* Passing NULL to find_jws should return the default advertisement */
  160. json_auto_t* adv = find_jws(tki, NULL);
  161. ASSERT(adv);
  162. /*
  163. * The default set of signing keys are the signing keys that are not
  164. * rotated. The payload is made of deriving keys that are also not
  165. * rotated. The default advertisement should be signed by this set of
  166. * default signing keys.
  167. */
  168. ASSERT(jose_jws_ver(NULL, adv, NULL, tki->m_sign, 1));
  169. /* find_jws should be able to respond to thumbprints of keys using any
  170. * of jose supported hash algorithms. */
  171. const char** hashes = supported_hashes();
  172. size_t idx;
  173. json_t* jwk;
  174. /* Let's put together all the keys, including rotated ones. */
  175. json_auto_t* keys = json_deep_copy(tki->m_keys);
  176. ASSERT(keys);
  177. ASSERT(json_array_extend(keys, tki->m_rotated_keys) == 0);
  178. ASSERT(json_array_size(keys) == (size_t)(tki->m_keys_count + tki->m_rotated_keys_count));
  179. for (int i = 0; hashes[i]; i++) {
  180. json_array_foreach(keys, idx, jwk) {
  181. if (!jwk_valid_for_signing(jwk)) {
  182. continue;
  183. }
  184. __attribute__((cleanup(cleanup_str))) char* thp = jwk_thumbprint(jwk, hashes[i]);
  185. ASSERT_WITH_MSG(thp, "i = %d, hash = %s, key idx = %d", i, hashes[i], idx);
  186. json_auto_t* jws = find_jws(tki, thp);
  187. ASSERT_WITH_MSG(jws, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
  188. /* Signing keys should sign the payload, in addition to the
  189. * default set of signing keys. */
  190. json_auto_t* sign = json_deep_copy(tki->m_sign);
  191. ASSERT_WITH_MSG(sign, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
  192. ASSERT_WITH_MSG(json_array_append(sign, jwk) == 0, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
  193. ASSERT_WITH_MSG(jose_jws_ver(NULL, jws, NULL, sign, 1), "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
  194. }
  195. }
  196. }
  197. static void
  198. test_find_jwk(void)
  199. {
  200. const struct thp_result test_data[] = {
  201. {"1HdF3XKRSsuZdkpXNurBPoL_pvxdvCOlHuhB4DP-4xWFqbZ51zo29kR4fSiT3BGy9UrHVJ26JMBLOA1vKq3lxA", 1},
  202. {"9U8qgy_YjyY6Isuq6QuiKEiYZgNJShcGgJx5FJzCu6m3N6zFaIPy_HDkxkVqAZ9E", 1},
  203. {"-bWkGaJi0Zdvxaj4DCp28umLcRA", 1},
  204. {"Cy73glFjs6B6RU7wy6vWxAc-2bJy5VJOT9LyK80eKgZ8k27wXZ-3rjsuNU5tua_yHWtluyoSYtjoKXfI0E8ESw", 1},
  205. {"kfjbqx_b3BsgPC87HwlOWL9daGMMHBzxcFLClw", 1},
  206. {"L4xg2tZXTEVbsK39bzOZM1jGWn3HtOxF5gh6F9YVf5Q", 1},
  207. {"LsVAV2ig5LlfstM8TRSf-c7IAkLpNYbIysNuRCVlxocRCGqAh6-f9PklM4nU4N-J", 1},
  208. {"OkAcDxYHNlo7-tul8OubYuWXB8CPEhAkcacCmhTclMU", 1},
  209. {"uZ0s8YTXcGcuWduWWBSiR2OjOVg", 1},
  210. {"WEpfFyeoNKkE2-TosN_bP-gd9UgRvQCZpVasZQ", 1},
  211. {NULL, 0},
  212. {"a", 0},
  213. {"foo", 0},
  214. {"bar", 0},
  215. {"XXXXXXXXXXXXXXXXXX", 0},
  216. };
  217. __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
  218. for (int i = 0, len = ARRAY_COUNT(test_data); i < len; i++) {
  219. json_auto_t* tjwk = find_jwk(tki, test_data[i].thp);
  220. ASSERT_WITH_MSG(!!tjwk == test_data[i].valid, "i = %d, thp = %s", i, test_data[i].thp);
  221. }
  222. /* Passing NULL to find_jwk should fail */
  223. json_auto_t* bad_jwk = find_jwk(tki, NULL);
  224. ASSERT(bad_jwk == NULL);
  225. /* find_jwk should be able to respond to thumbprints of keys using any
  226. * of jose supported hash algorithms. */
  227. const char** hashes = supported_hashes();
  228. size_t idx;
  229. json_t* jwk;
  230. /* Let's put together all the keys, including rotated ones. */
  231. json_auto_t* keys = json_deep_copy(tki->m_keys);
  232. ASSERT(keys);
  233. ASSERT(json_array_extend(keys, tki->m_rotated_keys) == 0);
  234. ASSERT(json_array_size(keys) == (size_t)(tki->m_keys_count + tki->m_rotated_keys_count));
  235. for (int i = 0; hashes[i]; i++) {
  236. json_array_foreach(keys, idx, jwk) {
  237. if (!jwk_valid_for_deriving_keys(jwk)) {
  238. continue;
  239. }
  240. __attribute__((cleanup(cleanup_str))) char* thp = jwk_thumbprint(jwk, hashes[i]);
  241. json_auto_t* tjwk = find_jwk(tki, thp);
  242. ASSERT_WITH_MSG(tjwk, "i = %d, hash = %s, key idx = %d, thp = %s", i, hashes[i], idx, thp);
  243. }
  244. }
  245. }
  246. static void
  247. test_read_keys(void)
  248. {
  249. __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki = read_keys(jwkdir);
  250. ASSERT(tki);
  251. /*
  252. * Keys in tests/keys:
  253. * - .uZ0s8YTXcGcuWduWWBSiR2OjOVg.jwk
  254. * - .r4E2wG1u_YyKUo0N0rIK7jJF5Xg.jwk
  255. * - qgmqJSo6AEEuVQY7zVlklqdTMqY.jwk
  256. * - -bWkGaJi0Zdvxaj4DCp28umLcRA.jwk
  257. */
  258. ASSERT(tki->m_keys_count == 2);
  259. ASSERT(tki->m_rotated_keys_count == 2);
  260. ASSERT(json_array_size(tki->m_keys) == 2);
  261. ASSERT(json_array_size(tki->m_rotated_keys) == 2);
  262. const char* invalid_jwkdir = "foobar";
  263. __attribute__((cleanup(cleanup_tang_keys_info))) struct tang_keys_info* tki2 = read_keys(invalid_jwkdir);
  264. ASSERT(tki2 == NULL);
  265. }
  266. static void
  267. run_tests(void)
  268. {
  269. test_read_keys();
  270. test_find_jwk();
  271. test_find_jws();
  272. test_jwk_generate();
  273. test_is_hash();
  274. test_create_new_keys();
  275. }
  276. int main(int argc, char** argv)
  277. {
  278. run_tests();
  279. return 0;
  280. }