1866 lines
40 KiB
C++
1866 lines
40 KiB
C++
// Copyright 2012 The Obvious Corporation.
|
|
|
|
#include "ursaNative.h"
|
|
#include <node_buffer.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/pem.h>
|
|
|
|
using namespace v8;
|
|
|
|
#ifdef _WIN32
|
|
#include <malloc.h>
|
|
#define VAR_ARRAY(type, name, size) type *name = (type *)_alloca(size)
|
|
#else
|
|
#define VAR_ARRAY(type, name, size) type name[size]
|
|
#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
|
|
#include <string.h>
|
|
#include <openssl/engine.h>
|
|
|
|
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
|
{
|
|
/* If the fields n and e in r are NULL, the corresponding input
|
|
* parameters MUST be non-NULL for n and e. d may be
|
|
* left NULL (in case only the public key is used).
|
|
*/
|
|
if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL))
|
|
return 0;
|
|
|
|
if (n != NULL)
|
|
{
|
|
BN_free(r->n);
|
|
r->n = n;
|
|
}
|
|
if (e != NULL)
|
|
{
|
|
BN_free(r->e);
|
|
r->e = e;
|
|
}
|
|
if (d != NULL)
|
|
{
|
|
BN_free(r->d);
|
|
r->d = d;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
|
|
{
|
|
/* If the fields p and q in r are NULL, the corresponding input
|
|
* parameters MUST be non-NULL.
|
|
*/
|
|
if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL))
|
|
return 0;
|
|
|
|
if (p != NULL)
|
|
{
|
|
BN_free(r->p);
|
|
r->p = p;
|
|
}
|
|
if (q != NULL)
|
|
{
|
|
BN_free(r->q);
|
|
r->q = q;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
|
|
{
|
|
/* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
|
|
* parameters MUST be non-NULL.
|
|
*/
|
|
if ((r->dmp1 == NULL && dmp1 == NULL) || (r->dmq1 == NULL && dmq1 == NULL) || (r->iqmp == NULL && iqmp == NULL))
|
|
return 0;
|
|
|
|
if (dmp1 != NULL)
|
|
{
|
|
BN_free(r->dmp1);
|
|
r->dmp1 = dmp1;
|
|
}
|
|
if (dmq1 != NULL)
|
|
{
|
|
BN_free(r->dmq1);
|
|
r->dmq1 = dmq1;
|
|
}
|
|
if (iqmp != NULL)
|
|
{
|
|
BN_free(r->iqmp);
|
|
r->iqmp = iqmp;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void RSA_get0_key(const RSA *r,
|
|
const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
|
{
|
|
if (n != NULL)
|
|
*n = r->n;
|
|
if (e != NULL)
|
|
*e = r->e;
|
|
if (d != NULL)
|
|
*d = r->d;
|
|
}
|
|
|
|
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
|
{
|
|
if (p != NULL)
|
|
*p = r->p;
|
|
if (q != NULL)
|
|
*q = r->q;
|
|
}
|
|
|
|
void RSA_get0_crt_params(const RSA *r,
|
|
const BIGNUM **dmp1, const BIGNUM **dmq1,
|
|
const BIGNUM **iqmp)
|
|
{
|
|
if (dmp1 != NULL)
|
|
*dmp1 = r->dmp1;
|
|
if (dmq1 != NULL)
|
|
*dmq1 = r->dmq1;
|
|
if (iqmp != NULL)
|
|
*iqmp = r->iqmp;
|
|
}
|
|
|
|
void DSA_get0_pqg(const DSA *d,
|
|
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
|
{
|
|
if (p != NULL)
|
|
*p = d->p;
|
|
if (q != NULL)
|
|
*q = d->q;
|
|
if (g != NULL)
|
|
*g = d->g;
|
|
}
|
|
|
|
int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
|
{
|
|
/* If the fields p, q and g in d are NULL, the corresponding input
|
|
* parameters MUST be non-NULL.
|
|
*/
|
|
if ((d->p == NULL && p == NULL) || (d->q == NULL && q == NULL) || (d->g == NULL && g == NULL))
|
|
return 0;
|
|
|
|
if (p != NULL)
|
|
{
|
|
BN_free(d->p);
|
|
d->p = p;
|
|
}
|
|
if (q != NULL)
|
|
{
|
|
BN_free(d->q);
|
|
d->q = q;
|
|
}
|
|
if (g != NULL)
|
|
{
|
|
BN_free(d->g);
|
|
d->g = g;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DSA_get0_key(const DSA *d,
|
|
const BIGNUM **pub_key, const BIGNUM **priv_key)
|
|
{
|
|
if (pub_key != NULL)
|
|
*pub_key = d->pub_key;
|
|
if (priv_key != NULL)
|
|
*priv_key = d->priv_key;
|
|
}
|
|
|
|
int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
|
{
|
|
/* If the field pub_key in d is NULL, the corresponding input
|
|
* parameters MUST be non-NULL. The priv_key field may
|
|
* be left NULL.
|
|
*/
|
|
if (d->pub_key == NULL && pub_key == NULL)
|
|
return 0;
|
|
|
|
if (pub_key != NULL)
|
|
{
|
|
BN_free(d->pub_key);
|
|
d->pub_key = pub_key;
|
|
}
|
|
if (priv_key != NULL)
|
|
{
|
|
BN_free(d->priv_key);
|
|
d->priv_key = priv_key;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
|
{
|
|
if (pr != NULL)
|
|
*pr = sig->r;
|
|
if (ps != NULL)
|
|
*ps = sig->s;
|
|
}
|
|
|
|
int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
|
{
|
|
if (r == NULL || s == NULL)
|
|
return 0;
|
|
BN_clear_free(sig->r);
|
|
BN_clear_free(sig->s);
|
|
sig->r = r;
|
|
sig->s = s;
|
|
return 1;
|
|
}
|
|
|
|
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
|
{
|
|
if (pr != NULL)
|
|
*pr = sig->r;
|
|
if (ps != NULL)
|
|
*ps = sig->s;
|
|
}
|
|
|
|
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
|
{
|
|
if (r == NULL || s == NULL)
|
|
return 0;
|
|
BN_clear_free(sig->r);
|
|
BN_clear_free(sig->s);
|
|
sig->r = r;
|
|
sig->s = s;
|
|
return 1;
|
|
}
|
|
|
|
void DH_get0_pqg(const DH *dh,
|
|
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
|
{
|
|
if (p != NULL)
|
|
*p = dh->p;
|
|
if (q != NULL)
|
|
*q = dh->q;
|
|
if (g != NULL)
|
|
*g = dh->g;
|
|
}
|
|
|
|
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
|
{
|
|
/* If the fields p and g in d are NULL, the corresponding input
|
|
* parameters MUST be non-NULL. q may remain NULL.
|
|
*/
|
|
if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL))
|
|
return 0;
|
|
|
|
if (p != NULL)
|
|
{
|
|
BN_free(dh->p);
|
|
dh->p = p;
|
|
}
|
|
if (q != NULL)
|
|
{
|
|
BN_free(dh->q);
|
|
dh->q = q;
|
|
}
|
|
if (g != NULL)
|
|
{
|
|
BN_free(dh->g);
|
|
dh->g = g;
|
|
}
|
|
|
|
if (q != NULL)
|
|
{
|
|
dh->length = BN_num_bits(q);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
|
{
|
|
if (pub_key != NULL)
|
|
*pub_key = dh->pub_key;
|
|
if (priv_key != NULL)
|
|
*priv_key = dh->priv_key;
|
|
}
|
|
|
|
int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
|
{
|
|
/* If the field pub_key in dh is NULL, the corresponding input
|
|
* parameters MUST be non-NULL. The priv_key field may
|
|
* be left NULL.
|
|
*/
|
|
if (dh->pub_key == NULL && pub_key == NULL)
|
|
return 0;
|
|
|
|
if (pub_key != NULL)
|
|
{
|
|
BN_free(dh->pub_key);
|
|
dh->pub_key = pub_key;
|
|
}
|
|
if (priv_key != NULL)
|
|
{
|
|
BN_free(dh->priv_key);
|
|
dh->priv_key = priv_key;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int DH_set_length(DH *dh, long length)
|
|
{
|
|
dh->length = length;
|
|
return 1;
|
|
}
|
|
|
|
const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx)
|
|
{
|
|
return ctx->iv;
|
|
}
|
|
|
|
unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx)
|
|
{
|
|
return ctx->iv;
|
|
}
|
|
#endif
|
|
|
|
Nan::Persistent<Function> constructor;
|
|
|
|
/*
|
|
* Initialization and binding
|
|
*/
|
|
#define NanThrowError(err) Nan::ThrowError(err);
|
|
#define NanNewBufferHandle(length) Nan::NewBuffer(length).ToLocalChecked()
|
|
#define args info
|
|
#define NanScope() Nan::HandleScope scope
|
|
#define NanReturnUndefined() \
|
|
{ \
|
|
info.GetReturnValue().Set(Nan::Undefined()); \
|
|
return; \
|
|
}
|
|
#define NanNew Nan::New
|
|
#define NanReturnValue(value) \
|
|
{ \
|
|
info.GetReturnValue().Set(value); \
|
|
return; \
|
|
}
|
|
#define NanFalse() Nan::False()
|
|
#define NanTrue() Nan::True()
|
|
|
|
#define RSA_PKCS1_SALT_LEN_HLEN -1
|
|
#define RSA_PKCS1_SALT_LEN_MAX -2
|
|
#define RSA_PKCS1_SALT_LEN_RECOVER -2
|
|
|
|
/**
|
|
* Top-level initialization function.
|
|
*/
|
|
void init(Local<Object> target)
|
|
{
|
|
NODE_DEFINE_CONSTANT(target, RSA_NO_PADDING);
|
|
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PADDING);
|
|
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_OAEP_PADDING);
|
|
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_HLEN);
|
|
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_MAX);
|
|
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_RECOVER);
|
|
|
|
RsaWrap::InitClass(target);
|
|
|
|
#ifdef _WIN32
|
|
// On Windows, we can't use Node's OpenSSL, so we link
|
|
// to a standalone OpenSSL library. Therefore, we need
|
|
// to initialize OpenSSL separately.
|
|
|
|
//TODO: Do I need to free these?
|
|
//I'm not sure where to call ERR_free_strings() and EVP_cleanup()
|
|
OpenSSL_add_all_digests();
|
|
OpenSSL_add_all_algorithms();
|
|
OpenSSL_add_all_ciphers();
|
|
ERR_load_crypto_strings();
|
|
#endif
|
|
}
|
|
|
|
NODE_MODULE(ursaNative, init)
|
|
|
|
/*
|
|
* Helper functions
|
|
*/
|
|
|
|
/**
|
|
* Schedule the current SSL error as a higher-level exception.
|
|
*/
|
|
static void scheduleSslException()
|
|
{
|
|
char *err = ERR_error_string(ERR_get_error(), NULL);
|
|
ERR_clear_error();
|
|
NanThrowError(err);
|
|
}
|
|
|
|
/**
|
|
* Schedule an "allocation failed" exception. This (tries) to allocate
|
|
* as well, which very well could (probably will) fail too, but it's the
|
|
* best we can do in a bad situation.
|
|
*/
|
|
static void scheduleAllocException()
|
|
{
|
|
NanThrowError("Allocation failed.");
|
|
}
|
|
|
|
/**
|
|
* Convert the given (BIGNUM *) to a Buffer of unsigned big-endian
|
|
* contents. Returns a Buffer-containing handle on success. Schedules an
|
|
* exception and returns Undefined() on failure.
|
|
*/
|
|
static Nan::NAN_METHOD_RETURN_TYPE bignumToBuffer(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
BIGNUM *number)
|
|
{
|
|
int length = BN_num_bytes(number);
|
|
Local<Object> result = NanNewBufferHandle(length);
|
|
|
|
if (BN_bn2bin(number, (unsigned char *)node::Buffer::Data(result)) < 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Convert the given memory-based (BIO *) to a Buffer of its contents.
|
|
* Returns a Buffer-containing handle on success. Schedules an
|
|
* exception and returns Undefined() on failure. In either case, the
|
|
* BIO is freed by the time this function returns.
|
|
*
|
|
* As a special case to help with error handling, if given a NULL
|
|
* argument, this simply returns Undefined().
|
|
*/
|
|
static Nan::NAN_METHOD_RETURN_TYPE bioToBuffer(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
BIO *bio)
|
|
{
|
|
if (bio == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
char *data;
|
|
long length = BIO_get_mem_data(bio, &data);
|
|
Local<Object> result = NanNewBufferHandle(length);
|
|
|
|
if (result.IsEmpty())
|
|
{
|
|
scheduleAllocException();
|
|
BIO_vfree(bio);
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
memcpy(node::Buffer::Data(result), data, length);
|
|
BIO_vfree(bio);
|
|
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Get a Buffer out of args[0], converted to a freshly-allocated
|
|
* memory BIO. Returns a non-null pointer on success. On failure,
|
|
* schedules an exception and returns NULL.
|
|
*/
|
|
static BIO *getArg0Bio(const Local<Object> buf)
|
|
{
|
|
if (!node::Buffer::HasInstance(buf))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[0].");
|
|
return NULL;
|
|
}
|
|
|
|
char *data = node::Buffer::Data(buf);
|
|
ssize_t length = node::Buffer::Length(buf);
|
|
BIO *bio = BIO_new_mem_buf(data, length);
|
|
|
|
if (bio == NULL)
|
|
{
|
|
scheduleSslException();
|
|
}
|
|
|
|
return bio;
|
|
}
|
|
|
|
static BIGNUM *getArgXBigNum(const Local<Object> buf)
|
|
{
|
|
if (!node::Buffer::HasInstance(buf))
|
|
{
|
|
NanThrowError("Expected a Buffer.");
|
|
return NULL;
|
|
}
|
|
char *data = node::Buffer::Data(buf);
|
|
ssize_t length = node::Buffer::Length(buf);
|
|
|
|
return BN_bin2bn(reinterpret_cast<unsigned char *>(data), length, NULL);
|
|
}
|
|
|
|
/**
|
|
* Get a Buffer out of args[1], converted to a freshly-allocated (char
|
|
* *). Returns a non-null pointer on success. On failure, schedules an
|
|
* exception and returns NULL.
|
|
*/
|
|
static char *copyBufferToCharStar(const Local<Object> buf)
|
|
{
|
|
|
|
if (!node::Buffer::HasInstance(buf))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
char *data = node::Buffer::Data(buf);
|
|
ssize_t length = node::Buffer::Length(buf);
|
|
char *result = (char *)malloc(length + 1);
|
|
|
|
if (result == NULL)
|
|
{
|
|
scheduleAllocException();
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(result, data, length);
|
|
result[length] = '\0';
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get a string out of args[] at the given index, converted to a
|
|
* freshly-allocated (char *). Returns a non-null pointer on
|
|
* success. On failure, schedules an exception and returns NULL.
|
|
*/
|
|
static char *copyBufferToUtf8String(const Local<String> str)
|
|
{
|
|
// static char *getArgString(const Arguments& args, int index) {
|
|
int length = str->Utf8Length();
|
|
char *result = (char *)malloc(length + 1);
|
|
|
|
if (result == NULL)
|
|
{
|
|
scheduleAllocException();
|
|
return NULL;
|
|
}
|
|
|
|
result[length] = 'x'; // Set up a small sanity check (see below).
|
|
str->WriteUtf8(result, length + 1);
|
|
|
|
if (result[length] != '\0')
|
|
{
|
|
const char *message = "String conversion failed.";
|
|
NanThrowError(message);
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Generate a key, using one of the two possibly-available functions.
|
|
* This prefers the newer function, if available.
|
|
*/
|
|
static RSA *generateKey(int num, unsigned long e)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER < 0x009080001
|
|
return RSA_generate_key(num, e, NULL, NULL);
|
|
#else
|
|
BIGNUM *eBig = BN_new();
|
|
|
|
if (eBig == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!BN_set_word(eBig, e))
|
|
{
|
|
BN_free(eBig);
|
|
return NULL;
|
|
}
|
|
|
|
RSA *result = RSA_new();
|
|
|
|
if (result == NULL)
|
|
{
|
|
BN_free(eBig);
|
|
return NULL;
|
|
}
|
|
|
|
if (RSA_generate_key_ex(result, num, eBig, NULL) < 0)
|
|
{
|
|
RSA_free(result);
|
|
result = NULL;
|
|
}
|
|
|
|
BN_free(eBig);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Utility function implementation
|
|
*/
|
|
|
|
/**
|
|
* Call the OpenSSL function OBJ_txt2nid() on the given string.
|
|
* This returns a number representing the text that, as far as I
|
|
* (danfuzz) know, is not necessarily stable across versions of
|
|
* OpenSSL, so it's only safe to use transiently.
|
|
*/
|
|
NAN_METHOD(TextToNid)
|
|
{
|
|
NanScope();
|
|
|
|
if (args.Length() < 1)
|
|
{
|
|
NanThrowError("Missing args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsString())
|
|
{
|
|
NanThrowError("Expected a string in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<String> str = args[0].As<String>();
|
|
char *name = copyBufferToUtf8String(str);
|
|
if (name == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int nid = OBJ_txt2nid(name);
|
|
free(name);
|
|
|
|
if (nid == NID_undef)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(NanNew<Number>(nid));
|
|
}
|
|
|
|
/*
|
|
* RsaWrap implementation
|
|
*/
|
|
|
|
/**
|
|
* Initialize the bindings for this class.
|
|
*/
|
|
void RsaWrap::InitClass(Local<Object> target)
|
|
{
|
|
Local<String> className = NanNew("RsaWrap").ToLocalChecked();
|
|
|
|
// Basic instance setup
|
|
Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
|
|
|
|
tpl->SetClassName(className);
|
|
tpl->InstanceTemplate()->SetInternalFieldCount(1); // req'd by ObjectWrap
|
|
|
|
Nan::SetPrototypeMethod(tpl, "generatePrivateKey", GeneratePrivateKey);
|
|
Nan::SetPrototypeMethod(tpl, "getExponent", GetExponent);
|
|
Nan::SetPrototypeMethod(tpl, "getPrivateExponent", GetPrivateExponent);
|
|
Nan::SetPrototypeMethod(tpl, "getModulus", GetModulus);
|
|
Nan::SetPrototypeMethod(tpl, "getPrivateKeyPem", GetPrivateKeyPem);
|
|
Nan::SetPrototypeMethod(tpl, "getPublicKeyPem", GetPublicKeyPem);
|
|
Nan::SetPrototypeMethod(tpl, "privateDecrypt", PrivateDecrypt);
|
|
Nan::SetPrototypeMethod(tpl, "privateEncrypt", PrivateEncrypt);
|
|
Nan::SetPrototypeMethod(tpl, "publicDecrypt", PublicDecrypt);
|
|
Nan::SetPrototypeMethod(tpl, "publicEncrypt", PublicEncrypt);
|
|
Nan::SetPrototypeMethod(tpl, "setPrivateKeyPem", SetPrivateKeyPem);
|
|
Nan::SetPrototypeMethod(tpl, "setPublicKeyPem", SetPublicKeyPem);
|
|
Nan::SetPrototypeMethod(tpl, "sign", Sign);
|
|
Nan::SetPrototypeMethod(tpl, "verify", Verify);
|
|
Nan::SetPrototypeMethod(tpl, "createPrivateKeyFromComponents", CreatePrivateKeyFromComponents);
|
|
Nan::SetPrototypeMethod(tpl, "createPublicKeyFromComponents", CreatePublicKeyFromComponents);
|
|
Nan::SetPrototypeMethod(tpl, "openPublicSshKey", OpenPublicSshKey);
|
|
Nan::SetPrototypeMethod(tpl, "addPSSPadding", AddPSSPadding);
|
|
Nan::SetPrototypeMethod(tpl, "verifyPSSPadding", VerifyPSSPadding);
|
|
|
|
// Store the constructor in the target bindings.
|
|
target->Set(NanNew("RsaWrap").ToLocalChecked(), tpl->GetFunction());
|
|
constructor.Reset(tpl->GetFunction());
|
|
|
|
target->Set(NanNew("textToNid").ToLocalChecked(), Nan::New<FunctionTemplate>(TextToNid)->GetFunction());
|
|
}
|
|
|
|
/**
|
|
* Straightforward constructor. Nothing much to initialize, other than
|
|
* to ensure that our one instance variable is sanely NULLed.
|
|
*/
|
|
RsaWrap::RsaWrap()
|
|
{
|
|
rsa = NULL;
|
|
}
|
|
|
|
/**
|
|
* Destructor, which is called automatically via the ObjectWrap mechanism
|
|
* when the corresponding high-level object gets gc'ed.
|
|
*/
|
|
RsaWrap::~RsaWrap()
|
|
{
|
|
if (rsa != NULL)
|
|
{
|
|
RSA_free(rsa);
|
|
}
|
|
}
|
|
|
|
NAN_METHOD(RsaWrap::OpenPublicSshKey)
|
|
{
|
|
NanScope();
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> obj_n = args[0].As<Object>();
|
|
Local<Object> obj_e = args[1].As<Object>();
|
|
int n_length = node::Buffer::Length(obj_n);
|
|
int e_length = node::Buffer::Length(obj_e);
|
|
unsigned char *data_n = (unsigned char *)malloc(n_length);
|
|
unsigned char *data_e = (unsigned char *)malloc(e_length);
|
|
memcpy(data_n, node::Buffer::Data(obj_n), n_length);
|
|
memcpy(data_e, node::Buffer::Data(obj_e), e_length);
|
|
|
|
if (obj->rsa == NULL)
|
|
{
|
|
obj->rsa = RSA_new();
|
|
}
|
|
|
|
obj->rsa_n = BN_new();
|
|
obj->rsa_e = BN_new();
|
|
|
|
BN_bin2bn(data_n, n_length, obj->rsa_n);
|
|
BN_bin2bn(data_e, e_length, obj->rsa_e);
|
|
RSA_set0_key(obj->rsa, obj->rsa_n, obj->rsa_e, NULL);
|
|
|
|
free(data_n);
|
|
free(data_e);
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
/**
|
|
* Get an (RsaWrap *) out of the given arguments, expecting the
|
|
* underlying (RSA *) to be non-null and more specifically a private
|
|
* key. Returns a non-null pointer on success. On failure, schedules
|
|
* an exception and returns null.
|
|
*/
|
|
RsaWrap *RsaWrap::expectPrivateKey(RsaWrap *obj)
|
|
{
|
|
obj = expectSet(obj);
|
|
|
|
if (obj != NULL) {
|
|
RSA_get0_key(obj->rsa, NULL, NULL, (const BIGNUM **)&obj->rsa_d);
|
|
}
|
|
|
|
// The "d" field should always be set on a private key and never
|
|
// set on a public key.
|
|
if ((obj == NULL) || (obj->rsa_d != NULL))
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
NanThrowError("Expected a private key.");
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Get an (RsaWrap *) out of the given arguments, expecting the underlying
|
|
* (RSA *) to be non-null. Returns a non-null pointer on success. On failure,
|
|
* schedules an exception and returns null.
|
|
*/
|
|
RsaWrap *RsaWrap::expectSet(RsaWrap *obj)
|
|
{
|
|
|
|
if (obj->rsa != NULL)
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
NanThrowError("Key not yet set.");
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Get an (RsaWrap *) out of the given arguments, expecting the underlying
|
|
* (RSA *) to be null. Returns a non-null pointer on success. On failure,
|
|
* schedules an exception and returns null.
|
|
*/
|
|
RsaWrap *RsaWrap::expectUnset(RsaWrap *obj)
|
|
{
|
|
|
|
if (obj->rsa == NULL)
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
NanThrowError("Key already set.");
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Construct an empty instance.
|
|
*/
|
|
NAN_METHOD(RsaWrap::New)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = new RsaWrap();
|
|
obj->Wrap(args.This());
|
|
|
|
NanReturnValue(args.This());
|
|
}
|
|
|
|
/**
|
|
* Set the underlying RSA struct to a newly-generated key pair.
|
|
*/
|
|
NAN_METHOD(RsaWrap::GeneratePrivateKey)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
// Sadly the change in V8 args type signature makes this messier now.
|
|
if (args.Length() < 1)
|
|
{
|
|
NanThrowError("Missing args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Missing args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[1]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int modulusBits = args[0]->Uint32Value();
|
|
int exponent = args[1]->Uint32Value();
|
|
|
|
// Sanity-check the arguments, since (as of this writing) OpenSSL
|
|
// either doesn't check, or at least doesn't consistently check:
|
|
//
|
|
// * The modulus bit count must be >= 512. Really, it just has to
|
|
// be a positive integer, but anything less than 512 is a
|
|
// horrendously bad idea.
|
|
//
|
|
// * The exponend must be positive and odd.
|
|
|
|
if (modulusBits < 512)
|
|
{
|
|
NanThrowError("Expected modulus bit count >= 512.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (exponent <= 0)
|
|
{
|
|
NanThrowError("Expected positive exponent.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if ((exponent & 1) == 0)
|
|
{
|
|
NanThrowError("Expected odd exponent.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
obj->rsa = generateKey(modulusBits, (unsigned long)exponent);
|
|
|
|
if (obj->rsa == NULL)
|
|
{
|
|
scheduleSslException();
|
|
}
|
|
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
/**
|
|
* Get the public exponent of the underlying RSA object. The return
|
|
* value is a Buffer containing the unsigned number in big-endian
|
|
* order.
|
|
*/
|
|
NAN_METHOD(RsaWrap::GetExponent)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
RSA_get0_key(obj->rsa, NULL, (const BIGNUM **)&obj->rsa_e, NULL);
|
|
bignumToBuffer(args, obj->rsa_e);
|
|
}
|
|
|
|
/**
|
|
* Get the private exponent of the underlying RSA object. The return
|
|
* value is a Buffer containing the unsigned number in big-endian
|
|
* order. The returned exponent is not encrypted in any way,
|
|
* so this should be used with caution.
|
|
*/
|
|
NAN_METHOD(RsaWrap::GetPrivateExponent)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectPrivateKey(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
RSA_get0_key(obj->rsa, NULL, NULL, (const BIGNUM **)&obj->rsa_d);
|
|
bignumToBuffer(args, obj->rsa_d);
|
|
}
|
|
|
|
/**
|
|
* Get the public modulus of the underlying RSA object. The return
|
|
* value is a Buffer containing the unsigned number in big-endian
|
|
* order.
|
|
*/
|
|
NAN_METHOD(RsaWrap::GetModulus)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
RSA_get0_key(obj->rsa, (const BIGNUM **)&obj->rsa_n, NULL, NULL);
|
|
bignumToBuffer(args, obj->rsa_n);
|
|
}
|
|
|
|
/**
|
|
* Get the private key of the underlying RSA object as a file
|
|
* in PEM format. The return value is a Buffer containing the
|
|
* file contents (in ASCII / UTF8). Note: This does not do any
|
|
* encryption of the results.
|
|
*/
|
|
NAN_METHOD(RsaWrap::GetPrivateKeyPem)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectPrivateKey(obj);
|
|
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
if (bio == NULL)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
char *password = NULL;
|
|
int passwordLen = 0;
|
|
const EVP_CIPHER *cipher = NULL;
|
|
|
|
if (args.Length() > 0)
|
|
{
|
|
Local<String> pstr = args[0].As<String>();
|
|
password = copyBufferToUtf8String(pstr);
|
|
|
|
Local<String> cstr = args[1].As<String>();
|
|
char *cipherName = copyBufferToUtf8String(cstr);
|
|
cipher = EVP_get_cipherbyname(cipherName);
|
|
free(cipherName);
|
|
}
|
|
|
|
if (password != NULL)
|
|
{
|
|
passwordLen = (int)strlen(password);
|
|
}
|
|
|
|
if (!PEM_write_bio_RSAPrivateKey(bio, obj->rsa,
|
|
cipher, (unsigned char *)password,
|
|
passwordLen, NULL, NULL))
|
|
{
|
|
scheduleSslException();
|
|
BIO_vfree(bio);
|
|
free(password);
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
free(password);
|
|
bioToBuffer(args, bio);
|
|
}
|
|
|
|
/**
|
|
* Get the public key of the underlying RSA object as a file
|
|
* in PEM format. The return value is a Buffer containing the
|
|
* file contents (in ASCII / UTF8).
|
|
*/
|
|
NAN_METHOD(RsaWrap::GetPublicKeyPem)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
if (bio == NULL)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!PEM_write_bio_RSA_PUBKEY(bio, obj->rsa))
|
|
{
|
|
scheduleSslException();
|
|
BIO_vfree(bio);
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
bioToBuffer(args, bio);
|
|
}
|
|
|
|
/**
|
|
* Perform decryption on the given buffer using the RSA key, which
|
|
* must be a private key, and padding mode.
|
|
*/
|
|
NAN_METHOD(RsaWrap::PrivateDecrypt)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectPrivateKey(obj);
|
|
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[0].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
size_t length = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
if (data == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int rsaLength = RSA_size(obj->rsa);
|
|
VAR_ARRAY(unsigned char, buf, rsaLength);
|
|
|
|
if (!args[1]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
int padding = args[1]->Uint32Value();
|
|
|
|
int bufLength = RSA_private_decrypt(length, (unsigned char *)data,
|
|
buf, obj->rsa, padding);
|
|
|
|
if (bufLength < 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> result = NanNewBufferHandle(bufLength);
|
|
memcpy(node::Buffer::Data(result), buf, bufLength);
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Perform encryption on the given buffer using the RSA key, which
|
|
* must be private, and padding mode.
|
|
*/
|
|
NAN_METHOD(RsaWrap::PrivateEncrypt)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectPrivateKey(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[0].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t length = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
if (data == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int rsaLength = RSA_size(obj->rsa);
|
|
Local<Object> result = NanNewBufferHandle(rsaLength);
|
|
|
|
if (!args[1]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
int padding = args[1]->Uint32Value();
|
|
|
|
int ret = RSA_private_encrypt(length, (unsigned char *)data,
|
|
(unsigned char *)node::Buffer::Data(result),
|
|
obj->rsa, padding);
|
|
|
|
if (ret < 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Perform decryption on the given buffer using the (public aspect of
|
|
* the) RSA key, and padding mode.
|
|
*/
|
|
NAN_METHOD(RsaWrap::PublicDecrypt)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[0].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t length = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
if (data == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int rsaLength = RSA_size(obj->rsa);
|
|
VAR_ARRAY(unsigned char, buf, rsaLength);
|
|
|
|
if (!args[1]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
int padding = args[1]->Uint32Value();
|
|
|
|
int bufLength = RSA_public_decrypt(length, (unsigned char *)data,
|
|
buf, obj->rsa, padding);
|
|
|
|
if (bufLength < 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> result = NanNewBufferHandle(bufLength);
|
|
memcpy(node::Buffer::Data(result), buf, bufLength);
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Perform encryption on the given buffer using the public (aspect of the)
|
|
* RSA key, and padding mode.
|
|
*/
|
|
NAN_METHOD(RsaWrap::PublicEncrypt)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[0].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t length = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
|
|
int rsaLength = RSA_size(obj->rsa);
|
|
Local<Object> result = NanNewBufferHandle(rsaLength);
|
|
|
|
if (!args[1]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
int padding = args[1]->Uint32Value();
|
|
|
|
int ret = RSA_public_encrypt(length, (unsigned char *)data,
|
|
(unsigned char *)node::Buffer::Data(result),
|
|
obj->rsa, padding);
|
|
|
|
if (ret < 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Sets the underlying RSA object to correspond to the given
|
|
* private key (a Buffer of PEM format data). This throws an
|
|
* exception if the underlying RSA had previously been set.
|
|
*/
|
|
NAN_METHOD(RsaWrap::SetPrivateKeyPem)
|
|
{
|
|
NanScope();
|
|
bool ok = true;
|
|
|
|
if (args.Length() < 1)
|
|
{
|
|
NanThrowError("Missing args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
ok &= (obj != NULL);
|
|
|
|
BIO *bio = NULL;
|
|
if (ok)
|
|
{
|
|
bio = getArg0Bio(args[0].As<Object>());
|
|
ok &= (bio != NULL);
|
|
}
|
|
|
|
Local<Object> buf = args[1].As<Object>();
|
|
char *password = NULL;
|
|
if (ok && (args.Length() >= 2))
|
|
{
|
|
password = copyBufferToCharStar(buf);
|
|
if (password == NULL)
|
|
{
|
|
NanThrowError("Expected a Buffer in args[1].");
|
|
}
|
|
ok &= (password != NULL);
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
obj->rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, password);
|
|
if (obj->rsa == NULL)
|
|
{
|
|
scheduleSslException();
|
|
}
|
|
}
|
|
|
|
if (bio != NULL)
|
|
{
|
|
BIO_vfree(bio);
|
|
}
|
|
if (password != NULL)
|
|
{
|
|
free(password);
|
|
};
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
/**
|
|
* Sets the underlying RSA object to correspond to the given
|
|
* public key (a Buffer of PEM format data). This throws an
|
|
* exception if the underlying RSA had previously been set.
|
|
*/
|
|
NAN_METHOD(RsaWrap::SetPublicKeyPem)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 1)
|
|
{
|
|
NanThrowError("Missing args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
BIO *bio = getArg0Bio(args[0].As<Object>());
|
|
if (bio == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
obj->rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
|
|
|
|
if (obj->rsa == NULL)
|
|
{
|
|
scheduleSslException();
|
|
}
|
|
|
|
BIO_vfree(bio);
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
/**
|
|
* Sign the given hash data. First argument indicates what kind of hash
|
|
* was performed. Returns a Buffer object.
|
|
*/
|
|
NAN_METHOD(RsaWrap::Sign)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectPrivateKey(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
int nid = args[0]->Uint32Value();
|
|
|
|
Local<Object> buffer = args[1].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t dataLength = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
if (data == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
unsigned int rsaSize = (unsigned int)RSA_size(obj->rsa);
|
|
unsigned int sigLength = rsaSize;
|
|
Local<Object> result = NanNewBufferHandle(sigLength);
|
|
|
|
int ret = RSA_sign(nid, (unsigned char *)data, dataLength,
|
|
(unsigned char *)node::Buffer::Data(result),
|
|
&sigLength, obj->rsa);
|
|
|
|
if (ret == 0)
|
|
{
|
|
// TODO: Will this leak the result buffer? Is it going to be gc'ed?
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (rsaSize != sigLength)
|
|
{
|
|
// Sanity check. Shouldn't ever happen in practice.
|
|
NanThrowError("Shouldn't happen.");
|
|
}
|
|
|
|
NanReturnValue(result);
|
|
}
|
|
|
|
/**
|
|
* Verify the signature on the given hash data. First argument indicates
|
|
* what kind of hash was performed. Throws an exception if the signature
|
|
* did not verify.
|
|
*/
|
|
NAN_METHOD(RsaWrap::Verify)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 3)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
int nid = args[0]->Uint32Value();
|
|
|
|
Local<Object> buffer = args[1].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t dataLength = node::Buffer::Length(buffer);
|
|
char *data = node::Buffer::Data(buffer);
|
|
if (data == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> sigBuffer = args[2].As<Object>();
|
|
if (!node::Buffer::HasInstance(sigBuffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[2].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t sigLength = node::Buffer::Length(sigBuffer);
|
|
char *sig = node::Buffer::Data(sigBuffer);
|
|
if (sig == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
int ret = RSA_verify(nid, (unsigned char *)data, dataLength,
|
|
(unsigned char *)sig, sigLength, obj->rsa);
|
|
if (ret == 0)
|
|
{
|
|
// Something went wrong; investigate!
|
|
unsigned long err = ERR_peek_error();
|
|
int lib = ERR_GET_LIB(err);
|
|
int reason = ERR_GET_REASON(err);
|
|
if ((lib == ERR_LIB_RSA) && (reason == RSA_R_BAD_SIGNATURE))
|
|
{
|
|
// This just means that the signature didn't match
|
|
// (as opposed to, say, a more dire failure in the library
|
|
// warranting an exception throw).
|
|
ERR_get_error(); // Consume the error (get it off the err stack).
|
|
NanReturnValue(NanFalse());
|
|
}
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(NanTrue());
|
|
}
|
|
|
|
/**
|
|
* Add PSS padding to a digest. First argument is digest algorithm ID,
|
|
* second is the digest, third is the salt length.
|
|
*/
|
|
NAN_METHOD(RsaWrap::AddPSSPadding)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 3)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
int nid = args[0]->Uint32Value();
|
|
const EVP_MD *Hash = EVP_get_digestbynid(nid);
|
|
if (Hash == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[1].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t mHashLength = node::Buffer::Length(buffer);
|
|
char *mHash = node::Buffer::Data(buffer);
|
|
if (mHash == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
if (mHashLength != (size_t)EVP_MD_size(Hash))
|
|
{
|
|
NanThrowError("Incorrect hash size.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[2]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[2].");
|
|
NanReturnUndefined();
|
|
}
|
|
int sLen = args[2]->Uint32Value();
|
|
|
|
unsigned int emLength = (unsigned int)RSA_size(obj->rsa);
|
|
Local<Object> EM = NanNewBufferHandle(emLength);
|
|
|
|
int ret = RSA_padding_add_PKCS1_PSS(obj->rsa,
|
|
(unsigned char *)node::Buffer::Data(EM),
|
|
(unsigned char *)mHash, Hash, sLen);
|
|
if (ret == 0)
|
|
{
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(EM);
|
|
}
|
|
|
|
/**
|
|
* Verify a signature with PSS padding. First argument is digest algorithm ID,
|
|
* second is the digest, third is the padded digest, fourth is the salt length.
|
|
*/
|
|
NAN_METHOD(RsaWrap::VerifyPSSPadding)
|
|
{
|
|
NanScope();
|
|
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectSet(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 4)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[0]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[0].");
|
|
NanReturnUndefined();
|
|
}
|
|
int nid = args[0]->Uint32Value();
|
|
const EVP_MD *Hash = EVP_get_digestbynid(nid);
|
|
if (Hash == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> buffer = args[1].As<Object>();
|
|
if (!node::Buffer::HasInstance(buffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[1].");
|
|
NanReturnUndefined();
|
|
}
|
|
size_t mHashLength = node::Buffer::Length(buffer);
|
|
char *mHash = node::Buffer::Data(buffer);
|
|
if (mHash == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
if (mHashLength != (size_t)EVP_MD_size(Hash))
|
|
{
|
|
NanThrowError("Incorrect hash size.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
Local<Object> emBuffer = args[2].As<Object>();
|
|
if (!node::Buffer::HasInstance(emBuffer))
|
|
{
|
|
NanThrowError("Expected a Buffer in args[2].");
|
|
NanReturnUndefined();
|
|
}
|
|
if (node::Buffer::Length(emBuffer) != (size_t)RSA_size(obj->rsa))
|
|
{
|
|
NanThrowError("Incorrect encoded message size.");
|
|
NanReturnUndefined();
|
|
}
|
|
char *EM = node::Buffer::Data(emBuffer);
|
|
if (EM == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (!args[3]->IsInt32())
|
|
{
|
|
NanThrowError("Expected a 32-bit integer in args[3].");
|
|
NanReturnUndefined();
|
|
}
|
|
int sLen = args[3]->Uint32Value();
|
|
|
|
int ret = RSA_verify_PKCS1_PSS(obj->rsa,
|
|
(unsigned char *)mHash, Hash, (unsigned char *)EM, sLen);
|
|
if (ret == 0)
|
|
{
|
|
// Something went wrong; investigate!
|
|
unsigned long err = ERR_peek_error();
|
|
int lib = ERR_GET_LIB(err);
|
|
int reason = ERR_GET_REASON(err);
|
|
if ((lib == ERR_LIB_RSA) && (reason == RSA_R_BAD_SIGNATURE))
|
|
{
|
|
// This just means that the signature didn't match
|
|
// (as opposed to, say, a more dire failure in the library
|
|
// warranting an exception throw).
|
|
ERR_get_error(); // Consume the error (get it off the err stack).
|
|
NanReturnValue(NanFalse());
|
|
}
|
|
scheduleSslException();
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NanReturnValue(NanTrue());
|
|
}
|
|
|
|
NAN_METHOD(RsaWrap::CreatePrivateKeyFromComponents)
|
|
{
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 8)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
obj->rsa = RSA_new();
|
|
if (obj->rsa == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
BIGNUM *modulus = NULL;
|
|
BIGNUM *exponent = NULL;
|
|
BIGNUM *p = NULL;
|
|
BIGNUM *q = NULL;
|
|
BIGNUM *dp = NULL;
|
|
BIGNUM *dq = NULL;
|
|
BIGNUM *inverseQ = NULL;
|
|
BIGNUM *d = NULL;
|
|
|
|
bool ok = true;
|
|
|
|
modulus = getArgXBigNum(args[0].As<Object>());
|
|
ok &= (modulus != NULL);
|
|
if (ok)
|
|
{
|
|
exponent = getArgXBigNum(args[1].As<Object>());
|
|
ok &= (exponent != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
p = getArgXBigNum(args[2].As<Object>());
|
|
ok &= (p != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
q = getArgXBigNum(args[3].As<Object>());
|
|
ok &= (q != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
dp = getArgXBigNum(args[4].As<Object>());
|
|
ok &= (dp != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
dq = getArgXBigNum(args[5].As<Object>());
|
|
ok &= (dq != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
inverseQ = getArgXBigNum(args[6].As<Object>());
|
|
ok &= (inverseQ != NULL);
|
|
}
|
|
if (ok)
|
|
{
|
|
d = getArgXBigNum(args[7].As<Object>());
|
|
ok &= (d != NULL);
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
RSA_set0_key(obj->rsa, modulus, exponent, d);
|
|
RSA_set0_factors(obj->rsa, p, q);
|
|
RSA_set0_crt_params(obj->rsa, dp, dq, inverseQ);
|
|
}
|
|
else
|
|
{
|
|
if (modulus)
|
|
{
|
|
BN_free(modulus);
|
|
}
|
|
if (exponent)
|
|
{
|
|
BN_free(exponent);
|
|
}
|
|
if (p)
|
|
{
|
|
BN_free(p);
|
|
}
|
|
if (q)
|
|
{
|
|
BN_free(q);
|
|
}
|
|
if (dp)
|
|
{
|
|
BN_free(dp);
|
|
}
|
|
if (dq)
|
|
{
|
|
BN_free(dq);
|
|
}
|
|
if (inverseQ)
|
|
{
|
|
BN_free(inverseQ);
|
|
}
|
|
if (d)
|
|
{
|
|
BN_free(d);
|
|
}
|
|
}
|
|
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
NAN_METHOD(RsaWrap::CreatePublicKeyFromComponents)
|
|
{
|
|
RsaWrap *obj = ObjectWrap::Unwrap<RsaWrap>(args.Holder());
|
|
obj = expectUnset(obj);
|
|
if (obj == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
NanThrowError("Not enough args.");
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
obj->rsa = RSA_new();
|
|
if (obj->rsa == NULL)
|
|
{
|
|
NanReturnUndefined();
|
|
}
|
|
|
|
BIGNUM *modulus = NULL;
|
|
BIGNUM *exponent = NULL;
|
|
|
|
bool ok = true;
|
|
|
|
modulus = getArgXBigNum(args[0].As<Object>());
|
|
ok &= (modulus != NULL);
|
|
if (ok)
|
|
{
|
|
exponent = getArgXBigNum(args[1].As<Object>());
|
|
ok &= (exponent != NULL);
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
RSA_set0_key(obj->rsa, modulus, exponent, NULL);
|
|
}
|
|
else
|
|
{
|
|
if (modulus)
|
|
{
|
|
BN_free(modulus);
|
|
}
|
|
if (exponent)
|
|
{
|
|
BN_free(exponent);
|
|
}
|
|
}
|
|
|
|
NanReturnUndefined();
|
|
}
|