├── .gitignore ├── Makefile ├── README.md ├── UNLICENSE ├── compact.c ├── config.h ├── pwcheck.c ├── pwcheck.h ├── sha1.c └── sha1.h /.gitignore: -------------------------------------------------------------------------------- 1 | pwcheck 2 | compact 3 | libpwcheck.so 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | CC = cc 3 | CFLAGS = -std=c99 -Wall -Wextra -O3 4 | 5 | all: pwcheck libpwcheck.so compact 6 | 7 | pwcheck: pwcheck.c sha1.c sha1.h 8 | $(CC) $(LDFLAGS) $(CFLAGS) -DCMDLINE -o $@ pwcheck.c sha1.c $(LDLIBS) 9 | 10 | libpwcheck.so: pwcheck.c sha1.c sha1.h 11 | $(CC) $(LDFLAGS) -shared $(CFLAGS) -fPIC -fvisibility=hidden \ 12 | -o $@ pwcheck.c sha1.c $(LDLIBS) 13 | 14 | compact: compact.c 15 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ compact.c $(LDLIBS) 16 | 17 | clean: 18 | rm -f pwcheck compact libpwcheck.so 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Database Lookup for "Have I Been Pwned" 2 | 3 | This is a little C99 library for efficiently checking passwords against 4 | the [Have I Been Pwned][pwn] dataset. The database is the sorted list of 5 | hashes converted to binary, truncated, and concatenated. Look-ups are a 6 | binary search on this memory-mapped file. 7 | 8 | Once warmed up, this library can test around 250,000 passwords per 9 | second. 10 | 11 | Only POSIX systems are currently supported. 12 | 13 | ## Compilation 14 | 15 | To build, run `make`. It produces: 16 | 17 | * `compact`: A command line program for constructing databases. 18 | 19 | * `pwcheck`: A simple command line password checking utility. 20 | 21 | * `libpwcheck.so`: For use by other programs, particularly those written 22 | in languages with a foreign function interface (FFI). 23 | 24 | ## Database generation 25 | 26 | To build a database from the "ordered by hash" dataset, pipe it through 27 | the `compact` command: 28 | 29 | $ ./compact pwned.db 30 | 31 | Hash truncation is controlled at *compile time* in `config.h`. With the 32 | default configuration, the 2.0 dataset (501m passwords) becomes a 3.8GB 33 | database. Since the database is memory mapped, it is not essential to 34 | have that much physical memory, but it *is* essential for maintaining 35 | high throughput. 36 | 37 | The `pwcheck` convenient utility queries a database without involving 38 | the library. It reads passwords, one per line, on standard input: 39 | 40 | $ echo correcthorsebatterystaple | ./pwcheck pwned.db 41 | correcthorsebatterystaple: found 42 | 43 | $ echo LyX | ./pwcheck pwned.db 44 | LyX: not found 45 | 46 | ## Shared library API 47 | 48 | The API for `libpwcheck.so` is very FFI-friendly: 49 | 50 | ```c 51 | /** 52 | * Open a database by its filename and return a handle. 53 | * Returns NULL if the file could not be opened. 54 | */ 55 | struct pwcheck *pwcheck_open(const char *); 56 | 57 | /** 58 | * Close a database and free its resources. 59 | */ 60 | void pwcheck_close(struct pwcheck *); 61 | 62 | /** 63 | * Return 0 if the null-terminated password is not in the database. 64 | */ 65 | int pwcheck_password(const struct pwcheck *, const char *); 66 | 67 | /** 68 | * Return 0 if the given SHA-1 hash is not in the database. 69 | */ 70 | int pwcheck_hash(const struct pwcheck *, const void *); 71 | ``` 72 | 73 | [pwn]: https://haveibeenpwned.com/Passwords 74 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /compact.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "config.h" 5 | 6 | static const unsigned char lookup[] = { 7 | ['0'] = 0x00, ['1'] = 0x01, ['2'] = 0x02, ['3'] = 0x03, 8 | ['4'] = 0x04, ['5'] = 0x05, ['6'] = 0x06, ['7'] = 0x07, 9 | ['8'] = 0x08, ['9'] = 0x09, ['A'] = 0x0A, ['B'] = 0x0B, 10 | ['C'] = 0x0C, ['D'] = 0x0D, ['E'] = 0x0E, ['F'] = 0x0F, 11 | }; 12 | 13 | int 14 | main(void) 15 | { 16 | char in[256]; 17 | while (fgets(in, sizeof(in), stdin)) { 18 | unsigned char out[HASH_LENGTH]; 19 | for (int i = 0; i < HASH_LENGTH; i++) { 20 | int nh = in[i * 2 + 0]; 21 | int nl = in[i * 2 + 1]; 22 | out[i] = (lookup[nh] << 4) | lookup[nl]; 23 | } 24 | if (!fwrite(out, HASH_LENGTH, 1, stdout)) { 25 | perror(in); 26 | return EXIT_FAILURE; 27 | } 28 | } 29 | if (ferror(stdin)) { 30 | perror(in); 31 | return EXIT_FAILURE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define HASH_LENGTH 8 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /pwcheck.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 2 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "sha1.h" 11 | #include "config.h" 12 | #include "pwcheck.h" 13 | 14 | struct pwcheck { 15 | void *data; 16 | size_t count; 17 | }; 18 | 19 | static int 20 | db_open(struct pwcheck *db, const char *dbfile) 21 | { 22 | int fd = open(dbfile, O_RDONLY); 23 | if (fd == -1) 24 | return 0; 25 | 26 | struct stat stat[1]; 27 | if (fstat(fd, stat) == -1) { 28 | close(fd); 29 | return 0; 30 | } 31 | db->count = stat->st_size / HASH_LENGTH; 32 | 33 | db->data = mmap(0, stat->st_size, PROT_READ, MAP_PRIVATE, fd, 0); 34 | close(fd); 35 | return db->data != MAP_FAILED; 36 | } 37 | 38 | static void 39 | db_close(struct pwcheck *db) 40 | { 41 | munmap(db->data, db->count * HASH_LENGTH); 42 | } 43 | 44 | static int 45 | bincmp(const void *a, const void *b) 46 | { 47 | return memcmp(a, b, HASH_LENGTH); 48 | } 49 | 50 | static char * 51 | db_search(const struct pwcheck *db, const void *hash) 52 | { 53 | return bsearch(hash, db->data, db->count, HASH_LENGTH, bincmp); 54 | } 55 | 56 | static void 57 | password_hash(unsigned char buf[SHA1_DIGEST_SIZE], const char *password) 58 | { 59 | SHA1_CTX ctx[1]; 60 | SHA1_Init(ctx); 61 | SHA1_Update(ctx, (void *)password, strlen(password)); 62 | SHA1_Final(ctx, buf); 63 | } 64 | 65 | #ifndef CMDLINE 66 | 67 | /* Shared library interface */ 68 | 69 | PWCHECK_API 70 | struct pwcheck * 71 | pwcheck_open(const char *filename) 72 | { 73 | struct pwcheck *db = malloc(sizeof(*db)); 74 | if (!db) 75 | return 0; 76 | if (!db_open(db, filename)) { 77 | free(db); 78 | return 0; 79 | } 80 | return db; 81 | } 82 | 83 | PWCHECK_API 84 | void 85 | pwcheck_close(struct pwcheck *db) 86 | { 87 | db_close(db); 88 | free(db); 89 | } 90 | 91 | PWCHECK_API 92 | int 93 | pwcheck_password(const struct pwcheck *db, const char *password) 94 | { 95 | unsigned char buf[SHA1_DIGEST_SIZE]; 96 | password_hash(buf, password); 97 | return !!db_search(db, buf); 98 | } 99 | 100 | PWCHECK_API 101 | int 102 | pwcheck_hash(const struct pwcheck *db, const void *hash) 103 | { 104 | return !!db_search(db, hash); 105 | } 106 | 107 | #else 108 | 109 | /* Command line interface */ 110 | 111 | static void 112 | usage(FILE *f) 113 | { 114 | fprintf(f, "usage: pwcheck DBFILE\n"); 115 | } 116 | 117 | int 118 | main(int argc, char **argv) 119 | { 120 | char *dbfile = 0; 121 | 122 | int option; 123 | while ((option = getopt(argc, argv, "h")) != -1) { 124 | switch (option) { 125 | case 'h': 126 | usage(stdout); 127 | exit(EXIT_SUCCESS); 128 | default: 129 | exit(EXIT_FAILURE); 130 | } 131 | } 132 | 133 | dbfile = argv[optind]; 134 | if (!dbfile) { 135 | fprintf(stderr, "pwcheck: requires a database file\n"); 136 | usage(stderr); 137 | exit(EXIT_FAILURE); 138 | } 139 | 140 | struct pwcheck db; 141 | if (!db_open(&db, dbfile)) { 142 | perror(dbfile); 143 | exit(EXIT_FAILURE); 144 | } 145 | 146 | /* Look up each word from standard input */ 147 | char line[256]; 148 | unsigned char buf[SHA1_DIGEST_SIZE]; 149 | while (fgets(line, sizeof(line), stdin)) { 150 | size_t last = strlen(line) - 1; 151 | if (line[last] == '\n') 152 | line[last] = 0; 153 | password_hash(buf, line); 154 | char *result = db_search(&db, buf); 155 | printf("%s: %s\n", line, result ? "found" : "not found"); 156 | } 157 | 158 | #ifdef __CYGWIN__ 159 | db_close(&db); 160 | #else 161 | (void)db_close; // munmap() is ridiculously slow on Cygwin 162 | #endif /* __CYGWIN__ */ 163 | } 164 | 165 | #endif /* CMDLINE */ 166 | -------------------------------------------------------------------------------- /pwcheck.h: -------------------------------------------------------------------------------- 1 | #ifndef PWCHECK_H 2 | #define PWCHECK_H 3 | 4 | #if defined(__GNUC__) || defined(__clang__) 5 | # define PWCHECK_API __attribute__((visibility("default"))) 6 | #else 7 | # define PWCHECK_API 8 | #endif 9 | 10 | struct pwcheck; 11 | 12 | /** 13 | * Open a database by its filename and return a handle. 14 | * Returns NULL if the file could not be opened. 15 | */ 16 | PWCHECK_API 17 | struct pwcheck *pwcheck_open(const char *); 18 | 19 | /** 20 | * Close a database and free its resources. 21 | */ 22 | PWCHECK_API 23 | void pwcheck_close(struct pwcheck *); 24 | 25 | /** 26 | * Return 0 if the null-terminated password is not in the database. 27 | */ 28 | PWCHECK_API 29 | int pwcheck_password(const struct pwcheck *, const char *); 30 | 31 | /** 32 | * Return 0 if the given SHA-1 hash is not in the database. 33 | */ 34 | PWCHECK_API 35 | int pwcheck_hash(const struct pwcheck *, const void *); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | SHA-1 in C 3 | By Steve Reid 4 | 100% Public Domain 5 | 6 | ----------------- 7 | Modified 7/98 8 | By James H. Brown 9 | Still 100% Public Domain 10 | 11 | Corrected a problem which generated improper hash values on 16 bit machines 12 | Routine SHA1Update changed from 13 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int 14 | len) 15 | to 16 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned 17 | long len) 18 | 19 | The 'len' parameter was declared an int which works fine on 32 bit machines. 20 | However, on 16 bit machines an int is too small for the shifts being done 21 | against 22 | it. This caused the hash function to generate incorrect values if len was 23 | greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). 24 | 25 | Since the file IO in main() reads 16K at a time, any file 8K or larger would 26 | be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million 27 | "a"s). 28 | 29 | I also changed the declaration of variables i & j in SHA1Update to 30 | unsigned long from unsigned int for the same reason. 31 | 32 | These changes should make no difference to any 32 bit implementations since 33 | an 34 | int and a long are the same size in those environments. 35 | 36 | -- 37 | I also corrected a few compiler warnings generated by Borland C. 38 | 1. Added #include for exit() prototype 39 | 2. Removed unused variable 'j' in SHA1Final 40 | 3. Changed exit(0) to return(0) at end of main. 41 | 42 | ALL changes I made can be located by searching for comments containing 'JHB' 43 | ----------------- 44 | Modified 8/98 45 | By Steve Reid 46 | Still 100% public domain 47 | 48 | 1- Removed #include and used return() instead of exit() 49 | 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 50 | 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net 51 | 52 | ----------------- 53 | Modified 4/01 54 | By Saul Kravitz 55 | Still 100% PD 56 | Modified to run on Compaq Alpha hardware. 57 | 58 | ----------------- 59 | Modified 07/2002 60 | By Ralph Giles 61 | Still 100% public domain 62 | modified for use with stdint types, autoconf 63 | code cleanup, removed attribution comments 64 | switched SHA1Final() argument order for consistency 65 | use SHA1_ prefix for public api 66 | move public api to sha1.h 67 | */ 68 | 69 | /* 70 | Test Vectors (from FIPS PUB 180-1) 71 | "abc" 72 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 73 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 74 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 75 | A million repetitions of "a" 76 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 77 | */ 78 | 79 | /* #define SHA1HANDSOFF */ 80 | 81 | #ifdef HAVE_CONFIG_H 82 | #include "config.h" 83 | #endif 84 | 85 | #include 86 | #include 87 | #include 88 | 89 | #include "sha1.h" 90 | 91 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); 92 | 93 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 94 | 95 | /* blk0() and blk() perform the initial expand. */ 96 | /* I got the idea of expanding during the round function from SSLeay */ 97 | /* FIXME: can we do this in an endian-proof way? */ 98 | #ifdef WORDS_BIGENDIAN 99 | #define blk0(i) block->l[i] 100 | #else 101 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 102 | |(rol(block->l[i],8)&0x00FF00FF)) 103 | #endif 104 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 105 | ^block->l[(i+2)&15]^block->l[i&15],1)) 106 | 107 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 108 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 109 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 110 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 111 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 112 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 113 | 114 | 115 | #ifdef VERBOSE /* SAK */ 116 | void SHAPrintContext(SHA1_CTX *context, char *msg){ 117 | printf("%s (%d,%d) %x %x %x %x %x\n", 118 | msg, 119 | context->count[0], context->count[1], 120 | context->state[0], 121 | context->state[1], 122 | context->state[2], 123 | context->state[3], 124 | context->state[4]); 125 | } 126 | #endif /* VERBOSE */ 127 | 128 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 129 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) 130 | { 131 | uint32_t a, b, c, d, e; 132 | typedef union { 133 | uint8_t c[64]; 134 | uint32_t l[16]; 135 | } CHAR64LONG16; 136 | CHAR64LONG16* block; 137 | 138 | #ifdef SHA1HANDSOFF 139 | static uint8_t workspace[64]; 140 | block = (CHAR64LONG16*)workspace; 141 | memcpy(block, buffer, 64); 142 | #else 143 | block = (CHAR64LONG16*)buffer; 144 | #endif 145 | 146 | /* Copy context->state[] to working vars */ 147 | a = state[0]; 148 | b = state[1]; 149 | c = state[2]; 150 | d = state[3]; 151 | e = state[4]; 152 | 153 | /* 4 rounds of 20 operations each. Loop unrolled. */ 154 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 155 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 156 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 157 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 158 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 159 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 160 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 161 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 162 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 163 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 164 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 165 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 166 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 167 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 168 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 169 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 170 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 171 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 172 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 173 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 174 | 175 | /* Add the working vars back into context.state[] */ 176 | state[0] += a; 177 | state[1] += b; 178 | state[2] += c; 179 | state[3] += d; 180 | state[4] += e; 181 | 182 | /* Wipe variables */ 183 | a = b = c = d = e = 0; 184 | } 185 | 186 | 187 | /* SHA1Init - Initialize new context */ 188 | void SHA1_Init(SHA1_CTX* context) 189 | { 190 | /* SHA1 initialization constants */ 191 | context->state[0] = 0x67452301; 192 | context->state[1] = 0xEFCDAB89; 193 | context->state[2] = 0x98BADCFE; 194 | context->state[3] = 0x10325476; 195 | context->state[4] = 0xC3D2E1F0; 196 | context->count[0] = context->count[1] = 0; 197 | } 198 | 199 | 200 | /* Run your data through this. */ 201 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) 202 | { 203 | size_t i, j; 204 | 205 | #ifdef VERBOSE 206 | SHAPrintContext(context, "before"); 207 | #endif 208 | 209 | j = (context->count[0] >> 3) & 63; 210 | if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; 211 | context->count[1] += (len >> 29); 212 | if ((j + len) > 63) { 213 | memcpy(&context->buffer[j], data, (i = 64-j)); 214 | SHA1_Transform(context->state, context->buffer); 215 | for ( ; i + 63 < len; i += 64) { 216 | SHA1_Transform(context->state, data + i); 217 | } 218 | j = 0; 219 | } 220 | else i = 0; 221 | memcpy(&context->buffer[j], &data[i], len - i); 222 | 223 | #ifdef VERBOSE 224 | SHAPrintContext(context, "after "); 225 | #endif 226 | } 227 | 228 | 229 | /* Add padding and return the message digest. */ 230 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) 231 | { 232 | uint32_t i; 233 | uint8_t finalcount[8]; 234 | 235 | for (i = 0; i < 8; i++) { 236 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 237 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 238 | } 239 | SHA1_Update(context, (uint8_t *)"\200", 1); 240 | while ((context->count[0] & 504) != 448) { 241 | SHA1_Update(context, (uint8_t *)"\0", 1); 242 | } 243 | SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ 244 | for (i = 0; i < SHA1_DIGEST_SIZE; i++) { 245 | digest[i] = (uint8_t) 246 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 247 | } 248 | 249 | /* Wipe variables */ 250 | i = 0; 251 | memset(context->buffer, 0, 64); 252 | memset(context->state, 0, 20); 253 | memset(context->count, 0, 8); 254 | memset(finalcount, 0, 8); /* SWR */ 255 | 256 | #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ 257 | SHA1_Transform(context->state, context->buffer); 258 | #endif 259 | } 260 | 261 | /*************************************************************/ 262 | 263 | #if 0 264 | int main(int argc, char** argv) 265 | { 266 | int i, j; 267 | SHA1_CTX context; 268 | unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384]; 269 | FILE* file; 270 | 271 | if (argc > 2) { 272 | puts("Public domain SHA-1 implementation - by Steve Reid "); 273 | puts("Modified for 16 bit environments 7/98 - by James H. Brown "); /* JHB */ 274 | puts("Produces the SHA-1 hash of a file, or stdin if no file is specified."); 275 | return(0); 276 | } 277 | if (argc < 2) { 278 | file = stdin; 279 | } 280 | else { 281 | if (!(file = fopen(argv[1], "rb"))) { 282 | fputs("Unable to open file.", stderr); 283 | return(-1); 284 | } 285 | } 286 | SHA1_Init(&context); 287 | while (!feof(file)) { /* note: what if ferror(file) */ 288 | i = fread(buffer, 1, 16384, file); 289 | SHA1_Update(&context, buffer, i); 290 | } 291 | SHA1_Final(&context, digest); 292 | fclose(file); 293 | for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) { 294 | for (j = 0; j < 4; j++) { 295 | printf("%02X", digest[i*4+j]); 296 | } 297 | putchar(' '); 298 | } 299 | putchar('\n'); 300 | return(0); /* JHB */ 301 | } 302 | #endif 303 | 304 | /* self test */ 305 | 306 | #ifdef TEST 307 | 308 | static char *test_data[] = { 309 | "abc", 310 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 311 | "A million repetitions of 'a'"}; 312 | static char *test_results[] = { 313 | "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D", 314 | "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1", 315 | "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"}; 316 | 317 | 318 | void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output) 319 | { 320 | int i,j; 321 | char *c = output; 322 | 323 | for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) { 324 | for (j = 0; j < 4; j++) { 325 | sprintf(c,"%02X", digest[i*4+j]); 326 | c += 2; 327 | } 328 | sprintf(c, " "); 329 | c += 1; 330 | } 331 | *(c - 1) = '\0'; 332 | } 333 | 334 | int main(int argc, char** argv) 335 | { 336 | int k; 337 | SHA1_CTX context; 338 | uint8_t digest[20]; 339 | char output[80]; 340 | 341 | fprintf(stdout, "verifying SHA-1 implementation... "); 342 | 343 | for (k = 0; k < 2; k++){ 344 | SHA1_Init(&context); 345 | SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k])); 346 | SHA1_Final(&context, digest); 347 | digest_to_hex(digest, output); 348 | 349 | if (strcmp(output, test_results[k])) { 350 | fprintf(stdout, "FAIL\n"); 351 | fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]); 352 | fprintf(stderr,"\t%s returned\n", output); 353 | fprintf(stderr,"\t%s is correct\n", test_results[k]); 354 | return (1); 355 | } 356 | } 357 | /* million 'a' vector we feed separately */ 358 | SHA1_Init(&context); 359 | for (k = 0; k < 1000000; k++) 360 | SHA1_Update(&context, (uint8_t*)"a", 1); 361 | SHA1_Final(&context, digest); 362 | digest_to_hex(digest, output); 363 | if (strcmp(output, test_results[2])) { 364 | fprintf(stdout, "FAIL\n"); 365 | fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]); 366 | fprintf(stderr,"\t%s returned\n", output); 367 | fprintf(stderr,"\t%s is correct\n", test_results[2]); 368 | return (1); 369 | } 370 | 371 | /* success */ 372 | fprintf(stdout, "ok\n"); 373 | return(0); 374 | } 375 | #endif /* TEST */ 376 | -------------------------------------------------------------------------------- /sha1.h: -------------------------------------------------------------------------------- 1 | /* public api for steve reid's public domain SHA-1 implementation */ 2 | /* this file is in the public domain */ 3 | 4 | #ifndef __SHA1_H 5 | #define __SHA1_H 6 | 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | uint32_t state[5]; 12 | uint32_t count[2]; 13 | uint8_t buffer[64]; 14 | } SHA1_CTX; 15 | 16 | #define SHA1_DIGEST_SIZE 20 17 | 18 | void SHA1_Init(SHA1_CTX* context); 19 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len); 20 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]); 21 | 22 | #endif /* __SHA1_H */ 23 | --------------------------------------------------------------------------------