Last active
February 14, 2019 05:11
-
-
Save devendranaga/98a940ee18e347d0acfd9fe5f453ec30 to your computer and use it in GitHub Desktop.
ECC keygen / sign and verify in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <openssl/evp.h> | |
#include <openssl/err.h> | |
#include <openssl/ecdsa.h> | |
#include <openssl/ec.h> | |
#include <openssl/conf.h> | |
#include <openssl/rand.h> | |
#include <openssl/pem.h> | |
#include <openssl/sha.h> | |
#include <openssl/md5.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdint.h> | |
class ecc_base { | |
public: | |
ecc_base() { | |
evp_sign_key = nullptr; | |
evp_verify_key = nullptr; | |
signature_len = sizeof(signature); | |
ERR_load_crypto_strings(); | |
OpenSSL_add_all_algorithms(); | |
OPENSSL_config(NULL); | |
RAND_poll(); | |
} | |
~ecc_base() { | |
if (evp_sign_key) | |
EVP_PKEY_free(evp_sign_key); | |
if (evp_verify_key) | |
EVP_PKEY_free(evp_verify_key); | |
EVP_cleanup(); | |
CRYPTO_cleanup_all_ex_data(); | |
ERR_free_strings(); | |
} | |
// loads in the pubkey | |
int load_pubkey(std::string pubkey) | |
{ | |
FILE *fp; | |
// load in the keys | |
fp = fopen(pubkey.c_str(), "r"); | |
if (!fp) { | |
return -1; | |
} | |
publickey = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL); | |
if (!publickey) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
evp_verify_key = EVP_PKEY_new(); | |
int ret; | |
ret = EVP_PKEY_assign_EC_KEY(evp_verify_key, publickey); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
fclose(fp); | |
std::cout << "pubkey load ok" << std::endl; | |
return 0; | |
} | |
int load_privkey(std::string privkey) | |
{ | |
FILE *fp; | |
fp = fopen(privkey.c_str(), "r"); | |
if (!fp) { | |
return -1; | |
} | |
privatekey = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL); | |
if (!privatekey) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
// validate the key | |
EC_KEY_check_key(privatekey); | |
evp_sign_key = EVP_PKEY_new(); | |
int ret; | |
ret = EVP_PKEY_assign_EC_KEY(evp_sign_key, privatekey); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
fclose(fp); | |
std::cout << "privkey load ok" << std::endl; | |
return 0; | |
} | |
int generate_keys(std::string pubkeyfile, std::string privkeyfile, std::string curve_name) | |
{ | |
EC_KEY *keygen; | |
int nid = to_nid(curve_name); | |
if (nid == -1) { | |
return -1; | |
} | |
// get curve name | |
keygen = EC_KEY_new_by_curve_name(nid); | |
if (!keygen) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
int ret; | |
// run the key generation .. we aren't doing the curve parameters | |
ret = EC_KEY_generate_key(keygen); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
ret = EC_KEY_check_key(keygen); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
// wirte the keys | |
FILE *fp; | |
fp = fopen(pubkeyfile.c_str(), "w"); | |
if (!fp) { | |
return -1; | |
} | |
PEM_write_EC_PUBKEY(fp, keygen); | |
fclose(fp); | |
fp = fopen(privkeyfile.c_str(), "w"); | |
if (!fp) { | |
return -1; | |
} | |
PEM_write_ECPrivateKey(fp, keygen, NULL, NULL, 0, NULL, NULL); | |
fclose(fp); | |
EC_KEY_free(keygen); | |
std::cout << "keygen success" << std::endl; | |
return 0; | |
} | |
int sign(uint8_t *msg, size_t msglen, std::string sha_alg) | |
{ | |
if (!evp_sign_key || !privatekey) { | |
std::cerr << "invalid sign key or private key is not loaded" << std::endl; | |
return -1; | |
} | |
const EVP_MD *md; | |
// mark the sha alg to use | |
if (sha_alg == "sha256") { | |
md = EVP_sha256(); | |
} else if (sha_alg == "sha1") { | |
md = EVP_sha1(); | |
} else { | |
return -1; | |
} | |
int ret; | |
EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); | |
ret = EVP_DigestSignInit(mdctx, NULL, md, NULL, evp_sign_key); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
ret = EVP_DigestSignUpdate(mdctx, msg, msglen); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
ret = EVP_DigestSignFinal(mdctx, signature, &signature_len); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
EVP_MD_CTX_destroy(mdctx); | |
std::cout << "signature generated : " << signature_len << " bytes" << std::endl; | |
return 0; | |
} | |
uint8_t *get_signature() | |
{ | |
return signature; | |
} | |
size_t get_signature_len() | |
{ | |
return signature_len; | |
} | |
int verify(uint8_t *msg, size_t msglen, uint8_t *signature, size_t signature_len, std::string sha_alg) | |
{ | |
if (!msg || !signature) { | |
std::cerr << "invalid msg or signature" << std::endl; | |
return -1; | |
} | |
const EVP_MD *md; | |
if (sha_alg == "sha256") { | |
md = EVP_sha256(); | |
} else if (sha_alg == "sha1") { | |
md = EVP_sha1(); | |
} else { | |
return -1; | |
} | |
int ret; | |
EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); | |
ret = EVP_DigestVerifyInit(mdctx, NULL, md, NULL, evp_verify_key); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
ret = EVP_DigestVerifyUpdate(mdctx, msg, msglen); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
ret = EVP_DigestVerifyFinal(mdctx, signature, signature_len); | |
if (ret != 1) { | |
ERR_print_errors_fp(stderr); | |
return -1; | |
} | |
EVP_MD_CTX_destroy(mdctx); | |
std::cout << "verify ok" << std::endl; | |
return 0; | |
} | |
void dump_signature() | |
{ | |
size_t i; | |
for (i = 0; i < signature_len; i ++) { | |
if (i != 0) { | |
if (i % 16 == 0) { | |
printf("\n"); | |
} else { | |
printf("::"); | |
} | |
} | |
printf("%02x", signature[i]); | |
} | |
printf("\n"); | |
} | |
private: | |
int to_nid(std::string curvename) | |
{ | |
if (curvename == "secp256k1") { | |
return NID_secp256k1; | |
} else if (curvename == "brainpool256r1") { | |
return NID_brainpoolP256r1; | |
} | |
return -1; | |
} | |
uint8_t signature[256]; | |
size_t signature_len; | |
EC_KEY *publickey; | |
EC_KEY *privatekey; | |
EVP_PKEY *evp_sign_key; | |
EVP_PKEY *evp_verify_key; | |
}; | |
// validate the class implementation | |
// | |
int main(int argc, char **argv) | |
{ | |
ecc_base ec; | |
int ret; | |
std::string msg = "try this to sign for ec "; | |
std::string curvename = "brainpool256r1"; | |
std::string pubkey = "./ec_brainpool_256r1.pub"; | |
std::string pkey = "./ec_brainpool_256r1.pkey"; | |
if (std::string(argv[1]) == "genkey") { | |
ret = ec.generate_keys(pubkey.c_str(), pkey.c_str(), curvename.c_str()); | |
if (ret != 0) { | |
std::cerr << "failure generating keys" << std::endl; | |
return -1; | |
} | |
} else if (std::string(argv[1]) == "sign_verify") { | |
ret = ec.load_pubkey(pubkey.c_str()); | |
if (ret != 0) { | |
std::cerr << "pubkey didn't load " << std::endl; | |
return -1; | |
} | |
ret = ec.load_privkey(pkey.c_str()); | |
if (ret != 0) { | |
std::cerr << "privkey didn't load" << std::endl; | |
return -1; | |
} | |
ret = ec.sign((uint8_t *)(msg.c_str()), msg.length(), "sha256"); | |
if (ret != 0) { | |
std::cerr << "failure to sign message" << std::endl; | |
return -1; | |
} | |
ec.dump_signature(); | |
ret = ec.verify((uint8_t *)(msg.c_str()), msg.length(), ec.get_signature(), ec.get_signature_len(), "sha256"); | |
if (ret != 0) { | |
std::cerr << "failed to verify message" << std::endl; | |
return -1; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment