diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 000000000..0349526d5 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'crypto_key', + 'sources': ['src/crypto_key.cc'], + 'include_dirs': [' gulp.task('build:copy', gulp.parallel('build:copy:views', () => gulp.src([ + './build/Release/crypto_key.node', './src/const.json', './src/server/web/views/**/*', './src/**/assets/**/*', diff --git a/package.json b/package.json index cc7174497..476a85f38 100644 --- a/package.json +++ b/package.json @@ -174,6 +174,7 @@ "mongodb": "3.2.3", "monk": "6.0.6", "ms": "2.1.1", + "nan": "2.12.1", "nested-property": "0.0.7", "node-fetch": "2.3.0", "nodemailer": "5.1.1", diff --git a/src/crypto_key.cc b/src/crypto_key.cc new file mode 100644 index 000000000..658586bae --- /dev/null +++ b/src/crypto_key.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include + +NAN_METHOD(extractPublic) +{ + const auto sourceString = info[0]->ToString(Nan::GetCurrentContext()).ToLocalChecked(); + if (!sourceString->IsOneByte()) { + Nan::ThrowError("Malformed character found"); + return; + } + + size_t sourceLength = sourceString->Length(); + const auto sourceBuf = new char[sourceLength]; + + Nan::DecodeWrite(sourceBuf, sourceLength, sourceString); + + const auto source = BIO_new_mem_buf(sourceBuf, sourceLength); + if (source == nullptr) { + Nan::ThrowError("Memory allocation failed"); + delete[] sourceBuf; + return; + } + + const auto rsa = PEM_read_bio_RSAPrivateKey(source, nullptr, nullptr, nullptr); + + BIO_free(source); + delete[] sourceBuf; + + if (rsa == nullptr) { + Nan::ThrowError("Decode failed"); + return; + } + + const auto destination = BIO_new(BIO_s_mem()); + if (destination == nullptr) { + Nan::ThrowError("Memory allocation failed"); + return; + } + + const auto result = PEM_write_bio_RSAPublicKey(destination, rsa); + + RSA_free(rsa); + + if (result != 1) { + Nan::ThrowError("Public key extraction failed"); + BIO_free(destination); + return; + } + + char *pem; + const auto pemLength = BIO_get_mem_data(destination, &pem); + + info.GetReturnValue().Set(Nan::Encode(pem, pemLength)); + BIO_free(destination); +} + +NAN_METHOD(generate) +{ + const auto exponent = BN_new(); + const auto mem = BIO_new(BIO_s_mem()); + const auto rsa = RSA_new(); + char *data; + long result; + + if (exponent == nullptr || mem == nullptr || rsa == nullptr) { + Nan::ThrowError("Memory allocation failed"); + goto done; + } + + result = BN_set_word(exponent, 65537); + if (result != 1) { + Nan::ThrowError("Exponent setting failed"); + goto done; + } + + result = RSA_generate_key_ex(rsa, 2048, exponent, nullptr); + if (result != 1) { + Nan::ThrowError("Key generation failed"); + goto done; + } + + result = PEM_write_bio_RSAPrivateKey(mem, rsa, NULL, NULL, 0, NULL, NULL); + if (result != 1) { + Nan::ThrowError("Key export failed"); + goto done; + } + + result = BIO_get_mem_data(mem, &data); + info.GetReturnValue().Set(Nan::Encode(data, result)); + +done: + RSA_free(rsa); + BIO_free(mem); + BN_free(exponent); +} + +NAN_MODULE_INIT(InitAll) +{ + Nan::Set(target, Nan::New("extractPublic").ToLocalChecked(), + Nan::GetFunction(Nan::New(extractPublic)).ToLocalChecked()); + + Nan::Set(target, Nan::New("generate").ToLocalChecked(), + Nan::GetFunction(Nan::New(generate)).ToLocalChecked()); +} + +NODE_MODULE(crypto_key, InitAll); diff --git a/src/crypto_key.d.ts b/src/crypto_key.d.ts new file mode 100644 index 000000000..9aa81a687 --- /dev/null +++ b/src/crypto_key.d.ts @@ -0,0 +1,2 @@ +export function extractPublic(keypair: string): string; +export function generate(): string;