Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add passphrase compatibility to the hw wallet #2218

Open
wants to merge 1 commit 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 36 additions & 5 deletions electron/src/hardware-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,30 @@ ipcMain.on('hwSendPin', (event, pin) => {
if (pin) {
lastPinPromiseResolve(pin);
} else {
lastPinPromiseReject(new Error("Cancelled"))
lastPinPromiseReject(new Error("Cancelled"));
}
});

let lastPassphrasePromiseResolve;
let lastPassphrasePromiseReject;

const passphraseEvent = function() {
return new Promise((resolve, reject) => {
lastPassphrasePromiseResolve = resolve;
lastPassphrasePromiseReject = reject;

console.log("Hardware wallet passphrase requested");
if (win) {
win.webContents.send('hwPassphraseRequested');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codacy has a fix for the issue: Strings must use doublequote.

Suggested change
win.webContents.send('hwPassphraseRequested');
win.webContents.send("hwPassphraseRequested");

}
});
};

ipcMain.on('hwSendPassphrase', (event, passphrase) => {
if (passphrase) {
lastPassphrasePromiseResolve(passphrase);
} else {
lastPassphrasePromiseReject(new Error("Cancelled"));
}
});

Expand All @@ -140,7 +163,7 @@ ipcMain.on('hwSendSeedWord', (event, word) => {
if (word) {
lastSeedWordPromiseResolve(word);
} else {
lastSeedWordPromiseReject(new Error("Cancelled"))
lastSeedWordPromiseReject(new Error("Cancelled"));
}
});

Expand All @@ -161,7 +184,7 @@ ipcMain.on('hwGetFeatures', (event, requestId) => {
});

ipcMain.on('hwGetAddresses', (event, requestId, addressN, startIndex, confirm) => {
const promise = deviceWallet.devAddressGen(addressN, startIndex, confirm, pinEvent);
const promise = deviceWallet.devAddressGen(addressN, startIndex, confirm, pinEvent, passphraseEvent);
promise.then(
addresses => { console.log("Addresses promise resolved", addresses); event.sender.send('hwGetAddressesResponse', requestId, addresses); },
error => { console.log("Addresses promise errored: ", error); event.sender.send('hwGetAddressesResponse', requestId, { error: error.toString() }); }
Expand Down Expand Up @@ -209,21 +232,29 @@ ipcMain.on('hwWipe', (event, requestId) => {
});

ipcMain.on('hwSignMessage', (event, requestId, addressIndex, message) => {
const promise = deviceWallet.devSkycoinSignMessage(addressIndex, message, pinEvent);
const promise = deviceWallet.devSkycoinSignMessage(addressIndex, message, pinEvent, passphraseEvent);
promise.then(
result => { console.log("Signature promise resolved", result); event.sender.send('hwSignMessageResponse', requestId, result); },
error => { console.log("Signature promise errored: ", error); event.sender.send('hwSignMessageResponse', requestId, { error: error.toString() }); }
);
});

ipcMain.on('hwSignTransaction', (event, requestId, inputs, outputs) => {
const promise = deviceWallet.devSkycoinTransactionSign(inputs, outputs, pinEvent);
const promise = deviceWallet.devSkycoinTransactionSign(inputs, outputs, pinEvent, passphraseEvent);
promise.then(
result => { console.log("Sign transaction promise resolved", result); event.sender.send('hwSignTransactionResponse', requestId, result); },
error => { console.log("Sign transaction promise errored: ", error); event.sender.send('hwSignTransactionResponse', requestId, { error: error.toString() }); }
);
});

ipcMain.on('hwApplySettings', (event, requestId, usePassphrase) => {
const promise = deviceWallet.devApplySettings(usePassphrase, pinEvent);
promise.then(
result => { console.log("Apply settings promise resolved", result); event.sender.send('hwApplySettingsResponse', requestId, result); },
error => { console.log("Apply settings promise errored: ", error); event.sender.send('hwApplySettingsResponse', requestId, { error: error.toString() }); }
);
});

module.exports = {
setWinRef,
setWalletsFolderPath
Expand Down
2 changes: 2 additions & 0 deletions src/gui/static/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { HwPinDialogComponent } from './components/layout/hardware-wallet/hw-pin
import { HwSeedWordDialogComponent } from './components/layout/hardware-wallet/hw-seed-word-dialog/hw-seed-word-dialog.component';
import { Bip39WordListService } from './services/bip39-word-list.service';
import { HwConfirmTxDialogComponent } from './components/layout/hardware-wallet/hw-confirm-tx-dialog/hw-confirm-tx-dialog.component';
import { HwPassphraseDialogComponent } from './components/layout/hardware-wallet/hw-passphrase-dialog/hw-passphrase-dialog.component';

@Component({
selector: 'app-root',
Expand All @@ -27,6 +28,7 @@ export class AppComponent implements OnInit {
translateService.use('en');

hwWalletService.requestPinComponent = HwPinDialogComponent;
hwWalletService.requestPassphraseComponent = HwPassphraseDialogComponent;
hwWalletService.requestWordComponent = HwSeedWordDialogComponent;
hwWalletService.signTransactionConfirmationComponent = HwConfirmTxDialogComponent;

Expand Down
9 changes: 9 additions & 0 deletions src/gui/static/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ import { Bip39WordListService } from './services/bip39-word-list.service';
import { HwDialogBaseComponent } from './components/layout/hardware-wallet/hw-dialog-base.component';
import { HwConfirmTxDialogComponent } from './components/layout/hardware-wallet/hw-confirm-tx-dialog/hw-confirm-tx-dialog.component';
import { HwConfirmAddressDialogComponent } from './components/layout/hardware-wallet/hw-confirm-address-dialog/hw-confirm-address-dialog.component';
import { HwPassphraseDialogComponent } from './components/layout/hardware-wallet/hw-passphrase-dialog/hw-passphrase-dialog.component';
import { HwPassphraseActivationDialogComponent } from './components/layout/hardware-wallet/hw-passphrase-activation-dialog/hw-passphrase-activation-dialog.component';
import { HwPassphraseHelpDialogComponent } from './components/layout/hardware-wallet/hw-passphrase-help-dialog/hw-passphrase-help-dialog.component';


const ROUTES = [
Expand Down Expand Up @@ -214,6 +217,9 @@ const ROUTES = [
HwDialogBaseComponent,
HwConfirmTxDialogComponent,
HwConfirmAddressDialogComponent,
HwPassphraseDialogComponent,
HwPassphraseActivationDialogComponent,
HwPassphraseHelpDialogComponent,
],
entryComponents: [
AddDepositAddressComponent,
Expand All @@ -240,6 +246,9 @@ const ROUTES = [
HwSeedWordDialogComponent,
HwConfirmTxDialogComponent,
HwConfirmAddressDialogComponent,
HwPassphraseDialogComponent,
HwPassphraseActivationDialogComponent,
HwPassphraseHelpDialogComponent,
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
<app-modal class="modal" [headline]="'hardware-wallet.added.title' | translate" [dialog]="dialogRef" [disableDismiss]="currentState === states.Initial || currentState === states.Finished">
<app-modal class="modal" [headline]="'hardware-wallet.added.title' | translate" [dialog]="dialogRef" [disableDismiss]="currentState !== states.Failed">
<app-hw-message *ngIf="currentState === states.Initial"
[text]="'hardware-wallet.added.wait' | translate"
[icon]="msgIcons.Spinner"
></app-hw-message>

<div *ngIf="currentState === states.PassphraseWarning">
<app-hw-message
[text]="'hardware-wallet.added.passphrase-warning' | translate"
[linkText]="'hardware-wallet.passphrase-help.link' | translate"
[icon]="msgIcons.Warning"
(linkClicked)="openHelp()"
></app-hw-message>

<div class="-buttons">
<app-button (action)="cancel()">
{{ 'hardware-wallet.general.cancel' | translate }}
</app-button>
<app-button (action)="startAdding()" class="primary">
{{ 'hardware-wallet.general.continue' | translate }}
</app-button>
</div>
</div>

<app-hw-message *ngIf="currentState === states.Adding"
[text]="'hardware-wallet.added.configuring' | translate"
[icon]="msgIcons.Spinner"
></app-hw-message>
Expand All @@ -24,7 +47,7 @@
</div>

<div class="-buttons" *ngIf="currentState === states.Finished || currentState === states.Failed">
<app-button (action)="saveNameAndCloseModal()" class="primary" [disabled]="!form.valid">
<app-button (action)="currentState === states.Finished ? saveNameAndCloseModal() : closeModal()" class="primary" [disabled]="form && !form.valid">
{{ 'hardware-wallet.general.close' | translate }}
</app-button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { WalletService } from '../../../../services/wallet.service';
import { HwWalletService } from '../../../../services/hw-wallet.service';
import { ChildHwDialogParams } from '../hw-options-dialog/hw-options-dialog.component';
import { HwDialogBaseComponent } from '../hw-dialog-base.component';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Wallet } from '../../../../app.datatypes';
import { HwPassphraseHelpDialogComponent } from '../hw-passphrase-help-dialog/hw-passphrase-help-dialog.component';

enum States {
Initial,
PassphraseWarning,
Adding,
Finished,
Failed,
}
Expand All @@ -20,7 +23,7 @@ enum States {
})
export class HwAddedDialogComponent extends HwDialogBaseComponent<HwAddedDialogComponent> {

closeIfHwDisconnected = false;
closeIfHwDisconnected = true;

currentState: States = States.Initial;
states = States;
Expand All @@ -34,8 +37,26 @@ export class HwAddedDialogComponent extends HwDialogBaseComponent<HwAddedDialogC
private walletService: WalletService,
hwWalletService: HwWalletService,
private formBuilder: FormBuilder,
private dialog: MatDialog,
) {
super(hwWalletService, dialogRef);
this.operationSubscription = hwWalletService.getFeatures().subscribe(features => {
if (features.rawResponse.passphraseProtection) {
this.currentState = States.PassphraseWarning;
} else {
this.startAdding();
}
});
}

cancel() {
this.data.requestOptionsComponentRefresh('hardware-wallet.added.canceled');
this.closeModal();
}

startAdding() {
this.currentState = States.Adding;

this.operationSubscription = this.walletService.createHardwareWallet().subscribe(wallet => {
this.walletService.updateWalletHasHwSecurityWarnings(wallet).subscribe(() => {
this.wallet = wallet;
Expand All @@ -44,6 +65,7 @@ export class HwAddedDialogComponent extends HwDialogBaseComponent<HwAddedDialogC
label: [wallet.label, Validators.required],
});

this.closeIfHwDisconnected = false;
this.currentState = States.Finished;
this.data.requestOptionsComponentRefresh();
});
Expand All @@ -58,4 +80,10 @@ export class HwAddedDialogComponent extends HwDialogBaseComponent<HwAddedDialogC
this.walletService.saveHardwareWallets();
this.closeModal();
}

openHelp() {
this.dialog.open(HwPassphraseHelpDialogComponent, <MatDialogConfig> {
width: '450px',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codacy has a fix for the issue: Strings must use doublequote.

Suggested change
width: '450px',
width: "450px",

});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<button mat-button color="primary" (click)="changePin()">
<div class="label">{{ (!needsPin ? 'hardware-wallet.options.change-pin' : 'hardware-wallet.options.create-pin') | translate }}</div>
</button>
<button mat-button color="primary" (click)="changePassphraseActivation()">
<div class="label">{{ (!hasPassphrase ? 'hardware-wallet.options.activate-passphrase' : 'hardware-wallet.options.deactivate-passphrase') | translate }}</div>
</button>
<button mat-button color="primary" (click)="confirmSeed()" *ngIf="!needsBackup">
<div class="label">{{ 'hardware-wallet.options.confirm-seed' | translate }}</div>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HwChangePinDialogComponent } from '../hw-change-pin-dialog/hw-change-pi
import { HwRestoreSeedDialogComponent } from '../hw-restore-seed-dialog/hw-restore-seed-dialog.component';
import { Observable } from 'rxjs/Observable';
import { HwDialogBaseComponent } from '../hw-dialog-base.component';
import { HwPassphraseActivationDialogComponent } from '../hw-passphrase-activation-dialog/hw-passphrase-activation-dialog.component';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codacy has a fix for the issue: Strings must use doublequote.

Suggested change
import { HwPassphraseActivationDialogComponent } from '../hw-passphrase-activation-dialog/hw-passphrase-activation-dialog.component';
import { HwPassphraseActivationDialogComponent } from "../hw-passphrase-activation-dialog/hw-passphrase-activation-dialog.component";


enum States {
Disconnected,
Expand All @@ -26,6 +27,7 @@ enum States {
export interface ChildHwDialogParams {
wallet: Wallet;
walletHasPin: boolean;
walletHasPassphrase: boolean;
requestOptionsComponentRefresh: any;
}

Expand All @@ -44,6 +46,7 @@ export class HwOptionsDialogComponent extends HwDialogBaseComponent<HwOptionsDia
customErrorMsg = '';
needsBackup: boolean;
needsPin: boolean;
hasPassphrase: boolean;

private dialogSubscription: ISubscription;

Expand Down Expand Up @@ -97,6 +100,10 @@ export class HwOptionsDialogComponent extends HwDialogBaseComponent<HwOptionsDia
this.openDialog(HwRestoreSeedDialogComponent);
}

changePassphraseActivation() {
this.openDialog(HwPassphraseActivationDialogComponent);
}

private openDialog(dialogType) {
this.customErrorMsg = '';

Expand All @@ -108,6 +115,7 @@ export class HwOptionsDialogComponent extends HwDialogBaseComponent<HwOptionsDia
config.data = <ChildHwDialogParams> {
wallet: this.wallet,
walletHasPin: !this.needsPin,
walletHasPassphrase: this.hasPassphrase,
requestOptionsComponentRefresh: ((error: string = null, recheckSecurityOnly: boolean = false) => {
if (!error) {
if (!recheckSecurityOnly) {
Expand Down Expand Up @@ -147,6 +155,7 @@ export class HwOptionsDialogComponent extends HwDialogBaseComponent<HwOptionsDia
return this.walletService.updateWalletHasHwSecurityWarnings(this.wallet).map(warnings => {
this.needsBackup = warnings.includes(HwSecurityWarnings.NeedsBackup);
this.needsPin = warnings.includes(HwSecurityWarnings.NeedsPin);
this.hasPassphrase = warnings.includes(HwSecurityWarnings.HasPassphrase);

return warnings;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<app-modal class="modal"
[headline]="(activating ? 'hardware-wallet.options.activate-passphrase' : 'hardware-wallet.options.deactivate-passphrase') | translate"
[dialog]="dialogRef"
[disableDismiss]="currentState === states.Processing">
<div *ngIf="currentState === states.Initial">
<app-hw-message
[text]="(activating ? 'hardware-wallet.activate-passphrase.warning-activate' : 'hardware-wallet.activate-passphrase.warning-deactivate') | translate"
[linkText]="'hardware-wallet.passphrase-help.link' | translate"
[icon]="msgIcons.Warning"
(linkClicked)="openHelp()"
></app-hw-message>

<div class="-buttons">
<app-button (action)="closeModal()">
{{ 'hardware-wallet.general.cancel' | translate }}
</app-button>
<app-button (action)="changeConfig()" class="primary">
{{ 'hardware-wallet.general.continue' | translate }}
</app-button>
</div>
</div>

<div *ngIf="currentState !== states.Initial">
<app-hw-message *ngIf="currentState === states.Processing"
[text]="'hardware-wallet.general.confirm' | translate"
[icon]="msgIcons.Confirm"
></app-hw-message>

<app-hw-message *ngIf="currentState === states.ReturnedSuccess"
[text]="'hardware-wallet.general.completed' | translate"
[icon]="msgIcons.Success"
></app-hw-message>

<app-hw-message *ngIf="currentState === states.ReturnedRefused"
[text]="'hardware-wallet.general.refused' | translate"
[icon]="msgIcons.Error"
></app-hw-message>

<app-hw-message *ngIf="currentState === states.Failed"
[text]="'hardware-wallet.general.generic-error' | translate"
[icon]="msgIcons.Error"
></app-hw-message>

<div class="-buttons" *ngIf="currentState !== states.Processing">
<app-button (action)="closeModal()" class="primary">
{{ 'hardware-wallet.general.close' | translate }}
</app-button>
</div>
</div>
</app-modal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { HwPassphraseActivationDialogComponent } from './hw-passphrase-activation-dialog.component';

describe('HwPassphraseActivationDialogComponent', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codacy has a fix for the issue: Strings must use doublequote.

Suggested change
describe('HwPassphraseActivationDialogComponent', () => {
describe("HwPassphraseActivationDialogComponent", () => {

let component: HwPassphraseActivationDialogComponent;
let fixture: ComponentFixture<HwPassphraseActivationDialogComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HwPassphraseActivationDialogComponent ],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(HwPassphraseActivationDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should be created', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codacy has a fix for the issue: Strings must use doublequote.

Suggested change
it('should be created', () => {
it("should be created", () => {

expect(component).toBeTruthy();
});
});