on master help sign in
Merge pull request #305 from urbit/fix/view-code
578aec1Anthony Arroyo
src/lib/keys.js+4−4
@@ -59,6 +59,9 @@ const jam = seed => {
59// simple alias to avoid importing kg into component scope
59// simple alias to avoid importing kg into component scope
60export const deriveNetworkKeys = seed => kg.deriveNetworkKeys(seed);
60export const deriveNetworkKeys = seed => kg.deriveNetworkKeys(seed);
61
61
62+export const createRing = pair =>
63+ pair.crypt.private + pair.auth.private + NETWORK_KEY_CURVE_PARAMETER;
64+
62/**
65/**
63 * @param {object} pair
66 * @param {object} pair
64 * @param {number} point
67 * @param {number} point
@@ -66,10 +69,7 @@ export const deriveNetworkKeys = seed => kg.deriveNetworkKeys(seed);
66 * @return {string}
69 * @return {string}
67 */
70 */
68export const compileNetworkingKey = (pair, point, revision) => {
71export const compileNetworkingKey = (pair, point, revision) => {
69− const bnsec = new BN(
72+ const bnsec = new BN(createRing(pair), 'hex');
70− pair.crypt.private + pair.auth.private + NETWORK_KEY_CURVE_PARAMETER,
71− 'hex'
72− );
73
73
74 const sed = noun.dwim(
74 const sed = noun.dwim(
75 noun.Atom.fromInt(point),
75 noun.Atom.fromInt(point),
src/lib/networkCode.jsnew file+50
@@ -0,0 +1,50 @@
1+import { hex2patp } from 'urbit-ob';
2+import { createRing } from 'lib/keys';
3+
4+import jssha256 from 'js-sha256';
5+
6+function shax(buf) {
7+ const hashed = jssha256.sha256.array(buf);
8+ return Buffer.from(hashed);
9+}
10+
11+function hex2buf(hex) {
12+ return Buffer.from(hex, 'hex').reverse();
13+}
14+
15+function buf2hex(buf) {
16+ return Buffer.from(buf)
17+ .reverse()
18+ .toString('hex');
19+}
20+
21+function shas(buf, salt) {
22+ return shax(xor(salt, shax(buf)));
23+}
24+
25+function xor(a, b) {
26+ const length = Math.max(a.byteLength, b.byteLength);
27+ const result = new Uint8Array(length);
28+ for (let i = 0; i < length; i++) {
29+ result[i] = a[i] ^ b[i];
30+ }
31+ return result;
32+}
33+
34+function shaf(buf, salt) {
35+ const result = shas(buf, salt);
36+ const halfway = result.length / 2;
37+ const front = result.slice(0, halfway);
38+ const back = result.slice(halfway, result.length);
39+ return xor(front, back);
40+}
41+
42+export const generateCode = pair => {
43+ const ring = hex2buf(createRing(pair));
44+ const salt = hex2buf('73736170'); // salt is the noun %pass
45+ const hash = shax(ring);
46+ const result = shaf(hash, salt);
47+ const half = result.slice(0, result.length / 2);
48+
49+ return hex2patp(buf2hex(half));
50+};
src/lib/useKeyfileGenerator.js+15−3
@@ -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,10 +29,15 @@ 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 = getDetails(_point);
33
36
37+ const networkRevision = details.matchWith({
34− const networkRevision = convertToInt(_details.keyRevisionNumber, 10);
38+ Just: ({ value }) => convertToInt(value.keyRevisionNumber, 10),
39+ Nothing: () => 0,
40+ });
35 const { isOwner, isManagementProxy } = useCurrentPermissions();
41 const { isOwner, isManagementProxy } = useCurrentPermissions();
36
42
37 const hasNetworkingKeys = networkRevision > 0;
43 const hasNetworkingKeys = networkRevision > 0;
@@ -48,6 +54,8 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
48 return;
54 return;
49 }
55 }
50
56
57+ const _details = need.details(details);
58+
51 const networkSeed = manualNetworkSeed
59 const networkSeed = manualNetworkSeed
52 ? Just(manualNetworkSeed)
60 ? Just(manualNetworkSeed)
53 : await attemptNetworkSeedDerivation({
61 : await attemptNetworkSeedDerivation({
@@ -71,6 +79,8 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
71
79
72 const pair = deriveNetworkKeys(_networkSeed);
80 const pair = deriveNetworkKeys(_networkSeed);
73
81
82+ setCode(generateCode(pair));
83+
74 if (!keysMatchChain(pair, _details)) {
84 if (!keysMatchChain(pair, _details)) {
75 setGenerating(false);
85 setGenerating(false);
76 setNotice('Derived networking keys do not match on-chain details.');
86 setNotice('Derived networking keys do not match on-chain details.');
@@ -88,7 +98,8 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
88 urbitWallet,
98 urbitWallet,
89 wallet,
99 wallet,
90 authMnemonic,
100 authMnemonic,
91− _details,
101+ setCode,
102+ details,
92 _point,
103 _point,
93 ]);
104 ]);
94
105
@@ -113,6 +124,7 @@ export default function useKeyfileGenerator(manualNetworkSeed) {
113 downloaded,
124 downloaded,
114 download,
125 download,
115 notice,
126 notice,
127+ code,
116 };
128 };
117
129
118 return { ...values, bind: values };
130 return { ...values, bind: values };
src/views/Point.js+14
@@ -10,6 +10,7 @@ import View from 'components/View';
10import Greeting from 'components/Greeting';
10import Greeting from 'components/Greeting';
11import Passport from 'components/Passport';
11import Passport from 'components/Passport';
12import { ForwardButton, BootArvoButton } from 'components/Buttons';
12import { ForwardButton, BootArvoButton } from 'components/Buttons';
13+import CopyButton from 'components/CopyButton';
13import { matchBlinky } from 'components/Blinky';
14import { matchBlinky } from 'components/Blinky';
14import DownloadSigilButton from 'components/DownloadSigilButton';
15import DownloadSigilButton from 'components/DownloadSigilButton';
15
16
@@ -18,6 +19,7 @@ import useInvites from 'lib/useInvites';
18import { useSyncOwnedPoints } from 'lib/useSyncPoints';
19import { useSyncOwnedPoints } from 'lib/useSyncPoints';
19import useCurrentPermissions from 'lib/useCurrentPermissions';
20import useCurrentPermissions from 'lib/useCurrentPermissions';
20import { useLocalRouter } from 'lib/LocalRouter';
21import { useLocalRouter } from 'lib/LocalRouter';
22+import useKeyfileGenerator from 'lib/useKeyfileGenerator';
21
23
22export default function Point() {
24export default function Point() {
23 const { pop, push, names } = useLocalRouter();
25 const { pop, push, names } = useLocalRouter();
@@ -26,6 +28,8 @@ export default function Point() {
26 const { wallet } = useWallet();
28 const { wallet } = useWallet();
27 const point = need.point(pointCursor);
29 const point = need.point(pointCursor);
28
30
31+ const { code, notice } = useKeyfileGenerator();
32+
29 const {
33 const {
30 isParent,
34 isParent,
31 isActiveOwner,
35 isActiveOwner,
@@ -109,6 +113,16 @@ export default function Point() {
109 Admin
113 Admin
110 </Grid.Item>
114 </Grid.Item>
111 <Grid.Divider />
115 <Grid.Divider />
116+ <Grid.Item
117+ full
118+ as={ForwardButton}
119+ accessory={code && <CopyButton text={code} />}
120+ detail={code || notice}
121+ disabled={!code}
122+ detailClassName="mono">
123+ Login Code
124+ </Grid.Item>
125+ <Grid.Divider />
112 {isParent && (
126 {isParent && (
113 <>
127 <>
114 <Grid.Item
128 <Grid.Item