Merge branch 'bip39'

Conflicts:
	browser/bundle.js
This commit is contained in:
Ryan X. Charles 2014-06-20 19:35:24 -07:00
commit ad5e83f9ff
10 changed files with 59 additions and 336 deletions

View File

@ -3,12 +3,6 @@
'node_shared_openssl%': 'true'
},
'targets': [
{
'target_name': 'cryptox',
'sources': [
'src/pbkdf2_sha512.cc'
],
},
{
'target_name': 'KeyModule',
'sources': [

View File

@ -29,7 +29,6 @@ var modules = [
'lib/HierarchicalKey',
'lib/BIP39',
'lib/BIP39WordlistEn',
'lib/cryptox',
'lib/Block',
'lib/Bloom',
'lib/Connection',
@ -81,11 +80,21 @@ var createBitcore = function(opts) {
exec('sh concat.sh', puts);
process.chdir(cwd);
if (!opts.includeall && (!opts.submodules || opts.submodules.length === 0)) {
if (!opts.stdout) console.log('Must use either -s or -a option. For more info use the --help option');
if (!opts.includeall && !opts.includemain && (!opts.submodules || opts.submodules.length === 0)) {
if (!opts.stdout) console.log('Must use either -s or -a or -m option. For more info use the --help option');
process.exit(1);
}
var submodules = opts.submodules;
if (opts.includemain) {
submodules = JSON.parse(JSON.stringify(modules));
submodules.splice(submodules.indexOf('lib/BIP39'), 1);
submodules.splice(submodules.indexOf('lib/BIP39WordlistEn'), 1);
var assert = require('assert');
assert(submodules.length == modules.length - 2);
}
if (opts.submodules) {
for (var i = 0; i < opts.submodules.length; i++) {
var sm = opts.submodules[i];
@ -114,7 +123,7 @@ var createBitcore = function(opts) {
expose: 'bitcore'
});
modules.forEach(function(m) {
if (opts.includeall || opts.submodules.indexOf(m) > -1) {
if (opts.includeall || submodules.indexOf(m) > -1) {
if (!opts.stdout) console.log('Including ' + m + ' in the browser bundle');
b.require('./' + opts.dir + m + '.js', {
expose: './' + m
@ -158,6 +167,7 @@ if (require.main === module) {
program
.version('0.0.1')
.option('-a, --includeall', 'Include all submodules.')
.option('-m, --includemain', 'Include main submodules.')
.option('-d, --dontminify', 'Don\'t minify the code.')
.option('-o, --stdout', 'Specify output as stdout')
.option('-D, --dir <dir>', 'Specify a base directory')

File diff suppressed because one or more lines are too long

View File

@ -1,32 +1,46 @@
var imports = require('soop').imports();
var coinUtil = imports.coinUtil || require('../util');
var cryptox = imports.cryptox || require('./cryptox');
var crypto = require('crypto');
var BIP39 = {};
var sjcl = imports.sjcl || require('./sjcl');
var SecureRandom = require('./SecureRandom');
var hmacSHA512 = function (key) {
var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha512);
this.encrypt = function () {
return hasher.encrypt.apply(hasher, arguments);
};
};
var pbkdf2Sync_sha512 = function(password, salt, iterations, keylen) {
var derivedKey = sjcl.misc.pbkdf2(password, salt, iterations, 512, hmacSHA512);
return sjcl.codec.hex.fromBits(derivedKey)
};
var BIP39 = function() {
};
BIP39.mnemonic = function(wordlist, bits) {
if (!bits)
bits = 128;
if (bits % 32 != 0)
throw new Error("bits must be multiple of 32");
var bytes = crypto.randomBytes(bits / 8);
return BIP39.to_mnemonic(wordlist, bytes);
var buf = SecureRandom.getRandomBuffer(bits / 8);
return BIP39.entropy2mnemonic(wordlist, buf);
}
BIP39.to_mnemonic = function(wordlist, bytes) {
var hash = coinUtil.sha256(new Buffer(bytes));
BIP39.entropy2mnemonic = function(wordlist, buf) {
var hash = coinUtil.sha256(buf);
var bin = "";
var bits = bytes.length * 8;
for (var i = 0 ; i < bytes.length ; i++) {
bin = bin + ("00000000" + bytes[i].toString(2)).slice(-8);
var bits = buf.length * 8;
for (var i = 0 ; i < buf.length ; i++) {
bin = bin + ("00000000" + buf[i].toString(2)).slice(-8);
}
var hashbits = hash[0].toString(2);
hashbits = ("00000000" + hashbits).slice(-8).slice(0, bits/32);
bin = bin + hashbits;
if (bin.length % 11 != 0)
throw new Error("interal error - entropy not an even multiple of 11 bits - " + bin.length);
throw new Error("internal error - entropy not an even multiple of 11 bits - " + bin.length);
var mnemonic = "";
for (var i = 0 ; i < bin.length / 11 ; i++) {
for (var i = 0; i < bin.length / 11; i++) {
if (mnemonic != "")
mnemonic = mnemonic + " ";
var wi = parseInt(bin.slice(i*11, (i+1)*11), 2);
@ -35,8 +49,12 @@ BIP39.to_mnemonic = function(wordlist, bytes) {
return mnemonic;
}
BIP39.mnemonic_to_seed = function(mnemonic, passphrase) {
return cryptox.pbkdf2Sync_sha512(mnemonic, "mnemonic" + passphrase, 2048, 64);
BIP39.mnemonic2seed = function(mnemonic, passphrase) {
if (!passphrase)
passphrase = "";
var hex = pbkdf2Sync_sha512(mnemonic, "mnemonic" + passphrase, 2048, 64);
var buf = new Buffer(hex, 'hex');
return buf;
}
module.exports = require('soop')(BIP39);

View File

@ -1,17 +0,0 @@
// Crypto extensions
//
// PBKDF2 with SHA512 - browser version
var sjcl = require('../sjcl');
var hmacSHA512 = function (key) {
var hasher = new sjcl.misc.hmac( key, sjcl.hash.sha512 );
this.encrypt = function () {
return hasher.encrypt.apply( hasher, arguments );
};
};
exports.pbkdf2Sync_sha512 = function(password, salt, iterations, keylen) {
var derivedKey = sjcl.misc.pbkdf2( password, salt, iterations, 512, hmacSHA512 );
return sjcl.codec.hex.fromBits( derivedKey )
};

View File

@ -1,5 +0,0 @@
if (process.versions) {
module.exports = require('./node/cryptox');
return;
}
module.exports = require('./browser/cryptox');

View File

@ -1,49 +0,0 @@
// Crypto extensions
//
// PBKDF2 with SHA512
var binding = require('bindings')('cryptox');
exports.pbkdf2_sha512 = function(password, salt, iterations, keylen, callback) {
if (typeof callback !== 'function')
throw new Error('No callback provided to pbkdf2');
return pbkdf2_sha512(password, salt, iterations, keylen, callback);
};
exports.pbkdf2Sync_sha512 = function(password, salt, iterations, keylen) {
return pbkdf2_sha512(password, salt, iterations, keylen);
};
function toBuf(str, encoding) {
encoding = encoding || 'binary';
if (typeof str === 'string') {
if (encoding === 'buffer')
encoding = 'binary';
str = new Buffer(str, encoding);
}
return str;
}
function pbkdf2_sha512(password, salt, iterations, keylen, callback) {
password = toBuf(password);
salt = toBuf(salt);
if (exports.DEFAULT_ENCODING === 'buffer')
return binding.PBKDF2(password, salt, iterations, keylen, callback);
// at this point, we need to handle encodings.
var encoding = exports.DEFAULT_ENCODING;
if (callback) {
binding.PBKDF2_sha512(password, salt, iterations, keylen, function(er, ret) {
if (ret)
ret = ret.toString(encoding);
callback(er, ret);
});
} else {
var ret = binding.PBKDF2_sha512(password, salt, iterations, keylen);
//return ret.toString(encoding);
return ret;
}
}

View File

@ -47,9 +47,9 @@
},
"scripts": {
"install": "node-gyp rebuild",
"test": "node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"test": "node browser/build.js -a && node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test",
"prepublish": "node browser/build.js -a"
"prepublish": "node browser/build.js -m"
},
"dependencies": {
"grunt-browserify": "~2.0.0",

View File

@ -1,228 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node_crypto.h"
#include "node_crypto_groups.h"
#include "v8.h"
#include "node_internals.h"
#include "node.h"
#include "node_buffer.h"
#include "string_bytes.h"
#include <string.h>
#ifdef _MSC_VER
#define strcasecmp _stricmp
#endif
#include <stdlib.h>
#include <errno.h>
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
# define OPENSSL_CONST const
#else
# define OPENSSL_CONST
#endif
#define ASSERT_IS_STRING_OR_BUFFER(val) \
if (!Buffer::HasInstance(val) && !val->IsString()) { \
return ThrowException(Exception::TypeError(String::New( \
"Not a string or buffer"))); \
}
#define ASSERT_IS_BUFFER(val) \
if (!Buffer::HasInstance(val)) { \
return ThrowException(Exception::TypeError(String::New("Not a buffer"))); \
}
namespace node {
namespace cryptox {
using namespace v8;
struct pbkdf2_req {
uv_work_t work_req;
int err;
char* pass;
size_t passlen;
char* salt;
size_t saltlen;
size_t iter;
char* key;
size_t keylen;
Persistent<Object> obj;
};
void EIO_PBKDF2_SHA512(pbkdf2_req* req) {
req->err = PKCS5_PBKDF2_HMAC(
req->pass,
req->passlen,
(unsigned char*)req->salt,
req->saltlen,
req->iter,
EVP_sha512(),
req->keylen,
(unsigned char*)req->key);
memset(req->pass, 0, req->passlen);
memset(req->salt, 0, req->saltlen);
}
void EIO_PBKDF2_SHA512(uv_work_t* work_req) {
pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req);
EIO_PBKDF2_SHA512(req);
}
void EIO_PBKDF2After_SHA512(pbkdf2_req* req, Local<Value> argv[2]) {
if (req->err) {
argv[0] = Local<Value>::New(Undefined());
argv[1] = Encode(req->key, req->keylen, BUFFER);
memset(req->key, 0, req->keylen);
} else {
argv[0] = Exception::Error(String::New("PBKDF2 error"));
argv[1] = Local<Value>::New(Undefined());
}
delete[] req->pass;
delete[] req->salt;
delete[] req->key;
delete req;
}
void EIO_PBKDF2After_SHA512(uv_work_t* work_req, int status) {
assert(status == 0);
pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req);
HandleScope scope;
Local<Value> argv[2];
Persistent<Object> obj = req->obj;
EIO_PBKDF2After_SHA512(req, argv);
MakeCallback(obj, "ondone", ARRAY_SIZE(argv), argv);
obj.Dispose();
}
Handle<Value> PBKDF2_SHA512(const Arguments& args) {
HandleScope scope;
const char* type_error = NULL;
char* pass = NULL;
char* salt = NULL;
ssize_t passlen = -1;
ssize_t saltlen = -1;
ssize_t keylen = -1;
ssize_t pass_written = -1;
ssize_t salt_written = -1;
ssize_t iter = -1;
pbkdf2_req* req = NULL;
if (args.Length() != 4 && args.Length() != 5) {
type_error = "Bad parameter";
goto err;
}
ASSERT_IS_BUFFER(args[0]);
passlen = Buffer::Length(args[0]);
if (passlen < 0) {
type_error = "Bad password";
goto err;
}
pass = new char[passlen];
pass_written = DecodeWrite(pass, passlen, args[0], BINARY);
assert(pass_written == passlen);
ASSERT_IS_BUFFER(args[1]);
saltlen = Buffer::Length(args[1]);
if (saltlen < 0) {
type_error = "Bad salt";
goto err;
}
salt = new char[saltlen];
salt_written = DecodeWrite(salt, saltlen, args[1], BINARY);
assert(salt_written == saltlen);
if (!args[2]->IsNumber()) {
type_error = "Iterations not a number";
goto err;
}
iter = args[2]->Int32Value();
if (iter < 0) {
type_error = "Bad iterations";
goto err;
}
if (!args[3]->IsNumber()) {
type_error = "Key length not a number";
goto err;
}
keylen = args[3]->Int32Value();
if (keylen < 0) {
type_error = "Bad key length";
goto err;
}
req = new pbkdf2_req;
req->err = 0;
req->pass = pass;
req->passlen = passlen;
req->salt = salt;
req->saltlen = saltlen;
req->iter = iter;
req->key = new char[keylen];
req->keylen = keylen;
if (args[4]->IsFunction()) {
req->obj = Persistent<Object>::New(Object::New());
req->obj->Set(String::New("ondone"), args[4]);
uv_queue_work(uv_default_loop(),
&req->work_req,
EIO_PBKDF2_SHA512,
EIO_PBKDF2After_SHA512);
return Undefined();
} else {
Local<Value> argv[2];
EIO_PBKDF2_SHA512(req);
EIO_PBKDF2After_SHA512(req, argv);
if (argv[0]->IsObject()) return ThrowException(argv[0]);
return scope.Close(argv[1]);
}
err:
delete[] salt;
delete[] pass;
return ThrowException(Exception::TypeError(String::New(type_error)));
}
void InitCryptox(Handle<Object> target) {
NODE_SET_METHOD(target, "PBKDF2_sha512", PBKDF2_SHA512);
}
}
}
NODE_MODULE(cryptox, node::cryptox::InitCryptox)

View File

@ -151,8 +151,8 @@ describe('BIP39', function() {
var code = vector[0];
var mnemonic = vector[1];
var seed = vector[2];
var mnemonic1 = BIP39.to_mnemonic(BIP39WordlistEn, new Buffer(code, 'hex'));
var seed1 = BIP39.mnemonic_to_seed(mnemonic, 'TREZOR');
var mnemonic1 = BIP39.entropy2mnemonic(BIP39WordlistEn, new Buffer(code, 'hex'));
var seed1 = BIP39.mnemonic2seed(mnemonic, 'TREZOR');
mnemonic1.should.equal(mnemonic);
seed1.toString('hex').should.equal(seed)
}