From 2aa295009858d1c2f63f846d5f7eb45803b08964 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 18 Jun 2020 03:07:43 +0200 Subject: [PATCH] [feat] oxyde scrypt --- app/modules/keypair/index.ts | 13 ++++---- app/modules/keypair/lib/scrypt.ts | 31 +++++-------------- neon/lib/index.ts | 2 +- neon/native/crypto.d.ts | 1 + neon/native/index.d.ts | 1 + neon/native/src/crypto.rs | 28 ++++++++++------- neon/native/src/lib.rs | 12 +++++++ .../extra/completion/duniter_completion.bash | 6 ++-- .../modules/keypair/keypair-module-test.ts | 4 +-- test/neon/test_crypto.ts | 14 ++++++++- 10 files changed, 64 insertions(+), 48 deletions(-) diff --git a/app/modules/keypair/index.ts b/app/modules/keypair/index.ts index a809f75fd..d69751b51 100644 --- a/app/modules/keypair/index.ts +++ b/app/modules/keypair/index.ts @@ -33,19 +33,18 @@ export const KeypairDependency = { desc: "Password to generate the keypair", }, { - value: "--keyN <N>", - desc: - "Scrypt `N` CPU/memory cost parameter. Must be a power of 2. Defaults to 4096.", + value: "--scrypt-log-n <log_n>", + desc: "Scrypt `log(n)` CPU/memory cost parameter. Defaults to 12.", parser: parseInt, }, { - value: "--keyr <r>", + value: "--scrypt-r <r>", desc: "Scrypt `r` The blocksize parameter, which fine-tunes sequential memory read size and performance. Defaults to 16.", parser: parseInt, }, { - value: "--keyp <p>", + value: "--scrypt-p <p>", desc: "Scrypt `p` Parallelization parameter. Defaults to 1.", parser: parseInt, }, @@ -99,11 +98,11 @@ export const KeypairDependency = { confDAL: any ) => { if ( - (program.keyN || program.keyr || program.keyp) && + (program.scryptLogN || program.scryptR || program.scryptP) && !(program.salt && program.passwd) ) { throw Error( - "Missing --salt and --passwd options along with --keyN|keyr|keyp option" + "Missing --salt and --passwd options along with --scrypt-log-n|scrypt-r|scrypt-p option" ); } diff --git a/app/modules/keypair/lib/scrypt.ts b/app/modules/keypair/lib/scrypt.ts index 3135ecad6..1c946dc61 100644 --- a/app/modules/keypair/lib/scrypt.ts +++ b/app/modules/keypair/lib/scrypt.ts @@ -11,8 +11,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import * as crypto from "crypto"; -import { KeyPairBuilder, seedToSecretKey } from "../../../../neon/lib"; +import { KeyPairBuilder, scrypt, seedToSecretKey } from "../../../../neon/lib"; const SEED_LENGTH = 32; // Length of the key @@ -20,7 +19,7 @@ const SEED_LENGTH = 32; // Length of the key * Generates a new keypair object from salt + password strings. * @param salt * @param key - * @param N Scrypt parameter N. Defaults to 4096. + * @param log_n Scrypt parameter log n. Defaults to 12. * @param r Scrypt parameter r. Defaults to 16. * @param p Scrypt parameter p. Defaults to 1. * @return keyPair An object containing the public and private keys, base58 encoded. @@ -28,28 +27,14 @@ const SEED_LENGTH = 32; // Length of the key export const Scrypt = async ( salt: string, key: string, - N = 4096, + log_n = 12, r = 16, p = 1 ) => { - const res: { pub: string; sec: string } = await new Promise( - (resolve, reject) => { - crypto.scrypt( - key, - salt, - SEED_LENGTH, - { N, r, p }, - (err: any, seed: Buffer) => { - if (err) return reject(err); - const pair = KeyPairBuilder.fromSeed(seed); - resolve({ - pub: pair.getPublicKey(), - sec: seedToSecretKey(seed), - }); - } - ); - } - ); + const seed = scrypt(salt, key, log_n, r, p); - return res; + return { + pub: KeyPairBuilder.fromSeed(seed).getPublicKey(), + sec: seedToSecretKey(seed), + }; }; diff --git a/neon/lib/index.ts b/neon/lib/index.ts index 48fdad95c..18314003a 100644 --- a/neon/lib/index.ts +++ b/neon/lib/index.ts @@ -1,3 +1,3 @@ -export { Ed25519Signator, generateRandomSeed, seedToSecretKey, sha256, verify, Wot } from "../native"; +export { Ed25519Signator, generateRandomSeed, seedToSecretKey, scrypt, sha256, verify, Wot } from "../native"; export { KeyPairBuilder } from "./crypto"; export { WotBuilder } from "./wot"; diff --git a/neon/native/crypto.d.ts b/neon/native/crypto.d.ts index ba6c22595..ea2710c89 100644 --- a/neon/native/crypto.d.ts +++ b/neon/native/crypto.d.ts @@ -11,5 +11,6 @@ export class Ed25519Signator { export function generateRandomSeed(): Buffer; export function seedToSecretKey(seed: Buffer): string; +export function scrypt(salt: string, password: string, log_n: number, r: number, p: number): Buffer; export function sha256(data: string): string; export function verify(message: Buffer | string, sig: string, pubkey: string): boolean; diff --git a/neon/native/index.d.ts b/neon/native/index.d.ts index e31c8cbdf..a1029c937 100644 --- a/neon/native/index.d.ts +++ b/neon/native/index.d.ts @@ -6,6 +6,7 @@ import * as _wot from './wot'; export import Ed25519Signator = _crypto.Ed25519Signator; export import generateRandomSeed = _crypto.generateRandomSeed; export import seedToSecretKey = _crypto.seedToSecretKey; +export import scrypt = _crypto.scrypt; export import sha256 = _crypto.sha256; export import verify = _crypto.verify; diff --git a/neon/native/src/crypto.rs b/neon/native/src/crypto.rs index 76414e8df..68573d2f7 100644 --- a/neon/native/src/crypto.rs +++ b/neon/native/src/crypto.rs @@ -13,13 +13,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -use crate::into_neon_res; +use crate::{create_js_buffer, into_neon_res}; use dup_crypto::bases::b58::ToBase58; use dup_crypto::hashs::Hash; use dup_crypto::keys::{ ed25519::{ - Ed25519KeyPair, KeyPairFromSeed32Generator, PublicKey as Ed25519PublicKey, - Signator as Ed25519Signator, Signature as Ed25519Signature, + Ed25519KeyPair, KeyPairFromSaltedPasswordGenerator, KeyPairFromSeed32Generator, + PublicKey as Ed25519PublicKey, Signator as Ed25519Signator, Signature as Ed25519Signature, }, KeyPair, PublicKey, Signator, Signature, }; @@ -34,14 +34,7 @@ pub fn generate_random_seed(mut cx: FunctionContext) -> JsResult<JsBuffer> { Seed32::random().map_err(|_| "fail to generate random seed"), )?; - let mut js_buffer = JsBuffer::new(&mut cx, 32)?; - - cx.borrow_mut(&mut js_buffer, |data| { - let slice = data.as_mut_slice::<u8>(); - slice.copy_from_slice(seed.as_ref()); - }); - - Ok(js_buffer) + create_js_buffer(&mut cx, seed.as_ref()) } pub fn seed_to_expanded_base58_secret_key(mut cx: FunctionContext) -> JsResult<JsString> { @@ -62,6 +55,19 @@ pub fn seed_to_expanded_base58_secret_key(mut cx: FunctionContext) -> JsResult<J Ok(cx.string(expanded_base58_secret_key)) } +pub fn scrypt(mut cx: FunctionContext) -> JsResult<JsBuffer> { + let salt = cx.argument::<JsString>(0)?.value(); + let password = cx.argument::<JsString>(1)?.value(); + let log_n = cx.argument::<JsNumber>(2)?.value() as u8; + let r = cx.argument::<JsNumber>(3)?.value() as u32; + let p = cx.argument::<JsNumber>(4)?.value() as u32; + + let seed = KeyPairFromSaltedPasswordGenerator::with_parameters(log_n, r, p) + .generate_seed(password.as_bytes(), salt.as_bytes()); + + create_js_buffer(&mut cx, seed.as_ref()) +} + pub fn sha256(mut cx: FunctionContext) -> JsResult<JsString> { let str_datas = cx.argument::<JsString>(0)?.value(); Ok(cx.string(Hash::compute_str(&str_datas).to_hex().to_uppercase())) diff --git a/neon/native/src/lib.rs b/neon/native/src/lib.rs index 94aa1964d..89da67cb5 100644 --- a/neon/native/src/lib.rs +++ b/neon/native/src/lib.rs @@ -18,6 +18,17 @@ mod wot; use neon::{prelude::*, register_module}; +fn create_js_buffer<'c, C: Context<'c>>(context: &mut C, bytes: &[u8]) -> JsResult<'c, JsBuffer> { + let mut js_buffer = unsafe { JsBuffer::uninitialized(context, bytes.len() as u32)? }; + + context.borrow_mut(&mut js_buffer, |data| { + let slice = data.as_mut_slice::<u8>(); + slice.copy_from_slice(bytes); + }); + + Ok(js_buffer) +} + fn into_neon_res<'c, C: Context<'c>, T, S: AsRef<str>>( context: &mut C, rust_result: Result<T, S>, @@ -34,6 +45,7 @@ register_module!(mut cx, { "seedToSecretKey", crate::crypto::seed_to_expanded_base58_secret_key, )?; + cx.export_function("scrypt", crate::crypto::scrypt)?; cx.export_function("sha256", crate::crypto::sha256)?; cx.export_function("verify", crate::crypto::verify)?; cx.export_class::<crate::crypto::JsKeyPair>("Ed25519Signator")?; diff --git a/release/extra/completion/duniter_completion.bash b/release/extra/completion/duniter_completion.bash index 9333250df..941b66532 100644 --- a/release/extra/completion/duniter_completion.bash +++ b/release/extra/completion/duniter_completion.bash @@ -87,9 +87,9 @@ direct_webstart \ --at \ --salt \ --passwd \ ---keyN \ ---keyr \ ---keyp \ +--scrypt-log-n \ +--scrypt-r \ +--scrypt-p \ --keyprompt \ --keyfile \ --nointeractive \ diff --git a/test/fast/modules/keypair/keypair-module-test.ts b/test/fast/modules/keypair/keypair-module-test.ts index 45998ad1e..5a2a3c46d 100644 --- a/test/fast/modules/keypair/keypair-module-test.ts +++ b/test/fast/modules/keypair/keypair-module-test.ts @@ -23,12 +23,12 @@ describe('Module usage', () => { try { const stack = Statics.minimalStack(); stack.registerDependency(KeypairDependency, 'duniter-keypair'); - await stack.executeStack(['node', 'index.js', 'config', '--memory', '--keyN', '2048']); + await stack.executeStack(['node', 'index.js', 'config', '--memory', '--scrypt-log-n', '2048']); } catch (e) { errMessage = e.message; } should.exist(errMessage); - should.equal(errMessage, 'Missing --salt and --passwd options along with --keyN|keyr|keyp option'); + should.equal(errMessage, 'Missing --salt and --passwd options along with --scrypt-log-n|scrypt-r|scrypt-p option'); }) it('no options on brand new node should generate random key', async () => { diff --git a/test/neon/test_crypto.ts b/test/neon/test_crypto.ts index 5a6ac3bd3..c001b9c5b 100644 --- a/test/neon/test_crypto.ts +++ b/test/neon/test_crypto.ts @@ -1,6 +1,6 @@ "use strict"; -import { Ed25519Signator, KeyPairBuilder, sha256, verify, generateRandomSeed, seedToSecretKey } from "../../neon/lib"; +import { Ed25519Signator, KeyPairBuilder, scrypt, sha256, verify, generateRandomSeed, seedToSecretKey } from "../../neon/lib"; import * as assert from "assert"; @@ -17,6 +17,18 @@ describe('ed25519 tests:', function(){ //assert.equal(rawSec, '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'); }) + it('scrypt 7iMV3b6j2hSj5WtrfchfvxivS9swN3opDgxudeHq64fb', function(done){ + const seed = scrypt( + "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV", + "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_", + 12, + 16, + 1); + let pub = KeyPairBuilder.fromSeed(seed).getPublicKey(); + assert.equal(pub, "7iMV3b6j2hSj5WtrfchfvxivS9swN3opDgxudeHq64fb") + done(); + }); + it('sha256 hello', function(done){ const msg = "hello"; const hash = sha256(msg); -- GitLab