-
-
Notifications
You must be signed in to change notification settings - Fork 276
/
Copy pathmessageSystemThunks.ts
155 lines (129 loc) · 4.82 KB
/
messageSystemThunks.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { decode, verify } from 'jws';
import { createThunk } from '@suite-common/redux-utils';
import { MessageSystem } from '@suite-common/suite-types';
import { getJWSPublicKey, isCodesignBuild, isNative } from '@trezor/env-utils';
import { scheduleAction } from '@trezor/utils';
import { ACTION_PREFIX, messageSystemActions } from './messageSystemActions';
import {
CONFIG_URL_REMOTE,
FETCH_CHECK_INTERVAL_IN_MS,
FETCH_CHECK_INTERVAL_IN_MS_MOBILE,
FETCH_INTERVAL_IN_MS,
FETCH_INTERVAL_IN_MS_MOBILE,
FETCH_TIMEOUT_IN_MS,
JWS_SIGN_ALGORITHM,
VERSION,
} from './messageSystemConstants';
import {
selectMessageSystemCurrentSequence,
selectMessageSystemTimestamp,
} from './messageSystemSelectors';
import { jws as configJwsLocal } from '../files/config.v1';
// Enable this for local development purposes:
// set to true to always fetch local JWS
const FORCE_LOCAL_JWS = false;
const getConfigJws = async () => {
if (FORCE_LOCAL_JWS) {
return {
configJws: configJwsLocal,
isRemote: false,
};
}
const remoteConfigUrl = isCodesignBuild()
? CONFIG_URL_REMOTE.stable
: CONFIG_URL_REMOTE.develop;
try {
const response = await scheduleAction(signal => fetch(remoteConfigUrl, { signal }), {
timeout: FETCH_TIMEOUT_IN_MS,
});
if (!response.ok) {
throw Error(response.statusText);
}
const configJws = await response.text();
return {
configJws,
isRemote: true,
};
} catch (error) {
console.error(`Fetching of remote JWS config failed: ${error}`);
return {
configJws: configJwsLocal,
isRemote: false,
};
}
};
export const fetchConfigThunk = createThunk(
`${ACTION_PREFIX}/fetchConfig`,
async (_, { dispatch, getState }) => {
const timestamp = selectMessageSystemTimestamp(getState());
const currentSequence = selectMessageSystemCurrentSequence(getState());
if (
Date.now() >=
timestamp + (isNative() ? FETCH_INTERVAL_IN_MS_MOBILE : FETCH_INTERVAL_IN_MS)
) {
try {
const { configJws, isRemote } = await getConfigJws();
const decodedJws = decode(configJws);
if (!decodedJws) {
throw Error('Decoding of config failed');
}
const algorithmInHeader = decodedJws?.header.alg;
if (algorithmInHeader !== JWS_SIGN_ALGORITHM) {
throw Error(`Wrong algorithm in JWS config header: ${algorithmInHeader}`);
}
const authenticityPublicKey = getJWSPublicKey();
if (!authenticityPublicKey) {
throw Error('JWS public key is not defined!');
}
const isAuthenticityValid = verify(
configJws,
JWS_SIGN_ALGORITHM,
authenticityPublicKey,
);
if (!isAuthenticityValid) {
throw Error('Config authenticity is invalid');
}
const config: MessageSystem = JSON.parse(decodedJws.payload);
if (VERSION !== config.version) {
throw Error('Config version is not supported');
}
const timestampNew = isRemote ? Date.now() : 0;
if (
currentSequence < config.sequence ||
FORCE_LOCAL_JWS /* Allow to skip sequence check for local testing */
) {
await dispatch(
messageSystemActions.fetchSuccessUpdate({
config,
timestamp: timestampNew,
}),
);
} else if (currentSequence === config.sequence) {
await dispatch(messageSystemActions.fetchSuccess({ timestamp: timestampNew }));
} else {
throw Error(
`Sequence of config (${config.sequence}) is older than the current one (${currentSequence}).`,
);
}
} catch (error) {
console.error(error);
await dispatch(messageSystemActions.fetchError());
}
}
},
);
export const initMessageSystemThunk = createThunk(
`${ACTION_PREFIX}/init`,
async (_, { dispatch }) => {
const checkConfig = async () => {
await dispatch(fetchConfigThunk());
setTimeout(
() => {
checkConfig();
},
isNative() ? FETCH_CHECK_INTERVAL_IN_MS_MOBILE : FETCH_CHECK_INTERVAL_IN_MS,
);
};
await checkConfig();
},
);