// Copyright 2012 The Obvious Corporation. #include "ursaNative.h" #include #include #include #include #include #include #include using namespace v8; #ifdef _WIN32 #include #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 #include 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 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 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 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 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 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 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(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 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 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 str = args[0].As(); char *name = copyBufferToUtf8String(str); if (name == NULL) { NanReturnUndefined(); } int nid = OBJ_txt2nid(name); free(name); if (nid == NID_undef) { scheduleSslException(); NanReturnUndefined(); } NanReturnValue(NanNew(nid)); } /* * RsaWrap implementation */ /** * Initialize the bindings for this class. */ void RsaWrap::InitClass(Local target) { Local className = NanNew("RsaWrap").ToLocalChecked(); // Basic instance setup Local tpl = NanNew(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(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(args.Holder()); obj = expectUnset(obj); if (args.Length() < 2) { NanThrowError("Not enough args."); NanReturnUndefined(); } Local obj_n = args[0].As(); Local obj_e = args[1].As(); 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(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(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(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(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(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 pstr = args[0].As(); password = copyBufferToUtf8String(pstr); Local cstr = args[1].As(); 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(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(args.Holder()); obj = expectPrivateKey(obj); if (obj == NULL) { NanReturnUndefined(); } if (args.Length() < 2) { NanThrowError("Not enough args."); NanReturnUndefined(); } Local buffer = args[0].As(); 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 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(args.Holder()); obj = expectPrivateKey(obj); if (obj == NULL) { NanReturnUndefined(); } if (args.Length() < 2) { NanThrowError("Not enough args."); NanReturnUndefined(); } Local buffer = args[0].As(); 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 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(args.Holder()); obj = expectSet(obj); if (obj == NULL) { NanReturnUndefined(); } if (args.Length() < 2) { NanThrowError("Not enough args."); NanReturnUndefined(); } Local buffer = args[0].As(); 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 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(args.Holder()); obj = expectSet(obj); if (obj == NULL) { NanReturnUndefined(); } if (args.Length() < 2) { NanThrowError("Not enough args."); NanReturnUndefined(); } Local buffer = args[0].As(); 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 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(args.Holder()); obj = expectUnset(obj); ok &= (obj != NULL); BIO *bio = NULL; if (ok) { bio = getArg0Bio(args[0].As()); ok &= (bio != NULL); } Local buf = args[1].As(); 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(args.Holder()); obj = expectUnset(obj); if (obj == NULL) { NanReturnUndefined(); } if (args.Length() < 1) { NanThrowError("Missing args[0]."); NanReturnUndefined(); } BIO *bio = getArg0Bio(args[0].As()); 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(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 buffer = args[1].As(); 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 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(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 buffer = args[1].As(); 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 sigBuffer = args[2].As(); 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(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 buffer = args[1].As(); 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 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(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 buffer = args[1].As(); 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 emBuffer = args[2].As(); 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(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()); ok &= (modulus != NULL); if (ok) { exponent = getArgXBigNum(args[1].As()); ok &= (exponent != NULL); } if (ok) { p = getArgXBigNum(args[2].As()); ok &= (p != NULL); } if (ok) { q = getArgXBigNum(args[3].As()); ok &= (q != NULL); } if (ok) { dp = getArgXBigNum(args[4].As()); ok &= (dp != NULL); } if (ok) { dq = getArgXBigNum(args[5].As()); ok &= (dq != NULL); } if (ok) { inverseQ = getArgXBigNum(args[6].As()); ok &= (inverseQ != NULL); } if (ok) { d = getArgXBigNum(args[7].As()); 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(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()); ok &= (modulus != NULL); if (ok) { exponent = getArgXBigNum(args[1].As()); 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(); }