@novnc/novnc 1.4.0-g90455ee → 1.4.0-ga0e6e7b
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/core/crypto/aes.js +178 -0
- package/core/crypto/bigint.js +34 -0
- package/core/crypto/crypto.js +90 -0
- package/core/{des.js → crypto/des.js} +68 -4
- package/core/crypto/dh.js +55 -0
- package/core/{util → crypto}/md5.js +8 -5
- package/core/crypto/rsa.js +132 -0
- package/core/ra2.js +33 -294
- package/core/rfb.js +34 -88
- package/lib/crypto/aes.js +485 -0
- package/lib/crypto/bigint.js +41 -0
- package/lib/crypto/crypto.js +111 -0
- package/lib/{des.js → crypto/des.js} +86 -7
- package/lib/crypto/dh.js +84 -0
- package/lib/crypto/md5.js +97 -0
- package/lib/crypto/rsa.js +314 -0
- package/lib/ra2.js +163 -635
- package/lib/rfb.js +77 -140
- package/package.json +1 -1
- package/lib/util/md5.js +0 -77
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export class AESECBCipher {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._key = null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
get algorithm() {
|
|
7
|
+
return { name: "AES-ECB" };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static async importKey(key, _algorithm, extractable, keyUsages) {
|
|
11
|
+
const cipher = new AESECBCipher;
|
|
12
|
+
await cipher._importKey(key, extractable, keyUsages);
|
|
13
|
+
return cipher;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async _importKey(key, extractable, keyUsages) {
|
|
17
|
+
this._key = await window.crypto.subtle.importKey(
|
|
18
|
+
"raw", key, {name: "AES-CBC"}, extractable, keyUsages);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async encrypt(_algorithm, plaintext) {
|
|
22
|
+
const x = new Uint8Array(plaintext);
|
|
23
|
+
if (x.length % 16 !== 0 || this._key === null) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const n = x.length / 16;
|
|
27
|
+
for (let i = 0; i < n; i++) {
|
|
28
|
+
const y = new Uint8Array(await window.crypto.subtle.encrypt({
|
|
29
|
+
name: "AES-CBC",
|
|
30
|
+
iv: new Uint8Array(16),
|
|
31
|
+
}, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
|
|
32
|
+
x.set(y, i * 16);
|
|
33
|
+
}
|
|
34
|
+
return x;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class AESEAXCipher {
|
|
39
|
+
constructor() {
|
|
40
|
+
this._rawKey = null;
|
|
41
|
+
this._ctrKey = null;
|
|
42
|
+
this._cbcKey = null;
|
|
43
|
+
this._zeroBlock = new Uint8Array(16);
|
|
44
|
+
this._prefixBlock0 = this._zeroBlock;
|
|
45
|
+
this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
|
46
|
+
this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get algorithm() {
|
|
50
|
+
return { name: "AES-EAX" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async _encryptBlock(block) {
|
|
54
|
+
const encrypted = await window.crypto.subtle.encrypt({
|
|
55
|
+
name: "AES-CBC",
|
|
56
|
+
iv: this._zeroBlock,
|
|
57
|
+
}, this._cbcKey, block);
|
|
58
|
+
return new Uint8Array(encrypted).slice(0, 16);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async _initCMAC() {
|
|
62
|
+
const k1 = await this._encryptBlock(this._zeroBlock);
|
|
63
|
+
const k2 = new Uint8Array(16);
|
|
64
|
+
const v = k1[0] >>> 6;
|
|
65
|
+
for (let i = 0; i < 15; i++) {
|
|
66
|
+
k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);
|
|
67
|
+
k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);
|
|
68
|
+
}
|
|
69
|
+
const lut = [0x0, 0x87, 0x0e, 0x89];
|
|
70
|
+
k2[14] ^= v >>> 1;
|
|
71
|
+
k2[15] = (k1[15] << 2) ^ lut[v];
|
|
72
|
+
k1[15] = (k1[15] << 1) ^ lut[v >> 1];
|
|
73
|
+
this._k1 = k1;
|
|
74
|
+
this._k2 = k2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async _encryptCTR(data, counter) {
|
|
78
|
+
const encrypted = await window.crypto.subtle.encrypt({
|
|
79
|
+
name: "AES-CTR",
|
|
80
|
+
counter: counter,
|
|
81
|
+
length: 128
|
|
82
|
+
}, this._ctrKey, data);
|
|
83
|
+
return new Uint8Array(encrypted);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async _decryptCTR(data, counter) {
|
|
87
|
+
const decrypted = await window.crypto.subtle.decrypt({
|
|
88
|
+
name: "AES-CTR",
|
|
89
|
+
counter: counter,
|
|
90
|
+
length: 128
|
|
91
|
+
}, this._ctrKey, data);
|
|
92
|
+
return new Uint8Array(decrypted);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async _computeCMAC(data, prefixBlock) {
|
|
96
|
+
if (prefixBlock.length !== 16) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const n = Math.floor(data.length / 16);
|
|
100
|
+
const m = Math.ceil(data.length / 16);
|
|
101
|
+
const r = data.length - n * 16;
|
|
102
|
+
const cbcData = new Uint8Array((m + 1) * 16);
|
|
103
|
+
cbcData.set(prefixBlock);
|
|
104
|
+
cbcData.set(data, 16);
|
|
105
|
+
if (r === 0) {
|
|
106
|
+
for (let i = 0; i < 16; i++) {
|
|
107
|
+
cbcData[n * 16 + i] ^= this._k1[i];
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
cbcData[(n + 1) * 16 + r] = 0x80;
|
|
111
|
+
for (let i = 0; i < 16; i++) {
|
|
112
|
+
cbcData[(n + 1) * 16 + i] ^= this._k2[i];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let cbcEncrypted = await window.crypto.subtle.encrypt({
|
|
116
|
+
name: "AES-CBC",
|
|
117
|
+
iv: this._zeroBlock,
|
|
118
|
+
}, this._cbcKey, cbcData);
|
|
119
|
+
|
|
120
|
+
cbcEncrypted = new Uint8Array(cbcEncrypted);
|
|
121
|
+
const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);
|
|
122
|
+
return mac;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static async importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
126
|
+
const cipher = new AESEAXCipher;
|
|
127
|
+
await cipher._importKey(key);
|
|
128
|
+
return cipher;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async _importKey(key) {
|
|
132
|
+
this._rawKey = key;
|
|
133
|
+
this._ctrKey = await window.crypto.subtle.importKey(
|
|
134
|
+
"raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
|
|
135
|
+
this._cbcKey = await window.crypto.subtle.importKey(
|
|
136
|
+
"raw", key, {name: "AES-CBC"}, false, ["encrypt"]);
|
|
137
|
+
await this._initCMAC();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async encrypt(algorithm, message) {
|
|
141
|
+
const ad = algorithm.additionalData;
|
|
142
|
+
const nonce = algorithm.iv;
|
|
143
|
+
const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
|
|
144
|
+
const encrypted = await this._encryptCTR(message, nCMAC);
|
|
145
|
+
const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
|
|
146
|
+
const mac = await this._computeCMAC(encrypted, this._prefixBlock2);
|
|
147
|
+
for (let i = 0; i < 16; i++) {
|
|
148
|
+
mac[i] ^= nCMAC[i] ^ adCMAC[i];
|
|
149
|
+
}
|
|
150
|
+
const res = new Uint8Array(16 + encrypted.length);
|
|
151
|
+
res.set(encrypted);
|
|
152
|
+
res.set(mac, encrypted.length);
|
|
153
|
+
return res;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async decrypt(algorithm, data) {
|
|
157
|
+
const encrypted = data.slice(0, data.length - 16);
|
|
158
|
+
const ad = algorithm.additionalData;
|
|
159
|
+
const nonce = algorithm.iv;
|
|
160
|
+
const mac = data.slice(data.length - 16);
|
|
161
|
+
const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
|
|
162
|
+
const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
|
|
163
|
+
const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);
|
|
164
|
+
for (let i = 0; i < 16; i++) {
|
|
165
|
+
computedMac[i] ^= nCMAC[i] ^ adCMAC[i];
|
|
166
|
+
}
|
|
167
|
+
if (computedMac.length !== mac.length) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
for (let i = 0; i < mac.length; i++) {
|
|
171
|
+
if (computedMac[i] !== mac[i]) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const res = await this._decryptCTR(encrypted, nCMAC);
|
|
176
|
+
return res;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function modPow(b, e, m) {
|
|
2
|
+
let r = 1n;
|
|
3
|
+
b = b % m;
|
|
4
|
+
while (e > 0n) {
|
|
5
|
+
if ((e & 1n) === 1n) {
|
|
6
|
+
r = (r * b) % m;
|
|
7
|
+
}
|
|
8
|
+
e = e >> 1n;
|
|
9
|
+
b = (b * b) % m;
|
|
10
|
+
}
|
|
11
|
+
return r;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function bigIntToU8Array(bigint, padLength=0) {
|
|
15
|
+
let hex = bigint.toString(16);
|
|
16
|
+
if (padLength === 0) {
|
|
17
|
+
padLength = Math.ceil(hex.length / 2);
|
|
18
|
+
}
|
|
19
|
+
hex = hex.padStart(padLength * 2, '0');
|
|
20
|
+
const length = hex.length / 2;
|
|
21
|
+
const arr = new Uint8Array(length);
|
|
22
|
+
for (let i = 0; i < length; i++) {
|
|
23
|
+
arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
24
|
+
}
|
|
25
|
+
return arr;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function u8ArrayToBigInt(arr) {
|
|
29
|
+
let hex = '0x';
|
|
30
|
+
for (let i = 0; i < arr.length; i++) {
|
|
31
|
+
hex += arr[i].toString(16).padStart(2, '0');
|
|
32
|
+
}
|
|
33
|
+
return BigInt(hex);
|
|
34
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { AESECBCipher, AESEAXCipher } from "./aes.js";
|
|
2
|
+
import { DESCBCCipher, DESECBCipher } from "./des.js";
|
|
3
|
+
import { RSACipher } from "./rsa.js";
|
|
4
|
+
import { DHCipher } from "./dh.js";
|
|
5
|
+
import { MD5 } from "./md5.js";
|
|
6
|
+
|
|
7
|
+
// A single interface for the cryptographic algorithms not supported by SubtleCrypto.
|
|
8
|
+
// Both synchronous and asynchronous implmentations are allowed.
|
|
9
|
+
class LegacyCrypto {
|
|
10
|
+
constructor() {
|
|
11
|
+
this._algorithms = {
|
|
12
|
+
"AES-ECB": AESECBCipher,
|
|
13
|
+
"AES-EAX": AESEAXCipher,
|
|
14
|
+
"DES-ECB": DESECBCipher,
|
|
15
|
+
"DES-CBC": DESCBCCipher,
|
|
16
|
+
"RSA-PKCS1-v1_5": RSACipher,
|
|
17
|
+
"DH": DHCipher,
|
|
18
|
+
"MD5": MD5,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
encrypt(algorithm, key, data) {
|
|
23
|
+
if (key.algorithm.name !== algorithm.name) {
|
|
24
|
+
throw new Error("algorithm does not match");
|
|
25
|
+
}
|
|
26
|
+
if (typeof key.encrypt !== "function") {
|
|
27
|
+
throw new Error("key does not support encryption");
|
|
28
|
+
}
|
|
29
|
+
return key.encrypt(algorithm, data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
decrypt(algorithm, key, data) {
|
|
33
|
+
if (key.algorithm.name !== algorithm.name) {
|
|
34
|
+
throw new Error("algorithm does not match");
|
|
35
|
+
}
|
|
36
|
+
if (typeof key.decrypt !== "function") {
|
|
37
|
+
throw new Error("key does not support encryption");
|
|
38
|
+
}
|
|
39
|
+
return key.decrypt(algorithm, data);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
importKey(format, keyData, algorithm, extractable, keyUsages) {
|
|
43
|
+
if (format !== "raw") {
|
|
44
|
+
throw new Error("key format is not supported");
|
|
45
|
+
}
|
|
46
|
+
const alg = this._algorithms[algorithm.name];
|
|
47
|
+
if (typeof alg === "undefined" || typeof alg.importKey !== "function") {
|
|
48
|
+
throw new Error("algorithm is not supported");
|
|
49
|
+
}
|
|
50
|
+
return alg.importKey(keyData, algorithm, extractable, keyUsages);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
generateKey(algorithm, extractable, keyUsages) {
|
|
54
|
+
const alg = this._algorithms[algorithm.name];
|
|
55
|
+
if (typeof alg === "undefined" || typeof alg.generateKey !== "function") {
|
|
56
|
+
throw new Error("algorithm is not supported");
|
|
57
|
+
}
|
|
58
|
+
return alg.generateKey(algorithm, extractable, keyUsages);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
exportKey(format, key) {
|
|
62
|
+
if (format !== "raw") {
|
|
63
|
+
throw new Error("key format is not supported");
|
|
64
|
+
}
|
|
65
|
+
if (typeof key.exportKey !== "function") {
|
|
66
|
+
throw new Error("key does not support exportKey");
|
|
67
|
+
}
|
|
68
|
+
return key.exportKey();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
digest(algorithm, data) {
|
|
72
|
+
const alg = this._algorithms[algorithm];
|
|
73
|
+
if (typeof alg !== "function") {
|
|
74
|
+
throw new Error("algorithm is not supported");
|
|
75
|
+
}
|
|
76
|
+
return alg(data);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
deriveBits(algorithm, key, length) {
|
|
80
|
+
if (key.algorithm.name !== algorithm.name) {
|
|
81
|
+
throw new Error("algorithm does not match");
|
|
82
|
+
}
|
|
83
|
+
if (typeof key.deriveBits !== "function") {
|
|
84
|
+
throw new Error("key does not support deriveBits");
|
|
85
|
+
}
|
|
86
|
+
return key.deriveBits(algorithm, length);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default new LegacyCrypto;
|
|
@@ -128,7 +128,7 @@ const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
|
|
128
128
|
|
|
129
129
|
/* eslint-enable comma-spacing */
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
class DES {
|
|
132
132
|
constructor(password) {
|
|
133
133
|
this.keys = [];
|
|
134
134
|
|
|
@@ -258,9 +258,73 @@ export default class DES {
|
|
|
258
258
|
}
|
|
259
259
|
return b;
|
|
260
260
|
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export class DESECBCipher {
|
|
264
|
+
constructor() {
|
|
265
|
+
this._cipher = null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
get algorithm() {
|
|
269
|
+
return { name: "DES-ECB" };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
273
|
+
const cipher = new DESECBCipher;
|
|
274
|
+
cipher._importKey(key);
|
|
275
|
+
return cipher;
|
|
276
|
+
}
|
|
261
277
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
_importKey(key, _extractable, _keyUsages) {
|
|
279
|
+
this._cipher = new DES(key);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
encrypt(_algorithm, plaintext) {
|
|
283
|
+
const x = new Uint8Array(plaintext);
|
|
284
|
+
if (x.length % 8 !== 0 || this._cipher === null) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const n = x.length / 8;
|
|
288
|
+
for (let i = 0; i < n; i++) {
|
|
289
|
+
x.set(this._cipher.enc8(x.slice(i * 8, i * 8 + 8)), i * 8);
|
|
290
|
+
}
|
|
291
|
+
return x;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export class DESCBCCipher {
|
|
296
|
+
constructor() {
|
|
297
|
+
this._cipher = null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
get algorithm() {
|
|
301
|
+
return { name: "DES-CBC" };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
static importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
305
|
+
const cipher = new DESCBCCipher;
|
|
306
|
+
cipher._importKey(key);
|
|
307
|
+
return cipher;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_importKey(key) {
|
|
311
|
+
this._cipher = new DES(key);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
encrypt(algorithm, plaintext) {
|
|
315
|
+
const x = new Uint8Array(plaintext);
|
|
316
|
+
let y = new Uint8Array(algorithm.iv);
|
|
317
|
+
if (x.length % 8 !== 0 || this._cipher === null) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const n = x.length / 8;
|
|
321
|
+
for (let i = 0; i < n; i++) {
|
|
322
|
+
for (let j = 0; j < 8; j++) {
|
|
323
|
+
y[j] ^= plaintext[i * 8 + j];
|
|
324
|
+
}
|
|
325
|
+
y = this._cipher.enc8(y);
|
|
326
|
+
x.set(y, i * 8);
|
|
327
|
+
}
|
|
328
|
+
return x;
|
|
265
329
|
}
|
|
266
330
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js";
|
|
2
|
+
|
|
3
|
+
class DHPublicKey {
|
|
4
|
+
constructor(key) {
|
|
5
|
+
this._key = key;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get algorithm() {
|
|
9
|
+
return { name: "DH" };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exportKey() {
|
|
13
|
+
return this._key;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class DHCipher {
|
|
18
|
+
constructor() {
|
|
19
|
+
this._g = null;
|
|
20
|
+
this._p = null;
|
|
21
|
+
this._gBigInt = null;
|
|
22
|
+
this._pBigInt = null;
|
|
23
|
+
this._privateKey = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get algorithm() {
|
|
27
|
+
return { name: "DH" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static generateKey(algorithm, _extractable) {
|
|
31
|
+
const cipher = new DHCipher;
|
|
32
|
+
cipher._generateKey(algorithm);
|
|
33
|
+
return { privateKey: cipher, publicKey: new DHPublicKey(cipher._publicKey) };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_generateKey(algorithm) {
|
|
37
|
+
const g = algorithm.g;
|
|
38
|
+
const p = algorithm.p;
|
|
39
|
+
this._keyBytes = p.length;
|
|
40
|
+
this._gBigInt = u8ArrayToBigInt(g);
|
|
41
|
+
this._pBigInt = u8ArrayToBigInt(p);
|
|
42
|
+
this._privateKey = window.crypto.getRandomValues(new Uint8Array(this._keyBytes));
|
|
43
|
+
this._privateKeyBigInt = u8ArrayToBigInt(this._privateKey);
|
|
44
|
+
this._publicKey = bigIntToU8Array(modPow(
|
|
45
|
+
this._gBigInt, this._privateKeyBigInt, this._pBigInt), this._keyBytes);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
deriveBits(algorithm, length) {
|
|
49
|
+
const bytes = Math.ceil(length / 8);
|
|
50
|
+
const pkey = new Uint8Array(algorithm.public);
|
|
51
|
+
const len = bytes > this._keyBytes ? bytes : this._keyBytes;
|
|
52
|
+
const secret = modPow(u8ArrayToBigInt(pkey), this._privateKeyBigInt, this._pBigInt);
|
|
53
|
+
return bigIntToU8Array(secret, len).slice(0, len);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/*
|
|
10
|
-
* Performs MD5 hashing on
|
|
10
|
+
* Performs MD5 hashing on an array of bytes, returns an array of bytes
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export function MD5(d) {
|
|
14
|
-
let
|
|
15
|
-
|
|
13
|
+
export async function MD5(d) {
|
|
14
|
+
let s = "";
|
|
15
|
+
for (let i = 0; i < d.length; i++) {
|
|
16
|
+
s += String.fromCharCode(d[i]);
|
|
17
|
+
}
|
|
18
|
+
return M(V(Y(X(s), 8 * s.length)));
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
function M(d) {
|
|
@@ -76,4 +79,4 @@ function add(d, g) {
|
|
|
76
79
|
|
|
77
80
|
function rol(d, g) {
|
|
78
81
|
return d << g | d >>> 32 - g;
|
|
79
|
-
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import Base64 from "../base64.js";
|
|
2
|
+
import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js";
|
|
3
|
+
|
|
4
|
+
export class RSACipher {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._keyLength = 0;
|
|
7
|
+
this._keyBytes = 0;
|
|
8
|
+
this._n = null;
|
|
9
|
+
this._e = null;
|
|
10
|
+
this._d = null;
|
|
11
|
+
this._nBigInt = null;
|
|
12
|
+
this._eBigInt = null;
|
|
13
|
+
this._dBigInt = null;
|
|
14
|
+
this._extractable = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get algorithm() {
|
|
18
|
+
return { name: "RSA-PKCS1-v1_5" };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_base64urlDecode(data) {
|
|
22
|
+
data = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
23
|
+
data = data.padEnd(Math.ceil(data.length / 4) * 4, "=");
|
|
24
|
+
return Base64.decode(data);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_padArray(arr, length) {
|
|
28
|
+
const res = new Uint8Array(length);
|
|
29
|
+
res.set(arr, length - arr.length);
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static async generateKey(algorithm, extractable, _keyUsages) {
|
|
34
|
+
const cipher = new RSACipher;
|
|
35
|
+
await cipher._generateKey(algorithm, extractable);
|
|
36
|
+
return { privateKey: cipher };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async _generateKey(algorithm, extractable) {
|
|
40
|
+
this._keyLength = algorithm.modulusLength;
|
|
41
|
+
this._keyBytes = Math.ceil(this._keyLength / 8);
|
|
42
|
+
const key = await window.crypto.subtle.generateKey(
|
|
43
|
+
{
|
|
44
|
+
name: "RSA-OAEP",
|
|
45
|
+
modulusLength: algorithm.modulusLength,
|
|
46
|
+
publicExponent: algorithm.publicExponent,
|
|
47
|
+
hash: {name: "SHA-256"},
|
|
48
|
+
},
|
|
49
|
+
true, ["encrypt", "decrypt"]);
|
|
50
|
+
const privateKey = await window.crypto.subtle.exportKey("jwk", key.privateKey);
|
|
51
|
+
this._n = this._padArray(this._base64urlDecode(privateKey.n), this._keyBytes);
|
|
52
|
+
this._nBigInt = u8ArrayToBigInt(this._n);
|
|
53
|
+
this._e = this._padArray(this._base64urlDecode(privateKey.e), this._keyBytes);
|
|
54
|
+
this._eBigInt = u8ArrayToBigInt(this._e);
|
|
55
|
+
this._d = this._padArray(this._base64urlDecode(privateKey.d), this._keyBytes);
|
|
56
|
+
this._dBigInt = u8ArrayToBigInt(this._d);
|
|
57
|
+
this._extractable = extractable;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static async importKey(key, _algorithm, extractable, keyUsages) {
|
|
61
|
+
if (keyUsages.length !== 1 || keyUsages[0] !== "encrypt") {
|
|
62
|
+
throw new Error("only support importing RSA public key");
|
|
63
|
+
}
|
|
64
|
+
const cipher = new RSACipher;
|
|
65
|
+
await cipher._importKey(key, extractable);
|
|
66
|
+
return cipher;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async _importKey(key, extractable) {
|
|
70
|
+
const n = key.n;
|
|
71
|
+
const e = key.e;
|
|
72
|
+
if (n.length !== e.length) {
|
|
73
|
+
throw new Error("the sizes of modulus and public exponent do not match");
|
|
74
|
+
}
|
|
75
|
+
this._keyBytes = n.length;
|
|
76
|
+
this._keyLength = this._keyBytes * 8;
|
|
77
|
+
this._n = new Uint8Array(this._keyBytes);
|
|
78
|
+
this._e = new Uint8Array(this._keyBytes);
|
|
79
|
+
this._n.set(n);
|
|
80
|
+
this._e.set(e);
|
|
81
|
+
this._nBigInt = u8ArrayToBigInt(this._n);
|
|
82
|
+
this._eBigInt = u8ArrayToBigInt(this._e);
|
|
83
|
+
this._extractable = extractable;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async encrypt(_algorithm, message) {
|
|
87
|
+
if (message.length > this._keyBytes - 11) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const ps = new Uint8Array(this._keyBytes - message.length - 3);
|
|
91
|
+
window.crypto.getRandomValues(ps);
|
|
92
|
+
for (let i = 0; i < ps.length; i++) {
|
|
93
|
+
ps[i] = Math.floor(ps[i] * 254 / 255 + 1);
|
|
94
|
+
}
|
|
95
|
+
const em = new Uint8Array(this._keyBytes);
|
|
96
|
+
em[1] = 0x02;
|
|
97
|
+
em.set(ps, 2);
|
|
98
|
+
em.set(message, ps.length + 3);
|
|
99
|
+
const emBigInt = u8ArrayToBigInt(em);
|
|
100
|
+
const c = modPow(emBigInt, this._eBigInt, this._nBigInt);
|
|
101
|
+
return bigIntToU8Array(c, this._keyBytes);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async decrypt(_algorithm, message) {
|
|
105
|
+
if (message.length !== this._keyBytes) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const msgBigInt = u8ArrayToBigInt(message);
|
|
109
|
+
const emBigInt = modPow(msgBigInt, this._dBigInt, this._nBigInt);
|
|
110
|
+
const em = bigIntToU8Array(emBigInt, this._keyBytes);
|
|
111
|
+
if (em[0] !== 0x00 || em[1] !== 0x02) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
let i = 2;
|
|
115
|
+
for (; i < em.length; i++) {
|
|
116
|
+
if (em[i] === 0x00) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (i === em.length) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return em.slice(i + 1, em.length);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async exportKey() {
|
|
127
|
+
if (!this._extractable) {
|
|
128
|
+
throw new Error("key is not extractable");
|
|
129
|
+
}
|
|
130
|
+
return { n: this._n, e: this._e, d: this._d };
|
|
131
|
+
}
|
|
132
|
+
}
|