blacklist-encode.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. * The blacklist encoder for RSA/DSA key blacklisting based on partial
  3. * fingerprints,
  4. * developed under Openwall Project for Owl - http://www.openwall.com/Owl/
  5. *
  6. * Copyright (c) 2008 Dmitry V. Levin <ldv at cvs.openwall.com>
  7. *
  8. * Permission to use, copy, modify, and distribute this software for any
  9. * purpose with or without fee is hereby granted, provided that the above
  10. * copyright notice and this permission notice appear in all copies.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  13. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  14. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  15. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  16. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  17. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  18. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19. *
  20. * The blacklist encoding was designed by Solar Designer and Dmitry V. Levin.
  21. * No intellectual property rights to the encoding scheme are claimed.
  22. *
  23. * This effort was supported by CivicActions - http://www.civicactions.com
  24. *
  25. * The file size to encode 294,903 of 48-bit fingerprints is just 1.3 MB,
  26. * which corresponds to less than 4.5 bytes per fingerprint.
  27. */
  28. #ifndef _GNU_SOURCE
  29. # define _GNU_SOURCE
  30. #endif
  31. #include <string.h>
  32. #include <stdlib.h>
  33. #include <stdint.h>
  34. #include <stdio.h>
  35. #include <errno.h>
  36. #include <error.h>
  37. #include <limits.h>
  38. static void *
  39. xmalloc(size_t size)
  40. {
  41. void *r = malloc(size);
  42. if (!r)
  43. error(EXIT_FAILURE, errno, "malloc: allocating %lu bytes",
  44. (unsigned long) size);
  45. return r;
  46. }
  47. static void *
  48. xcalloc(size_t nmemb, size_t size)
  49. {
  50. void *r = calloc(nmemb, size);
  51. if (!r)
  52. error(EXIT_FAILURE, errno, "calloc: allocating %lu*%lu bytes",
  53. (unsigned long) nmemb, (unsigned long) size);
  54. return r;
  55. }
  56. static void *
  57. xrealloc(void *ptr, size_t nmemb, size_t elem_size)
  58. {
  59. if (nmemb && ULONG_MAX / nmemb < elem_size)
  60. error(EXIT_FAILURE, 0, "realloc: nmemb*size > ULONG_MAX");
  61. size_t size = nmemb * elem_size;
  62. void *r = realloc(ptr, size);
  63. if (!r)
  64. error(EXIT_FAILURE, errno,
  65. "realloc: allocating %lu*%lu bytes",
  66. (unsigned long) nmemb, (unsigned long) elem_size);
  67. return r;
  68. }
  69. static char *
  70. xstrdup(const char *s)
  71. {
  72. size_t len = strlen(s);
  73. char *r = xmalloc(len + 1);
  74. memcpy(r, s, len + 1);
  75. return r;
  76. }
  77. static unsigned
  78. c2u(uint8_t c)
  79. {
  80. return (c >= 'a') ? (c - 'a' + 10) : (c - '0');
  81. }
  82. static char **records = NULL;
  83. static unsigned records_count = 0;
  84. static int
  85. comparator(const void *p1, const void *p2)
  86. {
  87. return strcmp(*(char *const *) p1, *(char *const *) p2);
  88. }
  89. static void
  90. read_stream(FILE *fp, unsigned bytes)
  91. {
  92. char *line = NULL;
  93. unsigned size = 0, allocated = 0, len = bytes * 2;
  94. int n;
  95. while ((n = getline(&line, &size, fp)) >= 0)
  96. {
  97. if (n > 0 && line[n - 1] == '\n')
  98. line[--n] = '\0';
  99. if (n < len || strspn(line, "0123456789abcdef") < n)
  100. continue; /* ignore short or invalid lines */
  101. line[len] = '\0';
  102. if (!records)
  103. records = xcalloc(allocated = 1024, sizeof(*records));
  104. if (records_count >= allocated)
  105. records = xrealloc(records, allocated *= 2,
  106. sizeof(*records));
  107. records[records_count++] = xstrdup(line);
  108. }
  109. free(line);
  110. records = xrealloc(records, records_count, sizeof(*records));
  111. if (records_count >= (1U << 24))
  112. error(EXIT_FAILURE, 0, "too many records: %u", records_count);
  113. qsort(records, records_count, sizeof(*records), comparator);
  114. }
  115. static void
  116. print_uint8(FILE *fp, uint8_t v)
  117. {
  118. fprintf(fp, "%c", v);
  119. }
  120. static void
  121. print_uint16(FILE *fp, uint16_t v)
  122. {
  123. fprintf(fp, "%c%c", v >> 8, v & 0xff);
  124. }
  125. static void
  126. print_uint24(FILE *fp, uint32_t v)
  127. {
  128. fprintf(fp, "%c%c%c", (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
  129. }
  130. int
  131. main(int ac, const char **av)
  132. {
  133. unsigned count, i, record_bytes, first_index = 0, prev_index = 0;
  134. int min_offset, max_offset;
  135. int *offsets;
  136. if (ac < 2)
  137. error(EXIT_FAILURE, 0, "insufficient arguments");
  138. if (ac > 2)
  139. error(EXIT_FAILURE, 0, "too many arguments");
  140. record_bytes = atoi(av[1]);
  141. if (record_bytes < 6 || record_bytes > 16)
  142. error(EXIT_FAILURE, 0, "fingerprint size out of bounds");
  143. read_stream(stdin, record_bytes);
  144. /* initialize global records offset table */
  145. offsets = xcalloc(65536, sizeof(*offsets));
  146. for (count = 0; count < records_count; ++count, prev_index = i)
  147. {
  148. const char *r = records[count];
  149. i = (((((c2u(r[0]) << 4) + c2u(r[1])) << 4) +
  150. c2u(r[2])) << 4) + c2u(r[3]);
  151. if (count == 0)
  152. first_index = i;
  153. else if (i == prev_index)
  154. continue;
  155. offsets[i] = count;
  156. }
  157. /* set offsets for indices without records */
  158. if (offsets[65536 - 1] == 0)
  159. offsets[65536 - 1] = records_count;
  160. for (i = 65536 - 2; i > first_index; --i)
  161. if (offsets[i] == 0)
  162. offsets[i] = offsets[i + 1];
  163. /* make global records offset table relative to
  164. expected position assuming uniform distribution. */
  165. for (i = 0, min_offset = 0, max_offset = 0; i < 65536; ++i)
  166. {
  167. offsets[i] -= (i * (unsigned long long) records_count) >> 16;
  168. if (offsets[i] < min_offset)
  169. min_offset = offsets[i];
  170. if (offsets[i] > max_offset)
  171. max_offset = offsets[i];
  172. }
  173. min_offset = -min_offset;
  174. if (min_offset < 0)
  175. error(EXIT_FAILURE, 0,
  176. "invalid offset shift: %d", min_offset);
  177. for (i = 0; i < 65536; ++i)
  178. {
  179. offsets[i] += min_offset;
  180. if (offsets[i] < 0 || offsets[i] >= 65536)
  181. error(EXIT_FAILURE, 0,
  182. "offset overflow for index %#x: %d",
  183. i, offsets[i]);
  184. }
  185. max_offset += min_offset;
  186. /* Header, 16 bytes */
  187. /* format version identifier */
  188. printf("SSH-FP00");
  189. /* index size, in bits */
  190. print_uint8(stdout, 16);
  191. /* offset size, in bits */
  192. print_uint8(stdout, 16);
  193. /* record size, in bits */
  194. print_uint8(stdout, record_bytes * 8);
  195. /* records count */
  196. print_uint24(stdout, records_count);
  197. /* offset shift */
  198. print_uint16(stdout, min_offset);
  199. fprintf(stderr, "records=%u, offset shift=%d, max offset=%d\n",
  200. records_count, min_offset, max_offset);
  201. /* Index, 65536 * 2 bytes */
  202. for (i = 0; i < 65536; ++i)
  203. print_uint16(stdout, offsets[i]);
  204. /* Fingerprints, records_count * (record_bytes-2) bytes */
  205. for (count = 0; count < records_count; ++count)
  206. {
  207. const char *r = records[count] + 4;
  208. for (i = 0; i < record_bytes - 2; ++i)
  209. print_uint8(stdout,
  210. c2u(r[i * 2]) * 16 + c2u(r[i * 2 + 1]));
  211. }
  212. if (fclose(stdout))
  213. error(EXIT_FAILURE, errno, "stdout");
  214. return 0;
  215. }