on master help sign in
feature: can copy login code from bridge
6ae2940Liam Fitzgerald
src/lib/networkCode.jsnew file+49
@@ -0,0 +1,49 @@
1+import { hex2patp } from 'urbit-ob';
2+
3+import jssha256 from 'js-sha256';
4+
5+function shax(buf) {
6+ const hashed = jssha256.sha256.array(buf);
7+ return Buffer.from(hashed);
8+}
9+
10+function hexToArrayBuffer(hex) {
11+ return Buffer.from(hex, 'hex').reverse();
12+}
13+
14+function buf2hex(buf) {
15+ return Buffer.from(buf)
16+ .reverse()
17+ .toString('hex');
18+}
19+
20+function shas(buf, salt) {
21+ return shax(xor(salt, shax(buf)));
22+}
23+
24+function xor(a, b) {
25+ const length = Math.max(a.byteLength, b.byteLength);
26+ const result = new Uint8Array(length);
27+ for (let i = 0; i < length; i++) {
28+ result[i] = a[i] ^ b[i];
29+ }
30+ return result;
31+}
32+
33+function shaf(buf, salt) {
34+ const result = shas(buf, salt);
35+ const halfway = result.length / 2;
36+ const front = result.slice(0, halfway);
37+ const back = result.slice(halfway, result.length);
38+ return xor(front, back);
39+}
40+
41+export const generateCode = async privKeyHex => {
42+ const privKey = hexToArrayBuffer(privKeyHex);
43+ const salt = hexToArrayBuffer('73736170');
44+ const hash = shax(privKey);
45+ const result = shaf(hash, salt);
46+ const half = result.slice(0, result.length / 2);
47+
48+ return hex2patp(buf2hex(half));
49+};
src/lib/useKeyfileGenerator.js+7
@@ -8,6 +8,7 @@ import { useWallet } from 'store/wallet';
8import { usePointCursor } from 'store/pointCursor';
8import { usePointCursor } from 'store/pointCursor';
9
9
10import * as need from 'lib/need';
10import * as need from 'lib/need';
11+import { generateCode } from 'lib/networkCode';
11
12
12import {
13import {
13 attemptNetworkSeedDerivation,
14 attemptNetworkSeedDerivation,
@@ -28,6 +29,8 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
28 const [generating, setGenerating] = useState(true);
29 const [generating, setGenerating] = useState(true);
29 const [keyfile, setKeyfile] = useState(false);
30 const [keyfile, setKeyfile] = useState(false);
30
31
32+ const [code, setCode] = useState(false);
33+
31 const _point = need.point(pointCursor);
34 const _point = need.point(pointCursor);
32 const _details = need.details(getDetails(_point));
35 const _details = need.details(getDetails(_point));
33
36
@@ -71,6 +74,8 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
71
74
72 const pair = deriveNetworkKeys(_networkSeed);
75 const pair = deriveNetworkKeys(_networkSeed);
73
76
77+ generateCode(pair.crypt.private + pair.auth.private + '42').then(setCode);
78+
74 if (!keysMatchChain(pair, _details)) {
79 if (!keysMatchChain(pair, _details)) {
75 setGenerating(false);
80 setGenerating(false);
76 setNotice('Derived networking keys do not match on-chain details.');
81 setNotice('Derived networking keys do not match on-chain details.');
@@ -88,6 +93,7 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
88 urbitWallet,
93 urbitWallet,
89 wallet,
94 wallet,
90 authMnemonic,
95 authMnemonic,
96+ setCode,
91 _details,
97 _details,
92 _point,
98 _point,
93 ]);
99 ]);
@@ -113,6 +119,7 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
113 downloaded,
119 downloaded,
114 download,
120 download,
115 notice,
121 notice,
122+ code,
116 };
123 };
117
124
118 return { ...values, bind: values };
125 return { ...values, bind: values };
src/views/Point.js+13
@@ -9,6 +9,7 @@ import View from 'components/View';
9import Greeting from 'components/Greeting';
9import Greeting from 'components/Greeting';
10import Passport from 'components/Passport';
10import Passport from 'components/Passport';
11import { ForwardButton, BootArvoButton } from 'components/Buttons';
11import { ForwardButton, BootArvoButton } from 'components/Buttons';
12+import CopyButton from 'components/CopyButton';
12import { matchBlinky } from 'components/Blinky';
13import { matchBlinky } from 'components/Blinky';
13import DownloadSigilButton from 'components/DownloadSigilButton';
14import DownloadSigilButton from 'components/DownloadSigilButton';
14
15
@@ -17,6 +18,7 @@ import useInvites from 'lib/useInvites';
17import { useSyncOwnedPoints } from 'lib/useSyncPoints';
18import { useSyncOwnedPoints } from 'lib/useSyncPoints';
18import useCurrentPermissions from 'lib/useCurrentPermissions';
19import useCurrentPermissions from 'lib/useCurrentPermissions';
19import { useLocalRouter } from 'lib/LocalRouter';
20import { useLocalRouter } from 'lib/LocalRouter';
21+import useKeyfileGenerator from 'lib/useKeyfileGenerator';
20
22
21export default function Point() {
23export default function Point() {
22 const { pop, push, names } = useLocalRouter();
24 const { pop, push, names } = useLocalRouter();
@@ -24,6 +26,8 @@ export default function Point() {
24
26
25 const point = need.point(pointCursor);
27 const point = need.point(pointCursor);
26
28
29+ const { code } = useKeyfileGenerator();
30+
27 const {
31 const {
28 isParent,
32 isParent,
29 isActiveOwner,
33 isActiveOwner,
@@ -101,6 +105,15 @@ export default function Point() {
101 Admin
105 Admin
102 </Grid.Item>
106 </Grid.Item>
103 <Grid.Divider />
107 <Grid.Divider />
108+ <Grid.Item
109+ full
110+ as={ForwardButton}
111+ accessory={<CopyButton text={code} />}
112+ detail={code}
113+ detailClassName="mono">
114+ Login Code
115+ </Grid.Item>
116+ <Grid.Divider />
104 {isParent && (
117 {isParent && (
105 <>
118 <>
106 <Grid.Item
119 <Grid.Item