Skip to content

Commit 3f844e2

Browse files
author
Chris Andrews
committed
New module - "rsa".
Provides access to RSA routines from OpenSSL, for public-key encryption and decryption.
0 parents  commit 3f844e2

11 files changed

Lines changed: 567 additions & 0 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*~
2+
.#*
3+
*#
4+
.lock-wscript
5+
build
6+
rsaBinding.node

LICENCE

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2011 Chris Andrews. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification, are
4+
permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice, this list of
7+
conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice, this list
10+
of conditions and the following disclaimer in the documentation and/or other materials
11+
provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY CHRIS ANDREWS ``AS IS'' AND ANY EXPRESS OR IMPLIED
14+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CHRIS ANDREWS OR
16+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
21+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
The views and conclusions contained in the software and documentation are those of the
24+
authors and should not be interpreted as representing official policies, either expressed
25+
or implied, of Chris Andrews.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# node-rsa
2+
3+
This module provides access to RSA public-key routines from OpenSSL.
4+
5+
Support is limited to RSAES-OAEP and encryption with a public key,
6+
decryption with a private key.
7+
8+
## Usage
9+
10+
See test/test.js.
11+
12+
## Licence
13+
14+
BSD, see LICENCE.

node_rsa.cc

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
#include "node_rsa.h"
2+
3+
#include <v8.h>
4+
5+
#include <node.h>
6+
#include <node_buffer.h>
7+
8+
#include <string.h>
9+
#include <stdlib.h>
10+
11+
#include <errno.h>
12+
13+
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
14+
# define OPENSSL_CONST const
15+
#else
16+
# define OPENSSL_CONST
17+
#endif
18+
19+
namespace node {
20+
21+
using namespace v8;
22+
23+
// hex_encode, hex_decode, base64, unbase64 nicked from node_crypto.cc
24+
25+
void hex_encode(unsigned char *md_value, int md_len, char** md_hexdigest,
26+
int* md_hex_len) {
27+
*md_hex_len = (2*(md_len));
28+
*md_hexdigest = (char *) malloc(*md_hex_len + 1);
29+
for (int i = 0; i < md_len; i++) {
30+
sprintf((char *)(*md_hexdigest + (i*2)), "%02x", md_value[i]);
31+
}
32+
}
33+
34+
#define hex2i(c) ((c) <= '9' ? ((c) - '0') : (c) <= 'Z' ? ((c) - 'A' + 10) \
35+
: ((c) - 'a' + 10))
36+
void hex_decode(unsigned char *input, int length, char** buf64,
37+
int* buf64_len) {
38+
*buf64_len = (length/2);
39+
*buf64 = (char*) malloc(length/2 + 1);
40+
char *b = *buf64;
41+
for(int i = 0; i < length-1; i+=2) {
42+
b[i/2] = (hex2i(input[i])<<4) | (hex2i(input[i+1]));
43+
}
44+
}
45+
46+
void base64(unsigned char *input, int length, char** buf64, int* buf64_len)
47+
{
48+
BIO *bmem, *b64;
49+
BUF_MEM *bptr;
50+
51+
b64 = BIO_new(BIO_f_base64());
52+
bmem = BIO_new(BIO_s_mem());
53+
b64 = BIO_push(b64, bmem);
54+
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
55+
BIO_write(b64, input, length);
56+
(void)BIO_flush(b64);
57+
BIO_get_mem_ptr(b64, &bptr);
58+
59+
*buf64_len = bptr->length;
60+
*buf64 = (char *)malloc(*buf64_len+1);
61+
memcpy(*buf64, bptr->data, bptr->length);
62+
char* b = *buf64;
63+
b[bptr->length] = 0;
64+
65+
BIO_free_all(b64);
66+
67+
}
68+
69+
void unbase64(unsigned char *input, int length, char** buffer, int* buffer_len)
70+
{
71+
BIO *b64, *bmem;
72+
*buffer = (char *)malloc(length);
73+
memset(*buffer, 0, length);
74+
75+
b64 = BIO_new(BIO_f_base64());
76+
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
77+
bmem = BIO_new_mem_buf(input, length);
78+
bmem = BIO_push(b64, bmem);
79+
80+
*buffer_len = BIO_read(bmem, *buffer, length);
81+
BIO_free_all(bmem);
82+
}
83+
84+
85+
86+
void RsaKeypair::Initialize(Handle<Object> target) {
87+
HandleScope scope;
88+
89+
Local<FunctionTemplate> t = FunctionTemplate::New(RsaKeypair::New);
90+
t->InstanceTemplate()->SetInternalFieldCount(1);
91+
t->SetClassName(String::NewSymbol("RsaKeypair"));
92+
93+
NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey",
94+
RsaKeypair::SetPublicKey);
95+
NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey",
96+
RsaKeypair::SetPrivateKey);
97+
NODE_SET_PROTOTYPE_METHOD(t, "encrypt",
98+
RsaKeypair::Encrypt);
99+
NODE_SET_PROTOTYPE_METHOD(t, "decrypt",
100+
RsaKeypair::Decrypt);
101+
102+
target->Set(String::NewSymbol("RsaKeypair"), t->GetFunction());
103+
}
104+
105+
Handle<Value> RsaKeypair::New(const Arguments& args) {
106+
HandleScope scope;
107+
RsaKeypair *p = new RsaKeypair();
108+
p->Wrap(args.Holder());
109+
p->privateKey = NULL;
110+
p->publicKey = NULL;
111+
return args.This();
112+
}
113+
114+
Handle<Value> RsaKeypair::SetPublicKey(const Arguments& args) {
115+
HandleScope scope;
116+
117+
RsaKeypair *kp = ObjectWrap::Unwrap<RsaKeypair>(args.Holder());
118+
119+
if (args.Length() != 1 ||
120+
!args[0]->IsString()) {
121+
return ThrowException(Exception::TypeError(
122+
String::New("Bad parameter")));
123+
}
124+
String::Utf8Value pubKey(args[0]->ToString());
125+
126+
BIO *bp = NULL;
127+
RSA *key = NULL;
128+
129+
bp = BIO_new(BIO_s_mem());
130+
if (!BIO_write(bp, *pubKey, strlen(*pubKey)))
131+
return False();
132+
133+
key = PEM_read_bio_RSA_PUBKEY(bp, NULL, NULL, NULL);
134+
if (key == NULL)
135+
return False();
136+
137+
kp->publicKey = key;
138+
BIO_free(bp);
139+
140+
return True();
141+
}
142+
143+
Handle<Value> RsaKeypair::SetPrivateKey(const Arguments& args) {
144+
HandleScope scope;
145+
146+
RsaKeypair *kp = ObjectWrap::Unwrap<RsaKeypair>(args.Holder());
147+
148+
if (args.Length() == 2 &&
149+
(!args[0]->IsString() || !args[1]->IsString())) {
150+
return ThrowException(Exception::TypeError(
151+
String::New("Bad parameter")));
152+
}
153+
if (args.Length() == 1 &&
154+
(!args[0]->IsString())) {
155+
return ThrowException(Exception::TypeError(
156+
String::New("Bad parameter")));
157+
}
158+
159+
BIO *bp = NULL;
160+
String::Utf8Value privKey(args[0]->ToString());
161+
162+
bp = BIO_new(BIO_s_mem());
163+
if (!BIO_write(bp, *privKey, strlen(*privKey)))
164+
return False();
165+
166+
RSA *key;
167+
if (args.Length() == 2) {
168+
String::Utf8Value passphrase(args[1]->ToString());
169+
key = PEM_read_bio_RSAPrivateKey(bp, NULL, 0, *passphrase);
170+
}
171+
else {
172+
key = PEM_read_bio_RSAPrivateKey(bp, NULL, 0, NULL);
173+
}
174+
if (key == NULL) {
175+
return False();
176+
}
177+
178+
kp->privateKey = key;
179+
BIO_free(bp);
180+
181+
return True();
182+
}
183+
184+
Handle<Value> RsaKeypair::Encrypt(const Arguments& args) {
185+
HandleScope scope;
186+
187+
RsaKeypair *kp = ObjectWrap::Unwrap<RsaKeypair>(args.Holder());
188+
189+
if (kp->publicKey == NULL) {
190+
Local<Value> exception = Exception::Error(String::New("Can't encrypt, no public key"));
191+
return ThrowException(exception);
192+
}
193+
194+
enum encoding enc = ParseEncoding(args[1]);
195+
ssize_t len = DecodeBytes(args[0], enc);
196+
197+
if (len < 0) {
198+
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
199+
return ThrowException(exception);
200+
}
201+
202+
// check per RSA_public_encrypt(3) when using OAEP
203+
if (len >= RSA_size(kp->publicKey) - 41) {
204+
Local<Value> exception = Exception::TypeError(String::New("Bad argument (too long for key size)"));
205+
return ThrowException(exception);
206+
}
207+
208+
unsigned char* buf = new unsigned char[len];
209+
ssize_t written = DecodeWrite((char *)buf, len, args[0], enc);
210+
assert(written == len);
211+
212+
int out_len = RSA_size(kp->publicKey);
213+
unsigned char *out = (unsigned char*)malloc(out_len);
214+
215+
int r = RSA_public_encrypt(len, buf, out, kp->publicKey, RSA_PKCS1_OAEP_PADDING);
216+
217+
if (r < 0) {
218+
char *err = ERR_error_string(ERR_get_error(), NULL);
219+
Local<String> full_err = String::Concat(String::New("RSA encrypt: "), String::New(err));
220+
Local<Value> exception = Exception::Error(full_err);
221+
return ThrowException(exception);
222+
}
223+
224+
Local<Value> outString;
225+
if (out_len == 0) {
226+
outString = String::New("");
227+
}
228+
else {
229+
if (args.Length() <= 2 || !args[2]->IsString()) {
230+
outString = Encode(out, out_len, BINARY);
231+
} else {
232+
char* out_hexdigest;
233+
int out_hex_len;
234+
String::Utf8Value encoding(args[2]->ToString());
235+
if (strcasecmp(*encoding, "hex") == 0) {
236+
hex_encode(out, out_len, &out_hexdigest, &out_hex_len);
237+
outString = Encode(out_hexdigest, out_hex_len, BINARY);
238+
free(out_hexdigest);
239+
} else if (strcasecmp(*encoding, "base64") == 0) {
240+
base64(out, out_len, &out_hexdigest, &out_hex_len);
241+
outString = Encode(out_hexdigest, out_hex_len, BINARY);
242+
free(out_hexdigest);
243+
} else if (strcasecmp(*encoding, "binary") == 0) {
244+
outString = Encode(out, out_len, BINARY);
245+
} else {
246+
outString = String::New("");
247+
Local<Value> exception = Exception::Error(String::New("RsaKeypair.encrypt encoding "
248+
"can be binary, base64 or hex"));
249+
return ThrowException(exception);
250+
}
251+
}
252+
}
253+
if (out) free(out);
254+
return scope.Close(outString);
255+
}
256+
257+
Handle<Value> RsaKeypair::Decrypt(const Arguments& args) {
258+
HandleScope scope;
259+
260+
RsaKeypair *kp = ObjectWrap::Unwrap<RsaKeypair>(args.Holder());
261+
262+
if (kp->privateKey == NULL) {
263+
Local<Value> exception = Exception::Error(String::New("Can't decrypt, no private key"));
264+
return ThrowException(exception);
265+
}
266+
267+
ssize_t len = DecodeBytes(args[0], BINARY);
268+
unsigned char* buf = new unsigned char[len];
269+
(void)DecodeWrite((char *)buf, len, args[0], BINARY);
270+
unsigned char* ciphertext;
271+
int ciphertext_len;
272+
273+
if (args.Length() <= 1 || !args[1]->IsString()) {
274+
// Binary - do nothing
275+
} else {
276+
String::Utf8Value encoding(args[1]->ToString());
277+
if (strcasecmp(*encoding, "hex") == 0) {
278+
hex_decode((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len);
279+
free(buf);
280+
buf = ciphertext;
281+
len = ciphertext_len;
282+
} else if (strcasecmp(*encoding, "base64") == 0) {
283+
unbase64((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len);
284+
free(buf);
285+
buf = ciphertext;
286+
len = ciphertext_len;
287+
} else if (strcasecmp(*encoding, "binary") == 0) {
288+
// Binary - do nothing
289+
} else {
290+
Local<Value> exception = Exception::Error(String::New("RsaKeypair.decrypt encoding "
291+
"can be binary, base64 or hex"));
292+
return ThrowException(exception);
293+
}
294+
}
295+
296+
// XXX is this check unnecessary? is it just len <= keysize?
297+
// check per RSA_public_encrypt(3) when using OAEP
298+
//if (len > RSA_size(kp->privateKey) - 41) {
299+
// Local<Value> exception = Exception::Error(String::New("Bad argument (too long for key size)"));
300+
// return ThrowException(exception);
301+
//}
302+
303+
int out_len = RSA_size(kp->privateKey);
304+
unsigned char *out = (unsigned char*)malloc(out_len);
305+
306+
out_len = RSA_private_decrypt(len, buf, out, kp->privateKey, RSA_PKCS1_OAEP_PADDING);
307+
308+
if (out_len < 0) {
309+
char *err = ERR_error_string(ERR_get_error(), NULL);
310+
Local<String> full_err = String::Concat(String::New("RSA decrypt: "), String::New(err));
311+
Local<Value> exception = Exception::Error(full_err);
312+
return ThrowException(exception);
313+
}
314+
315+
Local<Value> outString;
316+
if (out_len == 0) {
317+
outString = String::New("");
318+
} else if (args.Length() <= 2 || !args[2]->IsString()) {
319+
outString = Encode(out, out_len, BINARY);
320+
} else {
321+
enum encoding enc = ParseEncoding(args[2]);
322+
outString = Encode(out, out_len, enc);
323+
}
324+
325+
if (out) free(out);
326+
free(buf);
327+
return scope.Close(outString);
328+
}
329+
330+
331+
extern "C" void
332+
init(Handle<Object> target) {
333+
RsaKeypair::Initialize(target);
334+
}
335+
336+
} // namespace node

0 commit comments

Comments
 (0)