Skip to content

Commit

Permalink
Support signing messages on trezor
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusd committed Oct 9, 2018
1 parent bb520f9 commit 1c108a7
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 15 deletions.
45 changes: 43 additions & 2 deletions app/actions/TrezorActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import * as wallet from "wallet";
import * as selectors from "selectors";
import fs from "fs";
import { sprintf } from "sprintf-js";
import { rawHashToHex, rawToHex, hexToRaw } from "helpers";
import { rawHashToHex, rawToHex, hexToRaw, str2utf8hex, hex2b64 } from "helpers";
import { publishTransactionAttempt } from "./ControlActions";
import { model1_decred_homescreen } from "helpers/trezor";

import { EXTERNALREQUEST_TREZOR_BRIDGE } from "main_dev/externalRequests";
import { SIGNTX_ATTEMPT, SIGNTX_FAILED, SIGNTX_SUCCESS } from "./ControlActions";
import {
SIGNTX_ATTEMPT, SIGNTX_FAILED, SIGNTX_SUCCESS,
SIGNMESSAGE_ATTEMPT, SIGNMESSAGE_FAILED, SIGNMESSAGE_SUCCESS
} from "./ControlActions";

const hardeningConstant = 0x80000000;

Expand Down Expand Up @@ -293,6 +296,44 @@ export const signTransactionAttemptTrezor = (rawUnsigTx, constructTxResponse) =>
}
};

export const signMessageAttemptTrezor = (address, message) => async (dispatch, getState) => {

dispatch({ type: SIGNMESSAGE_ATTEMPT });

const device = selectors.trezorDevice(getState());
if (!device) {
dispatch({ error: "Device not connected", type: SIGNMESSAGE_FAILED });
return;
}

const chainParams = selectors.chainParams(getState());
const { grpc: { walletService } } = getState();

try {
const addrValidResp = await wallet.validateAddress(walletService, address);
if (!addrValidResp.getIsValid()) throw "Input has an invalid address " + address;
if (!addrValidResp.getIsMine()) throw "Trezor only supports signing with wallet addresses";
const addrIndex = addrValidResp.getIndex();
const addrBranch = addrValidResp.getIsInternal() ? 1 : 0;
const address_n = addressPath(addrIndex, addrBranch, WALLET_ACCOUNT,
chainParams.HDCoinType);

const signedMsg = await deviceRun(dispatch, getState, device, async session => {
await dispatch(checkTrezorIsDcrwallet(session));

return await session.signMessage(address_n, str2utf8hex(message),
chainParams.trezorCoinName, false);
});

const signature = hex2b64(signedMsg.message.signature);
dispatch({ getSignMessageSignature: signature, type: SIGNMESSAGE_SUCCESS });

} catch (error) {
dispatch({ error, type: SIGNMESSAGE_FAILED });
}

};

// walletTxToBtcjsTx converts a tx decoded by the decred wallet (ie,
// returned from the decodeRawTransaction call) into a bitcoinjs-compatible
// transaction (to be used in trezor)
Expand Down
46 changes: 33 additions & 13 deletions app/components/buttons/SignMessageButton.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { signMessagePage } from "connectors";
import { PassphraseModalButton } from "./index";
import { PassphraseModalButton, KeyBlueButton } from "./index";
import { FormattedMessage as T } from "react-intl";

@autobind
Expand All @@ -16,19 +16,39 @@ class SignMessageButton extends React.Component {
onSubmit && onSubmit();
}

async onAttemptSignMessageTrezor() {
const { address, message, disabled, signMessageAttemptTrezor, onSubmit } = this.props;
if (disabled || !signMessageAttemptTrezor) return;
await signMessageAttemptTrezor(address, message);
onSubmit && onSubmit();
}

render() {
const { disabled, isSigningMessage, className } = this.props;

return (
<PassphraseModalButton
modalTitle={<T id="securitycenter.signMessageModal" m="Sign Message" />}
className={className}
disabled={disabled}
onSubmit={this.onAttemptSignMessage}
loading={isSigningMessage}
buttonLabel={<T id="securitycenter.signMessageBtn" m="Sign Message" />}
/>
);
const { disabled, isSigningMessage, className, isTrezor } = this.props;

if (isTrezor) {
return (
<KeyBlueButton
className={className}
disabled={disabled}
onClick={this.onAttemptSignMessageTrezor}
loading={isSigningMessage}
>
<T id="securitycenter.signMessageBtn" m="Sign Message" />
</KeyBlueButton>
);
} else {
return (
<PassphraseModalButton
modalTitle={<T id="securitycenter.signMessageModal" m="Sign Message" />}
className={className}
disabled={disabled}
onSubmit={this.onAttemptSignMessage}
loading={isSigningMessage}
buttonLabel={<T id="securitycenter.signMessageBtn" m="Sign Message" />}
/>
);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions app/connectors/signMessagePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { bindActionCreators } from "redux";
import { selectorMap } from "../fp";
import * as sel from "../selectors";
import * as ca from "../actions/ControlActions";
import * as trza from "../actions/TrezorActions";

const mapStateToProps = selectorMap({
signMessageError: sel.signMessageError,
signMessageSignature: sel.signMessageSignature,
isSigningMessage: sel.isSigningMessage,
walletService: sel.walletService,
isSignMessageDisabled: sel.isSignMessageDisabled,
isTrezor: sel.isTrezor,
});

const mapDispatchToProps = dispatch => bindActionCreators({
signMessageAttempt: ca.signMessageAttempt,
validateAddress: ca.validateAddress,
signMessageCleanStore: ca.signMessageCleanStore,
signMessageAttemptTrezor: trza.signMessageAttemptTrezor,
}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps);
9 changes: 9 additions & 0 deletions app/helpers/byteActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@ export function hexReversedHashToArray(hexStr) {
res.reverse();
return res;
}

// str2utf8hex converts a (js, utf-16) string into (utf-8 encoded) hex.
export function str2utf8hex(str) {
return Buffer.from(str).toString("hex");
}

export function hex2b64(hex) {
return new Buffer(hex, "hex").toString("base64");
}

0 comments on commit 1c108a7

Please sign in to comment.