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