Overtorment
7 years ago
15 changed files with 905 additions and 6 deletions
@ -0,0 +1,4 @@ |
|||
test/ |
|||
.min-wd |
|||
.gitignore |
|||
.travis.yml |
@ -0,0 +1,52 @@ |
|||
2.0.0 / 2016-12-20 |
|||
------------------ |
|||
- removed class instantiation. Removed `coinstring` dep. |
|||
|
|||
1.4.0 / 2015-11-03 |
|||
------------------ |
|||
- added `progressCallback`. See: https://github.com/bitcoinjs/bip38/pull/16 |
|||
|
|||
1.3.0 / 2015-06-04 |
|||
------------------ |
|||
- use `createHash` and `aes` directly. https://github.com/cryptocoinjs/bip38 |
|||
- JavaScript Standard Style |
|||
|
|||
1.2.0 / 2015-01-05 |
|||
------------------ |
|||
- removed dependency upon `aes` package since Browserify now supports aes [Daniel Cousens](https://github.com/cryptocoinjs/bip38/pull/6) |
|||
- removed `crypto-browserify` devDep and removed `browser` field from `package.json`; no longer necessary |
|||
- added method `verify()` [Daniel Cousens](https://github.com/cryptocoinjs/bip38/pull/7) |
|||
|
|||
1.1.1 / 2014-09-19 |
|||
------------------ |
|||
- bugfix: enforce zero padding [Daniel Cousens](https://github.com/cryptocoinjs/bip38/commit/e73598d0fc1d1b3c04c132c34053e96bec6bd201) |
|||
- add MIT license to package.json |
|||
|
|||
1.1.0 / 2014-07-11 |
|||
------------------ |
|||
- added methods `encryptRaw` and `decryptRaw` [Daniel Cousens](https://github.com/cryptocoinjs/bip38/pull/4) |
|||
|
|||
1.0.0 / 2014-06-10 |
|||
------------------ |
|||
- upgraded `"scryptsy": "~0.2.0"` to `"scryptsy": "^1.0.0"` |
|||
- upgraded `"bigi": "~0.2.0"` to `"bigi": "^1.2.0"` |
|||
- removed `ecurve-names` dep |
|||
- upgraded `"ecurve": "~0.3.0"` to `"ecurve": "^0.8.0"` |
|||
- removed semicolons per http://cryptocoinjs.com/about/contributing/#semicolons |
|||
- removed `crypto-hashing` dep |
|||
- removed `bs58` dep, added `coinstring` dep |
|||
- removed `terst` for `assert` |
|||
- added TravisCI |
|||
- added Coveralls |
|||
- added testling |
|||
- removed static level methods, must call `new` |
|||
|
|||
0.1.0 / 2014-03-05 |
|||
------------------ |
|||
- added support to decrypt ECMultiplied keys, #1 |
|||
- made constructor work without `new` |
|||
- upgraded deps `ecurve`, `ecurve-names`, and `scryptsy` |
|||
|
|||
0.0.1 / 2014-02-28 |
|||
------------------ |
|||
- initial release |
@ -0,0 +1,21 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2013-2014 Cryptocoinjs 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. |
@ -0,0 +1,70 @@ |
|||
# bip38 |
|||
|
|||
[![build status](https://secure.travis-ci.org/bitcoinjs/bip38.svg)](http://travis-ci.org/bitcoinjs/bip38) |
|||
[![Coverage Status](https://img.shields.io/coveralls/cryptocoinjs/bip38.svg)](https://coveralls.io/r/cryptocoinjs/bip38) |
|||
[![Version](http://img.shields.io/npm/v/bip38.svg)](https://www.npmjs.org/package/bip38) |
|||
|
|||
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) |
|||
|
|||
A JavaScript component that adheres to the [BIP38](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki) standard to secure your crypto currency private keys. Fully compliant with Node.js and the browser (via Browserify). |
|||
|
|||
|
|||
## Why? |
|||
BIP38 is a standard process to encrypt Bitcoin and crypto currency private keys that is imprevious to brute force attacks thus protecting the user. |
|||
|
|||
|
|||
## Package Info |
|||
- homepage: [http://cryptocoinjs.com/modules/currency/bip38/](http://cryptocoinjs.com/modules/currency/bip38/) |
|||
- github: [https://github.com/cryptocoinjs/bip38](https://github.com/cryptocoinjs/bip38) |
|||
- tests: [https://github.com/cryptocoinjs/bip38/tree/master/test](https://github.com/cryptocoinjs/bip38/tree/master/test) |
|||
- issues: [https://github.com/cryptocoinjs/bip38/issues](https://github.com/cryptocoinjs/bip38/issues) |
|||
- license: **MIT** |
|||
- versioning: [http://semver-ftw.org](http://semver-ftw.org) |
|||
|
|||
|
|||
## Usage |
|||
|
|||
### Installation |
|||
|
|||
npm install --save bip38 |
|||
|
|||
|
|||
### API |
|||
### encrypt(buffer, compressed, passphrase[, progressCallback, scryptParams]) |
|||
|
|||
``` javascript |
|||
var bip38 = require('bip38') |
|||
var wif = require('wif') |
|||
|
|||
var myWifString = '5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR' |
|||
var decoded = wif.decode(myWifString) |
|||
|
|||
var encryptedKey = bip38.encrypt(decoded.privateKey, decoded.compressed, 'TestingOneTwoThree') |
|||
console.log(encryptedKey) |
|||
// => '6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg' |
|||
``` |
|||
|
|||
|
|||
### decrypt(encryptedKey, passhprase[, progressCallback, scryptParams]) |
|||
|
|||
``` javascript |
|||
var bip38 = require('bip38') |
|||
var wif = require('wif') |
|||
|
|||
var encryptedKey = '6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg' |
|||
var decryptedKey = bip38.decrypt(encryptedKey, 'TestingOneTwoThree', function (status) { |
|||
console.log(status.percent) // will print the precent every time current increases by 1000 |
|||
}) |
|||
|
|||
console.log(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed)) |
|||
// => '5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR' |
|||
``` |
|||
|
|||
|
|||
# References |
|||
- https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki |
|||
- https://github.com/pointbiz/bitaddress.org/issues/56 (Safari 6.05 issue) |
|||
- https://github.com/casascius/Bitcoin-Address-Utility/tree/master/Model |
|||
- https://github.com/nomorecoin/python-bip38-testing/blob/master/bip38.py |
|||
- https://github.com/pointbiz/bitaddress.org/blob/master/src/ninja.key.js |
|||
|
@ -0,0 +1,239 @@ |
|||
var aes = require('browserify-aes') |
|||
var assert = require('assert') |
|||
var Buffer = require('safe-buffer').Buffer |
|||
var bs58check = require('bs58check') |
|||
var createHash = require('create-hash') |
|||
var scrypt = require('./scryptsy') |
|||
var xor = require('buffer-xor/inplace') |
|||
|
|||
var ecurve = require('ecurve') |
|||
var curve = ecurve.getCurveByName('secp256k1') |
|||
|
|||
var BigInteger = require('bigi') |
|||
|
|||
// constants
|
|||
var SCRYPT_PARAMS = { |
|||
N: 16384, // specified by BIP38
|
|||
r: 8, |
|||
p: 8 |
|||
} |
|||
var NULL = Buffer.alloc(0) |
|||
|
|||
function hash160 (buffer) { |
|||
return createHash('rmd160').update( |
|||
createHash('sha256').update(buffer).digest() |
|||
).digest() |
|||
} |
|||
|
|||
function hash256 (buffer) { |
|||
return createHash('sha256').update( |
|||
createHash('sha256').update(buffer).digest() |
|||
).digest() |
|||
} |
|||
|
|||
function getAddress (d, compressed) { |
|||
var Q = curve.G.multiply(d).getEncoded(compressed) |
|||
var hash = hash160(Q) |
|||
var payload = Buffer.allocUnsafe(21) |
|||
payload.writeUInt8(0x00, 0) // XXX TODO FIXME bitcoin only??? damn you BIP38
|
|||
hash.copy(payload, 1) |
|||
|
|||
return bs58check.encode(payload) |
|||
} |
|||
|
|||
async function encryptRaw (buffer, compressed, passphrase, progressCallback, scryptParams) { |
|||
if (buffer.length !== 32) throw new Error('Invalid private key length') |
|||
scryptParams = scryptParams || SCRYPT_PARAMS |
|||
|
|||
var d = BigInteger.fromBuffer(buffer) |
|||
var address = getAddress(d, compressed) |
|||
var secret = Buffer.from(passphrase, 'utf8') |
|||
var salt = hash256(address).slice(0, 4) |
|||
|
|||
var N = scryptParams.N |
|||
var r = scryptParams.r |
|||
var p = scryptParams.p |
|||
|
|||
var scryptBuf = await scrypt(secret, salt, N, r, p, 64, progressCallback) |
|||
var derivedHalf1 = scryptBuf.slice(0, 32) |
|||
var derivedHalf2 = scryptBuf.slice(32, 64) |
|||
|
|||
var xorBuf = xor(derivedHalf1, buffer) |
|||
var cipher = aes.createCipheriv('aes-256-ecb', derivedHalf2, NULL) |
|||
cipher.setAutoPadding(false) |
|||
cipher.end(xorBuf) |
|||
|
|||
var cipherText = cipher.read() |
|||
|
|||
// 0x01 | 0x42 | flagByte | salt (4) | cipherText (32)
|
|||
var result = Buffer.allocUnsafe(7 + 32) |
|||
result.writeUInt8(0x01, 0) |
|||
result.writeUInt8(0x42, 1) |
|||
result.writeUInt8(compressed ? 0xe0 : 0xc0, 2) |
|||
salt.copy(result, 3) |
|||
cipherText.copy(result, 7) |
|||
|
|||
return result |
|||
} |
|||
|
|||
function encrypt (buffer, compressed, passphrase, progressCallback, scryptParams) { |
|||
return bs58check.encode(encryptRaw(buffer, compressed, passphrase, progressCallback, scryptParams)) |
|||
} |
|||
|
|||
// some of the techniques borrowed from: https://github.com/pointbiz/bitaddress.org
|
|||
async function decryptRaw (buffer, passphrase, progressCallback, scryptParams) { |
|||
// 39 bytes: 2 bytes prefix, 37 bytes payload
|
|||
if (buffer.length !== 39) throw new Error('Invalid BIP38 data length') |
|||
if (buffer.readUInt8(0) !== 0x01) throw new Error('Invalid BIP38 prefix') |
|||
scryptParams = scryptParams || SCRYPT_PARAMS |
|||
|
|||
// check if BIP38 EC multiply
|
|||
var type = buffer.readUInt8(1) |
|||
if (type === 0x43) return await decryptECMult(buffer, passphrase, progressCallback, scryptParams) |
|||
if (type !== 0x42) throw new Error('Invalid BIP38 type') |
|||
|
|||
passphrase = Buffer.from(passphrase, 'utf8') |
|||
|
|||
var flagByte = buffer.readUInt8(2) |
|||
var compressed = flagByte === 0xe0 |
|||
if (!compressed && flagByte !== 0xc0) throw new Error('Invalid BIP38 compression flag') |
|||
|
|||
var N = scryptParams.N |
|||
var r = scryptParams.r |
|||
var p = scryptParams.p |
|||
|
|||
var salt = buffer.slice(3, 7) |
|||
var scryptBuf = await scrypt(passphrase, salt, N, r, p, 64, progressCallback) |
|||
var derivedHalf1 = scryptBuf.slice(0, 32) |
|||
var derivedHalf2 = scryptBuf.slice(32, 64) |
|||
|
|||
var privKeyBuf = buffer.slice(7, 7 + 32) |
|||
var decipher = aes.createDecipheriv('aes-256-ecb', derivedHalf2, NULL) |
|||
decipher.setAutoPadding(false) |
|||
decipher.end(privKeyBuf) |
|||
|
|||
var plainText = decipher.read() |
|||
var privateKey = xor(derivedHalf1, plainText) |
|||
|
|||
// verify salt matches address
|
|||
var d = BigInteger.fromBuffer(privateKey) |
|||
var address = getAddress(d, compressed) |
|||
var checksum = hash256(address).slice(0, 4) |
|||
assert.deepEqual(salt, checksum) |
|||
|
|||
return { |
|||
privateKey: privateKey, |
|||
compressed: compressed |
|||
} |
|||
} |
|||
|
|||
async function decrypt (string, passphrase, progressCallback, scryptParams) { |
|||
return await decryptRaw(bs58check.decode(string), passphrase, progressCallback, scryptParams) |
|||
} |
|||
|
|||
async function decryptECMult (buffer, passphrase, progressCallback, scryptParams) { |
|||
passphrase = Buffer.from(passphrase, 'utf8') |
|||
buffer = buffer.slice(1) // FIXME: we can avoid this
|
|||
scryptParams = scryptParams || SCRYPT_PARAMS |
|||
|
|||
var flag = buffer.readUInt8(1) |
|||
var compressed = (flag & 0x20) !== 0 |
|||
var hasLotSeq = (flag & 0x04) !== 0 |
|||
|
|||
assert.equal((flag & 0x24), flag, 'Invalid private key.') |
|||
|
|||
var addressHash = buffer.slice(2, 6) |
|||
var ownerEntropy = buffer.slice(6, 14) |
|||
var ownerSalt |
|||
|
|||
// 4 bytes ownerSalt if 4 bytes lot/sequence
|
|||
if (hasLotSeq) { |
|||
ownerSalt = ownerEntropy.slice(0, 4) |
|||
|
|||
// else, 8 bytes ownerSalt
|
|||
} else { |
|||
ownerSalt = ownerEntropy |
|||
} |
|||
|
|||
var encryptedPart1 = buffer.slice(14, 22) // First 8 bytes
|
|||
var encryptedPart2 = buffer.slice(22, 38) // 16 bytes
|
|||
|
|||
var N = scryptParams.N |
|||
var r = scryptParams.r |
|||
var p = scryptParams.p |
|||
var preFactor = await scrypt(passphrase, ownerSalt, N, r, p, 32, progressCallback) |
|||
|
|||
var passFactor |
|||
if (hasLotSeq) { |
|||
var hashTarget = Buffer.concat([preFactor, ownerEntropy]) |
|||
passFactor = hash256(hashTarget) |
|||
} else { |
|||
passFactor = preFactor |
|||
} |
|||
|
|||
var passInt = BigInteger.fromBuffer(passFactor) |
|||
var passPoint = curve.G.multiply(passInt).getEncoded(true) |
|||
|
|||
var seedBPass = await scrypt(passPoint, Buffer.concat([addressHash, ownerEntropy]), 1024, 1, 1, 64) |
|||
|
|||
var derivedHalf1 = seedBPass.slice(0, 32) |
|||
var derivedHalf2 = seedBPass.slice(32, 64) |
|||
|
|||
var decipher = aes.createDecipheriv('aes-256-ecb', derivedHalf2, Buffer.alloc(0)) |
|||
decipher.setAutoPadding(false) |
|||
decipher.end(encryptedPart2) |
|||
|
|||
var decryptedPart2 = decipher.read() |
|||
var tmp = xor(decryptedPart2, derivedHalf1.slice(16, 32)) |
|||
var seedBPart2 = tmp.slice(8, 16) |
|||
|
|||
var decipher2 = aes.createDecipheriv('aes-256-ecb', derivedHalf2, Buffer.alloc(0)) |
|||
decipher2.setAutoPadding(false) |
|||
decipher2.write(encryptedPart1) // first 8 bytes
|
|||
decipher2.end(tmp.slice(0, 8)) // last 8 bytes
|
|||
|
|||
var seedBPart1 = xor(decipher2.read(), derivedHalf1.slice(0, 16)) |
|||
var seedB = Buffer.concat([seedBPart1, seedBPart2], 24) |
|||
var factorB = BigInteger.fromBuffer(hash256(seedB)) |
|||
|
|||
// d = passFactor * factorB (mod n)
|
|||
var d = passInt.multiply(factorB).mod(curve.n) |
|||
|
|||
return { |
|||
privateKey: d.toBuffer(32), |
|||
compressed: compressed |
|||
} |
|||
} |
|||
|
|||
function verify (string) { |
|||
var decoded = bs58check.decodeUnsafe(string) |
|||
if (!decoded) return false |
|||
|
|||
if (decoded.length !== 39) return false |
|||
if (decoded.readUInt8(0) !== 0x01) return false |
|||
|
|||
var type = decoded.readUInt8(1) |
|||
var flag = decoded.readUInt8(2) |
|||
|
|||
// encrypted WIF
|
|||
if (type === 0x42) { |
|||
if (flag !== 0xc0 && flag !== 0xe0) return false |
|||
|
|||
// EC mult
|
|||
} else if (type === 0x43) { |
|||
if ((flag & ~0x24)) return false |
|||
} else { |
|||
return false |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
module.exports = { |
|||
decrypt: decrypt, |
|||
decryptECMult: decryptECMult, |
|||
decryptRaw: decryptRaw, |
|||
encrypt: encrypt, |
|||
encryptRaw: encryptRaw, |
|||
verify: verify |
|||
} |
@ -0,0 +1,92 @@ |
|||
{ |
|||
"_from": "git+https://github.com/Overtorment/bip38.git", |
|||
"_id": "bip38@2.0.2", |
|||
"_inBundle": false, |
|||
"_integrity": "sha1-AhDzWwDTKrQ656q2Kxb4xmDIitY=", |
|||
"_location": "/bip38", |
|||
"_phantomChildren": {}, |
|||
"_requested": { |
|||
"type": "git", |
|||
"raw": "bip38@git+https://github.com/Overtorment/bip38.git", |
|||
"name": "bip38", |
|||
"escapedName": "bip38", |
|||
"rawSpec": "git+https://github.com/Overtorment/bip38.git", |
|||
"saveSpec": "git+https://github.com/Overtorment/bip38.git", |
|||
"fetchSpec": "https://github.com/Overtorment/bip38.git", |
|||
"gitCommittish": "master" |
|||
}, |
|||
"_requiredBy": [ |
|||
"/" |
|||
], |
|||
"_resolved": "git+https://github.com/Overtorment/bip38.git#7eb8701bf845f84eab71f0ef9a8db219420d080d", |
|||
"_spec": "bip38@git+https://github.com/Overtorment/bip38.git", |
|||
"_where": "/home/burn/Documents/BlueWallet", |
|||
"author": { |
|||
"name": "JP Richardson" |
|||
}, |
|||
"bugs": { |
|||
"url": "https://github.com/bitcoinjs/bip38/issues" |
|||
}, |
|||
"bundleDependencies": false, |
|||
"dependencies": { |
|||
"bigi": "^1.2.0", |
|||
"browserify-aes": "^1.0.1", |
|||
"bs58check": "<3.0.0", |
|||
"buffer-xor": "^1.0.2", |
|||
"create-hash": "^1.1.1", |
|||
"ecurve": "^1.0.0", |
|||
"pbkdf2": "^3.0.14" |
|||
}, |
|||
"deprecated": false, |
|||
"description": "BIP38 is a standard process to encrypt Bitcoin and crypto currency private keys that is impervious to brute force attacks thus protecting the user.", |
|||
"devDependencies": { |
|||
"coveralls": "^2.10.0", |
|||
"istanbul": "^0.2.11", |
|||
"mocha": "^2.3.3", |
|||
"mochify": "^2.1.1", |
|||
"standard": "^9.0.2", |
|||
"wif": "^2.0.1" |
|||
}, |
|||
"homepage": "http://cryptocoinjs.com/modules/currency/bip38/", |
|||
"keywords": [ |
|||
"bitcoin", |
|||
"crypto", |
|||
"cryptography", |
|||
"litecoin" |
|||
], |
|||
"main": "index.js", |
|||
"name": "bip38", |
|||
"repository": { |
|||
"url": "git+ssh://git@github.com/bitcoinjs/bip38.git", |
|||
"type": "git" |
|||
}, |
|||
"scripts": { |
|||
"browser-test": "mochify --wd -R spec --timeout 100000", |
|||
"coverage": "istanbul cover _mocha -- --reporter list test/*.js", |
|||
"coveralls": "npm run-script coverage && coveralls < coverage/lcov.info", |
|||
"standard": "standard", |
|||
"test": "npm run standard && npm run unit", |
|||
"unit": "mocha --ui bdd --timeout 240000" |
|||
}, |
|||
"version": "2.0.2", |
|||
"react-native": { |
|||
"path": "path-browserify", |
|||
"fs": "react-native-level-fs", |
|||
"_stream_transform": "readable-stream/transform", |
|||
"_stream_readable": "readable-stream/readable", |
|||
"_stream_writable": "readable-stream/writable", |
|||
"_stream_duplex": "readable-stream/duplex", |
|||
"_stream_passthrough": "readable-stream/passthrough", |
|||
"stream": "stream-browserify" |
|||
}, |
|||
"browser": { |
|||
"path": "path-browserify", |
|||
"fs": "react-native-level-fs", |
|||
"_stream_transform": "readable-stream/transform", |
|||
"_stream_readable": "readable-stream/readable", |
|||
"_stream_writable": "readable-stream/writable", |
|||
"_stream_duplex": "readable-stream/duplex", |
|||
"_stream_passthrough": "readable-stream/passthrough", |
|||
"stream": "stream-browserify" |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
test/ |
|||
.gitignore |
|||
.min-wd |
@ -0,0 +1,44 @@ |
|||
2.0.0 / 2016-05-26 |
|||
------------------ |
|||
- **breaking** Node v0.10 not supported anymore. |
|||
|
|||
1.2.1 / 2015-03-01 |
|||
------------------ |
|||
- now using standard for code formatting |
|||
- now using `pbkdf2` module over `pbkdf2-sha256`, huge performance increase in Node |
|||
|
|||
1.2.0 / 2014-12-11 |
|||
------------------ |
|||
- upgraded `pbkdf2-sha256` from `1.0.1` to `1.1.0` |
|||
- removed `browser` field for `crypto`; not-necessary anymore |
|||
|
|||
1.1.0 / 2014-07-28 |
|||
------------------ |
|||
- added `progressCallback` (Nadav Ivgi / #4)[https://github.com/cryptocoinjs/scryptsy/pull/4] |
|||
|
|||
1.0.0 / 2014-06-10 |
|||
------------------ |
|||
- moved tests to fixtures |
|||
- removed semilcolons per http://cryptocoinjs.com/about/contributing/#semicolons |
|||
- changed `module.exports.scrypt = funct..` to `module.exports = funct...` |
|||
- removed `terst` from dev deps |
|||
- upgraded `"pbkdf2-sha256": "~0.1.1"` to `"pbkdf2-sha256": "^1.0.1"` |
|||
- added `crypto-browserify` dev dep for `pbkdf2-sha256` tests |
|||
- added TravisCI |
|||
- added Coveralls |
|||
- added testling |
|||
|
|||
0.2.0 / 2014-03-05 |
|||
------------------ |
|||
- made a lot of scrypt functions internal along with variables to make thread safe |
|||
|
|||
0.1.0 / 2014-02-18 |
|||
------------------ |
|||
- changed spacing from 4 to 2 |
|||
- removed unneeded JavaScript implementations. Using `pbkdf2-sha256` dep now. |
|||
- add browser test support |
|||
- convert from `Array` to typed arrays and `Buffer` |
|||
|
|||
0.0.1 / 2014-02-18 |
|||
------------------ |
|||
- initial release. Forked from https://github.com/cheongwy/node-scrypt-js and added tests. |
@ -0,0 +1,70 @@ |
|||
scryptsy |
|||
======== |
|||
|
|||
[![build status](https://secure.travis-ci.org/cryptocoinjs/scryptsy.svg)](http://travis-ci.org/cryptocoinjs/scryptsy) |
|||
[![Coverage Status](https://img.shields.io/coveralls/cryptocoinjs/scryptsy.svg)](https://coveralls.io/r/cryptocoinjs/scryptsy) |
|||
[![Version](http://img.shields.io/npm/v/scryptsy.svg)](https://www.npmjs.org/package/scryptsy) |
|||
|
|||
`scryptsy` is a pure Javascript implementation of the [scrypt][wiki] key derivation function that is fully compatible with Node.js and the browser (via Browserify). |
|||
|
|||
|
|||
Why? |
|||
---- |
|||
|
|||
`Scrypt` is an integral part of many crypto currencies. It's a part of the [BIP38](https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki) standard for encrypting private Bitcoin keys. It also serves as the [proof-of-work system](http://en.wikipedia.org/wiki/Proof-of-work_system) for many crypto currencies, most notably: Litecoin and Dogecoin. |
|||
|
|||
|
|||
|
|||
Installation |
|||
------------ |
|||
|
|||
npm install --save scryptsy |
|||
|
|||
|
|||
|
|||
Example |
|||
------- |
|||
|
|||
```js |
|||
var scrypt = require('scryptsy') |
|||
|
|||
var key = "pleaseletmein" |
|||
var salt = "SodiumChloride" |
|||
var data = scrypt(key, salt, 16384, 8, 1, 64) |
|||
console.log(data.toString('hex')) |
|||
// => 7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887 |
|||
``` |
|||
|
|||
|
|||
API |
|||
--- |
|||
|
|||
### scrypt(key, salt, N, r, p, keyLenBytes, [progressCallback]) |
|||
|
|||
- **key**: The key. Either `Buffer` or `string`. |
|||
- **salt**: The salt. Either `Buffer` or `string`. |
|||
- **N**: The number of iterations. `number` (integer) |
|||
- **r**: Memory factor. `number` (integer) |
|||
- **p**: Parallelization factor. `number` (integer) |
|||
- **keyLenBytes**: The number of bytes to return. `number` (integer) |
|||
- **progressCallback**: Call callback on every `1000` ops. Passes in `{current, total, percent}` as first parameter to `progressCallback()`. |
|||
|
|||
Returns `Buffer`. |
|||
|
|||
|
|||
|
|||
Resources |
|||
--------- |
|||
- [Tarsnap Blurb on Scrypt][tarsnap] |
|||
- [Scrypt Whitepaper](http://www.tarsnap.com/scrypt/scrypt.pdf) |
|||
- [IETF Scrypt](https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00) (Test vector params are [incorrect](https://twitter.com/dchest/status/247734446881640448).) |
|||
|
|||
|
|||
License |
|||
------- |
|||
|
|||
MIT |
|||
|
|||
|
|||
[wiki]: http://en.wikipedia.org/wiki/Scrypt |
|||
[tarsnap]: http://www.tarsnap.com/scrypt.html |
@ -0,0 +1,195 @@ |
|||
/* eslint-disable camelcase */ |
|||
let pbkdf2 = require('pbkdf2') |
|||
|
|||
var MAX_VALUE = 0x7fffffff |
|||
|
|||
// N = Cpu cost, r = Memory cost, p = parallelization cost
|
|||
async function scrypt (key, salt, N, r, p, dkLen, progressCallback) { |
|||
if (N === 0 || (N & (N - 1)) !== 0) throw Error('N must be > 0 and a power of 2') |
|||
|
|||
if (N > MAX_VALUE / 128 / r) throw Error('Parameter N is too large') |
|||
if (r > MAX_VALUE / 128 / p) throw Error('Parameter r is too large') |
|||
|
|||
var XY = new Buffer(256 * r) |
|||
var V = new Buffer(128 * r * N) |
|||
|
|||
// pseudo global
|
|||
var B32 = new Int32Array(16) // salsa20_8
|
|||
var x = new Int32Array(16) // salsa20_8
|
|||
var _X = new Buffer(64) // blockmix_salsa8
|
|||
|
|||
// pseudo global
|
|||
var B = pbkdf2.pbkdf2Sync(key, salt, 1, p * 128 * r, 'sha256') |
|||
|
|||
var tickCallback |
|||
if (progressCallback) { |
|||
var totalOps = p * N * 2 |
|||
var currentOp = 0 |
|||
|
|||
tickCallback = function () { |
|||
return new Promise(function(resolve, reject) { |
|||
++currentOp |
|||
|
|||
// send progress notifications once every 1,000 ops
|
|||
if (currentOp % 1000 === 0) { |
|||
progressCallback({ |
|||
current: currentOp, |
|||
total: totalOps, |
|||
percent: (currentOp / totalOps) * 100.0 |
|||
}) |
|||
setTimeout(resolve, 10) |
|||
} else { |
|||
resolve() |
|||
} |
|||
}) |
|||
|
|||
|
|||
|
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < p; i++) { |
|||
await smix(B, i * 128 * r, r, N, V, XY) |
|||
if (typeof shold_stop_bip38 !== 'undefined') break; |
|||
} |
|||
|
|||
return pbkdf2.pbkdf2Sync(key, B, 1, dkLen, 'sha256') |
|||
|
|||
// all of these functions are actually moved to the top
|
|||
// due to function hoisting
|
|||
|
|||
async function smix (B, Bi, r, N, V, XY) { |
|||
var Xi = 0 |
|||
var Yi = 128 * r |
|||
var i |
|||
|
|||
B.copy(XY, Xi, Bi, Bi + Yi) |
|||
|
|||
for (i = 0; i < N; i++) { |
|||
XY.copy(V, i * Yi, Xi, Xi + Yi) |
|||
blockmix_salsa8(XY, Xi, Yi, r) |
|||
|
|||
if (tickCallback) { |
|||
await tickCallback() |
|||
if (typeof shold_stop_bip38 !== 'undefined') break; |
|||
} |
|||
} |
|||
|
|||
for (i = 0; i < N; i++) { |
|||
var offset = Xi + (2 * r - 1) * 64 |
|||
var j = XY.readUInt32LE(offset) & (N - 1) |
|||
blockxor(V, j * Yi, XY, Xi, Yi) |
|||
blockmix_salsa8(XY, Xi, Yi, r) |
|||
|
|||
if (tickCallback) { |
|||
await tickCallback() |
|||
if (typeof shold_stop_bip38 !== 'undefined') break; |
|||
} |
|||
} |
|||
|
|||
XY.copy(B, Bi, Xi, Xi + Yi) |
|||
} |
|||
|
|||
function blockmix_salsa8 (BY, Bi, Yi, r) { |
|||
var i |
|||
|
|||
arraycopy(BY, Bi + (2 * r - 1) * 64, _X, 0, 64) |
|||
|
|||
for (i = 0; i < 2 * r; i++) { |
|||
blockxor(BY, i * 64, _X, 0, 64) |
|||
salsa20_8(_X) |
|||
arraycopy(_X, 0, BY, Yi + (i * 64), 64) |
|||
} |
|||
|
|||
for (i = 0; i < r; i++) { |
|||
arraycopy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64) |
|||
} |
|||
|
|||
for (i = 0; i < r; i++) { |
|||
arraycopy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64) |
|||
} |
|||
} |
|||
|
|||
function R (a, b) { |
|||
return (a << b) | (a >>> (32 - b)) |
|||
} |
|||
|
|||
function salsa20_8 (B) { |
|||
var i |
|||
|
|||
for (i = 0; i < 16; i++) { |
|||
B32[i] = (B[i * 4 + 0] & 0xff) << 0 |
|||
B32[i] |= (B[i * 4 + 1] & 0xff) << 8 |
|||
B32[i] |= (B[i * 4 + 2] & 0xff) << 16 |
|||
B32[i] |= (B[i * 4 + 3] & 0xff) << 24 |
|||
// B32[i] = B.readUInt32LE(i*4) <--- this is signficantly slower even in Node.js
|
|||
} |
|||
|
|||
arraycopy(B32, 0, x, 0, 16) |
|||
|
|||
for (i = 8; i > 0; i -= 2) { |
|||
x[4] ^= R(x[0] + x[12], 7) |
|||
x[8] ^= R(x[4] + x[0], 9) |
|||
x[12] ^= R(x[8] + x[4], 13) |
|||
x[0] ^= R(x[12] + x[8], 18) |
|||
x[9] ^= R(x[5] + x[1], 7) |
|||
x[13] ^= R(x[9] + x[5], 9) |
|||
x[1] ^= R(x[13] + x[9], 13) |
|||
x[5] ^= R(x[1] + x[13], 18) |
|||
x[14] ^= R(x[10] + x[6], 7) |
|||
x[2] ^= R(x[14] + x[10], 9) |
|||
x[6] ^= R(x[2] + x[14], 13) |
|||
x[10] ^= R(x[6] + x[2], 18) |
|||
x[3] ^= R(x[15] + x[11], 7) |
|||
x[7] ^= R(x[3] + x[15], 9) |
|||
x[11] ^= R(x[7] + x[3], 13) |
|||
x[15] ^= R(x[11] + x[7], 18) |
|||
x[1] ^= R(x[0] + x[3], 7) |
|||
x[2] ^= R(x[1] + x[0], 9) |
|||
x[3] ^= R(x[2] + x[1], 13) |
|||
x[0] ^= R(x[3] + x[2], 18) |
|||
x[6] ^= R(x[5] + x[4], 7) |
|||
x[7] ^= R(x[6] + x[5], 9) |
|||
x[4] ^= R(x[7] + x[6], 13) |
|||
x[5] ^= R(x[4] + x[7], 18) |
|||
x[11] ^= R(x[10] + x[9], 7) |
|||
x[8] ^= R(x[11] + x[10], 9) |
|||
x[9] ^= R(x[8] + x[11], 13) |
|||
x[10] ^= R(x[9] + x[8], 18) |
|||
x[12] ^= R(x[15] + x[14], 7) |
|||
x[13] ^= R(x[12] + x[15], 9) |
|||
x[14] ^= R(x[13] + x[12], 13) |
|||
x[15] ^= R(x[14] + x[13], 18) |
|||
} |
|||
|
|||
for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i] |
|||
|
|||
for (i = 0; i < 16; i++) { |
|||
var bi = i * 4 |
|||
B[bi + 0] = (B32[i] >> 0 & 0xff) |
|||
B[bi + 1] = (B32[i] >> 8 & 0xff) |
|||
B[bi + 2] = (B32[i] >> 16 & 0xff) |
|||
B[bi + 3] = (B32[i] >> 24 & 0xff) |
|||
// B.writeInt32LE(B32[i], i*4) //<--- this is signficantly slower even in Node.js
|
|||
} |
|||
} |
|||
|
|||
// naive approach... going back to loop unrolling may yield additional performance
|
|||
function blockxor (S, Si, D, Di, len) { |
|||
for (var i = 0; i < len; i++) { |
|||
D[Di + i] ^= S[Si + i] |
|||
} |
|||
} |
|||
} |
|||
|
|||
function arraycopy (src, srcPos, dest, destPos, length) { |
|||
if (Buffer.isBuffer(src) && Buffer.isBuffer(dest)) { |
|||
src.copy(dest, destPos, srcPos, srcPos + length) |
|||
} else { |
|||
while (length--) { |
|||
dest[destPos++] = src[srcPos++] |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = scrypt |
@ -0,0 +1,87 @@ |
|||
{ |
|||
"_from": "scryptsy@^2.0.0", |
|||
"_id": "scryptsy@2.0.0", |
|||
"_inBundle": false, |
|||
"_integrity": "sha1-Jiw28CMc+nZU4jY/o5TNLexm83g=", |
|||
"_location": "/scryptsy", |
|||
"_phantomChildren": {}, |
|||
"_requested": { |
|||
"type": "range", |
|||
"registry": true, |
|||
"raw": "scryptsy@^2.0.0", |
|||
"name": "scryptsy", |
|||
"escapedName": "scryptsy", |
|||
"rawSpec": "^2.0.0", |
|||
"saveSpec": null, |
|||
"fetchSpec": "^2.0.0" |
|||
}, |
|||
"_requiredBy": [ |
|||
"/" |
|||
], |
|||
"_resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz", |
|||
"_shasum": "262c36f0231cfa7654e2363fa394cd2dec66f378", |
|||
"_spec": "scryptsy@^2.0.0", |
|||
"_where": "/home/burn/Documents/bip38", |
|||
"author": "", |
|||
"bugs": { |
|||
"url": "https://github.com/cryptocoinjs/scryptsy/issues" |
|||
}, |
|||
"bundleDependencies": false, |
|||
"dependencies": {}, |
|||
"deprecated": false, |
|||
"description": "Pure JavaScript implementation of the scrypt key deriviation function that is fully compatible with Node.js and the browser.", |
|||
"devDependencies": { |
|||
"coveralls": "^2.10.0", |
|||
"istanbul": "^0.3.5", |
|||
"mocha": "^2.2.0", |
|||
"mochify": "^2.1.0", |
|||
"standard": "^7.1.1" |
|||
}, |
|||
"homepage": "https://github.com/cryptocoinjs/scryptsy#readme", |
|||
"keywords": [ |
|||
"crytpo", |
|||
"cryptography", |
|||
"scrypt", |
|||
"kdf", |
|||
"litecoin", |
|||
"dogecoin", |
|||
"bitcoin", |
|||
"bip38" |
|||
], |
|||
"license": "MIT", |
|||
"main": "lib/scrypt.js", |
|||
"name": "scryptsy", |
|||
"repository": { |
|||
"url": "git+ssh://git@github.com/cryptocoinjs/scryptsy.git", |
|||
"type": "git" |
|||
}, |
|||
"scripts": { |
|||
"browser-test": "mochify --wd -R spec", |
|||
"coverage": "istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", |
|||
"coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info", |
|||
"lint": "standard", |
|||
"test": "mocha --ui bdd", |
|||
"unit": "mocha" |
|||
}, |
|||
"version": "2.0.0", |
|||
"react-native": { |
|||
"path": "path-browserify", |
|||
"fs": "react-native-level-fs", |
|||
"_stream_transform": "readable-stream/transform", |
|||
"_stream_readable": "readable-stream/readable", |
|||
"_stream_writable": "readable-stream/writable", |
|||
"_stream_duplex": "readable-stream/duplex", |
|||
"_stream_passthrough": "readable-stream/passthrough", |
|||
"stream": "stream-browserify" |
|||
}, |
|||
"browser": { |
|||
"path": "path-browserify", |
|||
"fs": "react-native-level-fs", |
|||
"_stream_transform": "readable-stream/transform", |
|||
"_stream_readable": "readable-stream/readable", |
|||
"_stream_writable": "readable-stream/writable", |
|||
"_stream_duplex": "readable-stream/duplex", |
|||
"_stream_passthrough": "readable-stream/passthrough", |
|||
"stream": "stream-browserify" |
|||
} |
|||
} |
Loading…
Reference in new issue