Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Use long derivation paths for App Private Keys (courtesy of blockstack.js) #1496

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
.*/node_modules/immutable/*
.*/blockstack.js/node_modules/*
.*/blockstack.js/src/*
.*/blockstack.js/.git/*
.*/tmp/nexe/*
.*/node_modules/styled-components/.*
.*/node_modules/.*
Expand Down
11 changes: 4 additions & 7 deletions app/js/UpdateStatePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import React, { Component } from 'react'
import Modal from 'react-modal'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { decryptMnemonic } from 'blockstack'
import Alert from './components/Alert'
import InputGroup from './components/InputGroup'
import { AccountActions } from './account/store/account'
import { IdentityActions } from './profiles/store/identity'
import { decrypt } from './utils'
import {
CURRENT_VERSION,
updateState,
Expand Down Expand Up @@ -138,21 +138,18 @@ class UpdateStatePage extends Component {



upgradeBlockstackState(event) {
upgradeBlockstackState(event) {
logger.trace('upgradeBlockstackState')
event.preventDefault()
this.setState({ upgradeInProgress: true })
//
// number of identities to generate
// default identity
// copy api settings

const { encryptedBackupPhrase } = this.props
const { password } = this.state

const dataBuffer = new Buffer(encryptedBackupPhrase, 'hex')
const password = this.state.password

decrypt(dataBuffer, password)
decryptMnemonic(encryptedBackupPhrase, password)
.then(backupPhraseBuffer => {
const backupPhrase = backupPhraseBuffer.toString()
logger.debug('upgradeBlockstackState: correct password!')
Expand Down
7 changes: 2 additions & 5 deletions app/js/account/BackupAccountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import bip39 from 'bip39'
import { HDNode } from 'bitcoinjs-lib'
import { decryptMnemonic } from 'blockstack'

import Alert from '@components/Alert'
import InputGroup from '@components/InputGroup'
import { decrypt } from '@utils'
import log4js from 'log4js'

import { AccountActions } from './store/account'
Expand Down Expand Up @@ -63,11 +63,8 @@ class BackupAccountPage extends Component {

decryptBackupPhrase() {
logger.trace('decryptBackupPhrase')

const password = this.state.password
const dataBuffer = new Buffer(this.props.encryptedBackupPhrase, 'hex')
logger.debug('Trying to decrypt recovery phrase...')
decrypt(dataBuffer, password).then(
decryptMnemonic(this.props.encryptedBackupPhrase, this.state.password).then(
plaintextBuffer => {
logger.debug('Keychain phrase successfully decrypted')
this.updateAlert('success', 'Keychain phrase decrypted')
Expand Down
6 changes: 3 additions & 3 deletions app/js/account/ChangePasswordPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { encryptMnemonic, decryptMnemonic } from 'blockstack'

import Alert from '@components/Alert'
import InputGroup from '@components/InputGroup'
import { AccountActions } from './store/account'
import { decrypt, encrypt } from '@utils'
import log4js from 'log4js'

const logger = log4js.getLogger('account/ChangePasswordPage.js')
Expand Down Expand Up @@ -67,7 +67,7 @@ class ChangePasswordPage extends Component {
const newPassword2 = this.state.newPassword2
const dataBuffer = new Buffer(this.props.encryptedBackupPhrase, 'hex')
logger.debug('Trying to decrypt recovery phrase...')
decrypt(dataBuffer, currentPassword).then(
decryptMnemonic(dataBuffer, currentPassword).then(
plaintextBuffer => {
logger.debug('Recovery phrase successfully decrypted')
if (newPassword.length < 8) {
Expand All @@ -77,7 +77,7 @@ class ChangePasswordPage extends Component {
this.updateAlert('danger', 'New passwords must match')
} else {
logger.debug('Trying to re-encrypt recovery phrase with new password...')
encrypt(plaintextBuffer, newPassword).then(ciphertextBuffer => {
encryptMnemonic(plaintextBuffer, newPassword).then(ciphertextBuffer => {
this.props.updateBackupPhrase(ciphertextBuffer.toString('hex'))
this.updateAlert('success', 'Password updated!')
this.setState({
Expand Down
31 changes: 15 additions & 16 deletions app/js/account/store/account/actions.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { HDNode } from 'bitcoinjs-lib'
import bip39 from 'bip39'
import { randomBytes } from 'crypto'
import {
authorizationHeaderValue,
btcToSatoshis,
satoshisToBtc,
encrypt,
getInsightUrls,
getBlockchainIdentities
} from '@utils'
import { isCoreEndpointDisabled } from '@utils/window-utils'
import { transactions, config, network } from 'blockstack'
import { transactions, config, network, BlockstackWallet } from 'blockstack'

import roundTo from 'round-to'
import * as types from './types'
Expand All @@ -31,7 +28,7 @@ const updateEmail = email => dispatch =>

function createAccount(
encryptedBackupPhrase,
masterKeychain,
keychainB58,
identitiesToGenerate
) {
logger.debug(`createAccount: identitiesToGenerate: ${identitiesToGenerate}`)
Expand All @@ -42,7 +39,7 @@ function createAccount(
firstBitcoinAddress,
identityAddresses,
identityKeypairs
} = getBlockchainIdentities(masterKeychain, identitiesToGenerate)
} = getBlockchainIdentities(keychainB58, identitiesToGenerate)

return {
type: types.CREATE_ACCOUNT,
Expand Down Expand Up @@ -449,20 +446,22 @@ const initializeWallet = (
let masterKeychain = null
if (backupPhrase && bip39.validateMnemonic(backupPhrase)) {
const seedBuffer = bip39.mnemonicToSeed(backupPhrase)
masterKeychain = HDNode.fromSeedBuffer(seedBuffer)
masterKeychain = BlockstackWallet.fromSeedBuffer(seedBuffer)
.toBase58()
} else {
// Create a new wallet
const STRENGTH = 128 // 128 bits generates a 12 word mnemonic
backupPhrase = bip39.generateMnemonic(STRENGTH, randomBytes)
backupPhrase = BlockstackWallet.generateMnemonic()
const seedBuffer = bip39.mnemonicToSeed(backupPhrase)
masterKeychain = HDNode.fromSeedBuffer(seedBuffer)
masterKeychain = BlockstackWallet.fromSeedBuffer(seedBuffer)
.toBase58()
}
return encrypt(new Buffer(backupPhrase), password).then(ciphertextBuffer => {
const encryptedBackupPhrase = ciphertextBuffer.toString('hex')
return dispatch(
createAccount(encryptedBackupPhrase, masterKeychain, identitiesToGenerate)
)
})
return BlockstackWallet.encryptMnemonic(backupPhrase, password)
.then(ciphertextBuffer => {
const encryptedBackupPhrase = ciphertextBuffer.toString('hex')
return dispatch(
createAccount(encryptedBackupPhrase, masterKeychain, identitiesToGenerate)
)
})
}

function newBitcoinAddress() {
Expand Down
9 changes: 4 additions & 5 deletions app/js/account/store/account/reducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as types from './types'
import { HDNode } from 'bitcoinjs-lib'
import { getBitcoinAddressNode } from '@utils'
import { BlockstackWallet } from 'blockstack'

const initialState = {
accountCreated: false, // persist
Expand Down Expand Up @@ -86,10 +85,10 @@ function AccountReducer(state = initialState, action) {
publicKeychain: state.bitcoinAccount.publicKeychain,
addresses: [
...state.bitcoinAccount.addresses,
getBitcoinAddressNode(
HDNode.fromBase58(state.bitcoinAccount.publicKeychain),
BlockstackWallet.getAddressFromBitcoinKeychain(
state.bitcoinAccount.publicKeychain,
state.bitcoinAccount.addressIndex + 1
).getAddress()
)
],
addressIndex: state.bitcoinAccount.addressIndex + 1,
balances: state.bitcoinAccount.balances
Expand Down
8 changes: 4 additions & 4 deletions app/js/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import {
getAppBucketUrl,
isLaterVersion
} from 'blockstack'
import { AppsNode } from '@utils/account-utils'
import { getCorrectAppPrivateKey } from '@utils/account-utils'
import {
fetchProfileLocations,
getDefaultProfileUrl
} from '@utils/profile-utils'
import { getTokenFileUrlFromZoneFile } from '@utils/zone-utils'
import { HDNode } from 'bitcoinjs-lib'
import log4js from 'log4js'
import { uploadProfile } from '../account/utils'
import { signProfileForUpload } from '@utils'
Expand Down Expand Up @@ -209,8 +208,9 @@ class AuthPage extends React.Component {
const privateKey = profileSigningKeypair.key
const appsNodeKey = profileSigningKeypair.appsNodeKey
const salt = profileSigningKeypair.salt
const appsNode = new AppsNode(HDNode.fromBase58(appsNodeKey), salt)
const appPrivateKey = appsNode.getAppNode(appDomain).getAppPrivateKey()

const appPrivateKey = getCorrectAppPrivateKey(this.state.scopes, profile, appsNodeKey,
salt, appDomain)

let profileUrlPromise

Expand Down
25 changes: 6 additions & 19 deletions app/js/profiles/store/identity/actions.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
// @flow
import { HDNode } from 'bitcoinjs-lib'
import bip39 from 'bip39'
import * as types from './types'
import { validateProofs } from 'blockstack'
import { validateProofs, decryptMnemonic, BlockstackWallet } from 'blockstack'
import {
authorizationHeaderValue,
decrypt,
deriveIdentityKeyPair,
getIdentityOwnerAddressNode,
getIdentityPrivateKeychain,
resolveZoneFileToProfile
} from '@utils/index'
import { DEFAULT_PROFILE, fetchProfileLocations } from '@utils/profile-utils'
Expand Down Expand Up @@ -221,25 +216,17 @@ function createNewProfile(
dispatch({ type: types.CREATE_NEW_REQUEST })

// Decrypt master keychain
const dataBuffer = new Buffer(encryptedBackupPhrase, 'hex')
logger.debug('createNewProfile: Trying to decrypt backup phrase...')
return decrypt(dataBuffer, password).then(
return decryptMnemonic(encryptedBackupPhrase, password).then(
plaintextBuffer => {
logger.debug('createNewProfile: Backup phrase successfully decrypted')
const backupPhrase = plaintextBuffer.toString()
const seedBuffer = bip39.mnemonicToSeed(backupPhrase)
const masterKeychain = HDNode.fromSeedBuffer(seedBuffer)
const identityPrivateKeychainNode = getIdentityPrivateKeychain(
masterKeychain
)
const wallet = BlockstackWallet.fromSeedBuffer(seedBuffer)

const index = nextUnusedAddressIndex
const identityOwnerAddressNode = getIdentityOwnerAddressNode(
identityPrivateKeychainNode,
index
)
const newIdentityKeypair = deriveIdentityKeyPair(
identityOwnerAddressNode
)
const newIdentityKeypair = wallet.getIdentityKeyPair(index, true)

logger.debug(
`createNewProfile: new identity: ${newIdentityKeypair.address}`
)
Expand Down
4 changes: 2 additions & 2 deletions app/js/seed/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { decryptMnemonic } from 'blockstack'
import { Initial, Password, Seed, SeedConfirm, Success } from './views'
import { decrypt } from '@utils/encryption-utils'
import { withRouter, browserHistory } from 'react-router'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
Expand Down Expand Up @@ -144,7 +144,7 @@ class SeedContainer extends Component {

const buffer = new Buffer(encryptedBackupPhrase, method)

return decrypt(buffer, password).then(result => {
return decryptMnemonic(buffer, password).then(result => {
if (this.state.seed !== result.toString()) {
return this.setState(
{
Expand Down
8 changes: 4 additions & 4 deletions app/js/sign-in/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
import { decryptMnemonic } from 'blockstack'
import { isBackupPhraseValid } from '@utils'
import { validateMnemonic } from 'bip39'
import { decrypt, isBackupPhraseValid } from '@utils'
import { browserHistory, withRouter } from 'react-router'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
Expand Down Expand Up @@ -169,7 +170,7 @@ class SignIn extends React.Component {
if (this.state.decrypt && !decrypting) {
this.setState({ decrypting: true })

decrypt(
decryptMnemonic(
new Buffer(encryptedKey, 'base64'),
this.state.password
)
Expand All @@ -190,8 +191,7 @@ class SignIn extends React.Component {
key: ''
})
})
}
else {
} else {
this.updateView(VIEWS.EMAIL)
}
}
Expand Down
6 changes: 2 additions & 4 deletions app/js/update/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import { withRouter, browserHistory } from 'react-router'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { decryptMnemonic } from 'blockstack'
import { AccountActions } from '../account/store/account'
import { IdentityActions } from '../profiles/store/identity'
import { Initial, Success, NoUpdate } from './views'
Expand All @@ -28,7 +29,6 @@ import {
hasLegacyCoreStateVersion,
migrateLegacyCoreEndpoints
} from '@utils/api-utils'
import { decrypt } from '@utils'
const VIEWS = {
INITIAL: 0,
SUCCESS: 1,
Expand Down Expand Up @@ -134,10 +134,8 @@ class UpdatePage extends React.Component {
console.error('No encryptedBackupPhrase, cannot continue')
return null
}
const dataBuffer = new Buffer(encryptedBackupPhrase, 'hex')
const { password } = this.state

return decrypt(dataBuffer, password)
return decryptMnemonic(encryptedBackupPhrase, this.state.password)
.then(backupPhraseBuffer => {
this.setState(
{
Expand Down
Loading