Skip to content

Instantly share code, notes, and snippets.

@devendranaga
Last active February 14, 2019 05:11
Show Gist options
  • Save devendranaga/98a940ee18e347d0acfd9fe5f453ec30 to your computer and use it in GitHub Desktop.
Save devendranaga/98a940ee18e347d0acfd9fe5f453ec30 to your computer and use it in GitHub Desktop.
ECC keygen / sign and verify in C++
#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