This repository has been archived by the owner on Aug 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathcrypt.js
273 lines (236 loc) · 8 KB
/
crypt.js
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Namespace with crypto related helper functions.
*/
goog.provide('goog.crypt');
goog.require('goog.asserts');
goog.require('goog.async.throwException');
/**
* Whether to async-throw on unicode input to the legacy versions of
* `goog.crypt.stringToByteArray` (i.e. when `throwSync` is false).
* NOTE: The default will change to `true` soon, after notifying users.
* @define {boolean}
*/
goog.crypt.ASYNC_THROW_ON_UNICODE_TO_BYTE =
goog.define('goog.crypt.ASYNC_THROW_ON_UNICODE_TO_BYTE', goog.DEBUG);
/**
* Test-only stub to make our use of async.throwException more testable.
* @const
*/
goog.crypt.TEST_ONLY = {};
/** Remappable alias. */
goog.crypt.TEST_ONLY.throwException = goog.async.throwException;
/** Configurable so that we can test the async-throw behavior. */
goog.crypt.TEST_ONLY.alwaysThrowSynchronously = goog.DEBUG;
/**
* Turns a string into an array of bytes; a "byte" being a JS number in the
* range 0-255. Multi-byte characters will throw.
* @param {string} str String value to arrify.
* @return {!Array<number>} Array of numbers corresponding to the
* UCS character codes of each character in str.
*/
goog.crypt.binaryStringToByteArray = function(str) {
return goog.crypt.stringToByteArray(str, true);
};
/**
* Turns a string into an array of bytes; a "byte" being a JS number in the
* range 0-255. Multi-byte characters are written as little-endian.
* @param {string} str String value to arrify.
* @param {boolean=} throwSync Whether to throw synchronously.
* @return {!Array<number>} Array of numbers corresponding to the
* UCS character codes of each character in str.
*/
goog.crypt.stringToByteArray = function(str, throwSync) {
'use strict';
var output = [], p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
// NOTE: c <= 0xffff since JavaScript strings are UTF-16.
if (c > 0xff) {
var err = new Error('go/unicode-to-byte-error');
// NOTE: fail faster in debug to catch errors reliably in tests.
if (goog.crypt.TEST_ONLY.alwaysThrowSynchronously || throwSync) {
throw err;
} else if (goog.crypt.ASYNC_THROW_ON_UNICODE_TO_BYTE) {
goog.crypt.TEST_ONLY.throwException(err);
}
output[p++] = c & 0xff;
c >>= 8;
}
output[p++] = c;
}
return output;
};
/**
* Turns an array of numbers into the string given by the concatenation of the
* characters to which the numbers correspond.
* @param {!Uint8Array|!Array<number>} bytes Array of numbers representing
* characters.
* @return {string} Stringification of the array.
*/
goog.crypt.byteArrayToString = function(bytes) {
return goog.crypt.byteArrayToBinaryString(bytes);
};
/**
* Turns an array of numbers into the string given by the concatenation of the
* characters to which the numbers correspond.
* @param {!Uint8Array|!Array<number>} bytes Array of numbers representing
* characters.
* @return {string} Stringification of the array.
*/
goog.crypt.byteArrayToBinaryString = function(bytes) {
'use strict';
var CHUNK_SIZE = 8192;
// Special-case the simple case for speed's sake.
if (bytes.length <= CHUNK_SIZE) {
return String.fromCharCode.apply(null, bytes);
}
// The remaining logic splits conversion by chunks since
// Function#apply() has a maximum parameter count.
// See discussion: http://goo.gl/LrWmZ9
var str = '';
for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
var chunk = Array.prototype.slice.call(bytes, i, i + CHUNK_SIZE);
str += String.fromCharCode.apply(null, chunk);
}
return str;
};
/**
* Turns an array of numbers into the hex string given by the concatenation of
* the hex values to which the numbers correspond.
* @param {Uint8Array|Array<number>} array Array of numbers representing
* characters.
* @param {string=} opt_separator Optional separator between values
* @return {string} Hex string.
*/
goog.crypt.byteArrayToHex = function(array, opt_separator) {
'use strict';
return Array.prototype.map
.call(
array,
function(numByte) {
'use strict';
var hexByte = numByte.toString(16);
return hexByte.length > 1 ? hexByte : '0' + hexByte;
})
.join(opt_separator || '');
};
/**
* Converts a hex string into an integer array.
* @param {string} hexString Hex string of 16-bit integers (two characters
* per integer).
* @return {!Array<number>} Array of {0,255} integers for the given string.
*/
goog.crypt.hexToByteArray = function(hexString) {
'use strict';
goog.asserts.assert(
hexString.length % 2 == 0, 'Key string length must be multiple of 2');
var arr = [];
for (var i = 0; i < hexString.length; i += 2) {
arr.push(parseInt(hexString.substring(i, i + 2), 16));
}
return arr;
};
/**
* Converts a JS string to a UTF-8 "byte" array.
* @param {string} str 16-bit unicode string.
* @return {!Array<number>} UTF-8 byte array.
*/
goog.crypt.stringToUtf8ByteArray = function(str) {
return goog.crypt.textToByteArray(str);
};
/**
* Converts a JS string to a UTF-8 "byte" array.
* @param {string} str 16-bit unicode string.
* @return {!Array<number>} UTF-8 byte array.
*/
goog.crypt.textToByteArray = function(str) {
'use strict';
// TODO(user): Use native implementations if/when available
var out = [], p = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
};
/**
* Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
* @param {Uint8Array|Array<number>} bytes UTF-8 byte array.
* @return {string} 16-bit Unicode string.
*/
goog.crypt.utf8ByteArrayToString = function(bytes) {
return goog.crypt.byteArrayToText(bytes);
};
/**
* Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
* @param {Uint8Array|Array<number>} bytes UTF-8 byte array.
* @return {string} 16-bit Unicode string.
*/
goog.crypt.byteArrayToText = function(bytes) {
'use strict';
// TODO(user): Use native implementations if/when available
var out = [], pos = 0, c = 0;
while (pos < bytes.length) {
var c1 = bytes[pos++];
if (c1 < 128) {
out[c++] = String.fromCharCode(c1);
} else if (c1 > 191 && c1 < 224) {
var c2 = bytes[pos++];
out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
} else if (c1 > 239 && c1 < 365) {
// Surrogate Pair
var c2 = bytes[pos++];
var c3 = bytes[pos++];
var c4 = bytes[pos++];
var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) -
0x10000;
out[c++] = String.fromCharCode(0xD800 + (u >> 10));
out[c++] = String.fromCharCode(0xDC00 + (u & 1023));
} else {
var c2 = bytes[pos++];
var c3 = bytes[pos++];
out[c++] =
String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
}
}
return out.join('');
};
/**
* XOR two byte arrays.
* @param {!Uint8Array|!Int8Array|!Array<number>} bytes1 Byte array 1.
* @param {!Uint8Array|!Int8Array|!Array<number>} bytes2 Byte array 2.
* @return {!Array<number>} Resulting XOR of the two byte arrays.
*/
goog.crypt.xorByteArray = function(bytes1, bytes2) {
'use strict';
goog.asserts.assert(
bytes1.length == bytes2.length, 'XOR array lengths must match');
var result = [];
for (var i = 0; i < bytes1.length; i++) {
result.push(bytes1[i] ^ bytes2[i]);
}
return result;
};