460 lines
12 KiB
C++
460 lines
12 KiB
C++
|
|
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved
|
||
|
|
//
|
||
|
|
// DRBG class for deterministic random bit generation and predictable RSA keys
|
||
|
|
|
||
|
|
#include <pthread.h>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
#include <openssl/ec.h>
|
||
|
|
#include <openssl/rsa.h>
|
||
|
|
#include <openssl/pem.h>
|
||
|
|
|
||
|
|
#include "eyeutils.h"
|
||
|
|
#include "eyelog.h"
|
||
|
|
#include "eyedrbg.h"
|
||
|
|
|
||
|
|
using namespace std;
|
||
|
|
using namespace sequencelogic;
|
||
|
|
|
||
|
|
pthread_mutex_t ionu_drbg_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||
|
|
|
||
|
|
//#define JAVASCRIPT 1 // set to enable drbg information to match with libeye.js
|
||
|
|
//#define DEBUG_RSA 1
|
||
|
|
#ifdef DEBUG_RSA
|
||
|
|
int ionuRSA_check_key(const RSA *key);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
EyeDRBG::EyeDRBG (const unsigned char* seed, size_t seedlen)
|
||
|
|
{
|
||
|
|
_data = new unsigned char[IONU_DRBG_BUFFER_LEN];
|
||
|
|
_ctr = (unsigned char) seedlen & 0x0ff;
|
||
|
|
if (_data) {
|
||
|
|
std::string mdhex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, seed, seedlen);
|
||
|
|
//cout << (char*)seed << " " << mdhex << endl;
|
||
|
|
sequencelogic::HexToBinary (mdhex.c_str(), &_data[IONU_DRBG_BUFFER_LEN - 32]);
|
||
|
|
FillBuffer();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeDRBG::~EyeDRBG ()
|
||
|
|
{
|
||
|
|
sequencelogic::MemSet (_data, 0, IONU_DRBG_BUFFER_LEN);
|
||
|
|
delete[] _data;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EyeDRBG::FillBuffer ()
|
||
|
|
{
|
||
|
|
_pos = 0;
|
||
|
|
size_t loc = IONU_DRBG_BUFFER_LEN - 32;
|
||
|
|
unsigned char hashbuf[33];
|
||
|
|
for (size_t i = 0; i < IONU_DRBG_BUFFER_LEN; i += 32) {
|
||
|
|
memcpy (hashbuf, &_data[loc], 32);
|
||
|
|
hashbuf[32] = _ctr++;
|
||
|
|
std::string mdhex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, hashbuf, 33);
|
||
|
|
#ifdef JAVASCRIPT
|
||
|
|
//cout << mdhex << endl;
|
||
|
|
#endif
|
||
|
|
sequencelogic::HexToBinary (mdhex.c_str(), &_data[i]);
|
||
|
|
loc = i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeDRBG::RandBytes (unsigned char* bytes, size_t len)
|
||
|
|
{
|
||
|
|
if (! _data)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Set mutex lock while getting bytes to keep it all thread safe
|
||
|
|
pthread_mutex_lock (&ionu_drbg_mutex);
|
||
|
|
|
||
|
|
size_t ba = IONU_DRBG_BUFFER_LEN - _pos;
|
||
|
|
|
||
|
|
// We have all the bytes needed
|
||
|
|
if (ba >= len) {
|
||
|
|
sequencelogic::MemCpy (bytes, &_data[_pos], len);
|
||
|
|
_pos += len;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// Copy any remaining bytes
|
||
|
|
if (ba > 0 ) {
|
||
|
|
sequencelogic::MemCpy (bytes, &_data[_pos], ba);
|
||
|
|
bytes += ba;
|
||
|
|
}
|
||
|
|
size_t fb = len - ba; // Calculate number of bytes still needed
|
||
|
|
// Loop until we've generated all the required bytes
|
||
|
|
while (fb > 0) {
|
||
|
|
FillBuffer ();
|
||
|
|
ba = IONU_DRBG_BUFFER_LEN;
|
||
|
|
sequencelogic::MemCpy (bytes, &_data[0], ba > fb ? fb : ba);
|
||
|
|
if (fb > ba) {
|
||
|
|
bytes += ba;
|
||
|
|
fb -= IONU_DRBG_BUFFER_LEN;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
_pos = fb;
|
||
|
|
fb = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pthread_mutex_unlock (&ionu_drbg_mutex);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Create a deterministic random BIGNUM for RSA key generation
|
||
|
|
// Code adapted from OpenSSL and Secure Programming Cookbook
|
||
|
|
bool EyeDRBG::GenerateRSAPrime (BIGNUM *rsap)
|
||
|
|
{
|
||
|
|
// Only works for IONU_RSA_KEY_BITS size keys, prime is IONU_RSA_KEY_BITS / 2
|
||
|
|
size_t bytes = SL_RSA_KEY_BITS / 16;
|
||
|
|
unsigned char* buff = new unsigned char[bytes];
|
||
|
|
if (buff == NULL)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
size_t count = 0;
|
||
|
|
bool good = false;
|
||
|
|
while (!good) {
|
||
|
|
if (! RandBytes (buff, bytes)) { // Get bytes from our DRBG
|
||
|
|
delete[] buff;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
// Set 2 most significant bits so p * q is exactly nbits, and least to ensure odd
|
||
|
|
buff[0] |= 0xc0;
|
||
|
|
buff[bytes - 1] |= 1;
|
||
|
|
// Convert to BIGNUM and see if it is prime
|
||
|
|
BN_bin2bn (buff, static_cast<int>(bytes), rsap);
|
||
|
|
if (BN_is_prime_fasttest_ex (rsap, BN_prime_checks, NULL, 1, NULL) == 1)
|
||
|
|
good = true;
|
||
|
|
count++;
|
||
|
|
}
|
||
|
|
#ifdef JAVASCRIPT
|
||
|
|
cout << count << endl;
|
||
|
|
#endif
|
||
|
|
// Clear out the memory containing the prime
|
||
|
|
sequencelogic::MemSet (buff, 0, bytes);
|
||
|
|
delete[] buff;
|
||
|
|
return good;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create an RSA key of IONU_RSA_KEY_BITS size using DRBG random number generator
|
||
|
|
// Code adapted from OpenSSL and Secure Programming Cookbook
|
||
|
|
bool EyeDRBG::GenerateDRBGRSAKey (RSA* rsa)
|
||
|
|
{
|
||
|
|
if (rsa == NULL)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
BN_CTX *ctx = BN_CTX_new();
|
||
|
|
if (ctx == NULL)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
bool rc = false;
|
||
|
|
BIGNUM* r0 = NULL;
|
||
|
|
BIGNUM* r1 = NULL;
|
||
|
|
BIGNUM* r2 = NULL;
|
||
|
|
BIGNUM* tmp = NULL;
|
||
|
|
|
||
|
|
// Get some temporary BIGNUMS for scratch pad calculations
|
||
|
|
BN_CTX_start (ctx);
|
||
|
|
r0 = BN_CTX_get (ctx);
|
||
|
|
r1 = BN_CTX_get (ctx);
|
||
|
|
r2 = BN_CTX_get (ctx);
|
||
|
|
if (r2 == NULL) goto cleanup;
|
||
|
|
|
||
|
|
// Allocate the RSA BIGNUMs
|
||
|
|
rsa->n = BN_new();
|
||
|
|
rsa->d = BN_new();
|
||
|
|
rsa->e = BN_new();
|
||
|
|
rsa->p = BN_new();
|
||
|
|
rsa->q = BN_new();
|
||
|
|
rsa->dmp1 = BN_new();
|
||
|
|
rsa->dmq1 = BN_new();
|
||
|
|
rsa->iqmp = BN_new();
|
||
|
|
|
||
|
|
if (! (rsa->n && rsa->d && rsa->e && rsa->p && rsa->q && rsa->dmp1 && rsa->dmq1 && rsa->iqmp))
|
||
|
|
goto cleanup;
|
||
|
|
|
||
|
|
BN_set_word (rsa->e, SL_RSA_PUBLIC_EXP);
|
||
|
|
|
||
|
|
/* generate p and q */
|
||
|
|
for (;;) {
|
||
|
|
if (! GenerateRSAPrime (rsa->p)) goto cleanup;
|
||
|
|
if (! BN_sub (r2, rsa->p, BN_value_one())) goto cleanup;
|
||
|
|
if (! BN_gcd (r1, r2, rsa->e, ctx)) goto cleanup;
|
||
|
|
if (BN_is_one (r1)) break;
|
||
|
|
}
|
||
|
|
for (;;) {
|
||
|
|
if (! GenerateRSAPrime (rsa->q)) goto cleanup;
|
||
|
|
if (BN_cmp (rsa->p, rsa->q) == 0) continue; //goto cleanup;
|
||
|
|
if (! BN_sub (r2, rsa->q, BN_value_one())) goto cleanup;
|
||
|
|
if (! BN_gcd (r1, r2, rsa->e, ctx)) goto cleanup;
|
||
|
|
if (BN_is_one (r1))
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// If q is greater than p, swap them for performance
|
||
|
|
if (BN_cmp (rsa->p, rsa->q) < 0) {
|
||
|
|
tmp = rsa->p;
|
||
|
|
rsa->p = rsa->q;
|
||
|
|
rsa->q = tmp;
|
||
|
|
}
|
||
|
|
// Calculate n
|
||
|
|
if (! BN_mul (rsa->n, rsa->p, rsa->q, ctx)) goto cleanup;
|
||
|
|
|
||
|
|
// Calculate d, and leave p-1 in r1, q-1 in r2
|
||
|
|
if (! BN_sub (r1, rsa->p, BN_value_one())) goto cleanup; // p-1 */
|
||
|
|
if (! BN_sub (r2, rsa->q, BN_value_one())) goto cleanup; // q-1 */
|
||
|
|
if (! BN_mul (r0, r1, r2, ctx)) goto cleanup; // (p-1)(q-1) */
|
||
|
|
if (! BN_mod_inverse(rsa->d, rsa->e, r0, ctx)) goto cleanup; // d */
|
||
|
|
|
||
|
|
/* calculate d mod (p-1) */
|
||
|
|
if (! BN_mod (rsa->dmp1, rsa->d, r1, ctx)) goto cleanup;
|
||
|
|
|
||
|
|
/* calculate d mod (q-1) */
|
||
|
|
if (!BN_mod(rsa->dmq1, rsa->d, r2, ctx)) goto cleanup;
|
||
|
|
|
||
|
|
/* calculate inverse of q mod p */
|
||
|
|
if (!BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx)) goto cleanup;
|
||
|
|
|
||
|
|
// Success?
|
||
|
|
#ifdef DEBUG_RSA
|
||
|
|
if (sequencelogic::RSA_check_key (rsa) != 1)
|
||
|
|
rc = false;
|
||
|
|
else
|
||
|
|
#endif
|
||
|
|
rc = true;
|
||
|
|
cleanup:
|
||
|
|
if (! rc) {
|
||
|
|
IONUDEBUG ("GenerateRSAPrime failed");
|
||
|
|
}
|
||
|
|
if (ctx != NULL) {
|
||
|
|
BN_CTX_end(ctx);
|
||
|
|
BN_CTX_free(ctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef DEBUG_RSA
|
||
|
|
int sequencelogic::RSA_check_key(const RSA *key)
|
||
|
|
{
|
||
|
|
BIGNUM *i, *j, *k, *l, *m;
|
||
|
|
BN_CTX *ctx;
|
||
|
|
int r;
|
||
|
|
int ret=1;
|
||
|
|
|
||
|
|
i = BN_new();
|
||
|
|
j = BN_new();
|
||
|
|
k = BN_new();
|
||
|
|
l = BN_new();
|
||
|
|
m = BN_new();
|
||
|
|
ctx = BN_CTX_new();
|
||
|
|
if (i == NULL || j == NULL || k == NULL || l == NULL ||
|
||
|
|
m == NULL || ctx == NULL)
|
||
|
|
{
|
||
|
|
ret = -1;
|
||
|
|
printf ("ERR_R_MALLOC_FAILURE\n");
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* p prime? */
|
||
|
|
r = BN_is_prime_ex(key->p, BN_prime_checks, NULL, NULL);
|
||
|
|
if (r != 1)
|
||
|
|
{
|
||
|
|
ret = r;
|
||
|
|
if (r != 0)
|
||
|
|
goto err;
|
||
|
|
printf ("RSA_R_P_NOT_PRIME\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* q prime? */
|
||
|
|
r = BN_is_prime_ex(key->q, BN_prime_checks, NULL, NULL);
|
||
|
|
if (r != 1)
|
||
|
|
{
|
||
|
|
ret = r;
|
||
|
|
if (r != 0)
|
||
|
|
goto err;
|
||
|
|
printf ("RSA_R_Q_NOT_PRIME\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* n = p*q? */
|
||
|
|
r = BN_mul(i, key->p, key->q, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
if (BN_cmp(i, key->n) != 0)
|
||
|
|
{
|
||
|
|
ret = 0;
|
||
|
|
printf ("RSA_R_N_DOES_NOT_EQUAL_P_Q\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* d*e = 1 mod lcm(p-1,q-1)? */
|
||
|
|
|
||
|
|
r = BN_sub(i, key->p, BN_value_one());
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
r = BN_sub(j, key->q, BN_value_one());
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
/* now compute k = lcm(i,j) */
|
||
|
|
r = BN_mul(l, i, j, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
r = BN_gcd(m, i, j, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
r = BN_div(k, NULL, l, m, ctx); /* remainder is 0 */
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
r = BN_mod_mul(i, key->d, key->e, k, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
if (!BN_is_one(i))
|
||
|
|
{
|
||
|
|
ret = 0;
|
||
|
|
printf ("RSA_R_D_E_NOT_CONGRUENT_TO_1\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (key->dmp1 != NULL && key->dmq1 != NULL && key->iqmp != NULL)
|
||
|
|
{
|
||
|
|
/* dmp1 = d mod (p-1)? */
|
||
|
|
r = BN_sub(i, key->p, BN_value_one());
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
r = BN_mod(j, key->d, i, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
if (BN_cmp(j, key->dmp1) != 0)
|
||
|
|
{
|
||
|
|
ret = 0;
|
||
|
|
printf ("RSA_R_DMP1_NOT_CONGRUENT_TO_D\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* dmq1 = d mod (q-1)? */
|
||
|
|
r = BN_sub(i, key->q, BN_value_one());
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
r = BN_mod(j, key->d, i, ctx);
|
||
|
|
if (!r) { ret = -1; goto err; }
|
||
|
|
|
||
|
|
if (BN_cmp(j, key->dmq1) != 0)
|
||
|
|
{
|
||
|
|
ret = 0;
|
||
|
|
printf ("RSA_R_DMQ1_NOT_CONGRUENT_TO_D\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* iqmp = q^-1 mod p? */
|
||
|
|
if(!BN_mod_inverse(i, key->q, key->p, ctx))
|
||
|
|
{
|
||
|
|
ret = -1;
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (BN_cmp(i, key->iqmp) != 0)
|
||
|
|
{
|
||
|
|
ret = 0;
|
||
|
|
printf ("RSA_R_IQMP_NOT_INVERSE_OF_Q\n");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
err:
|
||
|
|
if (i != NULL) BN_free(i);
|
||
|
|
if (j != NULL) BN_free(j);
|
||
|
|
if (k != NULL) BN_free(k);
|
||
|
|
if (l != NULL) BN_free(l);
|
||
|
|
if (m != NULL) BN_free(m);
|
||
|
|
if (ctx != NULL) BN_CTX_free(ctx);
|
||
|
|
return (ret);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
typedef struct ec_extra_data_st {
|
||
|
|
struct ec_extra_data_st *next;
|
||
|
|
void *data;
|
||
|
|
void *(*dup_func)(void *);
|
||
|
|
void (*free_func)(void *);
|
||
|
|
void (*clear_free_func)(void *);
|
||
|
|
} EC_EXTRA_DATA; /* used in EC_GROUP */
|
||
|
|
|
||
|
|
struct ec_key_st {
|
||
|
|
int version;
|
||
|
|
|
||
|
|
EC_GROUP *group;
|
||
|
|
|
||
|
|
EC_POINT *pub_key;
|
||
|
|
BIGNUM *priv_key;
|
||
|
|
|
||
|
|
unsigned int enc_flag;
|
||
|
|
point_conversion_form_t conv_form;
|
||
|
|
|
||
|
|
int references;
|
||
|
|
int flags;
|
||
|
|
|
||
|
|
EC_EXTRA_DATA *method_data;
|
||
|
|
} /* EC_KEY */;
|
||
|
|
|
||
|
|
|
||
|
|
bool EyeDRBG::GenerateDRBGECKey (struct ec_key_st *eckey)
|
||
|
|
{
|
||
|
|
bool rc = true;
|
||
|
|
BN_CTX *ctx = NULL;
|
||
|
|
BIGNUM *priv_key = NULL, *order = NULL;
|
||
|
|
EC_POINT *pub_key = NULL;
|
||
|
|
|
||
|
|
if (!eckey || !eckey->group)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
if ((order = BN_new()) == NULL) rc = false;
|
||
|
|
if ((ctx = BN_CTX_new()) == NULL) rc = false;
|
||
|
|
|
||
|
|
if (eckey->priv_key == NULL)
|
||
|
|
{
|
||
|
|
priv_key = BN_new();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
priv_key = eckey->priv_key;
|
||
|
|
|
||
|
|
if (priv_key == NULL) {
|
||
|
|
IONUDEBUG ("EyeDRBG::GenerateDRBGECKey(): failed to get private key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!EC_GROUP_get_order (eckey->group, order, ctx)) {
|
||
|
|
IONUDEBUG ("EyeDRBG::GenerateDRBGECKey(): get_order failed");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int n = BN_num_bits (order);
|
||
|
|
size_t bytes = n / 8;
|
||
|
|
unsigned char* buff = new unsigned char[bytes];
|
||
|
|
if (buff == NULL)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
do {
|
||
|
|
if (! RandBytes (buff, bytes)) { // Get bytes from our DRBG
|
||
|
|
delete[] buff;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
// Convert to BIGNUM and see if it is prime
|
||
|
|
BN_bin2bn (buff, static_cast<int>(bytes), priv_key);
|
||
|
|
}
|
||
|
|
while (BN_is_zero (priv_key));
|
||
|
|
delete[] buff;
|
||
|
|
|
||
|
|
if (eckey->pub_key == NULL)
|
||
|
|
{
|
||
|
|
pub_key = EC_POINT_new (eckey->group);
|
||
|
|
if (pub_key == NULL)
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
pub_key = eckey->pub_key;
|
||
|
|
|
||
|
|
if (!EC_POINT_mul (eckey->group, pub_key, priv_key, NULL, NULL, ctx))
|
||
|
|
rc = false;
|
||
|
|
|
||
|
|
eckey->priv_key = priv_key;
|
||
|
|
eckey->pub_key = pub_key;
|
||
|
|
|
||
|
|
if (order) BN_free (order);
|
||
|
|
if (pub_key != NULL && eckey->pub_key == NULL) EC_POINT_free (pub_key);
|
||
|
|
if (priv_key != NULL && eckey->priv_key == NULL) BN_free (priv_key);
|
||
|
|
if (ctx != NULL) BN_CTX_free (ctx);
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|