| /* |
| This program implements the ECIES public key encryption scheme based on the |
| NIST B163 elliptic curve and the XTEA block cipher. The code was written |
| as an accompaniment for an article published in phrack #63 and is released to |
| the public domain. |
| */ |
| |
| #define UPDATE_SEED_FROM_RANDOM_DEVICE |
| #define RANDOM_DEV_RANDOM_TIMEOUT 1 |
| |
| //#include <stdint.h> |
| #include "ecc.h" |
| #include <stdlib.h> |
| #include <string.h> |
| #ifndef NO_RANDOM_DEVICE |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| //#if !defined RANDOM_CHAR_DEV && !defined RANDOM_DEV_RANDOM_TIMEOUT |
| //#ifndef RANDOM_CHAR_DEV |
| #ifdef RANDOM_DEV_RANDOM_TIMEOUT |
| #undef RANDOM_CHAR_DEV |
| #endif |
| #if !defined RANDOM_CHAR_DEV || defined RANDOM_DEV_RANDOM_TIMEOUT |
| #define RANDOM_CHAR_DEV "/dev/urandom" |
| #endif |
| #endif |
| |
| /* |
| static void debug_print_bitstr(const uint32_t *a) { |
| int i; |
| for(i=0; i<ECIES_NUMWORDS; i++) fprintf(stderr, "0x%x\n", (unsigned int)a[i]); |
| } |
| static void debug_print_bytes(const ECIES_byte_t *a, size_t len) { |
| unsigned int i; |
| for(i=0; i<len; i++) fprintf(stderr, "0x%hhx, ", a[i]); |
| fputc('\n', stderr); |
| } |
| static void debug_print_integers(const uint32_t *a, size_t len) { |
| unsigned int i; |
| for(i=0; i<len; i++) fprintf(stderr, "0x%x\n", (unsigned int)a[i]); |
| } |
| */ |
| |
| /* the following type will represent bit vectors of length (ECIES_DEGREE+MARGIN) */ |
| typedef uint32_t bitstr_t[ECIES_NUMWORDS]; |
| |
| /* some basic bit-manipulation routines that act on these vectors follow */ |
| #define bitstr_getbit(A, idx) ((A[(idx) / 32] >> ((idx) % 32)) & 1) |
| #define bitstr_setbit(A, idx) MACRO( A[(idx) / 32] |= 1 << ((idx) % 32) ) |
| #define bitstr_clrbit(A, idx) MACRO( A[(idx) / 32] &= ~(1 << ((idx) % 32)) ) |
| |
| #define bitstr_clear(A) MACRO( memset(A, 0, sizeof(bitstr_t)) ) |
| #define bitstr_copy(A, B) MACRO( memcpy(A, B, sizeof(bitstr_t)) ) |
| #define bitstr_swap(A, B) MACRO( bitstr_t h; \ |
| bitstr_copy(h, A); bitstr_copy(A, B); bitstr_copy(B, h) ) |
| #define bitstr_is_equal(A, B) (! memcmp(A, B, sizeof(bitstr_t))) |
| |
| static int bitstr_is_clear(const bitstr_t x) |
| { |
| int i; |
| for(i = 0; i < ECIES_NUMWORDS && ! *x++; i++); |
| return i == ECIES_NUMWORDS; |
| } |
| |
| /* return the number of the highest one-bit + 1 */ |
| static int bitstr_sizeinbits(const bitstr_t x) |
| { |
| int i; |
| uint32_t mask; |
| for(x += ECIES_NUMWORDS, i = 32 * ECIES_NUMWORDS; i > 0 && ! *--x; i -= 32); |
| if (i) |
| for(mask = 1 << 31; ! (*x & mask); mask >>= 1, i--); |
| return i; |
| } |
| |
| /* left-shift by 'count' digits */ |
| static void bitstr_lshift(bitstr_t A, const bitstr_t B, int count) |
| { |
| int i, offs = 4 * (count / 32); |
| memmove((char*)A + offs, B, sizeof(bitstr_t) - offs); |
| memset(A, 0, offs); |
| if (count %= 32) { |
| for(i = ECIES_NUMWORDS - 1; i > 0; i--) |
| A[i] = (A[i] << count) | (A[i - 1] >> (32 - count)); |
| A[0] <<= count; |
| } |
| } |
| |
| static void bitstr_load(bitstr_t bstr, const ECIES_byte_t *data, ECIES_size_t len){ |
| uint32_t *bptr = bstr + ((len + 3) / 4) - 1; |
| |
| len %= 4; |
| |
| if(len > 0){ |
| *bptr = 0; |
| if(len > 1){ |
| if(len > 2){ |
| *bptr |= (uint32_t)(*data++) << 16; |
| } |
| *bptr |= (uint32_t)(*data++) << 8; |
| } |
| *bptr |= (uint32_t)(*data++); |
| bptr--; |
| } |
| |
| for(; bptr >= bstr; bptr--){ |
| *bptr = (uint32_t)(*data++) << 24; |
| *bptr |= (uint32_t)(*data++) << 16; |
| *bptr |= (uint32_t)(*data++) << 8; |
| *bptr |= (uint32_t)(*data++); |
| } |
| } |
| |
| static void bitstr_dump(ECIES_byte_t *data, ECIES_size_t len, const bitstr_t bstr){ |
| const uint32_t *bptr = bstr + ((len + 3) / 4) - 1; |
| |
| len %= 4; |
| |
| if(len > 0){ |
| if(len > 1){ |
| if(len > 2){ |
| *data++ = *bptr >> 16; |
| } |
| *data++ = *bptr >> 8; |
| } |
| *data++ = *bptr; |
| bptr--; |
| } |
| |
| for(; bptr >= bstr; bptr--){ |
| *data++ = *bptr >> 24; |
| *data++ = *bptr >> 16; |
| *data++ = *bptr >> 8; |
| *data++ = *bptr; |
| } |
| } |
| |
| /* (raw) import from a byte array */ |
| static void bitstr_import(bitstr_t x, const ECIES_byte_t *s) |
| { |
| int i; |
| for(x += ECIES_NUMWORDS, i = 0; i < ECIES_NUMWORDS; i++, s += 4) |
| *--x = CHARS2INT(s); |
| } |
| |
| /* (raw) export to a byte array */ |
| static void bitstr_export(ECIES_byte_t *s, const bitstr_t x) |
| { |
| int i; |
| for(x += ECIES_NUMWORDS, i = 0; i < ECIES_NUMWORDS; i++, s += 4) |
| INT2CHARS(s, *--x); |
| } |
| |
| /* this type will represent field elements */ |
| typedef bitstr_t elem_t; |
| |
| /* the reduction polynomial */ |
| static const elem_t poly = { ECIES_POLY }; |
| |
| #define field_set1(A) MACRO( A[0] = 1; memset(A + 1, 0, sizeof(elem_t) - 4) ) |
| |
| int field_is1(const elem_t x) |
| { |
| int i; |
| if (*x++ != 1) return 0; |
| for(i = 1; i < ECIES_NUMWORDS && ! *x++; i++); |
| return i == ECIES_NUMWORDS; |
| } |
| |
| /* field addition */ |
| static void field_add(elem_t z, const elem_t x, const elem_t y) |
| { |
| int i; |
| for(i = 0; i < ECIES_NUMWORDS; i++) |
| *z++ = *x++ ^ *y++; |
| } |
| |
| #define field_add1(A) MACRO( A[0] ^= 1 ) |
| |
| /* field multiplication */ |
| static void field_mult(elem_t z, const elem_t x, const elem_t y) |
| { |
| elem_t b; |
| int i, j; |
| /* assert(z != y); */ |
| bitstr_copy(b, x); |
| if (bitstr_getbit(y, 0)) |
| bitstr_copy(z, x); |
| else |
| bitstr_clear(z); |
| for(i = 1; i < ECIES_DEGREE; i++) { |
| for(j = ECIES_NUMWORDS - 1; j > 0; j--) |
| b[j] = (b[j] << 1) | (b[j - 1] >> 31); |
| b[0] <<= 1; |
| if (bitstr_getbit(b, ECIES_DEGREE)) |
| field_add(b, b, poly); |
| if (bitstr_getbit(y, i)) |
| field_add(z, z, b); |
| } |
| } |
| |
| /* field inversion */ |
| static void field_invert(elem_t z, const elem_t x) |
| { |
| //fprintf(stderr, "function: field_invert(%p, %p)\n", z, x); |
| //debug_print_bitstr(x); |
| elem_t u, v, g, h; |
| int i; |
| bitstr_copy(u, x); |
| bitstr_copy(v, poly); |
| bitstr_clear(g); |
| field_set1(z); |
| //fprintf(stderr, "%d, %d\n", bitstr_sizeinbits(u), bitstr_sizeinbits(v)); |
| while (! field_is1(u)) { |
| //fprintf(stderr, "u[0] = %d\n", (int)u[0]); |
| i = bitstr_sizeinbits(u) - bitstr_sizeinbits(v); |
| //fprintf(stderr, "field_invert: i = %d\n", i); |
| if (i < 0) { |
| bitstr_swap(u, v); bitstr_swap(g, z); i = -i; |
| } |
| bitstr_lshift(h, v, i); |
| //debug_print_bitstr(h); |
| field_add(u, u, h); |
| bitstr_lshift(h, g, i); |
| field_add(z, z, h); |
| } |
| } |
| |
| /* The following routines do the ECC arithmetic. Elliptic curve points |
| are represented by pairs (x,y) of elem_t. It is assumed that curve |
| coefficient 'a' is equal to 1 (this is the case for all NIST binary |
| curves). Coefficient 'b' is given in 'coeff_b'. '(base_x, base_y)' |
| is a point that generates a large prime order group. */ |
| |
| static const elem_t coeff_b = { ECIES_COEFF_B }, base_x = { ECIES_BASE_X }, base_y = { ECIES_BASE_Y }; |
| |
| #define point_is_zero(x, y) (bitstr_is_clear(x) && bitstr_is_clear(y)) |
| #define point_set_zero(x, y) MACRO( bitstr_clear(x); bitstr_clear(y) ) |
| #define point_copy(x1, y1, x2, y2) MACRO( bitstr_copy(x1, x2); \ |
| bitstr_copy(y1, y2) ) |
| |
| /* check if y^2 + x*y = x^3 + *x^2 + coeff_b holds */ |
| static int is_point_on_curve(const elem_t x, const elem_t y) |
| { |
| elem_t a, b; |
| if (point_is_zero(x, y)) |
| return 1; |
| field_mult(a, x, x); |
| field_mult(b, a, x); |
| field_add(a, a, b); |
| field_add(a, a, coeff_b); |
| field_mult(b, y, y); |
| field_add(a, a, b); |
| field_mult(b, x, y); |
| return bitstr_is_equal(a, b); |
| } |
| |
| /* double the point (x,y) */ |
| static void point_double(elem_t x, elem_t y) |
| { |
| //fprintf(stderr, "function: point_double(%p, %p)\n", x, y); |
| //debug_print_bitstr(x); |
| if (! bitstr_is_clear(x)) { |
| elem_t a; |
| field_invert(a, x); |
| field_mult(a, a, y); |
| field_add(a, a, x); |
| field_mult(y, x, x); |
| field_mult(x, a, a); |
| field_add1(a); |
| field_add(x, x, a); |
| field_mult(a, a, x); |
| field_add(y, y, a); |
| } |
| else |
| bitstr_clear(y); |
| } |
| |
| /* add two points together (x1, y1) := (x1, y1) + (x2, y2) */ |
| static void point_add(elem_t x1, elem_t y1, const elem_t x2, const elem_t y2) |
| { |
| if (! point_is_zero(x2, y2)) { |
| if (point_is_zero(x1, y1)) |
| point_copy(x1, y1, x2, y2); |
| else { |
| if (bitstr_is_equal(x1, x2)) { |
| if (bitstr_is_equal(y1, y2)) |
| point_double(x1, y1); |
| else |
| point_set_zero(x1, y1); |
| } |
| else { |
| elem_t a, b, c, d; |
| field_add(a, y1, y2); |
| field_add(b, x1, x2); |
| field_invert(c, b); |
| field_mult(c, c, a); |
| field_mult(d, c, c); |
| field_add(d, d, c); |
| field_add(d, d, b); |
| field_add1(d); |
| field_add(x1, x1, d); |
| field_mult(a, x1, c); |
| field_add(a, a, d); |
| field_add(y1, y1, a); |
| bitstr_copy(x1, d); |
| } |
| } |
| } |
| } |
| |
| typedef bitstr_t exp_t; |
| |
| static const exp_t base_order = { ECIES_BASE_ORDER }; |
| |
| /* point multiplication via double-and-add algorithm */ |
| static void point_mult(elem_t x, elem_t y, const exp_t exp) |
| { |
| elem_t X, Y; |
| int i; |
| point_set_zero(X, Y); |
| for(i = bitstr_sizeinbits(exp) - 1; i >= 0; i--) { |
| point_double(X, Y); |
| if (bitstr_getbit(exp, i)) |
| point_add(X, Y, x, y); |
| } |
| point_copy(x, y, X, Y); |
| } |
| |
| #if RAND_MAX >= ((1 << 32) - 1) /* 4 random bytes */ |
| #define RAND_BYTES 4 |
| #elif RAND_MAX >= ((1 << 24) - 1) /* 3 random bytes */ |
| #define RAND_BYTES 3 |
| #elif RAND_MAX >= ((1 << 16) - 1) /* 2 random bytes */ |
| #define RAND_BYTES 2 |
| #elif RAND_MAX >= ((1 << 8) - 1) /* 1 random byte */ |
| #define RAND_BYTES 1 |
| #else |
| #error "RAND_MAX too small!" |
| #endif |
| |
| static void random_sync_read(int fd, void *buffer, int count) { |
| char *p = buffer; |
| do { |
| int s = read(fd, p, count); |
| if(s < 0) { |
| //perror("read"); |
| perror(RANDOM_CHAR_DEV); |
| exit(1); |
| } |
| if(!s) { |
| fprintf(stderr, "error: " RANDOM_CHAR_DEV " EOF\n"); |
| exit(1); |
| } |
| count -= s; |
| p += s; |
| } while(count > 0); |
| } |
| |
| #ifdef RANDOM_DEV_RANDOM_TIMEOUT |
| static void random_read(void *buffer, int count) { |
| int fd = open("/dev/random", O_RDONLY); |
| if(fd == -1) { |
| fd = open("/dev/urandom", O_RDONLY); |
| if(fd == -1) { |
| perror("/dev/urandom"); |
| exit(1); |
| } |
| return random_sync_read(fd, buffer, count); |
| } |
| char *p = buffer; |
| fd_set random_fd_set; |
| struct timeval tv = { .tv_sec = RANDOM_DEV_RANDOM_TIMEOUT }; |
| do { |
| FD_ZERO(&random_fd_set); |
| FD_SET(fd, &random_fd_set); |
| int n = select(fd + 1, &random_fd_set, NULL, NULL, &tv); |
| if(n < 0) { |
| perror("select"); |
| exit(1); |
| } |
| if(!n) { |
| fprintf(stderr, "/dev/random timed out after %ld sec\n", RANDOM_DEV_RANDOM_TIMEOUT - tv.tv_sec); |
| close(fd); |
| fd = open("/dev/urandom", O_RDONLY); |
| if(fd == -1) { |
| perror("/dev/urandom"); |
| exit(1); |
| } |
| return random_sync_read(fd, p, count); |
| } |
| int s = read(fd, p, count); |
| if(s < 0) { |
| perror("/dev/random"); |
| exit(1); |
| } |
| if(!s) { |
| fprintf(stderr, "error: /dev/random EOF\n"); |
| exit(1); |
| } |
| count -= s; |
| } while(count > 0); |
| } |
| #endif |
| |
| #if defined UPDATE_SEED_FROM_RANDOM_DEVICE && !defined NO_RANDOM_DEVICE |
| //static void update_seed() { |
| static unsigned int get_seed() { |
| unsigned int seed; |
| #ifdef RANDOM_DEV_RANDOM_TIMEOUT |
| random_read(&seed, sizeof seed); |
| #else |
| int fd = open(RANDOM_CHAR_DEV, O_RDONLY); |
| if(fd == -1) { |
| perror(RANDOM_CHAR_DEV); |
| exit(1); |
| } |
| random_sync_read(fd, &seed, sizeof seed); |
| #endif |
| //srandom(seed); |
| return seed; |
| } |
| #endif |
| |
| /* draw a random value 'exp' with 1 <= exp < n */ |
| static void get_random_exponent(exp_t exp) |
| { |
| ECIES_byte_t buf[4 * ECIES_NUMWORDS]; |
| ECIES_byte_t *ptr = buf + 4 * ECIES_NUMWORDS - 1; |
| int r; |
| long int val; |
| |
| //#if (!defined NO_RANDOM_DEVICE || !defined RANDOM_DEV_RANDOM_TIMEOUT) && !defined UPDATE_SEED_FROM_RANDOM_DEVICE |
| #ifndef NO_RANDOM_DEVICE |
| #ifdef UPDATE_SEED_FROM_RANDOM_DEVICE |
| static unsigned int seed; |
| if(!seed) { |
| seed = get_seed(); |
| srandom(seed); |
| } |
| #elif !defined RANDOM_DEV_RANDOM_TIMEOUT |
| int fd = open(RANDOM_CHAR_DEV, O_RDONLY); |
| if(fd == -1) { |
| perror(RANDOM_CHAR_DEV); |
| exit(1); |
| } |
| #endif |
| #endif |
| do { |
| while(ptr >= buf + RAND_BYTES) { |
| #if defined NO_RANDOM_DEVICE || defined UPDATE_SEED_FROM_RANDOM_DEVICE |
| val = random(); |
| #elif defined RANDOM_DEV_RANDOM_TIMEOUT |
| random_read(&val, sizeof val); |
| #else |
| random_sync_read(fd, &val, sizeof val); |
| #endif |
| *ptr-- = val; |
| for(r = 1; r < RAND_BYTES; r++){ |
| val >>= 8; |
| *ptr-- = val; |
| } |
| } |
| if(ptr >= buf){ |
| #if defined NO_RANDOM_DEVICE || defined UPDATE_SEED_FROM_RANDOM_DEVICE |
| val = random(); |
| #elif defined RANDOM_DEV_RANDOM_TIMEOUT |
| random_read(&val, sizeof val); |
| #else |
| random_sync_read(fd, &val, sizeof val); |
| #endif |
| while(ptr >= buf) { |
| *ptr-- = val; |
| val >>= 8; |
| } |
| } |
| bitstr_import(exp, buf); |
| for(r = bitstr_sizeinbits(base_order) - 1; r < ECIES_NUMWORDS * 32; r++) |
| bitstr_clrbit(exp, r); |
| } while(bitstr_is_clear(exp)); |
| #if !defined NO_RANDOM_DEVICE && !defined UPDATE_SEED_FROM_RANDOM_DEVICE |
| close(fd); |
| #endif |
| } |
| |
| //typedef void (*crypt_key_convert_func_t)(void *, const ECIES_byte_t *); |
| |
| static void XTEA_init_key(uint32_t *k, const ECIES_byte_t *key) |
| { |
| k[0] = CHARS2INT(key + 0); k[1] = CHARS2INT(key + 4); |
| k[2] = CHARS2INT(key + 8); k[3] = CHARS2INT(key + 12); |
| } |
| |
| static unsigned int XTEA_key_bytes() { |
| return 16; |
| } |
| /* the XTEA block cipher */ |
| static void XTEA_encipher_block(ECIES_byte_t *data, const uint32_t *k) |
| { |
| //fprintf(stderr, "function: XTEA_encipher_block(%p, %p)\n", data, k); |
| uint32_t sum = 0, delta = 0x9e3779b9, y, z; |
| int i; |
| y = CHARS2INT(data); z = CHARS2INT(data + 4); |
| //fprintf(stderr, "XTEA_encipher_block: delta = %u\n", (unsigned int)delta); |
| //fprintf(stderr, "XTEA_encipher_block: y = %u, z = %u\n", y, z); |
| for(i = 0; i < 32; i++) { |
| y += ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum & 3]); |
| //fprintf(stderr, "XTEA_encipher_block: y = %u\n", (unsigned int)y); |
| sum += delta; |
| //fprintf(stderr, "XTEA_encipher_block: sum = %u\n", (unsigned int)sum); |
| z += ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum >> 11 & 3]); |
| //fprintf(stderr, "XTEA_encipher_block: z = %u\n", (unsigned int)z); |
| } |
| INT2CHARS(data, y); INT2CHARS(data + 4, z); |
| } |
| |
| /* encrypt in CTR mode */ |
| static void XTEA_ctr_crypt(ECIES_byte_t *data, ECIES_size_t size, const ECIES_byte_t *key) |
| { |
| uint32_t k[4], ctr = 0; |
| ECIES_size_t len, i; |
| ECIES_byte_t buf[8]; |
| XTEA_init_key(k, key); |
| while(size) { |
| INT2CHARS(buf, 0); INT2CHARS(buf + 4, ctr++); |
| XTEA_encipher_block(buf, k); |
| len = MIN(8, size); |
| for(i = 0; i < len; i++) |
| *data++ ^= buf[i]; |
| size -= len; |
| } |
| } |
| |
| /* calculate the CBC MAC */ |
| static void XTEA_cbc_mac(ECIES_byte_t *mac, const ECIES_byte_t *data, ECIES_size_t size, const ECIES_byte_t *key) |
| { |
| uint32_t k[4]; |
| ECIES_size_t len, i; |
| XTEA_init_key(k, key); |
| INT2CHARS(mac, 0); |
| INT2CHARS(mac + 4, size); |
| XTEA_encipher_block(mac, k); |
| while(size) { |
| len = MIN(ECIES_CHUNK_OVERHEAD, size); |
| for(i = 0; i < len; i++) |
| mac[i] ^= *data++; |
| XTEA_encipher_block(mac, k); |
| size -= len; |
| } |
| } |
| |
| /* modified(!) Davies-Meyer construction.*/ |
| static void XTEA_davies_meyer(ECIES_byte_t *out, const ECIES_byte_t *in, int ilen) |
| { |
| //fprintf(stderr, "function: XTEA_davies_meyer(%p, %p, %d)\n", out, in, ilen); |
| uint32_t k[4]; |
| ECIES_byte_t buf[8]; |
| ECIES_size_t i; |
| memset(out, 0, 8); |
| while(ilen--) { |
| //fprintf(stderr, "XTEA_davies_meyer: ilen = %d\n", ilen); |
| XTEA_init_key(k, in); |
| //debug_print_integers(k, 4); |
| //fputc('\n', stderr); |
| memcpy(buf, out, 8); |
| XTEA_encipher_block(buf, k); |
| //debug_print_bytes(buf, 8); |
| for(i = 0; i < 8; i++) |
| out[i] ^= buf[i]; |
| in += 16; |
| } |
| } |
| |
| static key_bytes_func_t current_key_bytes = XTEA_key_bytes; |
| static ctr_crypt_func_t current_ctr_crypt = XTEA_ctr_crypt; |
| static cbc_mac_func_t current_cbc_mac = XTEA_cbc_mac; |
| static davies_meyer_func_t current_davies_meyer = XTEA_davies_meyer; |
| static ECIES_byte_t *symmetric_crypt_nonce_location = NULL; |
| static const ECIES_size_t *symmetric_crypt_nonce_size_location = NULL; |
| |
| void ECIES_set_symmetric_crypt_functions(key_bytes_func_t key_bytes_func, ctr_crypt_func_t ctr_crypt_func, cbc_mac_func_t cbc_mac_func, davies_meyer_func_t davies_meyer_func) { |
| current_key_bytes = key_bytes_func; |
| current_ctr_crypt = ctr_crypt_func; |
| current_cbc_mac = cbc_mac_func; |
| current_davies_meyer = davies_meyer_func; |
| } |
| |
| void ECIES_set_symmetric_crypt_nonce_location(ECIES_byte_t *nonce, const ECIES_size_t *nonce_size) { |
| symmetric_crypt_nonce_location = nonce; |
| symmetric_crypt_nonce_size_location = nonce_size; |
| } |
| |
| /* generate a public/private key pair */ |
| void ECIES_generate_keys(ECIES_privkey_t *priv, |
| ECIES_pubkey_t *pub) |
| { |
| elem_t x, y; |
| exp_t k; |
| |
| get_random_exponent(k); |
| point_copy(x, y, base_x, base_y); |
| point_mult(x, y, k); |
| |
| bitstr_dump(pub->x, ECIES_KEY_SIZE, x); |
| bitstr_dump(pub->y, ECIES_KEY_SIZE, y); |
| bitstr_dump(priv->k, ECIES_KEY_SIZE, k); |
| } |
| |
| /* check that a given elem_t-pair is a valid point on the curve != 'o' */ |
| static int ECIES_intern_validate_pubkey(const elem_t Px, const elem_t Py) |
| { |
| //fprintf(stderr, "DEGREE = %d\nbitstr_sizeinbits(Px) = %d\nbitstr_sizeinbits(Py) = %d\n", |
| // ECIES_DEGREE, bitstr_sizeinbits(Px), bitstr_sizeinbits(Py)); |
| return (bitstr_sizeinbits(Px) > ECIES_DEGREE) || (bitstr_sizeinbits(Py) > ECIES_DEGREE) || |
| point_is_zero(Px, Py) || ! is_point_on_curve(Px, Py) ? -1 : 1; |
| } |
| |
| /* same thing, but check also that (Px,Py) generates a group of order n */ |
| int ECIES_validate_pubkey(const ECIES_pubkey_t *pubkey) |
| { |
| elem_t x, y; |
| |
| bitstr_load(x, pubkey->x, ECIES_KEY_SIZE); |
| bitstr_load(y, pubkey->y, ECIES_KEY_SIZE); |
| |
| if (ECIES_intern_validate_pubkey(x, y) < 0) |
| return -1; |
| |
| point_mult(x, y, base_order); |
| |
| return point_is_zero(x, y) ? 1 : -1; |
| } |
| |
| /* a non-standard KDF */ |
| static void ECIES_kdf(ECIES_byte_t *k1, ECIES_byte_t *k2, const elem_t Zx, |
| const elem_t Rx, const elem_t Ry) |
| { |
| //fprintf(stderr, "function: ECIES_kdf(%p, %p, %p, %p, %p)\n", k1, k2, Zx, Rx, Ry); |
| unsigned int key_bytes = current_key_bytes(); |
| ECIES_size_t bufsize = (3 * (4 * ECIES_NUMWORDS) + 1 + key_bytes - 1) & ~(key_bytes - 1); |
| ECIES_byte_t buf[bufsize]; |
| memset(buf, 0, bufsize); |
| bitstr_export(buf, Zx); |
| bitstr_export(buf + 4 * ECIES_NUMWORDS, Rx); |
| bitstr_export(buf + 8 * ECIES_NUMWORDS, Ry); |
| buf[12 * ECIES_NUMWORDS] = 0; current_davies_meyer(k1, buf, bufsize / key_bytes); |
| //debug_print_bytes(k1, 8); |
| buf[12 * ECIES_NUMWORDS] = 1; current_davies_meyer(k1 + key_bytes / 2, buf, bufsize / key_bytes); |
| buf[12 * ECIES_NUMWORDS] = 2; current_davies_meyer(k2, buf, bufsize / key_bytes); |
| buf[12 * ECIES_NUMWORDS] = 3; current_davies_meyer(k2 + key_bytes / 2, buf, bufsize / key_bytes); |
| } |
| |
| void ECIES_encrypt(ECIES_byte_t *msg, const char *raw, ECIES_size_t len, const ECIES_pubkey_t *pubkey){ |
| ECIES_stream_t stm; |
| |
| ECIES_encrypt_start(&stm, msg, pubkey); |
| |
| memcpy(msg + ECIES_START_OVERHEAD, raw, len); |
| |
| ECIES_encrypt_chunk(&stm, msg + ECIES_START_OVERHEAD, len); |
| } |
| |
| int ECIES_decrypt(char *raw, ECIES_size_t len, const ECIES_byte_t *msg, const ECIES_privkey_t *privkey){ |
| int res; |
| ECIES_stream_t stm; |
| ECIES_byte_t mac[ECIES_CHUNK_OVERHEAD]; |
| |
| if((res = ECIES_decrypt_start(&stm, msg, privkey)) < 0){ |
| return res; |
| } |
| |
| current_cbc_mac(mac, msg + ECIES_START_OVERHEAD, len, stm.k2); |
| |
| if(memcmp(mac, msg + ECIES_START_OVERHEAD + len, ECIES_CHUNK_OVERHEAD)){ |
| return -2; |
| } |
| |
| memcpy(raw, msg + ECIES_START_OVERHEAD, len); |
| |
| current_ctr_crypt((ECIES_byte_t*)raw, len, stm.k1); |
| |
| return 1; |
| } |
| |
| void ECIES_encrypt_start(ECIES_stream_t *stm, ECIES_byte_t *msg, const ECIES_pubkey_t *pubkey) |
| { |
| elem_t Rx, Ry, Zx, Zy; |
| exp_t k; |
| |
| do { |
| get_random_exponent(k); |
| bitstr_load(Zx, pubkey->x, ECIES_KEY_SIZE); |
| bitstr_load(Zy, pubkey->y, ECIES_KEY_SIZE); |
| point_mult(Zx, Zy, k); |
| point_double(Zx, Zy); /* cofactor h = 2 on B163 */ |
| } while(point_is_zero(Zx, Zy)); |
| point_copy(Rx, Ry, base_x, base_y); |
| point_mult(Rx, Ry, k); |
| |
| if(symmetric_crypt_nonce_location && symmetric_crypt_nonce_size_location) { |
| bitstr_dump(symmetric_crypt_nonce_location, *symmetric_crypt_nonce_size_location, Ry); |
| } |
| |
| stm->k1 = malloc(current_key_bytes()); |
| stm->k2 = malloc(current_key_bytes()); |
| ECIES_kdf(stm->k1, stm->k2, Zx, Rx, Ry); |
| |
| bitstr_export(msg, Rx); |
| bitstr_export(msg + 4 * ECIES_NUMWORDS, Ry); |
| } |
| |
| void ECIES_encrypt_chunk(const ECIES_stream_t *stm, ECIES_byte_t *msg, ECIES_size_t len) |
| { |
| current_ctr_crypt(msg, len, stm->k1); |
| current_cbc_mac(msg + len, msg, len, stm->k2); |
| } |
| |
| /* ECIES decryption */ |
| int ECIES_decrypt_start(ECIES_stream_t *stm, const ECIES_byte_t *msg, const ECIES_privkey_t *privkey) |
| { |
| //fprintf(stderr, "function: ECIES_decrypt_start(%p, %p, %p)\n", stm, msg, privkey); |
| elem_t Rx, Ry, Zx, Zy; |
| exp_t d; |
| |
| bitstr_import(Rx, msg); |
| bitstr_import(Ry, msg + 4 * ECIES_NUMWORDS); |
| if (ECIES_intern_validate_pubkey(Rx, Ry) < 0) return -1; |
| |
| if(symmetric_crypt_nonce_location && symmetric_crypt_nonce_size_location) { |
| bitstr_dump(symmetric_crypt_nonce_location, *symmetric_crypt_nonce_size_location, Ry); |
| } |
| |
| bitstr_load(d, privkey->k, ECIES_KEY_SIZE); |
| point_copy(Zx, Zy, Rx, Ry); |
| point_mult(Zx, Zy, d); |
| point_double(Zx, Zy); /* cofactor h = 2 on B163 */ |
| |
| if (point_is_zero(Zx, Zy)) return -1; |
| |
| stm->k1 = malloc(current_key_bytes()); |
| stm->k2 = malloc(current_key_bytes()); |
| ECIES_kdf(stm->k1, stm->k2, Zx, Rx, Ry); |
| |
| return 1; |
| } |
| |
| int ECIES_decrypt_chunk(const ECIES_stream_t *stm, ECIES_byte_t *msg, ECIES_size_t len) |
| { |
| //fprintf(stderr, "function: ECIES_decrypt_chunk(%p, %p, %u)\n", stm, msg, (unsigned int)len); |
| ECIES_byte_t mac[ECIES_CHUNK_OVERHEAD]; |
| |
| current_cbc_mac(mac, msg, len, stm->k2); |
| if (memcmp(mac, msg + len, ECIES_CHUNK_OVERHEAD)) |
| return -2; |
| |
| current_ctr_crypt(msg, len, stm->k1); |
| |
| return 1; |
| } |
| |
| void ECIES_free_stream(ECIES_stream_t *stm) { |
| free(stm->k1); |
| free(stm->k2); |
| stm->k1 = NULL; |
| stm->k2 = NULL; |
| } |