on master help sign in
Merge pull request #1012 from urbit/t/549-metamask-can-download-keyfile

auth: use keccak256 to derive token

commitc3a479db9836a0071e02c8933ce3dafa08777349 authorHunter Miller <hunter@hmiller.dev> dateWed 30 Mar 2022, 14:22 (5 years ago) parent8914f70090f664
10 files changed+123−48
package-lock.json+14−17 package.json+1−1 src/components/L2/Modal.scss+12 src/components/L2/Window/HeaderPane.scss+5 src/lib/authToken.ts+11−5 src/store/wallet.js+10−1 src/views/Login/LoginSelector.scss−12 src/views/Login/Mnemonic.tsx+13−1 src/views/Login/Ticket.tsx+11−1 src/views/UrbitOS/Home.tsx+46−10
package-lock.json+14−17
@@ -14239,15 +14239,14 @@
1423914239 }
1424014240 },
1424114241 "ethereumjs-util": {
14242− "version": "7.1.0",
14243− "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.0.tgz",
14244− "integrity": "sha512-kR+vhu++mUDARrsMMhsjjzPduRVAeundLGXucGRHF3B4oEltOUspfgCVco4kckucj3FMlLaZHUl9n7/kdmr6Tw==",
14242+ "version": "7.1.4",
14243+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz",
14244+ "integrity": "sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A==",
1424514245 "requires": {
1424614246 "@types/bn.js": "^5.1.0",
1424714247 "bn.js": "^5.1.2",
1424814248 "create-hash": "^1.1.2",
1424914249 "ethereum-cryptography": "^0.1.3",
14250− "ethjs-util": "0.1.6",
1425114250 "rlp": "^2.2.4"
1425214251 },
1425314252 "dependencies": {
@@ -14311,15 +14310,6 @@
1431114310 }
1431214311 }
1431314312 },
14314− "ethjs-util": {
14315− "version": "0.1.6",
14316− "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
14317− "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
14318− "requires": {
14319− "is-hex-prefixed": "1.0.0",
14320− "strip-hex-prefix": "1.0.0"
14321− }
14322− },
1432314313 "event-iterator": {
1432414314 "version": "1.2.0",
1432514315 "resolved": "https://registry.npmjs.org/event-iterator/-/event-iterator-1.2.0.tgz",
@@ -28808,11 +28798,18 @@
2880828798 }
2880928799 },
2881028800 "rlp": {
28811− "version": "2.2.6",
28812− "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz",
28813− "integrity": "sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==",
28801+ "version": "2.2.7",
28802+ "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz",
28803+ "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==",
2881428804 "requires": {
28815− "bn.js": "^4.11.1"
28805+ "bn.js": "^5.2.0"
28806+ },
28807+ "dependencies": {
28808+ "bn.js": {
28809+ "version": "5.2.0",
28810+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
28811+ "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw=="
28812+ }
2881628813 }
2881728814 },
2881828815 "rn-host-detect": {
package.json+1−1
@@ -50,7 +50,7 @@
5050 "copy-to-clipboard": "^3.2.0",
5151 "date-fns": "^2.25.0",
5252 "ethereum-blockies-base64": "^1.0.2",
53− "ethereumjs-util": "^7.0.10",
53+ "ethereumjs-util": "^7.1.4",
5454 "file-saver": "^2.0.0",
5555 "final-form": "^4.18.4",
5656 "final-form-arrays": "^3.0.1",
src/components/L2/Modal.scss+12
@@ -39,3 +39,15 @@
3939 }
4040 }
4141}
42+
43+.info-modal-content {
44+ position: relative;
45+ background-color: white;
46+ width: 280px;
47+ border-radius: $large-border-radius;
48+ font-family: $default-font;
49+ padding: 24px;
50+ font-size: 14px;
51+ line-height: 24px;
52+ max-width: 100%;
53+}
src/components/L2/Window/HeaderPane.scss+5
@@ -33,6 +33,11 @@
3333 padding: 8px 16px;
3434 font-family: $default-font;
3535
36+ &.keyfile-unavailable {
37+ height: unset;
38+ background-color: rgba(0, 0, 0, 0.6);
39+ }
40+
3641 &:hover {
3742 opacity: 0.8;
3843 }
src/lib/authToken.ts+11−5
@@ -9,13 +9,15 @@ import { ledgerSignMessage } from './ledger';
99import { trezorSignMessage } from './trezor';
1010import BridgeWallet from './types/BridgeWallet';
1111import { Hash } from '@urbit/roller-api';
12+import { keccak256 } from 'ethereumjs-util';
1213
1314const MESSAGE = 'Bridge Authentication Token';
1415
15−function signMessage(privateKey: Buffer) {
16+function signMessage(privateKey: Buffer, useLegacyTokenSigning = false) {
1617 const msg = '\x19Ethereum Signed Message:\n' + MESSAGE.length + MESSAGE;
17− // #ecdsaSign requires a 32-byte buffer, hence sha256
18− const hashed = crypto.sha256(Buffer.from(msg));
18+ const hashed = useLegacyTokenSigning
19+ ? crypto.sha256(Buffer.from(msg))
20+ : keccak256(Buffer.from(msg));
1921 const { signature } = ecdsaSign(Buffer.from(hashed), privateKey);
2022
2123 // add key recovery parameter
@@ -61,6 +63,7 @@ type WalletConnectAuthTokenArgs = {
6163type DefaultAuthTokenArgs = {
6264 wallet: BridgeWallet;
6365 walletType?: symbol;
66+ useLegacyTokenSigning?: boolean;
6467};
6568
6669type GetAuthTokenArgs =
@@ -101,8 +104,11 @@ const getWalletConnectAuthToken = ({
101104 return connector.signPersonalMessage([MESSAGE, address]);
102105};
103106
104−const getDefaultAuthToken = ({ wallet }: DefaultAuthTokenArgs) => {
105− const signature = signMessage(wallet.privateKey!);
107+const getDefaultAuthToken = ({
108+ wallet,
109+ useLegacyTokenSigning = false,
110+}: DefaultAuthTokenArgs) => {
111+ const signature = signMessage(wallet.privateKey!, useLegacyTokenSigning);
106112
107113 const token = `0x${Buffer.from(signature).toString('hex')}`;
108114
src/store/wallet.js+10−1
@@ -53,6 +53,11 @@ function _useWallet(initialWallet = Nothing(), initialMnemonic = Nothing()) {
5353
5454 const [authToken, setAuthToken] = useState(Nothing());
5555
56+ // See: https://github.com/urbit/bridge/issues/549#issuecomment-1048359617
57+ // This is used for legacy compatibility; this flow should eventually be
58+ // be removed after ~1 year.
59+ const [useLegacyTokenSigning, setUseLegacyTokenSigning] = useState(false);
60+
5661 const { web3 } = useNetwork();
5762
5863 useEffect(() => {
@@ -72,11 +77,12 @@ function _useWallet(initialWallet = Nothing(), initialMnemonic = Nothing()) {
7277 walletType,
7378 walletHdPath,
7479 web3: _web3,
80+ useLegacyTokenSigning,
7581 });
7682
7783 setAuthToken(Just(token));
7884 })();
79− }, [wallet, walletType, walletHdPath, web3]);
85+ }, [wallet, walletType, walletHdPath, web3, useLegacyTokenSigning]);
8086
8187 const setWalletType = useCallback(
8288 walletType => {
@@ -132,6 +138,7 @@ function _useWallet(initialWallet = Nothing(), initialMnemonic = Nothing()) {
132138 setAuthMnemonic(Nothing());
133139 setNetworkSeed(Nothing());
134140 setNetworkRevision(Nothing());
141+ setUseLegacyTokenSigning(false);
135142 }, [
136143 _setWalletType,
137144 setWalletHdPath,
@@ -167,6 +174,8 @@ function _useWallet(initialWallet = Nothing(), initialMnemonic = Nothing()) {
167174 //
168175 authToken,
169176 setAuthToken,
177+ useLegacyTokenSigning,
178+ setUseLegacyTokenSigning,
170179 };
171180}
172181
src/views/Login/LoginSelector.scss−12
@@ -60,18 +60,6 @@
6060 }
6161 }
6262
63− .info-modal-content {
64− position: relative;
65− background-color: white;
66− width: 280px;
67− border-radius: $large-border-radius;
68− font-family: $default-font;
69− padding: 24px;
70− font-size: 14px;
71− line-height: 24px;
72− max-width: 100%;
73− }
74−
7563 .master-ticket {
7664 .body-pane {
7765 .input-form {
src/views/Login/Mnemonic.tsx+13−1
@@ -35,7 +35,13 @@ interface MnemonicProps {
3535export default function Mnemonic({ className, goHome }: MnemonicProps) {
3636 useLoginView(WALLET_TYPES.MNEMONIC);
3737
38− const { setWallet, setAuthMnemonic, setWalletHdPath }: any = useWallet();
38+ const {
39+ setWallet,
40+ setAuthMnemonic,
41+ setWalletHdPath,
42+ setUseLegacyTokenSigning,
43+ useLegacyTokenSigning,
44+ }: any = useWallet();
3945
4046 const [skipValidation, setSkipValidation] = useState(false);
4147 const [phraseHdPath, setPhraseHdPath] = useState(false);
@@ -96,6 +102,12 @@ export default function Mnemonic({ className, goHome }: MnemonicProps) {
96102 label: 'Passphrase & HD Path',
97103 onClick: () => setPhraseHdPath(!phraseHdPath),
98104 },
105+ {
106+ selected: useLegacyTokenSigning,
107+ key: 'useLegacyTokenSigning',
108+ label: 'Use Legacy Token',
109+ onClick: () => setUseLegacyTokenSigning(!useLegacyTokenSigning),
110+ },
99111 ];
100112
101113 return (
src/views/Login/Ticket.tsx+11−1
@@ -43,7 +43,11 @@ interface TicketProps {
4343export default function Ticket({ className, goHome }: TicketProps) {
4444 useLoginView(WALLET_TYPES.TICKET);
4545
46− const { setUrbitWallet }: any = useWallet();
46+ const {
47+ setUrbitWallet,
48+ setUseLegacyTokenSigning,
49+ useLegacyTokenSigning,
50+ }: any = useWallet();
4751 const { setPointCursor }: any = usePointCursor();
4852 const impliedPoint = useImpliedPoint();
4953 const didWarn = useRef(false);
@@ -172,6 +176,12 @@ export default function Ticket({ className, goHome }: TicketProps) {
172176 label: 'Shards',
173177 onClick: () => setUseShards(!useShards),
174178 },
179+ {
180+ selected: useLegacyTokenSigning,
181+ key: 'useLegacyTokenSigning',
182+ label: 'Use Legacy Token',
183+ onClick: () => setUseLegacyTokenSigning(!useLegacyTokenSigning),
184+ },
175185 ];
176186
177187 const ticketInputClass = usePassphrase && useShards ? 'ticket-input' : '';
src/views/UrbitOS/Home.tsx+46−10
@@ -7,6 +7,7 @@ import Window from 'components/L2/Window/Window';
77import HeaderPane from 'components/L2/Window/HeaderPane';
88import BodyPane from 'components/L2/Window/BodyPane';
99import { ReactComponent as KeyfileIcon } from 'assets/keyfile.svg';
10+import { ReactComponent as InfoIcon } from 'assets/info.svg';
1011
1112import { useLocalRouter } from 'lib/LocalRouter';
1213import { useSingleKeyfileGenerator } from 'lib/useKeyfileGenerator';
@@ -42,22 +43,57 @@ export default function UrbitOSHome() {
4243 names,
4344 ]);
4445
45− const { code, download } = useSingleKeyfileGenerator({});
46+ const { available, code, download, generating } = useSingleKeyfileGenerator(
47+ {}
48+ );
49+ const keyfileAvailable = available && !generating;
50+ const [showKeysUnavailableModal, setShowKeysUnavailableModal] = useState(
51+ false
52+ );
53+
54+ const headerButton = () => {
55+ if (!hasSetNetworkKeys) {
56+ return null;
57+ }
58+
59+ if (generating) {
60+ return <Box className="header-button keyfile">Generating...</Box>;
61+ }
62+
63+ return keyfileAvailable ? (
64+ <Button className="header-button keyfile" onClick={download}>
65+ <KeyfileIcon />
66+ Download Keyfile
67+ </Button>
68+ ) : (
69+ <>
70+ <Button
71+ className="header-button keyfile-unavailable"
72+ onClick={() => setShowKeysUnavailableModal(true)}>
73+ <InfoIcon />
74+ &nbsp;Keyfile Unavailable
75+ </Button>
76+ <Modal
77+ show={showKeysUnavailableModal}
78+ hide={() => setShowKeysUnavailableModal(false)}>
79+ <Box className="info-modal-content">
80+ <div className="fw-bold mb5">Keyfile Unavailable</div>
81+ <div className="mb5">
82+ If you set networking keys with an older version of Bridge, try
83+ logging in again with "Use Legacy Token" selected.
84+ </div>
85+ </Box>
86+ </Modal>
87+ </>
88+ );
89+ };
4690
4791 return (
4892 <Window className="os-home">
4993 <HeaderPane>
5094 <Row className="header-row">
5195 <h5>OS</h5>
52− {hasSetNetworkKeys && (
53− <Button
54− className="header-button keyfile"
55− disabled={!hasSetNetworkKeys}
56− onClick={download}>
57− <KeyfileIcon />
58− Download Keyfile
59− </Button>
60− )}
96+ {headerButton()}
6197 </Row>
6298 </HeaderPane>
6399 <BodyPane>