/********************************************************************* ###################################################################### ## ## Created by: Denis Filatov ## ## Copyleft (c) 2015 ## This code is provided under the CeCill-C license agreement. ###################################################################### *********************************************************************/ #define _CRT_SECURE_NO_WARNINGS #include "ecc_api.h" #include #include #include #include #include #include #include #define FIELD_SIZE 32 #define ARRAYSIZE(A) (sizeof(A)/sizeof(A[0])) static char * _bin2hex(char * hex, int hlen, const char * bin, int blen); typedef struct { int nid; const char * name; const char * p; const char * a; const char * b; const char * x; const char * y; const char * z; const char * seed; }ecc_api_curve_data; static ecc_api_curve_data _config[] = { { NID_X9_62_prime256v1, "P-256", NULL, }, { NID_X9_62_prime256v1, "P-256", NULL, }, }; static EC_GROUP * _curves[ARRAYSIZE(_config)] = { NULL }; static EC_GROUP* _ecc_curve_by_data(const ecc_api_curve_data * d) { BIGNUM *p, *a, *b, *x, *y, *z; EC_GROUP *group; EC_POINT *P; if (d->nid){ return EC_GROUP_new_by_curve_name(d->nid); } x = BN_new(); y = BN_new(); z = BN_new(); p = BN_new(); a = BN_new(); b = BN_new(); x = BN_new(); y = BN_new(); z = BN_new(); BN_hex2bn(&p, d->p); BN_hex2bn(&a, d->a); BN_hex2bn(&b, d->b); group = EC_GROUP_new_curve_GFp(p, a, b, NULL); EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); BN_hex2bn(&x, d->x); BN_hex2bn(&z, d->z); P = EC_POINT_new(group); EC_POINT_set_compressed_coordinates_GFp(group, P, x, 1, NULL); if (EC_POINT_is_on_curve(group, P, NULL)){ EC_GROUP_set_generator(group, P, z, BN_value_one()); } EC_POINT_free(P); BN_clear_free(p); BN_clear_free(a); BN_clear_free(b); BN_clear_free(x); BN_clear_free(y); BN_clear_free(z); return group; } int ecc_api_init() { int i; for (i = 0; i < ARRAYSIZE(_config); i++){ _curves[i] = _ecc_curve_by_data(&_config[i]); } return 0; } int ecc_api_done() { int i; for (i = 0; i < ARRAYSIZE(_config); i++){ EC_GROUP_free(_curves[i]); } return 0; } void * ecc_api_key_gen(ecc_pk_algorithm pk_alg, ecc_sym_algorithm sym_alg) { EC_KEY * key = NULL; if (_curves[pk_alg]){ key = EC_KEY_new(); if (key){ EC_KEY_set_group(key, _curves[pk_alg]); EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); if (!EC_KEY_generate_key(key)){ ERR_print_errors_fp(stderr); fflush(stderr); EC_KEY_free(key); key = NULL; } } } return key; } void * ecc_api_key_init(ecc_pk_algorithm pk_alg, ecc_sym_algorithm sym_alg, const char* pkey) { EC_KEY * eckey = NULL; BIGNUM * bn = BN_new(); const EC_GROUP * group = _curves[pk_alg]; if (group){ if (BN_bin2bn((const unsigned char*)pkey, 32, bn)){ eckey = EC_KEY_new(); if (eckey){ if (EC_KEY_set_group(eckey, group)){ if (EC_KEY_set_private_key(eckey, bn)){ EC_POINT * point; point = EC_POINT_new(group); if (EC_POINT_mul(group, point, bn, NULL, NULL, NULL)){ EC_KEY_set_public_key(eckey, point); } EC_POINT_free(point); } } } } } BN_free(bn); return (void*)eckey; } void ecc_api_key_free(void* key) { EC_KEY_free(key); } int ecc_api_key_private(void* key, char* buf) { int len = -1; if (key){ const BIGNUM * bn; bn = EC_KEY_get0_private_key(key); if (bn){ len = BN_num_bytes(bn); if (buf){ BN_bn2bin(bn, (unsigned char*)buf); } } } return len; } int ecc_api_key_public(void* key, char * px, char * py) { const EC_GROUP * ecgroup; const EC_POINT * ecpoint; const EC_KEY * eckey = (EC_KEY*)key; BIGNUM x, y; int bcount = -1; if( key && px && py ) { ecgroup = EC_KEY_get0_group(eckey); ecpoint = EC_KEY_get0_public_key(eckey); //fill public key data BN_init(&x); BN_init(&y); if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint, &x, &y, NULL)){ bcount = BN_num_bytes(&x); for(; bcount < FIELD_SIZE; bcount++) *(px++) = 0; // add padding with zeros BN_bn2bin(&x, (unsigned char*)px); bcount = BN_num_bytes(&y); for(; bcount < FIELD_SIZE; bcount++) *(py++) = 0; // add padding with zeros BN_bn2bin(&y, (unsigned char*)py); } BN_clear_free(&x); BN_clear_free(&y); } return bcount; } static int _pass_cb(char *buf, int size, int rwflag, void *u) { fprintf(stderr, "Ask for a pass phrase"); return 0; } int ecc_api_key_private_save(void* key, const char* path, ecc_format format) { int rc = -1; EC_KEY * eckey = (EC_KEY *)key; if (eckey){ FILE * f = fopen(path, "wb"); if (f){ if (format == ecc_pem){ rc = PEM_write_ECPrivateKey(f, eckey, NULL, NULL, 0, _pass_cb, NULL) ? 0 : -1; if (rc < 0){ ERR_print_errors_fp(stderr); } } else{ const BIGNUM * ecbn; ecbn = EC_KEY_get0_private_key(eckey); if (ecbn){ int bnlen = BN_num_bytes(ecbn); int len = (bnlen < 32) ? 32 : bnlen; char * buf = (char *)OPENSSL_malloc(len*2+1); if (bnlen < len) memset(buf, 0, len - bnlen); BN_bn2bin(ecbn, (unsigned char *)(buf + len - bnlen)); if (format == ecc_hex){ char * c = _bin2hex(buf, len * 2 + 1, buf, len); *c = 0; len = c - buf; rc = 0; } rc = (len == fwrite(buf, 1, len, f)) ? 0 : -1; OPENSSL_free(buf); } } fclose(f); if (rc < 0){ ERR_print_errors_fp(stderr); remove(path); rc = -1; } } else{ perror(path); } } return rc; } void * ecc_api_key_private_load(const char* path, ecc_pk_algorithm pk_alg) { EC_KEY * eckey = NULL; FILE * f = fopen(path, "rb"); if (f){ eckey = PEM_read_ECPrivateKey(f, NULL, NULL, NULL); if (eckey == NULL){ BIGNUM * bn = NULL; fseek(f, 0, SEEK_END); int len = ftell(f); fseek(f, 0, SEEK_SET); char * buf = OPENSSL_malloc(len + 1); if (len == fread(buf, 1, len, f)){ buf[len] = 0; // try hex first if (len != BN_hex2bn(&bn, buf)){ if (bn){ BN_free(bn); bn = NULL; } bn = BN_bin2bn((const unsigned char*)buf, len, NULL); } } OPENSSL_free(buf); if (bn){ eckey = EC_KEY_new(); if (eckey){ EC_KEY_set_group(eckey, _curves[pk_alg]); if (EC_KEY_set_private_key(eckey, bn)){ EC_POINT * point; const EC_GROUP * group; group = EC_KEY_get0_group(eckey); point = EC_POINT_new(group); if (EC_POINT_mul(group, point, bn, NULL, NULL, NULL)){ EC_KEY_set_public_key(eckey, point); } EC_POINT_free(point); } else{ EC_KEY_free(eckey); eckey = NULL; } } BN_free(bn); } } fclose(f); } return eckey; } int ecc_api_key_public_save(void* key, const char* path, ecc_format format) { EC_KEY * eckey = (EC_KEY *)key; int rc = -1; if (eckey){ FILE * f = fopen(path, "wb"); if (f){ if (format == ecc_pem){ rc = PEM_write_EC_PUBKEY(f, eckey) ? 0 : -1; } else{ size_t len; char * buf = NULL; const EC_POINT * point = EC_KEY_get0_public_key(eckey); const EC_GROUP * group = EC_KEY_get0_group(eckey); if (format == ecc_hex){ buf = EC_POINT_point2hex(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL); len = strlen(buf); } else if (format == ecc_bin){ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); if (len > 0){ buf = OPENSSL_malloc(len + 1); if (len != EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)buf, len, NULL)){ OPENSSL_free(buf); buf = NULL; } } } if (buf){ if (len == fwrite(buf, 1, len, f)){ rc = 0; } OPENSSL_free(buf); buf = NULL; } } fclose(f); if (rc < 0){ ERR_print_errors_fp(stderr); remove(path); } } else{ perror(path); } } return rc; } void * ecc_api_key_public_set(ecc_pk_algorithm pk_alg, ecc_point_type ptype, const char * px, const char * py) { EC_KEY * eckey = NULL; EC_POINT * point = NULL; const EC_GROUP * group; BIGNUM *x, *y = NULL; int rc = 0; group = _curves[0]; point = EC_POINT_new(group); x = BN_bin2bn((const unsigned char*)px, 32, NULL); if (x){ if (ptype == ecc_uncompressed){ y = BN_bin2bn((const unsigned char*)py, 32, NULL); if (y){ rc = EC_POINT_set_affine_coordinates_GFp(group, point, x, y, NULL); BN_clear_free(y); } } else { rc = EC_POINT_set_compressed_coordinates_GFp(group, point, x, ptype & 1, NULL); } BN_clear_free(x); if (rc){ eckey = EC_KEY_new(); if (eckey){ EC_KEY_set_group(eckey, group); EC_KEY_set_public_key(eckey, point); } } } EC_POINT_free(point); return eckey; } void * ecc_api_key_public_load(const char* path, ecc_pk_algorithm pk_alg) { EC_KEY * eckey = NULL; FILE * f = fopen(path, "rb"); EC_POINT * point = NULL; const EC_GROUP * group = _curves[0]; if (f){ eckey = PEM_read_EC_PUBKEY(f, &eckey, NULL, NULL); if (eckey == NULL){ fseek(f, 0, SEEK_END); int len = ftell(f); fseek(f, 0, SEEK_SET); char * buf = OPENSSL_malloc(len + 1); if (len == fread(buf, 1, len, f)){ buf[len] = 0; // try hex first point = EC_POINT_hex2point(group, buf, NULL, NULL); if (point == NULL){ // try oct point = EC_POINT_new(group); if (!EC_POINT_oct2point(group, point, (const unsigned char*)buf, len, NULL)){ EC_POINT_free(point); point = NULL; } } if (point){ eckey = EC_KEY_new(); if (eckey){ EC_KEY_set_group(eckey, group); EC_KEY_set_public_key(eckey, point); } EC_POINT_free(point); } } OPENSSL_free(buf); } fclose(f); } return eckey; } int sha256_calculate(char* hash, const char * ptr, int len) { SHA256_CTX ctx; SHA256_Init(&ctx); SHA256_Update(&ctx, ptr, len); SHA256_Final((unsigned char*)hash, &ctx); return 0; } int ecc_sign(void * key, const char * data, int length, char ** psig, int maxsize) { EC_KEY * eckey; unsigned char *sig = (unsigned char *)*psig; if (65 <= maxsize){ eckey = EC_KEY_dup((EC_KEY*)key); if(eckey){ unsigned char hash[32]; SHA256_CTX ctx; ECDSA_SIG * ecdsa; SHA256_Init(&ctx); SHA256_Update(&ctx, data, length); SHA256_Final(hash, &ctx); ecdsa = ECDSA_do_sign(hash, 32, eckey); EC_KEY_free(eckey); if (ecdsa){ int i, bcount; *(sig++) = 0; // ECC_POINT type (x_coordinate_only) bcount = BN_num_bytes(ecdsa->r); for(i=bcount; i < FIELD_SIZE; i++) *(sig++) = 0; // add padding with zeros BN_bn2bin(ecdsa->r, sig); sig += bcount; bcount = BN_num_bytes(ecdsa->s); for(i=bcount; i < FIELD_SIZE; i++) *(sig++) = 0; // add padding with zeros BN_bn2bin(ecdsa->s, sig); sig += bcount; ECDSA_SIG_free(ecdsa); *psig = (char *)sig; return 0; } } } return -1; } static const char* _hexDigits = "0123456789ABCDEF"; static char * _bin2hex(char * hex, int hlen, const char * bin, int blen) { const unsigned char *b, *e; char * s; // sanity check if (hlen >= 0 && hlen < blen * 2) return NULL; b = (const unsigned char *)bin; e = b + blen - 1; s = hex + blen * 2; if (s < hex + hlen) *s = 0; for (; b <= e; e--){ *(--s) = _hexDigits[(*e) & 0xF]; *(--s) = _hexDigits[(*e) >> 4]; } return hex + blen * 2; }