utils-0.3/000075500000000000000000000000001101657636200125215ustar00rootroot00000000000000utils-0.3/blacklist-check.c000064400000000000000000000134161101657636200157150ustar00rootroot00000000000000/* * The blacklist checker for RSA/DSA key blacklisting based on partial * fingerprints, * developed under Openwall Project for Owl - http://www.openwall.com/Owl/ * * Copyright (c) 2008 Dmitry V. Levin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * The blacklist encoding was designed by Solar Designer and Dmitry V. Levin. * No intellectual property rights to the encoding scheme are claimed. * * This effort was supported by CivicActions - http://www.civicactions.com * * The file size to encode 294,903 of 48-bit fingerprints is just 1.3 MB, * which corresponds to less than 4.5 bytes per fingerprint. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include static unsigned c2u(uint8_t c) { return (c >= 'a') ? (c - 'a' + 10) : (c - '0'); } static ssize_t read_retry(int fd, void *buf, size_t records) { return TEMP_FAILURE_RETRY(read(fd, buf, records)); } static ssize_t read_loop(int fd, char *buffer, size_t records) { ssize_t offset = 0; while (records > 0) { ssize_t block = read_retry(fd, &buffer[offset], records); if (block <= 0) return offset ? : block; offset += block; records -= block; } return offset; } typedef struct { /* format version identifier */ char version[8]; /* index size, in bits */ uint8_t index_size; /* offset size, in bits */ uint8_t offset_size; /* record size, in bits */ uint8_t record_bits; /* number of records */ uint8_t records[3]; /* offset shift */ uint8_t shift[2]; } fp_header; static int open_blacklist(const char *fname, unsigned *bytes, unsigned *records, unsigned *shift) { int fd; unsigned expected; struct stat st; fp_header header; if ((fd = open(fname, O_RDONLY)) < 0) { error(0, errno, "open: %s", fname); return -1; } if (fstat(fd, &st)) { error(0, errno, "fstat: %s", fname); close(fd); return -1; } if (read_loop(fd, (char *) &header, sizeof header) != sizeof header) { error(0, errno, "read header: %s", fname); close(fd); return -1; } if (header.index_size != 16 || header.offset_size != 16) { error(0, 0, "%s: unsupported file format", fname); close(fd); return -1; } *bytes = (header.record_bits >> 3) - 2; *records = (((header.records[0] << 8) + header.records[1]) << 8) + header.records[2]; *shift = (header.shift[0] << 8) + header.shift[1]; expected = sizeof(header) + 0x20000 + (*records) * (*bytes); if (st.st_size != expected) { error(0, 0, "%s: expected file size %u, found file size %lu", fname, expected, (unsigned long) st.st_size); close(fd); return -1; } return fd; } static int expected_offset(uint16_t index, uint16_t shift, unsigned records) { return ((index * (long long) records) >> 16) - shift; } static int xlseek(const char *fname, int fd, unsigned seek) { if (lseek(fd, seek, SEEK_SET) != seek) { error(0, errno, "lseek: %s", fname); return -1; } return 0; } static int check(const char *fname, const char *s) { int fd; unsigned bytes, records, shift; unsigned i, j; int offset, off_end; uint16_t index; /* max number of bytes stored in record_bits, minus two bytes used for index */ uint8_t buf[(0xff >> 3) - 2]; if (strlen(s) != 32 || strlen(s) != strspn(s, "0123456789abcdef")) { fprintf(stderr, "invalid fingerprint: %s\n", s); return 1; } fd = open_blacklist(fname, &bytes, &records, &shift); if (fd < 0) return 1; index = (((((c2u(s[0]) << 4) | c2u(s[1])) << 4) | c2u(s[2])) << 4) | c2u(s[3]); if (xlseek(fname, fd, sizeof(fp_header) + index * 2)) { close(fd); return 1; } if (read_loop(fd, (char *) buf, 4) != 4) { error(0, errno, "read offsets: %s", fname); close(fd); return 1; } offset = (buf[0] << 8) + buf[1] + expected_offset(index, shift, records); if (offset < 0 || offset > records) { error(0, 0, "index=%#x, offset overflow: %d", index, offset); close(fd); return 1; } if (index < 0xffff) { off_end = (buf[2] << 8) + buf[3] + expected_offset(index + 1, shift, records); if (off_end < offset || off_end > records) { error(0, 0, "index=%#x, offset overflow: %d", index, off_end); close(fd); return 1; } } else off_end = records; if (xlseek(fname, fd, sizeof(fp_header) + 0x20000 + offset * bytes)) { close(fd); return 1; } for (i = 0; i < off_end - offset; ++i) { if (read_loop(fd, (char *) buf, bytes) != bytes) { error(0, errno, "read fingerprints: %s", fname); close(fd); return 1; } for (j = 0; j < bytes; ++j) if (((c2u(s[4 + j * 2]) << 4) | c2u(s[5 + j * 2])) != buf[j]) break; if (j >= bytes) { fprintf(stderr, "BAD: %s offset=%u, offcnt=%u, i=%u\n", s, offset, off_end - offset, i); close(fd); return 1; } } fprintf(stderr, "OK: %s offset=%u, offcnt=%u\n", s, offset, off_end - offset); close(fd); return 0; } int main(int ac, const char **av) { int i, rc = 0; if (ac < 3) error(EXIT_FAILURE, 0, "insufficient arguments"); for (i = 2; i < ac; ++i) rc |= check(av[1], av[i]); return rc; } utils-0.3/blacklist-encode.c000064400000000000000000000146041101657636200160750ustar00rootroot00000000000000/* * The blacklist encoder for RSA/DSA key blacklisting based on partial * fingerprints, * developed under Openwall Project for Owl - http://www.openwall.com/Owl/ * * Copyright (c) 2008 Dmitry V. Levin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * The blacklist encoding was designed by Solar Designer and Dmitry V. Levin. * No intellectual property rights to the encoding scheme are claimed. * * This effort was supported by CivicActions - http://www.civicactions.com * * The file size to encode 294,903 of 48-bit fingerprints is just 1.3 MB, * which corresponds to less than 4.5 bytes per fingerprint. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include static void * xmalloc(size_t size) { void *r = malloc(size); if (!r) error(EXIT_FAILURE, errno, "malloc: allocating %lu bytes", (unsigned long) size); return r; } static void * xcalloc(size_t nmemb, size_t size) { void *r = calloc(nmemb, size); if (!r) error(EXIT_FAILURE, errno, "calloc: allocating %lu*%lu bytes", (unsigned long) nmemb, (unsigned long) size); return r; } static void * xrealloc(void *ptr, size_t nmemb, size_t elem_size) { if (nmemb && ULONG_MAX / nmemb < elem_size) error(EXIT_FAILURE, 0, "realloc: nmemb*size > ULONG_MAX"); size_t size = nmemb * elem_size; void *r = realloc(ptr, size); if (!r) error(EXIT_FAILURE, errno, "realloc: allocating %lu*%lu bytes", (unsigned long) nmemb, (unsigned long) elem_size); return r; } static char * xstrdup(const char *s) { size_t len = strlen(s); char *r = xmalloc(len + 1); memcpy(r, s, len + 1); return r; } static unsigned c2u(uint8_t c) { return (c >= 'a') ? (c - 'a' + 10) : (c - '0'); } static char **records = NULL; static unsigned records_count = 0; static int comparator(const void *p1, const void *p2) { return strcmp(*(char *const *) p1, *(char *const *) p2); } static void read_stream(FILE *fp, unsigned bytes) { char *line = NULL; unsigned size = 0, allocated = 0, len = bytes * 2; int n; while ((n = getline(&line, &size, fp)) >= 0) { if (n > 0 && line[n - 1] == '\n') line[--n] = '\0'; if (n < len || strspn(line, "0123456789abcdef") < n) continue; /* ignore short or invalid lines */ line[len] = '\0'; if (!records) records = xcalloc(allocated = 1024, sizeof(*records)); if (records_count >= allocated) records = xrealloc(records, allocated *= 2, sizeof(*records)); records[records_count++] = xstrdup(line); } free(line); records = xrealloc(records, records_count, sizeof(*records)); if (records_count >= (1U << 24)) error(EXIT_FAILURE, 0, "too many records: %u", records_count); qsort(records, records_count, sizeof(*records), comparator); } static void print_uint8(FILE *fp, uint8_t v) { fprintf(fp, "%c", v); } static void print_uint16(FILE *fp, uint16_t v) { fprintf(fp, "%c%c", v >> 8, v & 0xff); } static void print_uint24(FILE *fp, uint32_t v) { fprintf(fp, "%c%c%c", (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff); } int main(int ac, const char **av) { unsigned count, i, record_bytes, first_index = 0, prev_index = 0; int min_offset, max_offset; int *offsets; if (ac < 2) error(EXIT_FAILURE, 0, "insufficient arguments"); if (ac > 2) error(EXIT_FAILURE, 0, "too many arguments"); record_bytes = atoi(av[1]); if (record_bytes < 6 || record_bytes > 16) error(EXIT_FAILURE, 0, "fingerprint size out of bounds"); read_stream(stdin, record_bytes); /* initialize global records offset table */ offsets = xcalloc(65536, sizeof(*offsets)); for (count = 0; count < records_count; ++count, prev_index = i) { const char *r = records[count]; i = (((((c2u(r[0]) << 4) + c2u(r[1])) << 4) + c2u(r[2])) << 4) + c2u(r[3]); if (count == 0) first_index = i; else if (i == prev_index) continue; offsets[i] = count; } /* set offsets for indices without records */ if (offsets[65536 - 1] == 0) offsets[65536 - 1] = records_count; for (i = 65536 - 2; i > first_index; --i) if (offsets[i] == 0) offsets[i] = offsets[i + 1]; /* make global records offset table relative to expected position assuming uniform distribution. */ for (i = 0, min_offset = 0, max_offset = 0; i < 65536; ++i) { offsets[i] -= (i * (unsigned long long) records_count) >> 16; if (offsets[i] < min_offset) min_offset = offsets[i]; if (offsets[i] > max_offset) max_offset = offsets[i]; } min_offset = -min_offset; if (min_offset < 0) error(EXIT_FAILURE, 0, "invalid offset shift: %d", min_offset); for (i = 0; i < 65536; ++i) { offsets[i] += min_offset; if (offsets[i] < 0 || offsets[i] >= 65536) error(EXIT_FAILURE, 0, "offset overflow for index %#x: %d", i, offsets[i]); } max_offset += min_offset; /* Header, 16 bytes */ /* format version identifier */ printf("SSH-FP00"); /* index size, in bits */ print_uint8(stdout, 16); /* offset size, in bits */ print_uint8(stdout, 16); /* record size, in bits */ print_uint8(stdout, record_bytes * 8); /* records count */ print_uint24(stdout, records_count); /* offset shift */ print_uint16(stdout, min_offset); fprintf(stderr, "records=%u, offset shift=%d, max offset=%d\n", records_count, min_offset, max_offset); /* Index, 65536 * 2 bytes */ for (i = 0; i < 65536; ++i) print_uint16(stdout, offsets[i]); /* Fingerprints, records_count * (record_bytes-2) bytes */ for (count = 0; count < records_count; ++count) { const char *r = records[count] + 4; for (i = 0; i < record_bytes - 2; ++i) print_uint8(stdout, c2u(r[i * 2]) * 16 + c2u(r[i * 2 + 1])); } if (fclose(stdout)) error(EXIT_FAILURE, errno, "stdout"); return 0; }