https://github.com/henrycg/earand
Raw File
Tip revision: 362625f3847ddd2e2d5a90cf5f93b4b3ea76e059 authored by Henry Corrigan-Gibbs on 21 May 2018, 19:54:59 UTC
Merge branch 'master' of github.com:henrycg/earand
Tip revision: 362625f
rsa_device.c
#include <string.h>
#include <unistd.h>

#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>

#include "product_proof.h"
#include "rsa_device.h"
#include "rsa_ea.h"
#include "rsa_params.h"
#include "ssl_client.h"
#include "util.h"

static bool MakePrime(RsaParams params, const BIGNUM* value, 
    BIGNUM** delta, BN_CTX* ctx);
static EVP_PKEY* CreateRsaKey(const_RsaDevice d);
static bool GenerateCertRequest(RsaDevice d, X509_REQ* req);

struct rsa_device {
  RsaParams params;
  BIGNUM* x;
  BIGNUM* y;
  BIGNUM* x_prime;
  BIGNUM* y_prime;
  BIGNUM* rand_p;
  BIGNUM* rand_q;
  BIGNUM* p; // First RSA factor   p = x + x' + delta_x
  BIGNUM* q; // Second RSA factor  q = y + y' + delta_y
  BIGNUM* n; // n = p*q

  X509* cert; // certificate (EA or CA signed)

  unsigned char* ea_sig;
  int ea_sig_len;
};

void RunEaSession(SSL* ssl, void* data) 
{
  int rfd, wfd;
  FILE* rfp;
  FILE* wfp;
  SetupFileDescriptors(ssl, &rfd, &rfp, &wfd, &wfp);

  RsaDevice device = (RsaDevice)data;

  // Device makes entropy request
  BIGNUM* v1 = BN_new();
  BIGNUM* v2 = BN_new(); 
  BIGNUM* v3 = BN_new(); 
  BIGNUM* v4 = BN_new(); 

  CHECK_CALL(v1);
  CHECK_CALL(v2);
  CHECK_CALL(RsaDevice_GenEntropyRequest(device, v1, v2));

  PrintTime("Sending commits to EA");
  // Send mode flag
  CHECK_CALL(fprintf(wfp, "%d\n", RSA_CLIENT));
  CHECK_CALL(!fflush(wfp));

  CHECK_CALL(WriteOneBignum(STRING_COMMIT_X, sizeof(STRING_COMMIT_X), wfp, v1));
  CHECK_CALL(WriteOneBignum(STRING_COMMIT_Y, sizeof(STRING_COMMIT_Y), wfp, v2));
  CHECK_CALL(!fflush(wfp));

  PrintTime("...done");

  // Read x', y' from EA
  PrintTime("Reading entropy from EA");
  CHECK_CALL(ReadOneBignum(&v1, rfp, STRING_X_PRIME));
  CHECK_CALL(ReadOneBignum(&v2, rfp, STRING_Y_PRIME));
  PrintTime("...done");

  CHECK_CALL(RsaDevice_SetEntropyResponse(device, v1, v2));

  // Send proof to EA 
  ProductEvidence ev = NULL;
  X509_REQ* req = X509_REQ_new();
  CHECK_CALL(req);
  CHECK_CALL(RsaDevice_GenEaSigningRequest(device, req, v1, v2, v3, &ev));
  CHECK_CALL(ev);

  PrintTime("Sending cert to EA");
  CHECK_CALL(i2d_X509_REQ_fp(wfp, req));
  //fprintf(wfp, "\n");
  CHECK_CALL(!fflush(wfp));

  CHECK_CALL(WriteOneBignum(STRING_DELTA_X, sizeof(STRING_DELTA_X), wfp, v1));
  CHECK_CALL(WriteOneBignum(STRING_DELTA_Y, sizeof(STRING_DELTA_Y), wfp, v2));
  CHECK_CALL(WriteOneBignum(STRING_MODULUS_RAND, sizeof(STRING_MODULUS_RAND), wfp, v3));
  CHECK_CALL(ProductEvidence_Serialize(ev, wfp));
  CHECK_CALL(!fflush(wfp));
  PrintTime("...done");

  X509_REQ_free(req);

  ProductEvidence_Free(ev);

  X509* cert = NULL;
  PrintTime("Reading cert from EA");
  if(!(cert = d2i_X509_fp(rfp, NULL))) {
    fatal("Could not read X509 response");
  }
  PrintTime("...done");

  fclose(rfp);
  fclose(wfp);

  BN_clear_free(v1);
  BN_clear_free(v2);
  BN_clear_free(v3);
  BN_clear_free(v4);

  // Give EA signature back to device
  CHECK_CALL(RsaDevice_SetEaCertResponse(device, cert));

  X509_free(cert);
  return;
}

RsaDevice RsaDevice_New(RsaParams params)
{
  RsaDevice d = safe_malloc(sizeof(*d));
  d->params = params;
  d->x = NULL;
  d->y = NULL;
  d->x_prime = NULL;
  d->y_prime = NULL;
  d->rand_p = NULL;
  d->rand_q = NULL;
  d->p = NULL;
  d->q = NULL;
  d->n = NULL;
  //d->proof_st = NULL;
  //d->proof_ev = NULL;
  d->ea_sig = NULL;
  d->cert = NULL;

  return d;
}

void RsaDevice_Free(RsaDevice d)
{
  //if(d->proof_st) ProductStatement_Free(d->proof_st);
  //if(d->proof_ev) ProductEvidence_Free(d->proof_ev);
  if(d->x) BN_clear_free(d->x);
  if(d->y) BN_clear_free(d->y);
  if(d->x_prime) BN_clear_free(d->x_prime);
  if(d->y_prime) BN_clear_free(d->y_prime);
  if(d->rand_p) BN_clear_free(d->rand_p);
  if(d->rand_q) BN_clear_free(d->rand_q);
  if(d->p) BN_clear_free(d->p);
  if(d->q) BN_clear_free(d->q);
  if(d->n) BN_free(d->n);

  if(d->ea_sig) free(d->ea_sig);
  if(d->cert) X509_free(d->cert);
  
  free(d);
}

X509* RsaDevice_RunProtocol(RsaDevice d, bool ca_sign,
    const char* ea_hostname, int ea_port,
    const char* ca_hostname, int ca_port)
{
  PrintTime("Connecting to EA");
  CHECK_CALL(MakeSSLRequest(ea_hostname, ea_port, &RunEaSession, (void*)d));
  PrintTime("Closed connection to EA");

  struct ca_request_data rr;
  rr.client_type = RSA_CLIENT;
  rr.cert = X509_new();

  CHECK_CALL(rr.cert);
  PrintTime("Generating CA request");
  CHECK_CALL(RsaDevice_GenCaCertRequest(d, &(rr.cert)));
  PrintTime("Done generating CA request");

  if(ca_sign) {
    PrintTime("Beginning CA session");
    CHECK_CALL(MakeSSLRequest(ca_hostname, ca_port, &RequestCaSignatureClient, (void*)&rr));
  }

  return rr.cert;
}

bool RsaDevice_GenEntropyRequest(RsaDevice d,
    BIGNUM* commit_x, BIGNUM* commit_y)
{
  if(d->x || d->y) return false;

  PrintTime("Getting x, y, r_p, r_q");
  CHECK_CALL(d->x = RsaParams_RandomLargeValue(d->params));
  CHECK_CALL(d->y = RsaParams_RandomLargeValue(d->params));
  CHECK_CALL(d->rand_p = IntegerGroup_RandomExponent(RsaParams_GetGroup(d->params)));
  CHECK_CALL(d->rand_q = IntegerGroup_RandomExponent(RsaParams_GetGroup(d->params)));
  PrintTime("...done");

  bool retval = (d->x && d->y && d->rand_p && d->rand_q);

  PrintTime("Generating C(x), C(y)");
  BIGNUM* cx = IntegerGroup_Commit(RsaParams_GetGroup(d->params), d->x, d->rand_p);
  BIGNUM* cy = IntegerGroup_Commit(RsaParams_GetGroup(d->params), d->y, d->rand_q);
  PrintTime("...done");


  CHECK_CALL(cx);
  CHECK_CALL(cy);

  CHECK_CALL(BN_copy(commit_x, cx));
  CHECK_CALL(BN_copy(commit_y, cy));

  BN_clear_free(cx);
  BN_clear_free(cy);

  return retval;
}

bool RsaDevice_SetEntropyResponse(RsaDevice d, 
    const BIGNUM* x_prime, const BIGNUM* y_prime)
{
  if(!RsaParams_InRange(d->params, x_prime)) return false;
  if(!RsaParams_InRange(d->params, y_prime)) return false;

  CHECK_CALL(d->x_prime = BN_dup(x_prime));
  CHECK_CALL(d->y_prime = BN_dup(y_prime));

  return true;
}

bool RsaDevice_GenEaSigningRequest(RsaDevice d,
    X509_REQ* req, BIGNUM* delta_x, BIGNUM* delta_y,
    BIGNUM* rand_n, ProductEvidence* ev)
{
  BN_zero(delta_x);
  BN_zero(delta_y);

  PrintTime("Calculating x+x' and y+y'");
  // p = x + x'
  CHECK_CALL(d->p = BN_dup(d->x));
  CHECK_CALL(BN_add(d->p, d->p, d->x_prime));

  // q = y + y'
  CHECK_CALL(d->q = BN_dup(d->y));
  CHECK_CALL(BN_add(d->q, d->q, d->y_prime));
  PrintTime("...done");

  PrintTime("Making finding deltas to make p and q prime");
  if(!MakePrime(d->params, d->p, &delta_x, RsaParams_GetCtx(d->params)))
    return false;

  if(!MakePrime(d->params, d->q, &delta_y, RsaParams_GetCtx(d->params)))
    return false;
  PrintTime("...done");

  PrintTime("Adding deltas to p and q");
  // p = x + x' + delta_x
  CHECK_CALL(BN_add(d->p, d->p, delta_x));

  // q = y + y' + delta_y
  CHECK_CALL(BN_add(d->q, d->q, delta_y));
  PrintTime("...done");

  PrintTime("Calculating n");
  // n = p*q
  CHECK_CALL(d->n = BN_new());
  CHECK_CALL(BN_mul(d->n, d->p, d->q, RsaParams_GetCtx(d->params)));
  ASSERT(BN_cmp(d->n, IntegerGroup_GetQ(RsaParams_GetGroup(d->params))) == -1);
  PrintTime("...done");

  const_IntegerGroup group = RsaParams_GetGroup(d->params);

  // Get randomness for commitment to n
  PrintTime("Picking r_n");
  BIGNUM* r_n = IntegerGroup_RandomExponent(group);
  CHECK_CALL(r_n);
  CHECK_CALL(BN_copy(rand_n, r_n));
  BN_clear_free(r_n);
  PrintTime("...done");

  PrintTime("Generating commits to p,q,n");
  // Commit to p
  BIGNUM* commit_p = IntegerGroup_Commit(group, d->p, d->rand_p);
  CHECK_CALL(commit_p);

  // Commit to q
  BIGNUM* commit_q = IntegerGroup_Commit(group, d->q, d->rand_q);

  // Commit to n
  BIGNUM* commit_n = IntegerGroup_Commit(group, d->n, rand_n);
  PrintTime("...done");

  /*
  printf("C(p) = "); BN_print_fp(stdout, commit_p); puts("");
  printf("C(q) = "); BN_print_fp(stdout, commit_q); puts("");
  printf("C(n) = "); BN_print_fp(stdout, commit_n); puts("");
  */
 
  PrintTime("Generating prodproof");
  ProductStatement st = ProductStatement_New(RsaParams_GetGroup(d->params),
      commit_p, commit_q, commit_n);
  PrintTime("...done");

  // Generate proof that n = p*q
  PrintTime("Generating prodevidence");
  *ev = ProductEvidence_New(st, d->p, d->rand_p, d->rand_q, rand_n);
  PrintTime("...done");

  ProductStatement_Free(st);

  BN_clear_free(commit_p);
  BN_clear_free(commit_q);
  BN_clear_free(commit_n);

  CHECK_CALL(req);
  PrintTime("Generating certificate");
  CHECK_CALL(GenerateCertRequest(d, req));
  PrintTime("...done");

  return true;
}

bool RsaDevice_SetEaCertResponse(RsaDevice d, X509* cert)
{
  d->cert = X509_dup(cert);
  CHECK_CALL(d->cert);
  return X509_verify(d->cert, RsaParams_GetEaPublicKey(d->params));
}

bool RsaDevice_GenCaCertRequest(RsaDevice d, X509** cert)
{
  //X509_print_fp(stderr, d->cert);
  CHECK_CALL(*cert = X509_dup(d->cert));
  return true;
}

bool RsaDevice_SetCaCertResponse(RsaDevice d, X509* cert)
{
  if(d->cert) X509_free(d->cert);
  CHECK_CALL(d->cert = X509_dup(cert));
  return X509_verify(cert, RsaParams_GetCaPublicKey(d->params));
}

const BIGNUM* RsaDevice_GetX(const_RsaDevice rsa)
{
  return rsa->x;
}

const BIGNUM* RsaDevice_GetY(const_RsaDevice rsa)
{
  return rsa->y;
}

const BIGNUM* RsaDevice_GetXPrime(const_RsaDevice rsa)
{
  return rsa->x_prime;
}

const BIGNUM* RsaDevice_GetYPrime(const_RsaDevice rsa)
{
  return rsa->y_prime;
}

const BIGNUM* RsaDevice_GetP(const_RsaDevice rsa)
{
  return rsa->p;
}

const BIGNUM* RsaDevice_GetQ(const_RsaDevice rsa)
{
  return rsa->q;
}

const BIGNUM* RsaDevice_GetN(const_RsaDevice rsa)
{
  return rsa->n;
}

bool MakePrime(RsaParams params, const BIGNUM* value, BIGNUM** delta_ret, 
    BN_CTX* ctx)
{
  BIGNUM* tmp = BN_dup(value);
  CHECK_CALL(tmp);

  // Find a delta such that 
  //    p = value + delta
  // is prime
  const int delta_max = RsaParams_GetDeltaMax(params);

  bool is_even = !BN_is_odd(tmp);
  if(is_even) {
    CHECK_CALL(BN_add_word(tmp, 1));
  }

  if(!RsaPrime(*delta_ret, tmp, ctx)) return false;
 
  if(is_even) {
    CHECK_CALL(BN_add_word(*delta_ret, 1));
  }

//  printf("%llu %d\n", BN_get_word(*delta_ret), delta_max);
  if(BN_get_word(*delta_ret) > delta_max) return false;

  BN_clear_free(tmp);

  return true;
}

EVP_PKEY* CreateRsaKey(const_RsaDevice d)
{
  BN_CTX* ctx = IntegerGroup_GetCtx(RsaParams_GetGroup(d->params));
  // phi(n) = (p-1)(q-1)
  BIGNUM *phi_n, *pm, *qm;
  phi_n = BN_new();
  CHECK_CALL(phi_n);
  CHECK_CALL(pm = BN_dup(d->p));
  CHECK_CALL(qm = BN_dup(d->q));
  CHECK_CALL(BN_sub_word(pm, 1));
  CHECK_CALL(BN_sub_word(qm, 1));
  CHECK_CALL(BN_mul(phi_n, pm, qm, ctx));

  EVP_PKEY *evp = EVP_PKEY_new();
  RSA *rsa = RSA_new();
  CHECK_CALL(evp);
  CHECK_CALL(rsa);

  CHECK_CALL(rsa->n = BN_dup(d->n)); // public modulus
  CHECK_CALL(rsa->e = BN_new()); // public exponent
  BN_set_word(rsa->e, RsaEncryptionExponent);

  rsa->d = BN_new();              // private exponent
  CHECK_CALL(rsa->d);
  CHECK_CALL(BN_mod_inverse(rsa->d, rsa->e, phi_n, ctx));

  CHECK_CALL(rsa->p = BN_dup(d->p)); // secret prime factor
  CHECK_CALL(rsa->q = BN_dup(d->q)); // secret prime factor
  rsa->dmp1 = BN_new();           // d mod (p-1)
  CHECK_CALL(rsa->dmp1);
  CHECK_CALL(BN_mod(rsa->dmp1, rsa->d, pm, ctx));

  rsa->dmq1 = BN_new();           // d mod (q-1)
  CHECK_CALL(rsa->dmq1);
  CHECK_CALL(BN_mod(rsa->dmq1, rsa->d, qm, ctx));

  rsa->iqmp = BN_new();           // q^-1 mod p
  CHECK_CALL(rsa->iqmp);
  CHECK_CALL(BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx));

  CHECK_CALL(EVP_PKEY_set1_RSA(evp, rsa));
  ASSERT(RSA_check_key(rsa));

  BN_clear_free(phi_n);
  BN_clear_free(pm);
  BN_clear_free(qm);

  return evp;
}

static bool GenerateCertRequest(RsaDevice d, X509_REQ* req)
{
  // Create key in EVP format
  EVP_PKEY* key = CreateRsaKey(d);
  CHECK_CALL(key);
  
  // Create x509 cert signing request (CSR)
  CHECK_CALL(X509_REQ_set_pubkey(req, key));

  // Add subject name to the CSR
  X509_NAME* subj = X509_REQ_get_subject_name(req);
  CHECK_CALL(X509_NAME_add_entry_by_txt(
      subj, "O", MBSTRING_ASC, 
      (const unsigned char *)"RSA Device", -1, -1, 0)); 
  CHECK_CALL(X509_REQ_set_subject_name(req, subj));

  //X509_REQ_print_fp(stderr, req);
  CHECK_CALL(X509_REQ_sign(req, key, EVP_sha1()));

  EVP_PKEY_free(key);
  return true;
}
back to top