From 9b594880c88b0fe7ba14df263cc571e507f50bd6 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 9 Apr 2019 19:02:02 +0900
Subject: [PATCH] Restore crypto_key to migration

---
 binding.gyp         |   9 ++++
 gulpfile.ts         |   1 +
 package.json        |   1 +
 src/crypto_key.cc   | 111 ++++++++++++++++++++++++++++++++++++++++++++
 src/crypto_key.d.ts |   2 +
 5 files changed, 124 insertions(+)
 create mode 100644 binding.gyp
 create mode 100644 src/crypto_key.cc
 create mode 100644 src/crypto_key.d.ts

diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000000..0349526d52
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,9 @@
+{
+	'targets': [
+		{
+			'target_name': 'crypto_key',
+			'sources': ['src/crypto_key.cc'],
+			'include_dirs': ['<!(node -e "require(\'nan\')")']
+		}
+	]
+}
diff --git a/gulpfile.ts b/gulpfile.ts
index bf0b87ef20..b2956c2403 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -49,6 +49,7 @@ gulp.task('build:copy:views', () =>
 
 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 cc71744971..476a85f387 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 0000000000..658586baed
--- /dev/null
+++ b/src/crypto_key.cc
@@ -0,0 +1,111 @@
+#include <nan.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/crypto.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+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<v8::String>("extractPublic").ToLocalChecked(),
+		Nan::GetFunction(Nan::New<v8::FunctionTemplate>(extractPublic)).ToLocalChecked());
+
+	Nan::Set(target, Nan::New<v8::String>("generate").ToLocalChecked(),
+		Nan::GetFunction(Nan::New<v8::FunctionTemplate>(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 0000000000..9aa81a687c
--- /dev/null
+++ b/src/crypto_key.d.ts
@@ -0,0 +1,2 @@
+export function extractPublic(keypair: string): string;
+export function generate(): string;