gnss-js 2.11.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/analysis.cjs +24 -25
- package/dist/analysis.js +2 -2
- package/dist/{clk-DGVBGRzA.d.cts → bias-sinex-OGMw9xEZ.d.cts} +66 -1
- package/dist/{clk-DGVBGRzA.d.ts → bias-sinex-OGMw9xEZ.d.ts} +66 -1
- package/dist/{chunk-MDO77CCC.js → chunk-2DJ4XENO.js} +45 -0
- package/dist/{chunk-IQHQHUOB.js → chunk-2KQMCJ5Z.js} +5 -3
- package/dist/chunk-E3Y6YKQM.js +65 -0
- package/dist/{chunk-3U5AX7PY.js → chunk-FEABDV5P.js} +24 -25
- package/dist/{chunk-RFPR4LKM.js → chunk-HWHD5E6V.js} +19 -75
- package/dist/{chunk-KUSUN7DP.js → chunk-MFCPYUEI.js} +1 -1
- package/dist/{chunk-SSWFX3P3.js → chunk-NIJ6WFQJ.js} +6 -321
- package/dist/{chunk-UMINQTMK.js → chunk-RWFJEVLJ.js} +589 -295
- package/dist/chunk-TWGZTRBL.js +323 -0
- package/dist/index.cjs +655 -316
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -9
- package/dist/osnma.cjs +1336 -0
- package/dist/osnma.d.cts +273 -0
- package/dist/osnma.d.ts +273 -0
- package/dist/osnma.js +1216 -0
- package/dist/positioning.cjs +585 -291
- package/dist/positioning.d.cts +114 -4
- package/dist/positioning.d.ts +114 -4
- package/dist/positioning.js +1 -1
- package/dist/rinex.cjs +70 -25
- package/dist/rinex.d.cts +2 -50
- package/dist/rinex.d.ts +2 -50
- package/dist/rinex.js +4 -2
- package/dist/sbf.js +7 -4
- package/dist/ubx.js +3 -2
- package/package.json +6 -1
package/dist/osnma.js
ADDED
|
@@ -0,0 +1,1216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
scanSbfFrames,
|
|
3
|
+
svidToPrn
|
|
4
|
+
} from "./chunk-E3Y6YKQM.js";
|
|
5
|
+
import {
|
|
6
|
+
galInavPageCrcOk
|
|
7
|
+
} from "./chunk-TWGZTRBL.js";
|
|
8
|
+
import "./chunk-HUXQABJI.js";
|
|
9
|
+
import "./chunk-V2D5KUKH.js";
|
|
10
|
+
|
|
11
|
+
// src/osnma/sha256.ts
|
|
12
|
+
var K = new Uint32Array([
|
|
13
|
+
1116352408,
|
|
14
|
+
1899447441,
|
|
15
|
+
3049323471,
|
|
16
|
+
3921009573,
|
|
17
|
+
961987163,
|
|
18
|
+
1508970993,
|
|
19
|
+
2453635748,
|
|
20
|
+
2870763221,
|
|
21
|
+
3624381080,
|
|
22
|
+
310598401,
|
|
23
|
+
607225278,
|
|
24
|
+
1426881987,
|
|
25
|
+
1925078388,
|
|
26
|
+
2162078206,
|
|
27
|
+
2614888103,
|
|
28
|
+
3248222580,
|
|
29
|
+
3835390401,
|
|
30
|
+
4022224774,
|
|
31
|
+
264347078,
|
|
32
|
+
604807628,
|
|
33
|
+
770255983,
|
|
34
|
+
1249150122,
|
|
35
|
+
1555081692,
|
|
36
|
+
1996064986,
|
|
37
|
+
2554220882,
|
|
38
|
+
2821834349,
|
|
39
|
+
2952996808,
|
|
40
|
+
3210313671,
|
|
41
|
+
3336571891,
|
|
42
|
+
3584528711,
|
|
43
|
+
113926993,
|
|
44
|
+
338241895,
|
|
45
|
+
666307205,
|
|
46
|
+
773529912,
|
|
47
|
+
1294757372,
|
|
48
|
+
1396182291,
|
|
49
|
+
1695183700,
|
|
50
|
+
1986661051,
|
|
51
|
+
2177026350,
|
|
52
|
+
2456956037,
|
|
53
|
+
2730485921,
|
|
54
|
+
2820302411,
|
|
55
|
+
3259730800,
|
|
56
|
+
3345764771,
|
|
57
|
+
3516065817,
|
|
58
|
+
3600352804,
|
|
59
|
+
4094571909,
|
|
60
|
+
275423344,
|
|
61
|
+
430227734,
|
|
62
|
+
506948616,
|
|
63
|
+
659060556,
|
|
64
|
+
883997877,
|
|
65
|
+
958139571,
|
|
66
|
+
1322822218,
|
|
67
|
+
1537002063,
|
|
68
|
+
1747873779,
|
|
69
|
+
1955562222,
|
|
70
|
+
2024104815,
|
|
71
|
+
2227730452,
|
|
72
|
+
2361852424,
|
|
73
|
+
2428436474,
|
|
74
|
+
2756734187,
|
|
75
|
+
3204031479,
|
|
76
|
+
3329325298
|
|
77
|
+
]);
|
|
78
|
+
var W = new Uint32Array(64);
|
|
79
|
+
function compress(h, block, off) {
|
|
80
|
+
for (let i = 0; i < 16; i++) {
|
|
81
|
+
const j = off + 4 * i;
|
|
82
|
+
W[i] = block[j] << 24 | block[j + 1] << 16 | block[j + 2] << 8 | block[j + 3];
|
|
83
|
+
}
|
|
84
|
+
for (let i = 16; i < 64; i++) {
|
|
85
|
+
const a2 = W[i - 15];
|
|
86
|
+
const b2 = W[i - 2];
|
|
87
|
+
const s0 = (a2 >>> 7 | a2 << 25) ^ (a2 >>> 18 | a2 << 14) ^ a2 >>> 3;
|
|
88
|
+
const s1 = (b2 >>> 17 | b2 << 15) ^ (b2 >>> 19 | b2 << 13) ^ b2 >>> 10;
|
|
89
|
+
W[i] = W[i - 16] + s0 + W[i - 7] + s1 | 0;
|
|
90
|
+
}
|
|
91
|
+
let a = h[0];
|
|
92
|
+
let b = h[1];
|
|
93
|
+
let c = h[2];
|
|
94
|
+
let d = h[3];
|
|
95
|
+
let e = h[4];
|
|
96
|
+
let f = h[5];
|
|
97
|
+
let g = h[6];
|
|
98
|
+
let hh = h[7];
|
|
99
|
+
for (let i = 0; i < 64; i++) {
|
|
100
|
+
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
101
|
+
const ch = e & f ^ ~e & g;
|
|
102
|
+
const t1 = hh + S1 + ch + K[i] + W[i] | 0;
|
|
103
|
+
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
104
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
105
|
+
const t2 = S0 + maj | 0;
|
|
106
|
+
hh = g;
|
|
107
|
+
g = f;
|
|
108
|
+
f = e;
|
|
109
|
+
e = d + t1 | 0;
|
|
110
|
+
d = c;
|
|
111
|
+
c = b;
|
|
112
|
+
b = a;
|
|
113
|
+
a = t1 + t2 | 0;
|
|
114
|
+
}
|
|
115
|
+
h[0] = h[0] + a | 0;
|
|
116
|
+
h[1] = h[1] + b | 0;
|
|
117
|
+
h[2] = h[2] + c | 0;
|
|
118
|
+
h[3] = h[3] + d | 0;
|
|
119
|
+
h[4] = h[4] + e | 0;
|
|
120
|
+
h[5] = h[5] + f | 0;
|
|
121
|
+
h[6] = h[6] + g | 0;
|
|
122
|
+
h[7] = h[7] + hh | 0;
|
|
123
|
+
}
|
|
124
|
+
function sha256(...parts) {
|
|
125
|
+
let len = 0;
|
|
126
|
+
for (const p of parts) len += p.length;
|
|
127
|
+
const padded = new Uint8Array(Math.ceil((len + 9) / 64) * 64);
|
|
128
|
+
let o = 0;
|
|
129
|
+
for (const p of parts) {
|
|
130
|
+
padded.set(p, o);
|
|
131
|
+
o += p.length;
|
|
132
|
+
}
|
|
133
|
+
padded[len] = 128;
|
|
134
|
+
const bitLen = len * 8;
|
|
135
|
+
const view = new DataView(padded.buffer);
|
|
136
|
+
view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
|
|
137
|
+
view.setUint32(padded.length - 4, bitLen >>> 0);
|
|
138
|
+
const h = new Uint32Array([
|
|
139
|
+
1779033703,
|
|
140
|
+
3144134277,
|
|
141
|
+
1013904242,
|
|
142
|
+
2773480762,
|
|
143
|
+
1359893119,
|
|
144
|
+
2600822924,
|
|
145
|
+
528734635,
|
|
146
|
+
1541459225
|
|
147
|
+
]);
|
|
148
|
+
for (let off = 0; off < padded.length; off += 64) compress(h, padded, off);
|
|
149
|
+
const out = new Uint8Array(32);
|
|
150
|
+
const ov = new DataView(out.buffer);
|
|
151
|
+
for (let i = 0; i < 8; i++) ov.setUint32(4 * i, h[i]);
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
function hmacSha256(key, msg) {
|
|
155
|
+
const k = key.length > 64 ? sha256(key) : key;
|
|
156
|
+
const ipad = new Uint8Array(64).fill(54);
|
|
157
|
+
const opad = new Uint8Array(64).fill(92);
|
|
158
|
+
for (let i = 0; i < k.length; i++) {
|
|
159
|
+
ipad[i] ^= k[i];
|
|
160
|
+
opad[i] ^= k[i];
|
|
161
|
+
}
|
|
162
|
+
return sha256(opad, sha256(ipad, msg));
|
|
163
|
+
}
|
|
164
|
+
var K512 = [
|
|
165
|
+
"428a2f98d728ae22",
|
|
166
|
+
"7137449123ef65cd",
|
|
167
|
+
"b5c0fbcfec4d3b2f",
|
|
168
|
+
"e9b5dba58189dbbc",
|
|
169
|
+
"3956c25bf348b538",
|
|
170
|
+
"59f111f1b605d019",
|
|
171
|
+
"923f82a4af194f9b",
|
|
172
|
+
"ab1c5ed5da6d8118",
|
|
173
|
+
"d807aa98a3030242",
|
|
174
|
+
"12835b0145706fbe",
|
|
175
|
+
"243185be4ee4b28c",
|
|
176
|
+
"550c7dc3d5ffb4e2",
|
|
177
|
+
"72be5d74f27b896f",
|
|
178
|
+
"80deb1fe3b1696b1",
|
|
179
|
+
"9bdc06a725c71235",
|
|
180
|
+
"c19bf174cf692694",
|
|
181
|
+
"e49b69c19ef14ad2",
|
|
182
|
+
"efbe4786384f25e3",
|
|
183
|
+
"0fc19dc68b8cd5b5",
|
|
184
|
+
"240ca1cc77ac9c65",
|
|
185
|
+
"2de92c6f592b0275",
|
|
186
|
+
"4a7484aa6ea6e483",
|
|
187
|
+
"5cb0a9dcbd41fbd4",
|
|
188
|
+
"76f988da831153b5",
|
|
189
|
+
"983e5152ee66dfab",
|
|
190
|
+
"a831c66d2db43210",
|
|
191
|
+
"b00327c898fb213f",
|
|
192
|
+
"bf597fc7beef0ee4",
|
|
193
|
+
"c6e00bf33da88fc2",
|
|
194
|
+
"d5a79147930aa725",
|
|
195
|
+
"06ca6351e003826f",
|
|
196
|
+
"142929670a0e6e70",
|
|
197
|
+
"27b70a8546d22ffc",
|
|
198
|
+
"2e1b21385c26c926",
|
|
199
|
+
"4d2c6dfc5ac42aed",
|
|
200
|
+
"53380d139d95b3df",
|
|
201
|
+
"650a73548baf63de",
|
|
202
|
+
"766a0abb3c77b2a8",
|
|
203
|
+
"81c2c92e47edaee6",
|
|
204
|
+
"92722c851482353b",
|
|
205
|
+
"a2bfe8a14cf10364",
|
|
206
|
+
"a81a664bbc423001",
|
|
207
|
+
"c24b8b70d0f89791",
|
|
208
|
+
"c76c51a30654be30",
|
|
209
|
+
"d192e819d6ef5218",
|
|
210
|
+
"d69906245565a910",
|
|
211
|
+
"f40e35855771202a",
|
|
212
|
+
"106aa07032bbd1b8",
|
|
213
|
+
"19a4c116b8d2d0c8",
|
|
214
|
+
"1e376c085141ab53",
|
|
215
|
+
"2748774cdf8eeb99",
|
|
216
|
+
"34b0bcb5e19b48a8",
|
|
217
|
+
"391c0cb3c5c95a63",
|
|
218
|
+
"4ed8aa4ae3418acb",
|
|
219
|
+
"5b9cca4f7763e373",
|
|
220
|
+
"682e6ff3d6b2b8a3",
|
|
221
|
+
"748f82ee5defb2fc",
|
|
222
|
+
"78a5636f43172f60",
|
|
223
|
+
"84c87814a1f0ab72",
|
|
224
|
+
"8cc702081a6439ec",
|
|
225
|
+
"90befffa23631e28",
|
|
226
|
+
"a4506cebde82bde9",
|
|
227
|
+
"bef9a3f7b2c67915",
|
|
228
|
+
"c67178f2e372532b",
|
|
229
|
+
"ca273eceea26619c",
|
|
230
|
+
"d186b8c721c0c207",
|
|
231
|
+
"eada7dd6cde0eb1e",
|
|
232
|
+
"f57d4f7fee6ed178",
|
|
233
|
+
"06f067aa72176fba",
|
|
234
|
+
"0a637dc5a2c898a6",
|
|
235
|
+
"113f9804bef90dae",
|
|
236
|
+
"1b710b35131c471b",
|
|
237
|
+
"28db77f523047d84",
|
|
238
|
+
"32caab7b40c72493",
|
|
239
|
+
"3c9ebe0a15c9bebc",
|
|
240
|
+
"431d67c49c100d4c",
|
|
241
|
+
"4cc5d4becb3e42b6",
|
|
242
|
+
"597f299cfc657e2a",
|
|
243
|
+
"5fcb6fab3ad6faec",
|
|
244
|
+
"6c44198c4a475817"
|
|
245
|
+
].map((h) => BigInt("0x" + h));
|
|
246
|
+
var M64 = (1n << 64n) - 1n;
|
|
247
|
+
var rotr = (x, n) => (x >> n | x << 64n - n) & M64;
|
|
248
|
+
function sha512(...parts) {
|
|
249
|
+
let len = 0;
|
|
250
|
+
for (const p of parts) len += p.length;
|
|
251
|
+
const padded = new Uint8Array(Math.ceil((len + 17) / 128) * 128);
|
|
252
|
+
let o = 0;
|
|
253
|
+
for (const p of parts) {
|
|
254
|
+
padded.set(p, o);
|
|
255
|
+
o += p.length;
|
|
256
|
+
}
|
|
257
|
+
padded[len] = 128;
|
|
258
|
+
const view = new DataView(padded.buffer);
|
|
259
|
+
view.setBigUint64(padded.length - 8, BigInt(len) * 8n);
|
|
260
|
+
const h = [
|
|
261
|
+
"6a09e667f3bcc908",
|
|
262
|
+
"bb67ae8584caa73b",
|
|
263
|
+
"3c6ef372fe94f82b",
|
|
264
|
+
"a54ff53a5f1d36f1",
|
|
265
|
+
"510e527fade682d1",
|
|
266
|
+
"9b05688c2b3e6c1f",
|
|
267
|
+
"1f83d9abfb41bd6b",
|
|
268
|
+
"5be0cd19137e2179"
|
|
269
|
+
].map((x) => BigInt("0x" + x));
|
|
270
|
+
const w = new Array(80);
|
|
271
|
+
for (let off = 0; off < padded.length; off += 128) {
|
|
272
|
+
for (let i = 0; i < 16; i++) w[i] = view.getBigUint64(off + 8 * i);
|
|
273
|
+
for (let i = 16; i < 80; i++) {
|
|
274
|
+
const a2 = w[i - 15];
|
|
275
|
+
const b2 = w[i - 2];
|
|
276
|
+
const s0 = rotr(a2, 1n) ^ rotr(a2, 8n) ^ a2 >> 7n;
|
|
277
|
+
const s1 = rotr(b2, 19n) ^ rotr(b2, 61n) ^ b2 >> 6n;
|
|
278
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 & M64;
|
|
279
|
+
}
|
|
280
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
281
|
+
for (let i = 0; i < 80; i++) {
|
|
282
|
+
const S1 = rotr(e, 14n) ^ rotr(e, 18n) ^ rotr(e, 41n);
|
|
283
|
+
const ch = e & f ^ ~e & M64 & g;
|
|
284
|
+
const t1 = hh + S1 + ch + K512[i] + w[i] & M64;
|
|
285
|
+
const S0 = rotr(a, 28n) ^ rotr(a, 34n) ^ rotr(a, 39n);
|
|
286
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
287
|
+
const t2 = S0 + maj & M64;
|
|
288
|
+
hh = g;
|
|
289
|
+
g = f;
|
|
290
|
+
f = e;
|
|
291
|
+
e = d + t1 & M64;
|
|
292
|
+
d = c;
|
|
293
|
+
c = b;
|
|
294
|
+
b = a;
|
|
295
|
+
a = t1 + t2 & M64;
|
|
296
|
+
}
|
|
297
|
+
[a, b, c, d, e, f, g, hh].forEach((v, i) => h[i] = h[i] + v & M64);
|
|
298
|
+
}
|
|
299
|
+
const out = new Uint8Array(64);
|
|
300
|
+
const ov = new DataView(out.buffer);
|
|
301
|
+
h.forEach((v, i) => ov.setBigUint64(8 * i, v));
|
|
302
|
+
return out;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/osnma/ecdsa.ts
|
|
306
|
+
var P256 = {
|
|
307
|
+
p: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
|
308
|
+
n: 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n,
|
|
309
|
+
b: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bn,
|
|
310
|
+
gx: 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296n,
|
|
311
|
+
gy: 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5n,
|
|
312
|
+
size: 32,
|
|
313
|
+
hash: (m) => sha256(m)
|
|
314
|
+
};
|
|
315
|
+
var P521 = {
|
|
316
|
+
p: (1n << 521n) - 1n,
|
|
317
|
+
n: 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409n,
|
|
318
|
+
b: 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00n,
|
|
319
|
+
gx: 0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66n,
|
|
320
|
+
gy: 0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650n,
|
|
321
|
+
size: 66,
|
|
322
|
+
hash: (m) => sha512(m)
|
|
323
|
+
};
|
|
324
|
+
var mod = (a, m) => {
|
|
325
|
+
const r = a % m;
|
|
326
|
+
return r < 0n ? r + m : r;
|
|
327
|
+
};
|
|
328
|
+
function powMod(b, e, m) {
|
|
329
|
+
let r = 1n;
|
|
330
|
+
b = mod(b, m);
|
|
331
|
+
while (e > 0n) {
|
|
332
|
+
if (e & 1n) r = r * b % m;
|
|
333
|
+
b = b * b % m;
|
|
334
|
+
e >>= 1n;
|
|
335
|
+
}
|
|
336
|
+
return r;
|
|
337
|
+
}
|
|
338
|
+
var inv = (a, m) => powMod(a, m - 2n, m);
|
|
339
|
+
var INF = [0n, 1n, 0n];
|
|
340
|
+
function dbl(P, [x, y, z]) {
|
|
341
|
+
if (z === 0n || y === 0n) return INF;
|
|
342
|
+
const z2 = z * z % P;
|
|
343
|
+
const m = mod(3n * (x - z2) * (x + z2), P);
|
|
344
|
+
const y2 = y * y % P;
|
|
345
|
+
const s = 4n * x * y2 % P;
|
|
346
|
+
const x3 = mod(m * m - 2n * s, P);
|
|
347
|
+
const y3 = mod(m * (s - x3) - 8n * y2 * y2, P);
|
|
348
|
+
const z3 = 2n * y * z % P;
|
|
349
|
+
return [x3, y3, z3];
|
|
350
|
+
}
|
|
351
|
+
function add(P, p, q) {
|
|
352
|
+
if (p[2] === 0n) return q;
|
|
353
|
+
if (q[2] === 0n) return p;
|
|
354
|
+
const z1z1 = p[2] * p[2] % P;
|
|
355
|
+
const z2z2 = q[2] * q[2] % P;
|
|
356
|
+
const u1 = p[0] * z2z2 % P;
|
|
357
|
+
const u2 = q[0] * z1z1 % P;
|
|
358
|
+
const s1 = p[1] * q[2] * z2z2 % P;
|
|
359
|
+
const s2 = q[1] * p[2] * z1z1 % P;
|
|
360
|
+
if (u1 === u2) return s1 === s2 ? dbl(P, p) : INF;
|
|
361
|
+
const h = mod(u2 - u1, P);
|
|
362
|
+
const r = mod(s2 - s1, P);
|
|
363
|
+
const h2 = h * h % P;
|
|
364
|
+
const h3 = h * h2 % P;
|
|
365
|
+
const x3 = mod(r * r - h3 - 2n * u1 * h2, P);
|
|
366
|
+
const y3 = mod(r * (u1 * h2 - x3) - s1 * h3, P);
|
|
367
|
+
const z3 = h * p[2] * q[2] % P;
|
|
368
|
+
return [x3, y3, z3];
|
|
369
|
+
}
|
|
370
|
+
function mulAdd(c, u1, u2, q) {
|
|
371
|
+
const g = [c.gx, c.gy, 1n];
|
|
372
|
+
const gq = add(c.p, g, q);
|
|
373
|
+
let r = INF;
|
|
374
|
+
const bits = Math.max(u1.toString(2).length, u2.toString(2).length);
|
|
375
|
+
for (let i = bits - 1; i >= 0; i--) {
|
|
376
|
+
r = dbl(c.p, r);
|
|
377
|
+
const b1 = u1 >> BigInt(i) & 1n;
|
|
378
|
+
const b2 = u2 >> BigInt(i) & 1n;
|
|
379
|
+
if (b1 && b2) r = add(c.p, r, gq);
|
|
380
|
+
else if (b1) r = add(c.p, r, g);
|
|
381
|
+
else if (b2) r = add(c.p, r, q);
|
|
382
|
+
}
|
|
383
|
+
return r;
|
|
384
|
+
}
|
|
385
|
+
var toBig = (b) => {
|
|
386
|
+
let x = 0n;
|
|
387
|
+
for (const v of b) x = x << 8n | BigInt(v);
|
|
388
|
+
return x;
|
|
389
|
+
};
|
|
390
|
+
function decodeKey(c, key) {
|
|
391
|
+
const n = c.size;
|
|
392
|
+
const onCurve = (x2, y2) => mod(y2 * y2 - (x2 * x2 * x2 - 3n * x2 + c.b), c.p) === 0n;
|
|
393
|
+
if (key.length === 2 * n + 1 && key[0] === 4) {
|
|
394
|
+
const x2 = toBig(key.subarray(1, n + 1));
|
|
395
|
+
const y2 = toBig(key.subarray(n + 1));
|
|
396
|
+
return onCurve(x2, y2) ? [x2, y2] : null;
|
|
397
|
+
}
|
|
398
|
+
if (key.length !== n + 1 || key[0] !== 2 && key[0] !== 3) return null;
|
|
399
|
+
const x = toBig(key.subarray(1));
|
|
400
|
+
if (x >= c.p) return null;
|
|
401
|
+
const rhs = mod(x * x * x - 3n * x + c.b, c.p);
|
|
402
|
+
let y = powMod(rhs, (c.p + 1n) / 4n, c.p);
|
|
403
|
+
if (y * y % c.p !== rhs) return null;
|
|
404
|
+
if (Number(y & 1n) !== (key[0] & 1)) y = c.p - y;
|
|
405
|
+
return [x, y];
|
|
406
|
+
}
|
|
407
|
+
function verify(c, pubKey, msg, sig) {
|
|
408
|
+
const q = decodeKey(c, pubKey);
|
|
409
|
+
if (!q || sig.length !== 2 * c.size) return false;
|
|
410
|
+
const r = toBig(sig.subarray(0, c.size));
|
|
411
|
+
const s = toBig(sig.subarray(c.size));
|
|
412
|
+
if (r <= 0n || r >= c.n || s <= 0n || s >= c.n) return false;
|
|
413
|
+
const e = toBig(c.hash(msg));
|
|
414
|
+
const w = inv(s, c.n);
|
|
415
|
+
const x = mulAdd(c, e * w % c.n, r * w % c.n, [q[0], q[1], 1n]);
|
|
416
|
+
if (x[2] === 0n) return false;
|
|
417
|
+
const zi = inv(x[2], c.p);
|
|
418
|
+
return x[0] * zi * zi % c.p % c.n === r;
|
|
419
|
+
}
|
|
420
|
+
var p256Verify = (pubKey, msg, sig) => verify(P256, pubKey, msg, sig);
|
|
421
|
+
var p521Verify = (pubKey, msg, sig) => verify(P521, pubKey, msg, sig);
|
|
422
|
+
|
|
423
|
+
// src/osnma/merkle.ts
|
|
424
|
+
var fromHex = (h) => new Uint8Array(h.match(/../g).map((b) => parseInt(b, 16)));
|
|
425
|
+
var OSNMA_MERKLE_ROOT = fromHex(
|
|
426
|
+
"832E15EDE55655EAC6E399A539477B7C034CCE24C3C93FFC904ACD9BF842F04E"
|
|
427
|
+
);
|
|
428
|
+
var OSNMA_KNOWN_PUBLIC_KEYS = [
|
|
429
|
+
{
|
|
430
|
+
pkid: 2,
|
|
431
|
+
npkt: 1,
|
|
432
|
+
key: fromHex(
|
|
433
|
+
"02219204B5CA6C46B623EEED6CDD2CDDB1F7D6A7532767E5B8DA0DE1EBD695FC99"
|
|
434
|
+
),
|
|
435
|
+
mid: 1,
|
|
436
|
+
path: [
|
|
437
|
+
fromHex(
|
|
438
|
+
"7B944FA20915C7931D48DD016D94F9C6381FD37DC6C125D97015272FDDE41393"
|
|
439
|
+
),
|
|
440
|
+
fromHex(
|
|
441
|
+
"6AAFDE28017BF0744D42819CE40E3A0CDA1ECA3F7A4EA67E134E7AA714C1E843"
|
|
442
|
+
),
|
|
443
|
+
fromHex(
|
|
444
|
+
"DE73D209E4C5BCDC34CD117F2FE40FD08B110009997AD2B3291D3A2CF29943F9"
|
|
445
|
+
),
|
|
446
|
+
fromHex(
|
|
447
|
+
"84DE3669E6DA551292979E5B8D045787FA967C57CC23638A30237614EDD9171A"
|
|
448
|
+
)
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
];
|
|
452
|
+
function merkleLeafOk(leaf, mid, path, root) {
|
|
453
|
+
if (path.length !== 4 || mid < 0 || mid > 15) return false;
|
|
454
|
+
let x = sha256(leaf);
|
|
455
|
+
let i = mid;
|
|
456
|
+
for (const node of path) {
|
|
457
|
+
x = i & 1 ? sha256(node, x) : sha256(x, node);
|
|
458
|
+
i >>= 1;
|
|
459
|
+
}
|
|
460
|
+
return x.every((b, k) => b === root[k]);
|
|
461
|
+
}
|
|
462
|
+
function publicKeyLeaf(npkt, pkid, key) {
|
|
463
|
+
const leaf = new Uint8Array(1 + key.length);
|
|
464
|
+
leaf[0] = (npkt & 15) << 4 | pkid & 15;
|
|
465
|
+
leaf.set(key, 1);
|
|
466
|
+
return leaf;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/osnma/receiver.ts
|
|
470
|
+
var bitAt = (b, i) => b[i >> 3] >> 7 - (i & 7) & 1;
|
|
471
|
+
function bitsU(b, start, n) {
|
|
472
|
+
let x = 0;
|
|
473
|
+
for (let i = 0; i < n; i++) x = x * 2 + bitAt(b, start + i);
|
|
474
|
+
return x;
|
|
475
|
+
}
|
|
476
|
+
function copyBits(dst, at, src, start, n) {
|
|
477
|
+
for (let i = 0; i < n; i++)
|
|
478
|
+
if (bitAt(src, start + i)) dst[at + i >> 3] |= 128 >> (at + i & 7);
|
|
479
|
+
}
|
|
480
|
+
function sliceBits(src, start, n) {
|
|
481
|
+
const out = new Uint8Array(Math.ceil(n / 8));
|
|
482
|
+
copyBits(out, 0, src, start, n);
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
485
|
+
var eqBytes = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
486
|
+
function eqBits(a, b, nBits) {
|
|
487
|
+
for (let i = 0; i < nBits; i++) if (bitAt(a, i) !== bitAt(b, i)) return false;
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
var WEEK = 604800;
|
|
491
|
+
var HISTORY_SUBFRAMES = 80;
|
|
492
|
+
var MAX_DERIVATIONS = 3e3;
|
|
493
|
+
var MIN_TAG_BITS = { 0: 80, 4: 40, 12: 80 };
|
|
494
|
+
var KEY_BITS = [96, 104, 112, 120, 128, 160, 192, 224, 256];
|
|
495
|
+
var TAG_BITS = { 5: 20, 6: 24, 7: 28, 8: 32, 9: 40 };
|
|
496
|
+
var MACLT = {
|
|
497
|
+
27: [
|
|
498
|
+
["00S", "00E", "00E", "00E", "12S", "00E"],
|
|
499
|
+
["00S", "00E", "00E", "04S", "12S", "00E"]
|
|
500
|
+
],
|
|
501
|
+
28: [
|
|
502
|
+
["00S", "00E", "00E", "00E", "00S", "00E", "00E", "12S", "00E", "00E"],
|
|
503
|
+
["00S", "00E", "00E", "00S", "00E", "00E", "04S", "12S", "00E", "00E"]
|
|
504
|
+
],
|
|
505
|
+
31: [
|
|
506
|
+
["00S", "00E", "00E", "12S", "00E"],
|
|
507
|
+
["00S", "00E", "00E", "12S", "04S"]
|
|
508
|
+
],
|
|
509
|
+
33: [
|
|
510
|
+
["00S", "00E", "04S", "00E", "12S", "00E"],
|
|
511
|
+
["00S", "00E", "00E", "12S", "00E", "12E"]
|
|
512
|
+
],
|
|
513
|
+
34: [
|
|
514
|
+
["00S", "FLX", "04S", "FLX", "12S", "00E"],
|
|
515
|
+
["00S", "FLX", "00E", "12S", "00E", "12E"]
|
|
516
|
+
],
|
|
517
|
+
35: [
|
|
518
|
+
["00S", "FLX", "04S", "FLX", "12S", "FLX"],
|
|
519
|
+
["00S", "FLX", "FLX", "12S", "FLX", "FLX"]
|
|
520
|
+
],
|
|
521
|
+
36: [
|
|
522
|
+
["00S", "FLX", "04S", "FLX", "12S"],
|
|
523
|
+
["00S", "FLX", "00E", "12S", "12E"]
|
|
524
|
+
],
|
|
525
|
+
37: [
|
|
526
|
+
["00S", "00E", "04S", "00E", "12S"],
|
|
527
|
+
["00S", "00E", "00E", "12S", "12E"]
|
|
528
|
+
],
|
|
529
|
+
38: [
|
|
530
|
+
["00S", "FLX", "04S", "FLX", "12S"],
|
|
531
|
+
["00S", "FLX", "FLX", "12S", "FLX"]
|
|
532
|
+
],
|
|
533
|
+
39: [
|
|
534
|
+
["00S", "FLX", "04S", "FLX"],
|
|
535
|
+
["00S", "FLX", "00E", "12S"]
|
|
536
|
+
],
|
|
537
|
+
40: [
|
|
538
|
+
["00S", "00E", "04S", "12S"],
|
|
539
|
+
["00S", "00E", "00E", "12E"]
|
|
540
|
+
]
|
|
541
|
+
};
|
|
542
|
+
var ADKD0_WORDS = [
|
|
543
|
+
// [word type, first bit in the 128-bit word, bit count]
|
|
544
|
+
[1, 6, 120],
|
|
545
|
+
[2, 6, 120],
|
|
546
|
+
[3, 6, 122],
|
|
547
|
+
[4, 6, 120],
|
|
548
|
+
[5, 6, 67]
|
|
549
|
+
];
|
|
550
|
+
var ADKD0_BITS = 549;
|
|
551
|
+
var ADKD4_BITS = 141;
|
|
552
|
+
var NMAS = ["reserved", "test", "operational", "dont-use"];
|
|
553
|
+
var CPKS = [
|
|
554
|
+
"reserved",
|
|
555
|
+
"nominal",
|
|
556
|
+
"end-of-chain",
|
|
557
|
+
"chain-revoked",
|
|
558
|
+
"new-public-key",
|
|
559
|
+
"public-key-revoked",
|
|
560
|
+
"new-merkle-tree",
|
|
561
|
+
"alert-message"
|
|
562
|
+
];
|
|
563
|
+
var gstOf = (abs) => ({
|
|
564
|
+
wn: Math.floor(abs / WEEK),
|
|
565
|
+
tow: abs % WEEK
|
|
566
|
+
});
|
|
567
|
+
function gst32(abs) {
|
|
568
|
+
const { wn, tow } = gstOf(abs);
|
|
569
|
+
const v = (wn & 4095) << 20 | tow & 1048575;
|
|
570
|
+
return new Uint8Array([
|
|
571
|
+
v >>> 24,
|
|
572
|
+
v >>> 16 & 255,
|
|
573
|
+
v >>> 8 & 255,
|
|
574
|
+
v & 255
|
|
575
|
+
]);
|
|
576
|
+
}
|
|
577
|
+
var hexOf = (b) => Array.from(b, (v) => v.toString(16).padStart(2, "0")).join("");
|
|
578
|
+
var prnName = (svid) => `E${String(svid).padStart(2, "0")}`;
|
|
579
|
+
var OsnmaReceiver = class {
|
|
580
|
+
roots;
|
|
581
|
+
/** An authenticated OSNMA Alert Message stopped all processing. */
|
|
582
|
+
alerted = false;
|
|
583
|
+
/** Revoked public keys and chains (hex), never to be used again. */
|
|
584
|
+
revoked = /* @__PURE__ */ new Set();
|
|
585
|
+
pubKeys = /* @__PURE__ */ new Map();
|
|
586
|
+
collect = /* @__PURE__ */ new Map();
|
|
587
|
+
// per svid
|
|
588
|
+
dsms = /* @__PURE__ */ new Map();
|
|
589
|
+
// per DSM ID
|
|
590
|
+
chains = /* @__PURE__ */ new Map();
|
|
591
|
+
// per CID
|
|
592
|
+
macks = [];
|
|
593
|
+
words = /* @__PURE__ */ new Map();
|
|
594
|
+
sats = /* @__PURE__ */ new Map();
|
|
595
|
+
events = [];
|
|
596
|
+
nmaHeader = null;
|
|
597
|
+
keysVerified = 0;
|
|
598
|
+
keysRejected = 0;
|
|
599
|
+
lastKey = null;
|
|
600
|
+
tags = { ok: 0, failed: 0, unverifiable: 0, dummy: 0 };
|
|
601
|
+
osnmaPages = 0;
|
|
602
|
+
dontUseMacks = 0;
|
|
603
|
+
latestSf = 0;
|
|
604
|
+
/** Keys already rejected, as `sf:hex` (one event each). */
|
|
605
|
+
rejectedKeys = /* @__PURE__ */ new Set();
|
|
606
|
+
constructor(opts = {}) {
|
|
607
|
+
const r = opts.merkleRoot ?? OSNMA_MERKLE_ROOT;
|
|
608
|
+
this.roots = r instanceof Uint8Array ? [r] : [...r];
|
|
609
|
+
for (const r2 of opts.publicKeys ?? OSNMA_KNOWN_PUBLIC_KEYS) {
|
|
610
|
+
const leaf = publicKeyLeaf(r2.npkt, r2.pkid, r2.key);
|
|
611
|
+
if (this.leafOk(leaf, r2.mid, r2.path))
|
|
612
|
+
this.pubKeys.set(r2.pkid, { key: r2.key, npkt: r2.npkt, source: "known" });
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Feed one E1-B I/NAV nominal page pair: `page` holds the 114-bit even
|
|
617
|
+
* part (tail removed) followed by the 120-bit odd part, MSB first, as
|
|
618
|
+
* Septentrio's GALRawINAV carries it (CRC already checked by the
|
|
619
|
+
* caller). `wn`/`tow` give the GST of the page's first symbol.
|
|
620
|
+
*/
|
|
621
|
+
pushPage(svid, wn, tow, page) {
|
|
622
|
+
if (svid < 1 || svid > 36) return;
|
|
623
|
+
if (bitAt(page, 0) !== 0 || bitAt(page, 114) !== 1) return;
|
|
624
|
+
if (bitAt(page, 1) || bitAt(page, 115)) return;
|
|
625
|
+
const abs = wn * WEEK + Math.round(tow);
|
|
626
|
+
const sf = abs - abs % 30;
|
|
627
|
+
const idx = Math.floor(abs % 30 / 2);
|
|
628
|
+
const word = new Uint8Array(16);
|
|
629
|
+
copyBits(word, 0, page, 2, 112);
|
|
630
|
+
copyBits(word, 112, page, 116, 16);
|
|
631
|
+
this.storeWord(svid, sf, word);
|
|
632
|
+
const hk = bitsU(page, 132, 8);
|
|
633
|
+
const mack = sliceBits(page, 140, 32);
|
|
634
|
+
const osnma = hk !== 0 || mack.some((b) => b !== 0);
|
|
635
|
+
if (osnma) {
|
|
636
|
+
this.osnmaPages++;
|
|
637
|
+
this.sat(svid).transmitsOsnma = true;
|
|
638
|
+
}
|
|
639
|
+
let c = this.collect.get(svid);
|
|
640
|
+
if (c && c.sf !== sf) {
|
|
641
|
+
this.closeSubframe(svid, c);
|
|
642
|
+
c = void 0;
|
|
643
|
+
}
|
|
644
|
+
if (!c) {
|
|
645
|
+
c = { sf, hk: [], mack: [] };
|
|
646
|
+
this.collect.set(svid, c);
|
|
647
|
+
}
|
|
648
|
+
if (osnma) {
|
|
649
|
+
c.hk[idx] = hk;
|
|
650
|
+
c.mack[idx] = mack;
|
|
651
|
+
}
|
|
652
|
+
if (idx === 14) {
|
|
653
|
+
this.closeSubframe(svid, c);
|
|
654
|
+
this.collect.delete(svid);
|
|
655
|
+
}
|
|
656
|
+
if (sf > this.latestSf) {
|
|
657
|
+
this.latestSf = sf;
|
|
658
|
+
this.prune();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/** Process anything still buffered (end of a file). */
|
|
662
|
+
flush() {
|
|
663
|
+
for (const [svid, c] of this.collect) this.closeSubframe(svid, c);
|
|
664
|
+
this.collect.clear();
|
|
665
|
+
}
|
|
666
|
+
status() {
|
|
667
|
+
const h = this.nmaHeader;
|
|
668
|
+
return {
|
|
669
|
+
nmaStatus: h === null ? null : NMAS[h >> 6],
|
|
670
|
+
cid: h === null ? null : h >> 4 & 3,
|
|
671
|
+
cpks: h === null ? null : CPKS[h >> 1 & 7],
|
|
672
|
+
publicKeys: [...this.pubKeys].map(([pkid, k]) => ({
|
|
673
|
+
pkid,
|
|
674
|
+
source: k.source
|
|
675
|
+
})),
|
|
676
|
+
chains: [...this.chains.values()].map((c) => ({
|
|
677
|
+
cid: c.cid,
|
|
678
|
+
pkid: c.pkid,
|
|
679
|
+
hashFunction: c.hashFunction,
|
|
680
|
+
macFunction: c.macFunction,
|
|
681
|
+
keyBits: c.keyBits,
|
|
682
|
+
tagBits: c.tagBits,
|
|
683
|
+
maclt: c.maclt,
|
|
684
|
+
gst0: c.gst0,
|
|
685
|
+
supported: c.supported
|
|
686
|
+
})),
|
|
687
|
+
keysVerified: this.keysVerified,
|
|
688
|
+
keysRejected: this.keysRejected,
|
|
689
|
+
lastKey: this.lastKey === null ? null : gstOf(this.lastKey),
|
|
690
|
+
tags: { ...this.tags },
|
|
691
|
+
satellites: [...this.sats.entries()].sort((a, b) => a[0] - b[0]).map(([, s]) => ({
|
|
692
|
+
prn: s.prn,
|
|
693
|
+
transmitsOsnma: s.transmitsOsnma,
|
|
694
|
+
ced: { ...s.ced },
|
|
695
|
+
timing: { ...s.timing }
|
|
696
|
+
})),
|
|
697
|
+
events: this.events.slice(),
|
|
698
|
+
osnmaPages: this.osnmaPages,
|
|
699
|
+
dontUseMacks: this.dontUseMacks,
|
|
700
|
+
alert: this.alerted
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
/* ---------------------------- internals --------------------------- */
|
|
704
|
+
sat(svid) {
|
|
705
|
+
let s = this.sats.get(svid);
|
|
706
|
+
if (!s) {
|
|
707
|
+
s = {
|
|
708
|
+
prn: prnName(svid),
|
|
709
|
+
transmitsOsnma: false,
|
|
710
|
+
ced: {
|
|
711
|
+
ok: 0,
|
|
712
|
+
failed: 0,
|
|
713
|
+
authenticatedIodNav: null,
|
|
714
|
+
lastAuthenticated: null
|
|
715
|
+
},
|
|
716
|
+
timing: { ok: 0, failed: 0, lastAuthenticated: null },
|
|
717
|
+
acc: /* @__PURE__ */ new Map()
|
|
718
|
+
};
|
|
719
|
+
this.sats.set(svid, s);
|
|
720
|
+
}
|
|
721
|
+
return s;
|
|
722
|
+
}
|
|
723
|
+
event(e) {
|
|
724
|
+
this.events.push(e);
|
|
725
|
+
if (this.events.length > 200) this.events.shift();
|
|
726
|
+
}
|
|
727
|
+
storeWord(svid, sf, word) {
|
|
728
|
+
const wt = bitsU(word, 0, 6);
|
|
729
|
+
if (!(wt >= 1 && wt <= 6 || wt === 10)) return;
|
|
730
|
+
let m = this.words.get(svid);
|
|
731
|
+
if (!m) this.words.set(svid, m = /* @__PURE__ */ new Map());
|
|
732
|
+
let list = m.get(wt);
|
|
733
|
+
if (!list) m.set(wt, list = []);
|
|
734
|
+
const last = list[list.length - 1];
|
|
735
|
+
if (last && last.sf === sf) last.word = word;
|
|
736
|
+
else list.push({ sf, word });
|
|
737
|
+
if (list.length > HISTORY_SUBFRAMES) list.shift();
|
|
738
|
+
}
|
|
739
|
+
prune() {
|
|
740
|
+
const oldest = this.latestSf - HISTORY_SUBFRAMES * 30;
|
|
741
|
+
while (this.macks.length && this.macks[0].sf < oldest) {
|
|
742
|
+
const m = this.macks.shift();
|
|
743
|
+
if (!m.fastDone || !m.slowDone) this.tags.unverifiable++;
|
|
744
|
+
}
|
|
745
|
+
for (const c of this.chains.values())
|
|
746
|
+
for (const sf of c.keys.keys())
|
|
747
|
+
if (sf < oldest && sf !== this.lastKey) c.keys.delete(sf);
|
|
748
|
+
}
|
|
749
|
+
closeSubframe(svid, c) {
|
|
750
|
+
for (let i = 0; i < 15; i++)
|
|
751
|
+
if (c.hk[i] === void 0 || !c.mack[i]) return;
|
|
752
|
+
const hk = new Uint8Array(c.hk);
|
|
753
|
+
const mack = new Uint8Array(60);
|
|
754
|
+
for (let i = 0; i < 15; i++) mack.set(c.mack[i], 4 * i);
|
|
755
|
+
const nma = hk[0];
|
|
756
|
+
this.nmaHeader = nma;
|
|
757
|
+
if (this.alerted || nma >> 6 === 0) return;
|
|
758
|
+
this.applyRevocations(nma);
|
|
759
|
+
this.onDsmBlock(c.sf, nma, hk[1] >> 4, hk[1] & 15, hk.subarray(2));
|
|
760
|
+
this.macks.push({
|
|
761
|
+
sf: c.sf,
|
|
762
|
+
prna: svid,
|
|
763
|
+
nmaHeader: nma,
|
|
764
|
+
mack,
|
|
765
|
+
fastDone: false,
|
|
766
|
+
slowDone: false
|
|
767
|
+
});
|
|
768
|
+
this.onKeyFrom(this.macks[this.macks.length - 1]);
|
|
769
|
+
this.processMacks();
|
|
770
|
+
}
|
|
771
|
+
leafOk(leaf, mid, path) {
|
|
772
|
+
return this.roots.some((root) => merkleLeafOk(leaf, mid, path, root));
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* "Don't use" headers announcing a revocation (ICD §5.4.1, §5.5.3):
|
|
776
|
+
* drop the revoked chain or public key and never accept it again.
|
|
777
|
+
*/
|
|
778
|
+
applyRevocations(nma) {
|
|
779
|
+
if (nma >> 6 !== 3) return;
|
|
780
|
+
const cpks = nma >> 1 & 7;
|
|
781
|
+
const chain = this.chains.get(nma >> 4 & 3);
|
|
782
|
+
if (cpks === 3 && chain) {
|
|
783
|
+
this.revoked.add(hexOf(chain.kroot));
|
|
784
|
+
this.chains.delete(chain.cid);
|
|
785
|
+
}
|
|
786
|
+
if (cpks === 5 && chain) {
|
|
787
|
+
const pk = this.pubKeys.get(chain.pkid);
|
|
788
|
+
if (pk) this.revoked.add(hexOf(pk.key));
|
|
789
|
+
this.pubKeys.delete(chain.pkid);
|
|
790
|
+
for (const [cid, c] of this.chains)
|
|
791
|
+
if (c.pkid === chain.pkid) {
|
|
792
|
+
this.revoked.add(hexOf(c.kroot));
|
|
793
|
+
this.chains.delete(cid);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/* ----------------------------- DSM ------------------------------- */
|
|
798
|
+
onDsmBlock(sf, nma, id, bid, block) {
|
|
799
|
+
let d = this.dsms.get(id);
|
|
800
|
+
const prev = d?.blocks[bid];
|
|
801
|
+
if (d && prev && !eqBytes(prev, block)) d = void 0;
|
|
802
|
+
if (!d) {
|
|
803
|
+
d = { blocks: [], nmaHeader: nma };
|
|
804
|
+
this.dsms.set(id, d);
|
|
805
|
+
}
|
|
806
|
+
d.blocks[bid] = block.slice();
|
|
807
|
+
d.nmaHeader = nma;
|
|
808
|
+
const b0 = d.blocks[0];
|
|
809
|
+
if (!b0) return;
|
|
810
|
+
const nb = (b0[0] >> 4) + 6;
|
|
811
|
+
if (nb < 7 || nb > 16) return;
|
|
812
|
+
for (let i = 0; i < nb; i++) if (!d.blocks[i]) return;
|
|
813
|
+
const msg = new Uint8Array(13 * nb);
|
|
814
|
+
for (let i = 0; i < nb; i++) msg.set(d.blocks[i], 13 * i);
|
|
815
|
+
this.dsms.delete(id);
|
|
816
|
+
if (id >= 12) this.onPkr(sf, msg);
|
|
817
|
+
else this.onKroot(sf, d.nmaHeader, msg);
|
|
818
|
+
}
|
|
819
|
+
onPkr(sf, msg) {
|
|
820
|
+
const mid = msg[0] & 15;
|
|
821
|
+
const path = [0, 1, 2, 3].map((j) => msg.slice(1 + 32 * j, 33 + 32 * j));
|
|
822
|
+
const npkt = msg[129] >> 4;
|
|
823
|
+
const npkid = msg[129] & 15;
|
|
824
|
+
const gst = gstOf(sf);
|
|
825
|
+
if (npkt === 4) {
|
|
826
|
+
const leaf2 = msg.slice(129);
|
|
827
|
+
if (this.leafOk(leaf2, mid, path)) {
|
|
828
|
+
this.event({
|
|
829
|
+
gst,
|
|
830
|
+
kind: "alert-message",
|
|
831
|
+
message: "OSNMA Alert Message received: stop using OSNMA."
|
|
832
|
+
});
|
|
833
|
+
this.alerted = true;
|
|
834
|
+
this.pubKeys.clear();
|
|
835
|
+
this.chains.clear();
|
|
836
|
+
}
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const keyLen = npkt === 1 ? 33 : npkt === 3 ? 67 : 0;
|
|
840
|
+
if (!keyLen) return;
|
|
841
|
+
const key = msg.slice(130, 130 + keyLen);
|
|
842
|
+
const leaf = msg.slice(129, 130 + keyLen);
|
|
843
|
+
if (this.revoked.has(hexOf(key))) return;
|
|
844
|
+
if (!this.leafOk(leaf, mid, path)) {
|
|
845
|
+
this.event({
|
|
846
|
+
gst,
|
|
847
|
+
kind: "public-key-rejected",
|
|
848
|
+
message: `Broadcast public key PKID ${npkid} does not verify against the Merkle root.`
|
|
849
|
+
});
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const had = this.pubKeys.get(npkid);
|
|
853
|
+
if (!had || !eqBytes(had.key, key))
|
|
854
|
+
this.pubKeys.set(npkid, { key, npkt, source: "broadcast" });
|
|
855
|
+
this.event({
|
|
856
|
+
gst,
|
|
857
|
+
kind: "public-key-verified",
|
|
858
|
+
message: had && eqBytes(had.key, key) ? `Broadcast public key PKID ${npkid} verified against the Merkle root (matches the known key).` : `Public key PKID ${npkid} received and verified against the Merkle root.`
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
onKroot(sf, nma, msg) {
|
|
862
|
+
const gst = gstOf(sf);
|
|
863
|
+
const pkid = msg[0] & 15;
|
|
864
|
+
const cid = msg[1] >> 6;
|
|
865
|
+
const hf = msg[1] >> 2 & 3;
|
|
866
|
+
const mf = msg[1] & 3;
|
|
867
|
+
const ks = msg[2] >> 4;
|
|
868
|
+
const ts = msg[2] & 15;
|
|
869
|
+
const maclt = msg[3];
|
|
870
|
+
const wnk = (msg[4] & 15) << 8 | msg[5];
|
|
871
|
+
const towhk = msg[6];
|
|
872
|
+
const keyBits = KEY_BITS[ks];
|
|
873
|
+
const tagBits = TAG_BITS[ts];
|
|
874
|
+
if (!keyBits || !tagBits) return;
|
|
875
|
+
const klen = keyBits / 8;
|
|
876
|
+
const pk = this.pubKeys.get(pkid);
|
|
877
|
+
if (!pk) return;
|
|
878
|
+
const m = new Uint8Array(13 + klen);
|
|
879
|
+
m[0] = nma;
|
|
880
|
+
m.set(msg.subarray(1, 13 + klen), 1);
|
|
881
|
+
const sigLen = pk.npkt === 1 ? 64 : 132;
|
|
882
|
+
const sig = msg.subarray(13 + klen, 13 + klen + sigLen);
|
|
883
|
+
const verify2 = pk.npkt === 1 ? p256Verify : p521Verify;
|
|
884
|
+
if (sig.length !== sigLen || !verify2(pk.key, m, sig)) {
|
|
885
|
+
this.event({
|
|
886
|
+
gst,
|
|
887
|
+
kind: "kroot-rejected",
|
|
888
|
+
message: `DSM-KROOT (chain ${cid}) signature does not verify with public key PKID ${pkid}.`
|
|
889
|
+
});
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const kroot = msg.slice(13, 13 + klen);
|
|
893
|
+
if (this.revoked.has(hexOf(kroot))) return;
|
|
894
|
+
const gst0 = wnk * WEEK + towhk * 3600;
|
|
895
|
+
if ((nma >> 1 & 7) !== 6) {
|
|
896
|
+
for (const id of [...this.pubKeys.keys()])
|
|
897
|
+
if (id < pkid) this.pubKeys.delete(id);
|
|
898
|
+
}
|
|
899
|
+
const existing = this.chains.get(cid);
|
|
900
|
+
if (existing && eqBytes(existing.alpha, msg.subarray(7, 13))) {
|
|
901
|
+
existing.keys.set(gst0 - 30, kroot);
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const chain = {
|
|
905
|
+
cid,
|
|
906
|
+
pkid,
|
|
907
|
+
hashFunction: ["SHA-256", "reserved", "SHA3-256", "reserved"][hf],
|
|
908
|
+
macFunction: ["HMAC-SHA-256", "CMAC-AES", "reserved", "reserved"][mf],
|
|
909
|
+
keyBits,
|
|
910
|
+
tagBits,
|
|
911
|
+
maclt,
|
|
912
|
+
gst0: gstOf(gst0),
|
|
913
|
+
supported: hf === 0 && mf === 0 && maclt in MACLT,
|
|
914
|
+
kroot,
|
|
915
|
+
alpha: msg.slice(7, 13),
|
|
916
|
+
keys: /* @__PURE__ */ new Map([[gst0 - 30, kroot]])
|
|
917
|
+
};
|
|
918
|
+
this.chains.set(cid, chain);
|
|
919
|
+
this.event({
|
|
920
|
+
gst,
|
|
921
|
+
kind: "kroot-verified",
|
|
922
|
+
message: `TESLA root key of chain ${cid} verified with public key PKID ${pkid}.`
|
|
923
|
+
});
|
|
924
|
+
if (!chain.supported)
|
|
925
|
+
this.event({
|
|
926
|
+
gst,
|
|
927
|
+
kind: "unsupported",
|
|
928
|
+
message: `Chain ${cid} uses ${chain.hashFunction}/${chain.macFunction}, MACLT ${maclt}: tags not verified.`
|
|
929
|
+
});
|
|
930
|
+
for (const r of this.macks) this.onKeyFrom(r);
|
|
931
|
+
this.processMacks();
|
|
932
|
+
}
|
|
933
|
+
/* ---------------------------- TESLA ------------------------------ */
|
|
934
|
+
/** F(K) for the key of subframe `sf`: the key of subframe sf − 30. */
|
|
935
|
+
oneWay(c, key, sf) {
|
|
936
|
+
return sha256(key, gst32(sf - 30), c.alpha).slice(0, c.keyBits / 8);
|
|
937
|
+
}
|
|
938
|
+
chainFor(nmaHeader) {
|
|
939
|
+
return this.chains.get(nmaHeader >> 4 & 3);
|
|
940
|
+
}
|
|
941
|
+
/** Verify the TESLA key carried in a MACK against the chain. */
|
|
942
|
+
onKeyFrom(r) {
|
|
943
|
+
const c = this.chainFor(r.nmaHeader);
|
|
944
|
+
if (!c || !c.supported) return;
|
|
945
|
+
const nt = Math.floor((480 - c.keyBits) / (c.tagBits + 16));
|
|
946
|
+
const key = sliceBits(r.mack, nt * (c.tagBits + 16), c.keyBits);
|
|
947
|
+
const known = c.keys.get(r.sf);
|
|
948
|
+
if (known) {
|
|
949
|
+
if (!eqBytes(known, key)) this.rejectKey(r);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const tag = `${r.sf}:${hexOf(key)}`;
|
|
953
|
+
if (this.rejectedKeys.has(tag)) return;
|
|
954
|
+
let anchor = -Infinity;
|
|
955
|
+
for (const sf of c.keys.keys()) if (sf < r.sf && sf > anchor) anchor = sf;
|
|
956
|
+
if (anchor === -Infinity) return;
|
|
957
|
+
const n = (r.sf - anchor) / 30;
|
|
958
|
+
if (n > MAX_DERIVATIONS) return;
|
|
959
|
+
let k = key;
|
|
960
|
+
for (let i = 0, sf = r.sf; i < n; i++, sf -= 30) k = this.oneWay(c, k, sf);
|
|
961
|
+
if (eqBytes(k, c.keys.get(anchor))) {
|
|
962
|
+
c.keys.set(r.sf, key);
|
|
963
|
+
this.keysVerified++;
|
|
964
|
+
if (this.lastKey === null || r.sf > this.lastKey) this.lastKey = r.sf;
|
|
965
|
+
} else {
|
|
966
|
+
this.rejectedKeys.add(tag);
|
|
967
|
+
this.rejectKey(r);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
rejectKey(r) {
|
|
971
|
+
this.keysRejected++;
|
|
972
|
+
this.event({
|
|
973
|
+
gst: gstOf(r.sf),
|
|
974
|
+
kind: "key-rejected",
|
|
975
|
+
prn: prnName(r.prna),
|
|
976
|
+
message: `TESLA key from ${prnName(r.prna)} does not hash back to the verified chain.`
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
/* ----------------------------- tags ------------------------------ */
|
|
980
|
+
processMacks() {
|
|
981
|
+
for (const r of this.macks) {
|
|
982
|
+
if (r.fastDone && r.slowDone) continue;
|
|
983
|
+
if (r.nmaHeader >> 6 === 3) {
|
|
984
|
+
r.fastDone = r.slowDone = true;
|
|
985
|
+
this.dontUseMacks++;
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
const c = this.chainFor(r.nmaHeader);
|
|
989
|
+
if (!c || !c.supported) continue;
|
|
990
|
+
const fastKey = c.keys.get(r.sf + 30);
|
|
991
|
+
const slowKey = c.keys.get(r.sf + 330);
|
|
992
|
+
if (!r.fastDone && fastKey) {
|
|
993
|
+
this.verifyTags(c, r, fastKey, false);
|
|
994
|
+
r.fastDone = true;
|
|
995
|
+
}
|
|
996
|
+
if (!r.slowDone && slowKey) {
|
|
997
|
+
this.verifyTags(c, r, slowKey, true);
|
|
998
|
+
r.slowDone = true;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
verifyTags(c, r, key, slow) {
|
|
1003
|
+
const lt = c.tagBits;
|
|
1004
|
+
const nt = Math.floor((480 - c.keyBits) / (lt + 16));
|
|
1005
|
+
const seq = MACLT[c.maclt][r.sf / 30 % 2];
|
|
1006
|
+
if (seq.length !== nt) return;
|
|
1007
|
+
const nmas = r.nmaHeader >> 6;
|
|
1008
|
+
const tagAt = (i) => sliceBits(r.mack, i * (lt + 16), lt);
|
|
1009
|
+
const infoAt = (i) => bitsU(r.mack, i * (lt + 16) + lt, 16);
|
|
1010
|
+
if (!slow) {
|
|
1011
|
+
const flex = [];
|
|
1012
|
+
seq.forEach((s, i) => s === "FLX" && flex.push(i));
|
|
1013
|
+
const m = new Uint8Array(5 + 2 * flex.length);
|
|
1014
|
+
m[0] = r.prna;
|
|
1015
|
+
m.set(gst32(r.sf), 1);
|
|
1016
|
+
flex.forEach((i, k) => {
|
|
1017
|
+
const v = infoAt(i);
|
|
1018
|
+
m[5 + 2 * k] = v >> 8;
|
|
1019
|
+
m[6 + 2 * k] = v & 255;
|
|
1020
|
+
});
|
|
1021
|
+
const macseq = bitsU(r.mack, lt, 12);
|
|
1022
|
+
const calc = bitsU(hmacSha256(key, m), 0, 12);
|
|
1023
|
+
if (macseq !== calc) {
|
|
1024
|
+
this.event({
|
|
1025
|
+
gst: gstOf(r.sf),
|
|
1026
|
+
kind: "macseq-failed",
|
|
1027
|
+
prn: prnName(r.prna),
|
|
1028
|
+
message: `MACSEQ from ${prnName(r.prna)} does not verify: its tag list is not trusted.`
|
|
1029
|
+
});
|
|
1030
|
+
this.tags.failed++;
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
for (let i = 0; i < nt; i++) {
|
|
1035
|
+
let prnd;
|
|
1036
|
+
let adkd;
|
|
1037
|
+
let cop;
|
|
1038
|
+
if (i === 0) {
|
|
1039
|
+
prnd = r.prna;
|
|
1040
|
+
adkd = 0;
|
|
1041
|
+
cop = bitsU(r.mack, lt + 12, 4);
|
|
1042
|
+
} else {
|
|
1043
|
+
const info = infoAt(i);
|
|
1044
|
+
prnd = info >> 8;
|
|
1045
|
+
adkd = info >> 4 & 15;
|
|
1046
|
+
cop = info & 15;
|
|
1047
|
+
}
|
|
1048
|
+
if (adkd === 12 !== slow) continue;
|
|
1049
|
+
const slot = seq[i];
|
|
1050
|
+
if (slot !== "FLX") {
|
|
1051
|
+
const want = parseInt(slot.slice(0, 2), 10);
|
|
1052
|
+
const self = slot[2] === "S";
|
|
1053
|
+
if (adkd !== want || prnd === r.prna !== self) {
|
|
1054
|
+
this.tags.failed++;
|
|
1055
|
+
this.event({
|
|
1056
|
+
gst: gstOf(r.sf),
|
|
1057
|
+
kind: "tag-failed",
|
|
1058
|
+
prn: prnName(r.prna),
|
|
1059
|
+
message: `Tag ${i} from ${prnName(r.prna)} breaks the MAC look-up table (ADKD ${adkd}).`
|
|
1060
|
+
});
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (prnd < 1 || prnd > 36 || adkd !== 0 && adkd !== 4 && adkd !== 12)
|
|
1065
|
+
continue;
|
|
1066
|
+
const nav = cop === 0 ? new Uint8Array(
|
|
1067
|
+
Math.ceil((adkd === 4 ? ADKD4_BITS : ADKD0_BITS) / 8)
|
|
1068
|
+
) : adkd === 4 ? this.timingData(prnd, r.sf, cop) : this.cedData(prnd, r.sf, cop);
|
|
1069
|
+
if (!nav) {
|
|
1070
|
+
this.tags.unverifiable++;
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
const nBits = adkd === 4 ? ADKD4_BITS : ADKD0_BITS;
|
|
1074
|
+
const head = i === 0 ? 6 : 7;
|
|
1075
|
+
const m = new Uint8Array(head + Math.ceil((2 + nBits) / 8));
|
|
1076
|
+
let o = 0;
|
|
1077
|
+
if (i !== 0) m[o++] = prnd;
|
|
1078
|
+
m[o++] = r.prna;
|
|
1079
|
+
m.set(gst32(r.sf), o);
|
|
1080
|
+
o += 4;
|
|
1081
|
+
m[o++] = i + 1;
|
|
1082
|
+
m[o] = nmas << 6;
|
|
1083
|
+
copyBits(m, 8 * o + 2, nav, 0, nBits);
|
|
1084
|
+
const mac = hmacSha256(key, m);
|
|
1085
|
+
const ok = eqBits(mac, tagAt(i), lt);
|
|
1086
|
+
if (cop === 0) {
|
|
1087
|
+
if (ok) this.tags.dummy++;
|
|
1088
|
+
else this.tags.failed++;
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
this.onTag(prnd, adkd, r, nav, ok, lt);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
onTag(prnd, adkd, r, nav, ok, lt) {
|
|
1095
|
+
const s = this.sat(prnd);
|
|
1096
|
+
const stats = adkd === 4 ? s.timing : s.ced;
|
|
1097
|
+
if (!ok) {
|
|
1098
|
+
this.tags.failed++;
|
|
1099
|
+
stats.failed++;
|
|
1100
|
+
this.event({
|
|
1101
|
+
gst: gstOf(r.sf),
|
|
1102
|
+
kind: "tag-failed",
|
|
1103
|
+
prn: s.prn,
|
|
1104
|
+
message: `${adkd === 4 ? "Timing" : "Ephemeris"} tag for ${s.prn} (sent by ${prnName(r.prna)}) does not match its navigation data.`
|
|
1105
|
+
});
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
this.tags.ok++;
|
|
1109
|
+
stats.ok++;
|
|
1110
|
+
const group = adkd === 4 ? 4 : 0;
|
|
1111
|
+
const id = Array.from(nav).join(",");
|
|
1112
|
+
let a = s.acc.get(group);
|
|
1113
|
+
if (!a || a.key !== id) s.acc.set(group, a = { key: id, bits: 0 });
|
|
1114
|
+
a.bits += lt;
|
|
1115
|
+
if (a.bits >= MIN_TAG_BITS[adkd]) {
|
|
1116
|
+
if (adkd === 4) s.timing.lastAuthenticated = gstOf(r.sf);
|
|
1117
|
+
else {
|
|
1118
|
+
s.ced.lastAuthenticated = gstOf(r.sf);
|
|
1119
|
+
s.ced.authenticatedIodNav = bitsU(nav, 0, 10);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
/** Latest word of `wt` from `svid` in subframes [sf − 30·cop, sf − 30]. */
|
|
1124
|
+
wordIn(svid, wt, sf, cop) {
|
|
1125
|
+
const list = this.words.get(svid)?.get(wt);
|
|
1126
|
+
if (!list) return void 0;
|
|
1127
|
+
const lo = sf - 30 * Math.max(cop, 1);
|
|
1128
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
1129
|
+
const w = list[i];
|
|
1130
|
+
if (w.sf < sf && w.sf >= lo) return w.word;
|
|
1131
|
+
if (w.sf < lo) break;
|
|
1132
|
+
}
|
|
1133
|
+
return void 0;
|
|
1134
|
+
}
|
|
1135
|
+
/** ADKD 0/12 data: WT1-5 of one IODnav (Annex B.1). */
|
|
1136
|
+
cedData(svid, sf, cop) {
|
|
1137
|
+
const out = new Uint8Array(Math.ceil(ADKD0_BITS / 8));
|
|
1138
|
+
let at = 0;
|
|
1139
|
+
let iod = null;
|
|
1140
|
+
for (const [wt, start, n] of ADKD0_WORDS) {
|
|
1141
|
+
const w = this.wordIn(svid, wt, sf, cop);
|
|
1142
|
+
if (!w) return null;
|
|
1143
|
+
if (wt <= 4) {
|
|
1144
|
+
const v = bitsU(w, 6, 10);
|
|
1145
|
+
if (iod === null) iod = v;
|
|
1146
|
+
else if (v !== iod) return null;
|
|
1147
|
+
}
|
|
1148
|
+
copyBits(out, at, w, start, n);
|
|
1149
|
+
at += n;
|
|
1150
|
+
}
|
|
1151
|
+
return out;
|
|
1152
|
+
}
|
|
1153
|
+
/** ADKD 4 data: WT6 ‖ WT10 timing parameters (Annex B.2). */
|
|
1154
|
+
timingData(svid, sf, cop) {
|
|
1155
|
+
const w6 = this.wordIn(svid, 6, sf, cop);
|
|
1156
|
+
const w10 = this.wordIn(svid, 10, sf, Math.max(cop, 2));
|
|
1157
|
+
if (!w6 || !w10) return null;
|
|
1158
|
+
const out = new Uint8Array(Math.ceil(ADKD4_BITS / 8));
|
|
1159
|
+
copyBits(out, 0, w6, 6, 99);
|
|
1160
|
+
copyBits(out, 99, w10, 86, 42);
|
|
1161
|
+
return out;
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
// src/osnma/sbf.ts
|
|
1166
|
+
var SIG_E1BC = 17;
|
|
1167
|
+
var TOW_DNU = 4294967295;
|
|
1168
|
+
var GST_GPS_WEEK_OFFSET = 1024;
|
|
1169
|
+
function feedSbfOsnmaBlock(data, view, b, id, len, rx) {
|
|
1170
|
+
if (id !== 4023 || len < 52) return false;
|
|
1171
|
+
if ((data[b + 17] & 31) !== SIG_E1BC) return false;
|
|
1172
|
+
const prn = svidToPrn(data[b + 14]);
|
|
1173
|
+
if (!prn || prn[0] !== "E") return false;
|
|
1174
|
+
const towMs = view.getUint32(b + 8, true);
|
|
1175
|
+
const wnc = view.getUint16(b + 12, true);
|
|
1176
|
+
if (towMs === TOW_DNU || wnc === 65535) return false;
|
|
1177
|
+
const page = new Uint8Array(32);
|
|
1178
|
+
for (let k = 0; k < 8; k++) {
|
|
1179
|
+
const w = view.getUint32(b + 20 + 4 * k, true);
|
|
1180
|
+
page[4 * k] = w >>> 24;
|
|
1181
|
+
page[4 * k + 1] = w >>> 16 & 255;
|
|
1182
|
+
page[4 * k + 2] = w >>> 8 & 255;
|
|
1183
|
+
page[4 * k + 3] = w & 255;
|
|
1184
|
+
}
|
|
1185
|
+
if (!galInavPageCrcOk(page)) return false;
|
|
1186
|
+
let tow = Math.round(towMs / 1e3) - 2;
|
|
1187
|
+
let wn = wnc - GST_GPS_WEEK_OFFSET;
|
|
1188
|
+
if (tow < 0) {
|
|
1189
|
+
tow += 604800;
|
|
1190
|
+
wn--;
|
|
1191
|
+
}
|
|
1192
|
+
rx.pushPage(parseInt(prn.slice(1), 10), wn, tow, page);
|
|
1193
|
+
return true;
|
|
1194
|
+
}
|
|
1195
|
+
function verifySbfOsnma(data, opts) {
|
|
1196
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1197
|
+
const rx = new OsnmaReceiver(opts);
|
|
1198
|
+
scanSbfFrames(data, view, (id, b, len) => {
|
|
1199
|
+
feedSbfOsnmaBlock(data, view, b, id, len, rx);
|
|
1200
|
+
});
|
|
1201
|
+
rx.flush();
|
|
1202
|
+
return rx.status();
|
|
1203
|
+
}
|
|
1204
|
+
export {
|
|
1205
|
+
OSNMA_KNOWN_PUBLIC_KEYS,
|
|
1206
|
+
OSNMA_MERKLE_ROOT,
|
|
1207
|
+
OsnmaReceiver,
|
|
1208
|
+
feedSbfOsnmaBlock,
|
|
1209
|
+
hmacSha256,
|
|
1210
|
+
merkleLeafOk,
|
|
1211
|
+
p256Verify,
|
|
1212
|
+
p521Verify,
|
|
1213
|
+
sha256,
|
|
1214
|
+
sha512,
|
|
1215
|
+
verifySbfOsnma
|
|
1216
|
+
};
|