Browse Source

FIX: BIP38 import support

receivehooks
Overtorment 5 years ago
parent
commit
c4be1ac0da
  1. 0
      blue_modules/bip38/.npmignore
  2. 0
      blue_modules/bip38/CHANGELOG.md
  3. 0
      blue_modules/bip38/LICENSE
  4. 0
      blue_modules/bip38/README.md
  5. 21
      blue_modules/bip38/index.js
  6. 0
      blue_modules/bip38/package.json
  7. 0
      blue_modules/bip38/scryptsy/.npmignore
  8. 0
      blue_modules/bip38/scryptsy/CHANGELOG.md
  9. 0
      blue_modules/bip38/scryptsy/README.md
  10. 0
      blue_modules/bip38/scryptsy/lib/scrypt.js
  11. 0
      blue_modules/bip38/scryptsy/package.json
  12. 20
      class/walletImport.js
  13. 4
      package-lock.json
  14. 2
      package.json
  15. 9
      screen/selftest.js
  16. 10
      tests/unit/Bip38.test.js

0
bip38/.npmignore → blue_modules/bip38/.npmignore

0
bip38/CHANGELOG.md → blue_modules/bip38/CHANGELOG.md

0
bip38/LICENSE → blue_modules/bip38/LICENSE

0
bip38/README.md → blue_modules/bip38/README.md

21
bip38/index.js → blue_modules/bip38/index.js

@ -1,3 +1,4 @@
const BlueCrypto = require('react-native-blue-crypto');
var aes = require('browserify-aes')
var assert = require('assert')
var Buffer = require('safe-buffer').Buffer
@ -41,6 +42,18 @@ function getAddress (d, compressed) {
return bs58check.encode(payload)
}
async function scryptWrapper(secret, salt, N, r, p, dkLen, progressCallback) {
if (BlueCrypto.isAvailable()) {
secret = Buffer.from(secret).toString('hex');
salt = Buffer.from(salt).toString('hex');
const hex = await BlueCrypto.scrypt(secret, salt, N, r, p, dkLen);
return Buffer.from(hex, 'hex');
} else {
// fallback to js implementation
return scrypt(secret, salt, N, r, p, dkLen, progressCallback);
}
}
async function encryptRaw (buffer, compressed, passphrase, progressCallback, scryptParams) {
if (buffer.length !== 32) throw new Error('Invalid private key length')
scryptParams = scryptParams || SCRYPT_PARAMS
@ -54,7 +67,7 @@ async function encryptRaw (buffer, compressed, passphrase, progressCallback, scr
var r = scryptParams.r
var p = scryptParams.p
var scryptBuf = await scrypt(secret, salt, N, r, p, 64, progressCallback)
var scryptBuf = await scryptWrapper(secret, salt, N, r, p, 64, progressCallback)
var derivedHalf1 = scryptBuf.slice(0, 32)
var derivedHalf2 = scryptBuf.slice(32, 64)
@ -103,7 +116,7 @@ async function decryptRaw (buffer, passphrase, progressCallback, scryptParams) {
var p = scryptParams.p
var salt = buffer.slice(3, 7)
var scryptBuf = await scrypt(passphrase, salt, N, r, p, 64, progressCallback)
var scryptBuf = await scryptWrapper(passphrase, salt, N, r, p, 64, progressCallback)
var derivedHalf1 = scryptBuf.slice(0, 32)
var derivedHalf2 = scryptBuf.slice(32, 64)
@ -161,7 +174,7 @@ async function decryptECMult (buffer, passphrase, progressCallback, scryptParams
var N = scryptParams.N
var r = scryptParams.r
var p = scryptParams.p
var preFactor = await scrypt(passphrase, ownerSalt, N, r, p, 32, progressCallback)
var preFactor = await scryptWrapper(passphrase, ownerSalt, N, r, p, 32, progressCallback)
var passFactor
if (hasLotSeq) {
@ -174,7 +187,7 @@ async function decryptECMult (buffer, passphrase, progressCallback, scryptParams
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 seedBPass = await scryptWrapper(passPoint, Buffer.concat([addressHash, ownerEntropy]), 1024, 1, 1, 64)
var derivedHalf1 = seedBPass.slice(0, 32)
var derivedHalf2 = seedBPass.slice(32, 64)

0
bip38/package.json → blue_modules/bip38/package.json

0
bip38/scryptsy/.npmignore → blue_modules/bip38/scryptsy/.npmignore

0
bip38/scryptsy/CHANGELOG.md → blue_modules/bip38/scryptsy/CHANGELOG.md

0
bip38/scryptsy/README.md → blue_modules/bip38/scryptsy/README.md

0
bip38/scryptsy/lib/scrypt.js → blue_modules/bip38/scryptsy/lib/scrypt.js

0
bip38/scryptsy/package.json → blue_modules/bip38/scryptsy/package.json

20
class/walletImport.js

@ -17,6 +17,9 @@ const A = require('../analytics');
/** @type {AppStorage} */
const BlueApp = require('../BlueApp');
const loc = require('../loc');
const bip38 = require('../blue_modules/bip38');
const wif = require('wif');
const prompt = require('../prompt');
export default class WalletImport {
/**
@ -88,6 +91,8 @@ export default class WalletImport {
}
const placeholderWallet = WalletImport.addPlaceholderWallet(importText);
// Plan:
// -2. check if BIP38 encrypted
// -1. check lightning custodian
// 0. check if its HDSegwitBech32Wallet (BIP84)
// 1. check if its HDSegwitP2SHWallet (BIP49)
// 2. check if its HDLegacyP2PKHWallet (BIP44)
@ -99,6 +104,21 @@ export default class WalletImport {
// 7. check if its private key (legacy address) TODO
try {
if (importText.startsWith('6P')) {
let password = false;
do {
password = await prompt('This looks like password-protected private key (BIP38)', 'Enter password to decrypt', false);
} while (!password);
let decryptedKey = await bip38.decrypt(importText, password, status => {
console.warn(status.percent + '%');
});
if (decryptedKey) {
importText = wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed);
}
}
// is it lightning custodian?
if (importText.indexOf('blitzhub://') !== -1 || importText.indexOf('lndhub://') !== -1) {
let lnd = new LightningCustodianWallet();

4
package-lock.json

@ -11448,6 +11448,10 @@
"version": "git+https://github.com/BlueWallet/react-native-biometrics.git#570dedf776413b76bc414996f6a509135313a06f",
"from": "git+https://github.com/BlueWallet/react-native-biometrics.git#2.0.0"
},
"react-native-blue-crypto": {
"version": "git+https://github.com/Overtorment/react-native-blue-crypto.git#30a0a054c634033cb3d0247a0a18a95fd616e45e",
"from": "git+https://github.com/Overtorment/react-native-blue-crypto.git"
},
"react-native-camera": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.17.0.tgz",

2
package.json

@ -36,6 +36,7 @@
"podinstall": "./podinstall.sh",
"start": "node node_modules/react-native/local-cli/cli.js start",
"android": "react-native run-android",
"android:clean": "cd android; ./gradlew clean ; cd .. ; npm run android",
"ios": "react-native run-ios",
"postinstall": "./node_modules/.bin/rn-nodeify --install buffer,events,process,stream,util,inherits,fs,path --hack; npm run releasenotes2json; npm run podinstall; npx jetify",
"test": "npm run unit && npm run jest && npm run lint",
@ -96,6 +97,7 @@
"react-localization": "1.0.15",
"react-native": "0.61.5",
"react-native-biometrics": "git+https://github.com/BlueWallet/react-native-biometrics.git#2.0.0",
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto",
"react-native-camera": "3.17.0",
"react-native-default-preference": "1.4.1",
"react-native-device-info": "4.0.1",

9
screen/selftest.js

@ -4,6 +4,7 @@ import { BlueLoading, BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueNavig
import PropTypes from 'prop-types';
import { SegwitP2SHWallet, LegacyWallet, HDSegwitP2SHWallet, HDSegwitBech32Wallet } from '../class';
const bitcoin = require('bitcoinjs-lib');
const BlueCrypto = require('react-native-blue-crypto');
let BigNumber = require('bignumber.js');
let encryption = require('../encryption');
let BlueElectrum = require('../BlueElectrum');
@ -254,6 +255,14 @@ export default class Selftest extends Component {
}
//
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
const hex = await BlueCrypto.scrypt('717765727479', '4749345a22b23cf3', 64, 8, 8, 32); // using non-default parameters to speed it up (not-bip38 compliant)
if (hex !== 'F36AB2DC12377C788D61E6770126D8A01028C8F6D8FE01871CE0489A1F696A90')
throw new Error('react-native-blue-crypto is not ok');
}
//
} catch (Err) {
errorMessage += Err;
isOk = false;

10
tests/unit/Bip38.test.js

@ -2,7 +2,7 @@
let assert = require('assert');
it('bip38 decodes', async () => {
const bip38 = require('../../bip38');
const bip38 = require('../../blue_modules/bip38');
const wif = require('wif');
let encryptedKey = '6PRVWUbkzq2VVjRuv58jpwVjTeN46MeNmzUHqUjQptBJUHGcBakduhrUNc';
@ -25,11 +25,15 @@ it('bip38 decodes slow', async () => {
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
const bip38 = require('../../bip38');
const bip38 = require('../../blue_modules/bip38');
const wif = require('wif');
let encryptedKey = '6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN';
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status => process.stdout.write(parseInt(status.percent) + '%\r'));
let callbackWasCalled = false;
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', () => {
callbackWasCalled = true;
});
assert.ok(callbackWasCalled);
assert.strictEqual(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),

Loading…
Cancel
Save