Lukas Nowy fd947bd852 https
express server läuft jetzt mit https
2018-12-16 19:08:08 +01:00

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();
}