Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keypair to local account #1303

Merged
merged 1 commit into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.config
/.vscode
/node_modules
/build
/built
/data
npm-debug.log
Expand Down
9 changes: 9 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
'targets': [
{
'target_name': 'crypto_key',
'sources': ['src/crypto_key.cc'],
'include_dirs': ['<!(node -e "require(\'nan\')")']
}
]
}
1 change: 1 addition & 0 deletions gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ gulp.task('build:ts', () => {

gulp.task('build:copy', () =>
gulp.src([
'./build/Release/crypto_key.node',
'./src/**/assets/**/*',
'!./src/web/app/**/assets/**/*'
]).pipe(gulp.dest('./built/'))
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"morgan": "1.9.0",
"ms": "2.1.1",
"multer": "1.3.0",
"nan": "^2.10.0",
"node-sass": "4.7.2",
"node-sass-json-importer": "3.1.5",
"nprogress": "0.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/api/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type IUser = {
is_suspended: boolean;
keywords: string[];
account: {
keypair: string;
email: string;
links: string[];
password: string;
Expand Down Expand Up @@ -160,6 +161,7 @@ export const pack = (
delete _user.latest_post;

// Remove private properties
delete _user.account.keypair;
delete _user.account.password;
delete _user.account.token;
delete _user.account.two_factor_temp_secret;
Expand Down
2 changes: 2 additions & 0 deletions src/api/private/signup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as uuid from 'uuid';
import * as express from 'express';
import * as bcrypt from 'bcryptjs';
import { generate as generateKeypair } from '../../crypto_key';
import recaptcha = require('recaptcha-promise');
import User, { IUser, validateUsername, validatePassword, pack } from '../models/user';
import generateUserToken from '../common/generate-native-user-token';
Expand Down Expand Up @@ -119,6 +120,7 @@ export default async (req: express.Request, res: express.Response) => {
username: username,
username_lower: username.toLowerCase(),
account: {
keypair: generateKeypair(),
token: secret,
email: null,
links: null,
Expand Down
111 changes: 111 additions & 0 deletions src/crypto_key.cc
Original file line number Diff line number Diff line change
@@ -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();
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);
1 change: 1 addition & 0 deletions src/crypto_key.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function generate(): String;
2 changes: 2 additions & 0 deletions test/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@ function insertSakurako(opts) {
username: 'sakurako',
username_lower: 'sakurako',
account: {
keypair: '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtdTG9rlFWjNqhgbg2V6X5XF1WpQXZS3KNXykEWl2UAiMyfVV\nBvf3zQP0dDEdNtcqdPJgis03bpiHCzQusc/YLyHYB0m+TJXsxJatb8cqUogOFeE4\ngQ4Dc5kAT6gLh/d4yz03EIg9bizX07EiGWnZqWxb+21ypqsPxST64sAtG9f5O/G4\nXe2m3cSbfAAvEUP1Ig1LUNyJB4jhM60w1cQic/qO8++sk/+GoX9g71X+i4NArGv+\n1c11acDIIPGAAQpFeYVeGaKakNDNp8RtJJp8R8FLwJXZ4/gATBnScCiHUSrGfRly\nYyR0w/BNlQ6/NijAdB9pR5csPvyIPkx1gauZewIDAQABAoIBAQCwWf/mhuY2h6uG\n9eDZsZ7Mj2/sO7k9Dl4R5iMSKCDxmnlB3slqitExa+aJUqEs8R5icjkkJcjfYNuJ\nCEFJf3YCsGZfGyyQBtCuEh2ATcBEb2SJ3/f3YuoCEaB1oVwdsOzc4TAovpol4yQo\nUqHp1/mdElVb01jhQQN4h1c02IJnfzvfU1C8szBni+Etfd+MxqGfv006DY3KOEb3\nlCrCS3GmooJW2Fjj7q1kCcaEQbMB1/aQHLXd1qe3KJOzXh3Voxsp/jEH0hvp2TII\nfY9UK+b7mA+xlvXwKuTkHVaZm0ylg0nbembS8MF4GfFMujinSexvLrVKaQhdMFoF\nvBLxHYHRAoGBANfNVYJYeCDPFNLmak5Xg33Rfvc2II8UmrZOVdhOWs8ZK0pis9e+\nPo2MKtTzrzipXI2QXv5w7kO+LJWNDva+xRlW8Wlj9Dde9QdQ7Y8+dk7SJgf24DzM\n023elgX5DvTeLODjStk6SMPRL0FmGovUqAAA8ZeHtJzkIr1HROWnQiwnAoGBANez\nhFwKnVoQu0RpBz/i4W0RKIxOwltN2zmlN8KjJPhSy00A7nBUfKLRbcwiSHE98Yi/\nUrXwMwR5QeD2ngngRppddJnpiRfjNjnsaqeqNtpO8AxB3XjpCC5zmHUMFHKvPpDj\n1zU/F44li0YjKcMBebZy9PbfAjrIgJfxhPo/oXiNAoGAfx6gaTjOAp2ZaaZ7Jozc\nkyft/5et1DrR6+P3I4T8bxQncRj1UXfqhxzzOiAVrm3tbCKIIp/JarRCtRGzp9u2\nZPfXGzra6CcSdW3Rkli7/jBCYNynOIl7XjQI8ZnFmq6phwu80ntH07mMeZy4tHff\nQqlLpvQ0i1rDr/Wkexdsnm8CgYBgxha9ILoF/Xm3MJPjEsxmnYsen/tM8XpIu5pv\nxbhBfQvfKWrQlOcyOVnUexEbVVo3KvdVz0VkXW60GpE/BxNGEGXO49rxD6x1gl87\nh/+CJGZIaYiOxaY5CP2+jcPizEL6yG32Yq8TxD5fIkmLRu8vbxX+aIFclDY1dVNe\n3wt3xQKBgGEL0EjwRch+P2V+YHAhbETPrEqJjHRWT95pIdF9XtC8fasSOVH81cLX\nXXsX1FTvOJNwG9Nk8rQjYJXGTb2O/2unaazlYUwxKwVpwuGzz/vhH/roHZBAkIVT\njvpykpn9QMezEdpzj5BEv01QzSYBPzIh5myrpoJIoSW7py7zFG3h\n-----END RSA PRIVATE KEY-----\n',
token: '!00000000000000000000000000000000',
password: '$2a$08$FnHXg3tP.M/kINWgQSXNqeoBsiVrkj.ecXX8mW9rfBzMRkibYfjYy', // HimawariDaisuki06160907
profile: {},
Expand All @@ -1175,6 +1176,7 @@ function insertHimawari(opts) {
username: 'himawari',
username_lower: 'himawari',
account: {
keypair: '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtdTG9rlFWjNqhgbg2V6X5XF1WpQXZS3KNXykEWl2UAiMyfVV\nBvf3zQP0dDEdNtcqdPJgis03bpiHCzQusc/YLyHYB0m+TJXsxJatb8cqUogOFeE4\ngQ4Dc5kAT6gLh/d4yz03EIg9bizX07EiGWnZqWxb+21ypqsPxST64sAtG9f5O/G4\nXe2m3cSbfAAvEUP1Ig1LUNyJB4jhM60w1cQic/qO8++sk/+GoX9g71X+i4NArGv+\n1c11acDIIPGAAQpFeYVeGaKakNDNp8RtJJp8R8FLwJXZ4/gATBnScCiHUSrGfRly\nYyR0w/BNlQ6/NijAdB9pR5csPvyIPkx1gauZewIDAQABAoIBAQCwWf/mhuY2h6uG\n9eDZsZ7Mj2/sO7k9Dl4R5iMSKCDxmnlB3slqitExa+aJUqEs8R5icjkkJcjfYNuJ\nCEFJf3YCsGZfGyyQBtCuEh2ATcBEb2SJ3/f3YuoCEaB1oVwdsOzc4TAovpol4yQo\nUqHp1/mdElVb01jhQQN4h1c02IJnfzvfU1C8szBni+Etfd+MxqGfv006DY3KOEb3\nlCrCS3GmooJW2Fjj7q1kCcaEQbMB1/aQHLXd1qe3KJOzXh3Voxsp/jEH0hvp2TII\nfY9UK+b7mA+xlvXwKuTkHVaZm0ylg0nbembS8MF4GfFMujinSexvLrVKaQhdMFoF\nvBLxHYHRAoGBANfNVYJYeCDPFNLmak5Xg33Rfvc2II8UmrZOVdhOWs8ZK0pis9e+\nPo2MKtTzrzipXI2QXv5w7kO+LJWNDva+xRlW8Wlj9Dde9QdQ7Y8+dk7SJgf24DzM\n023elgX5DvTeLODjStk6SMPRL0FmGovUqAAA8ZeHtJzkIr1HROWnQiwnAoGBANez\nhFwKnVoQu0RpBz/i4W0RKIxOwltN2zmlN8KjJPhSy00A7nBUfKLRbcwiSHE98Yi/\nUrXwMwR5QeD2ngngRppddJnpiRfjNjnsaqeqNtpO8AxB3XjpCC5zmHUMFHKvPpDj\n1zU/F44li0YjKcMBebZy9PbfAjrIgJfxhPo/oXiNAoGAfx6gaTjOAp2ZaaZ7Jozc\nkyft/5et1DrR6+P3I4T8bxQncRj1UXfqhxzzOiAVrm3tbCKIIp/JarRCtRGzp9u2\nZPfXGzra6CcSdW3Rkli7/jBCYNynOIl7XjQI8ZnFmq6phwu80ntH07mMeZy4tHff\nQqlLpvQ0i1rDr/Wkexdsnm8CgYBgxha9ILoF/Xm3MJPjEsxmnYsen/tM8XpIu5pv\nxbhBfQvfKWrQlOcyOVnUexEbVVo3KvdVz0VkXW60GpE/BxNGEGXO49rxD6x1gl87\nh/+CJGZIaYiOxaY5CP2+jcPizEL6yG32Yq8TxD5fIkmLRu8vbxX+aIFclDY1dVNe\n3wt3xQKBgGEL0EjwRch+P2V+YHAhbETPrEqJjHRWT95pIdF9XtC8fasSOVH81cLX\nXXsX1FTvOJNwG9Nk8rQjYJXGTb2O/2unaazlYUwxKwVpwuGzz/vhH/roHZBAkIVT\njvpykpn9QMezEdpzj5BEv01QzSYBPzIh5myrpoJIoSW7py7zFG3h\n-----END RSA PRIVATE KEY-----\n',
token: '!00000000000000000000000000000001',
password: '$2a$08$OPESxR2RE/ZijjGanNKk6ezSqGFitqsbZqTjWUZPLhORMKxHCbc4O', // ilovesakurako
profile: {},
Expand Down
16 changes: 16 additions & 0 deletions tools/migration/node.1522066477.user-account-keypair.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { default: User } = require('../../built/api/models/user');
const { generate } = require('../../built/crypto_key');

const updates = [];

User.find({}).each(function(user) {
updates.push(User.update({ _id: user._id }, {
$set: {
account: {
keypair: generate(),
}
}
}));
}).then(function () {
Promise.all(updates)
}).then(process.exit);