Skip to content

Commit

Permalink
Added BIP39 Passphrase mismatch detection. When passphrase is enabled if
Browse files Browse the repository at this point in the history
a different passphrase is entered later on, eg. `passphrase_cache`
timeout, that doesn't match what's originally used to load the current
wallet session KKClient will display `Conflicting BIP39 Passphrase`
message.

This is done by generating and saving a session 'fingerprint' from the
passphrase. When a later passphrase is entered its 'fingerprint' is
computed and compared to the original.

The 'fingerprint' is derived using `pbkdf2(rounds = 100000,
output_length = 16 bytes, digest = sha512)`. 16 random bytes from
`crypto.getRandomValues` is used as the salt.

Note `BlockcypherApiToken` isn't used for this purpose since it
necessitates a `GetPublicKey` call to KeepKey which could trigger
`PinMatrixRequest`.
  • Loading branch information
greatwolf committed Aug 6, 2022
1 parent 492d5ec commit cd02f74
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 10 deletions.
104 changes: 94 additions & 10 deletions Background.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
h = e("@keepkey/device-client/dist/device-client-manager"),
m = e("@keepkey/device-client/dist/device-client"),
b = e("./modules/keepkeyjs/blockchainApis/blockcypher-metadata/blockcypher-api-token-service"),
y = e("../manifest.json");
y = e("../manifest.json"),
BIP39Session = e("./bip39-session-service").BIP39Session;
console.log("Keepkey Chrome Application Wallet:", y.version),
chrome.app.runtime.onLaunched.addListener(function()
{
Expand Down Expand Up @@ -299,6 +300,7 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
y = void 0,
c.AccountListManager.clearAccountList(),
b.BlockcypherApiTokenService.apiTokenPromise = null,
BIP39Session.clear(),
s.UiMessenger.sendMessageToUI("disconnected",
{
deviceId: e
Expand Down Expand Up @@ -375,7 +377,8 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
"./ui-messenger": 122,
"@keepkey/device-client/dist/device-client": 152,
"@keepkey/device-client/dist/device-client-manager": 151,
lodash: 368
lodash: 368,
"./bip39-session-service": 469
}],
2: [function(e, t, n)
{
Expand Down Expand Up @@ -2929,6 +2932,18 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
var r = e("../MessageDispatcher"),
i = e("../../account-list-manager"),
a = e("@keepkey/device-client/dist/device-client-manager"),
BIP39Session = e("./bip39-session-service").BIP39Session,
ui = e("../../ui-messenger"),
handleSend = function(client, e, mismatch)
{
client = mismatch
? client.then(t => t.cancel())
: client.then(t => t.sendPassphrase(e.passphrase))
return client.then(function()
{
e.passphrase = ""
})
},
o = function()
{
function e()
Expand All @@ -2938,13 +2953,29 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
}
return e.prototype.action = function(e)
{
return a.DeviceClientManager.instance.getActiveClient().then(function(t)
{
return t.sendPassphrase(e.passphrase)
}).then(function()
{
e.passphrase = ""
})
var activeClient = a.DeviceClientManager
.instance
.getActiveClient()
if (!BIP39Session.active)
{
BIP39Session.initiate(e.passphrase)
return handleSend(activeClient, e, false)
}
return BIP39Session
.isSame(e.passphrase)
.then(function (sameSession)
{
handleSend(activeClient, e, !sameSession).then(function()
{
if (!sameSession)
ui.UiMessenger
.sendMessageToUI("Conflicting Passphrase",
{
type: 'Passphrase',
message: `BIP39 Passphrase Mismatch`
})
})
})
},
e
}();
Expand All @@ -2953,7 +2984,9 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
{
"../../account-list-manager": 2,
"../MessageDispatcher": 6,
"@keepkey/device-client/dist/device-client-manager": 151
"../../ui-messenger": 122,
"@keepkey/device-client/dist/device-client-manager": 151,
"./bip39-session-service": 469
}],
36: [function(e, t, n)
{
Expand Down Expand Up @@ -80630,5 +80663,56 @@ var _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
{
"../device-message-helper": 153
}],
469: [function(e, t, n)
{
"use strict";
Object.defineProperty(n, "__esModule",
{
value: !0
});

var buffer = e("buffer").Buffer
var pbkdf2 = e("pbkdf2").pbkdf2
const ROUNDS = 100000
const KEYLEN = 16
const DIGEST = 'sha512'
var sessionid, salt
var setSession = function(_, hash) { sessionid = hash }
n.BIP39Session = function() {}
n.BIP39Session.initiate = function(passphrase)
{
salt = new buffer(KEYLEN)
crypto.getRandomValues(salt)
pbkdf2(passphrase, salt, ROUNDS, KEYLEN, DIGEST, setSession)
}
n.BIP39Session.isSame = function(passphrase)
{
return new Promise(function(resolve)
{
pbkdf2(passphrase, salt, ROUNDS, KEYLEN, DIGEST, function(_, hash)
{
resolve(hash.compare(sessionid) == 0)
})
})
}
n.BIP39Session.clear = function()
{
sessionid = undefined
salt = undefined
}
Object.defineProperty(n.BIP39Session, "active",
{
get: function t()
{
return !!(sessionid && salt)
},
enumerable: !0,
configurable: !1
})
},
{
buffer: 299,
pbkdf2: 380
}],
},
{}, [1]);
6 changes: 6 additions & 0 deletions chrome-wallet/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2119,6 +2119,7 @@ angular.module('kkWallet', ['ngRoute', 'ngAnimate', 'ui.bootstrap', 'monospaced.
c && e.invoke(t(c), this)
}]),
e.when('Conflicting Application', t('/conflict'), this),
e.when('Conflicting Passphrase', t('/conflict/:type'), this),
e.when('Failure', ['$injector', 'FailureMessageService', 'NavigationService', function(e, a, o)
{
var i = this.request.message.message || '',
Expand Down Expand Up @@ -2736,6 +2737,10 @@ angular.module('kkWallet', ['ngRoute', 'ngAnimate', 'ui.bootstrap', 'monospaced.
{
templateUrl: 'app/popup/disable-conflicting/conflicting-message.tpl.html',
goable: !1
}).when('/conflict/passphrase',
{
templateUrl: 'app/popup/conflicting/conflicting-passphrase.tpl.html',
goable: !1
}).when('/about',
{
templateUrl: 'app/popup/about/about.tpl.html',
Expand Down Expand Up @@ -3302,6 +3307,7 @@ angular.module('kkWallet', ['ngRoute', 'ngAnimate', 'ui.bootstrap', 'monospaced.
e.put('app/popup/device/device.tpl.html', '<section id=device-settings ng-controller=DeviceController><header><back-button class="button-left header-button"></back-button></header><div class=content><h2><span class=cornflower-txt><b>Settings</b></span> For Your<vendor-name></vendor-name></h2><div class=menu><ul><li><button class=menu-item ng-click="go(\'/label/settings\', \'slideLeft\')">Change Label</button></li><li ng-hide=!supportsRecoveryDryRun><button class=menu-item ng-click=recoveryDryRun()>Test Recovery Sentence</button></li><li><button class=menu-item ng-click=changePin()>{{device.pin_protection ? "Change PIN" : "Add PIN Protection"}}</button></li><li><button class=menu-item ng-click="go(\'/pin-timeout\', \'slideLeft\')">Change Pin Timeout</button></li><li><button class=menu-item ng-click=togglePassphrase(device)>{{device.passphrase_protection ? "Disable BIP39 Passphrase" : "Enable BIP39 Passphrase"}}</button></li><li><button class=menu-item ng-click=wipeDevice()>Wipe Device</button></li><li><button class=menu-item ng-click="go(\'/support\', \'slideLeft\')">Contact Support</button></li><li><button class=menu-item ng-click="go(\'/about\', \'slideLeft\')">About KeepKey</button></li><li><button class=menu-item ng-click="go(\'/acknowledgements\', \'slideLeft\')">Acknowledgements</button></li><li ng-show=showLoginButton><button class=menu-item ng-click=loginToSS()>Log in to ShapeShift</button></li><li ng-show=!showLoginButton><button class=menu-item ng-click=logoutFromSS()>Log out from Shapeshift</button></li></ul><notification></notification></div></div><div class=footer><div class=left><vendor-name></vendor-name>Firmware <span class=data>v{{device.major_version}}.{{device.minor_version}}.{{device.patch_version}}</span></div><div class=right>KeepKey Client v{{device.wallet_version}}</div></div><notification-message></notification-message></section>'),
e.put('app/popup/device/lifeboat.tpl.html', '<section id=device-settings ng-controller=LifeboatController><header><back-button class="button-left header-button" action=goToTop()></back-button></header><div class=content><h2><span class=cornflower-txt><b>Settings</b></span> For Your<vendor-name></vendor-name></h2><div class=menu><ul><li><button class=menu-item ng-click=wipeDevice()>Wipe Device</button></li><li><button class=menu-item ng-click="go(\'/pin-timeout\', \'slideLeft\')">Change Pin Timeout</button></li><li><button class=menu-item ng-click="go(\'/support\', \'slideLeft\')">Contact Support</button></li><li><button class=menu-item ng-click="go(\'/about\', \'slideLeft\')">About KeepKey</button></li></ul></div><notification></notification></div><div class=footer><div class=left><vendor-name></vendor-name>Firmware <span class=data>v{{device.major_version}}.{{device.minor_version}}.{{device.patch_version}}</span></div><div class=right>KeepKey Client v{{device.wallet_version}}</div></div><notification-message></notification-message></section>'),
e.put('app/popup/disable-conflicting/conflicting-message.tpl.html', '<section id=conflicting-application-screen><header></header><div class="content failure"><div class=content-inner><div class=description><div class=alert-icon><span class="fa fa-exclamation-triangle"></span> <span class=alert-text>Conflicting Application</span></div><div class=alert-description>There is another application running that conflicts with the KeepKey Client. Please close the KeepKey Client and any other applications which use your<vendor-name after=.></vendor-name>Unplug and reconnect your<vendor-name after=,></vendor-name>then reopen the KeepKey Client.</div></div></div></div><footer ng-include="\'app/popup/footer/footer.tpl.html\'"></footer></section>'),
e.put('app/popup/conflicting/conflicting-passphrase.tpl.html', '<section id=conflicting-application-screen><header></header><div class="content failure"><div class=content-inner><div class=description><div class=alert-icon><span class="fa fa-exclamation-triangle"></span> <span class=alert-text>Conflicting BIP39 Passphrase</span></div><div class=alert-description>The entered passphrase does not match the currently loaded <vendor-name></vendor-name> session. Unplug and reconnect your<vendor-name></vendor-name> and try again.</div></div></div></div><footer ng-include="\'app/popup/footer/footer.tpl.html\'"></footer></section>'),
e.put('app/popup/exchange/exchange.tpl.html', '<section id=build-transaction ng-controller=ExchangeController><header><back-button class="button-left header-button"></back-button></header><div class=content><account-balance account=sourceAccount loading=false fresh=fresh currency=currency name-display=name ng-if=sourceAccount.name></account-balance><form name=form novalidate ng-class="{exchange: true}"><source-entry field-name=source source-account=sourceAccount source-account-image-url=sourceAccountImageUrl ng-hide=sourceAccount.name></source-entry><exchange-setup-recipient-entry ng-show=sourceAccount.name ng-hide="!sourceAccount.name || destinationAccount.name" current-account=sourceAccount.id destination-account=destinationAccount destination-account-image-url=destinationAccountImageUrl field-name=dest form=form currency-name={{sourceAccount.coinType}} disabled=preparingTransaction></exchange-setup-recipient-entry><div class=exchange-setup><div class=exchange-images ng-class="{\'image-centered\': true}"><div ng-show=sourceAccount.name><img class=currency-logo ng-src={{sourceAccountImageUrl}}><div class=centered>{{sourceAccount.coinType}}</div></div><i class="fa fa-arrow-right" ng-if=destinationAccount.name></i><div class=shapeshift-exchange-container ng-if=destinationAccount.name><div class=shapeshift-fox-container><img class=fox src=assets/partner-logos/shapeshift.svg></div></div><i class="fa fa-arrow-right" ng-if=destinationAccount.name></i><div ng-if=destinationAccount.name><img class=currency-logo ng-src={{destinationAccountImageUrl}}><div class=centered>{{destinationAccount.coinType}}</div></div></div></div><div class="content-inner-centering sending two-column"><div class=amount-container><amount-entry amount=userInput.amount max-amount=maxSendAmount.toString() min-amount=minSendAmount.toString() max-reason=maxReason min-reason=minReason field-name=amount form=form disable=preparingTransaction currency=currency is-exchange-amount-label=false loading=!fresh.status ng-if=destinationAccount.name></amount-entry></div><div class=value-container ng-show=destinationAccount.name><div class=value-display><label>Estimated receive amount:</label><div class="estimated-value value"><formatted-amount ng-show=estimatedReceiveAmount amount=estimatedReceiveAmount currency=destinationCurrency></formatted-amount>&nbsp;</div></div></div></div><div class=fee-container ng-class="{\'two-column\': destinationAccount.name}" ng-show=destinationAccount.name><div class=value-container><div class="fee-display value-display"><label>Miner fee:</label><div class=value><formatted-amount class=fee ng-show=showFee() amount=getFee(userInput.feeLevel) currency=getFeeCurrency()></formatted-amount></div></div></div><div class=value-container ng-show=destinationAccount.name><div class="exchange-rate-display value-display"><label>Estimated rate:</label><div class=value><span>1&nbsp;{{currencySymbol}}&nbsp;&asymp;&nbsp;</span><formatted-amount amount=exchangeMarketInfo.rate currency=destinationCurrency></formatted-amount></div></div></div></div><div class=bch-fork-container ng-show=bchOnly><div class=bch-info-container><img class=bch-warning src=assets/img/warning-icon.png><div class=bch-fork-text>Due to the Bitcoin Cash fork, there is a risk of replay attacks on BCH transactions after 15 November 2020, 12:00 PM GMT (epoch 1605441600). This wallet will follow the BCHN chain. Learn more <a href=https://bchinfo.org/en/alert/2020-08-09-nov-upgrade target=_blank>here.</a></div></div><div class=bch-checkbox-container><input type=checkbox ng-model=enableBchButton> I understand and accept the risks.</div></div><div class="alert-info sending-alert" ng-show=exchangeDenied()><span ng-if="exchangeStatus.errorCode === \'geoRestriction\'">ShapeShift is not available in your area. Please see <a href={{exchangeStatus.url}} target=_blank>{{exchangeStatus.url}}</a> for more information.</span></div><div class=login-prompt><span ng-show=showLogin>Please log in to your ShapeShift account from the settings page in order to get the full benefits of FOX membership!</span> <button class=button type=submit ng-disabled="disableTradeButton || !enableBchButton" ng-click=buildTransaction()>Start Trade</button></div></form></div><ng-include src="\'app/popup/send/preparing.tpl.html\'" ng-if=preparingTransaction></ng-include></section>'),
e.put('app/popup/directives/AccountBalanceComponent.tpl.html', '<div class="account-balance-component online"><div ng-show=fresh.status class=connection-light></div><div ng-hide=fresh.status class=refreshing-balance><i class="fa fa-refresh fa-spin" aria-hidden=true></i></div><img ng-src={{accountImageUrl}}><wallet-balance class=details balance=account.highConfidenceBalance pending=account.lowConfidenceBalance loading=loading currency=currency></wallet-balance><div class=account-number ng-hide=singleAccount>{{name}} <button ng-if=canEdit() class="fa fa-pencil" ng-click=accountSettings() uib-tooltip="Change Account Name" tooltip-placement=bottom-right tooltip-popup-delay=500></button></div></div>'),
e.put('app/popup/directives/AmountEntry.tpl.html', '<div class=amount-entry><label>{{amountLabel}}</label><div class=input-field><span class=exchange-currency ng-show=isExchangeAmountLabel>{{symbol}}</span> <input ng-keypress=fillMaxDetector($event) name={{fieldName}} ng-model=amount , ng-focus="focusedElement = \'amount\'" ; ng-disabled="disabled || maxLessThanMin" bignumber-min={{bigMinAmount.toString()}} bignumber-max={{bigMaxAmount.toString()}} maxlength=30 currency={{currency}} autocomplete=off required><div ng-hide=loading tooltip-append-to-body=true class="max-value-button fa fa-arrow-up" ng-click=fillMax() uib-tooltip="Send maximum"></div></div><div class=invalid ng-messages=field.$error role=alert ng-show=form.$submitted><div ng-message=required>Required</div><div ng-message=max>{{maxReason}}: {{formattedMax.toString()}} {{symbol}}</div><div ng-message=min>{{minReason}}: {{formattedMin.toString()}} {{symbol}}</div><div ng-message=number>Invalid Amount</div></div></div>'),
Expand Down

0 comments on commit cd02f74

Please sign in to comment.