OLD | NEW |
| (Empty) |
1 // Tests for wrapKey and unwrapKey round tripping | |
2 | |
3 function run_test() { | |
4 var subtle = self.crypto.subtle; | |
5 | |
6 var wrappers = []; // Things we wrap (and upwrap) keys with | |
7 var keys = []; // Things to wrap and unwrap | |
8 var ecdhPeerKey; // ECDH peer public key needed for non-extractable ECDH
key comparison | |
9 | |
10 // Generate all the keys needed, then iterate over all combinations | |
11 // to test wrapping and unwrapping. | |
12 Promise.all([generateWrappingKeys(), generateKeysToWrap(), generateEcdhPeerK
ey()]) | |
13 .then(function(results) { | |
14 var promises = []; | |
15 wrappers.forEach(function(wrapper) { | |
16 keys.forEach(function(key) { | |
17 promises.push(testWrapping(wrapper, key)); | |
18 }) | |
19 }); | |
20 return Promise.all(promises); | |
21 }, function(err) { | |
22 promise_test(function(test) { | |
23 assert_unreached("A key failed to generate: " + err.name + ": " + er
r.message) | |
24 }, "Could not run all tests") | |
25 }) | |
26 .then(function() { | |
27 done(); | |
28 }, function(err) { | |
29 promise_test(function(test) { | |
30 assert_unreached("A test failed to run: " + err.name + ": " + err.me
ssage) | |
31 }, "Could not run all tests") | |
32 }); | |
33 | |
34 | |
35 function generateWrappingKeys() { | |
36 // There are five algorithms that can be used for wrapKey/unwrapKey. | |
37 // Generate one key with typical parameters for each kind. | |
38 // | |
39 // Note: we don't need cryptographically strong parameters for things | |
40 // like IV - just any legal value will do. | |
41 var parameters = [ | |
42 { | |
43 name: "RSA-OAEP", | |
44 generateParameters: {name: "RSA-OAEP", modulusLength: 4096, publ
icExponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, | |
45 wrapParameters: {name: "RSA-OAEP", label: new Uint8Array(8)} | |
46 }, | |
47 { | |
48 name: "AES-CTR", | |
49 generateParameters: {name: "AES-CTR", length: 128}, | |
50 wrapParameters: {name: "AES-CTR", counter: new Uint8Array(16), l
ength: 64} | |
51 }, | |
52 { | |
53 name: "AES-CBC", | |
54 generateParameters: {name: "AES-CBC", length: 128}, | |
55 wrapParameters: {name: "AES-CBC", iv: new Uint8Array(16)} | |
56 }, | |
57 { | |
58 name: "AES-GCM", | |
59 generateParameters: {name: "AES-GCM", length: 128}, | |
60 wrapParameters: {name: "AES-GCM", iv: new Uint8Array(16), additi
onalData: new Uint8Array(16), tagLength: 64} | |
61 }, | |
62 { | |
63 name: "AES-KW", | |
64 generateParameters: {name: "AES-KW", length: 128}, | |
65 wrapParameters: {name: "AES-KW"} | |
66 } | |
67 ]; | |
68 | |
69 return Promise.all(parameters.map(function(params) { | |
70 return subtle.generateKey(params.generateParameters, true, ["wrapKey
", "unwrapKey"]) | |
71 .then(function(key) { | |
72 var wrapper; | |
73 if (params.name === "RSA-OAEP") { // we have a key pair, not jus
t a key | |
74 wrapper = {wrappingKey: key.publicKey, unwrappingKey: key.pr
ivateKey, parameters: params}; | |
75 } else { | |
76 wrapper = {wrappingKey: key, unwrappingKey: key, parameters:
params}; | |
77 } | |
78 wrappers.push(wrapper); | |
79 return true; | |
80 }) | |
81 })); | |
82 } | |
83 | |
84 | |
85 function generateKeysToWrap() { | |
86 var parameters = [ | |
87 {algorithm: {name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicE
xponent: new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], pub
licUsages: ["verify"]}, | |
88 {algorithm: {name: "RSA-PSS", modulusLength: 1024, publicExponent: n
ew Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["sign"], publicUsages:
["verify"]}, | |
89 {algorithm: {name: "RSA-OAEP", modulusLength: 1024, publicExponent:
new Uint8Array([1,0,1]), hash: "SHA-256"}, privateUsages: ["decrypt"], publicUsa
ges: ["encrypt"]}, | |
90 {algorithm: {name: "ECDSA", namedCurve: "P-256"}, privateUsages: ["s
ign"], publicUsages: ["verify"]}, | |
91 {algorithm: {name: "ECDH", namedCurve: "P-256"}, privateUsages: ["de
riveBits"], publicUsages: []}, | |
92 {algorithm: {name: "AES-CTR", length: 128}, usages: ["encrypt", "dec
rypt"]}, | |
93 {algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "dec
rypt"]}, | |
94 {algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "dec
rypt"]}, | |
95 {algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwr
apKey"]}, | |
96 {algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["
sign", "verify"]} | |
97 ]; | |
98 | |
99 return Promise.all(parameters.map(function(params) { | |
100 var usages; | |
101 if ("usages" in params) { | |
102 usages = params.usages; | |
103 } else { | |
104 usages = params.publicUsages.concat(params.privateUsages); | |
105 } | |
106 | |
107 return subtle.generateKey(params.algorithm, true, usages) | |
108 .then(function(result) { | |
109 if (result.constructor === CryptoKey) { | |
110 keys.push({name: params.algorithm.name, algorithm: params.al
gorithm, usages: params.usages, key: result}); | |
111 } else { | |
112 keys.push({name: params.algorithm.name + " public key", algo
rithm: params.algorithm, usages: params.publicUsages, key: result.publicKey}); | |
113 keys.push({name: params.algorithm.name + " private key", alg
orithm: params.algorithm, usages: params.privateUsages, key: result.privateKey})
; | |
114 } | |
115 return true; | |
116 }); | |
117 })); | |
118 } | |
119 | |
120 function generateEcdhPeerKey() { | |
121 return subtle.generateKey({name: "ECDH", namedCurve: "P-256"},true,["der
iveBits"]) | |
122 .then(function(result){ | |
123 ecdhPeerKey = result.publicKey; | |
124 }); | |
125 } | |
126 | |
127 // Can we successfully "round-trip" (wrap, then unwrap, a key)? | |
128 function testWrapping(wrapper, toWrap) { | |
129 var formats; | |
130 | |
131 if (toWrap.name.includes("private")) { | |
132 formats = ["pkcs8", "jwk"]; | |
133 } else if (toWrap.name.includes("public")) { | |
134 formats = ["spki", "jwk"] | |
135 } else { | |
136 formats = ["raw", "jwk"] | |
137 } | |
138 | |
139 return Promise.all(formats.map(function(fmt) { | |
140 var originalExport; | |
141 return subtle.exportKey(fmt, toWrap.key).then(function(exportedKey)
{ | |
142 originalExport = exportedKey; | |
143 if (wrappingIsPossible(originalExport, wrapper.parameters.name))
{ | |
144 promise_test(function(test) { | |
145 return subtle.wrapKey(fmt, toWrap.key, wrapper.wrappingK
ey, wrapper.parameters.wrapParameters) | |
146 .then(function(wrappedResult) { | |
147 return subtle.unwrapKey(fmt, wrappedResult, wrapper.
unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true, toWrap
.usages); | |
148 }).then(function(unwrappedResult) { | |
149 assert_true(unwrappedResult.extractable, "Unwrapped
result is extractable"); | |
150 return subtle.exportKey(fmt, unwrappedResult) | |
151 }).then(function(roundTripExport) { | |
152 assert_true(equalExport(originalExport, roundTripExp
ort), "Post-wrap export matches original export"); | |
153 }, function(err) { | |
154 assert_unreached("Round trip for extractable key thr
ew an error - " + err.name + ': "' + err.message + '"'); | |
155 }); | |
156 }, "Can wrap and unwrap " + toWrap.name + " keys using " + f
mt + " and " + wrapper.parameters.name); | |
157 | |
158 if (canCompareNonExtractableKeys(toWrap.key)) { | |
159 promise_test(function(test){ | |
160 return subtle.wrapKey(fmt, toWrap.key, wrapper.wrapp
ingKey, wrapper.parameters.wrapParameters) | |
161 .then(function(wrappedResult) { | |
162 return subtle.unwrapKey(fmt, wrappedResult, wrap
per.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false, t
oWrap.usages); | |
163 }).then(function(unwrappedResult){ | |
164 assert_false(unwrappedResult.extractable, "Unwra
pped result is non-extractable"); | |
165 return equalKeys(toWrap.key, unwrappedResult); | |
166 }).then(function(result){ | |
167 assert_true(result, "Unwrapped key matches origi
nal"); | |
168 }).catch(function(err){ | |
169 assert_unreached("Round trip for key unwrapped n
on-extractable threw an error - " + err.name + ': "' + err.message + '"'); | |
170 }); | |
171 }, "Can wrap and unwrap " + toWrap.name + " keys as non-
extractable using " + fmt + " and " + wrapper.parameters.name); | |
172 | |
173 if (fmt === "jwk") { | |
174 promise_test(function(test){ | |
175 var wrappedKey; | |
176 return wrapAsNonExtractableJwk(toWrap.key,wrappe
r).then(function(wrappedResult){ | |
177 wrappedKey = wrappedResult; | |
178 return subtle.unwrapKey("jwk", wrappedKey, w
rapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, false
, toWrap.usages); | |
179 }).then(function(unwrappedResult){ | |
180 assert_false(unwrappedResult.extractable, "U
nwrapped key is non-extractable"); | |
181 return equalKeys(toWrap.key,unwrappedResult)
; | |
182 }).then(function(result){ | |
183 assert_true(result, "Unwrapped key matches o
riginal"); | |
184 }).catch(function(err){ | |
185 assert_unreached("Round trip for non-extract
able key threw an error - " + err.name + ': "' + err.message + '"'); | |
186 }).then(function(){ | |
187 return subtle.unwrapKey("jwk", wrappedKey, w
rapper.unwrappingKey, wrapper.parameters.wrapParameters, toWrap.algorithm, true,
toWrap.usages); | |
188 }).then(function(unwrappedResult){ | |
189 assert_unreached("Unwrapping a non-extractab
le JWK as extractable should fail"); | |
190 }).catch(function(err){ | |
191 assert_equals(err.name, "DataError", "Unwrap
ping a non-extractable JWK as extractable fails with DataError"); | |
192 }); | |
193 }, "Can unwrap " + toWrap.name + " non-extractable k
eys using jwk and " + wrapper.parameters.name); | |
194 } | |
195 } | |
196 } | |
197 }); | |
198 })); | |
199 } | |
200 | |
201 // Implement key wrapping by hand to wrap a key as non-extractable JWK | |
202 function wrapAsNonExtractableJwk(key, wrapper){ | |
203 var wrappingKey = wrapper.wrappingKey, | |
204 encryptKey; | |
205 | |
206 return subtle.exportKey("jwk",wrappingKey) | |
207 .then(function(jwkWrappingKey){ | |
208 // Update the key generation parameters to work as key import parame
ters | |
209 var params = Object.create(wrapper.parameters.generateParameters); | |
210 if(params.name === "AES-KW") { | |
211 params.name = "AES-CBC"; | |
212 jwkWrappingKey.alg = "A"+params.length+"CBC"; | |
213 } else if (params.name === "RSA-OAEP") { | |
214 params.modulusLength = undefined; | |
215 params.publicExponent = undefined; | |
216 } | |
217 jwkWrappingKey.key_ops = ["encrypt"]; | |
218 return subtle.importKey("jwk", jwkWrappingKey, params, true, ["encry
pt"]); | |
219 }).then(function(importedWrappingKey){ | |
220 encryptKey = importedWrappingKey; | |
221 return subtle.exportKey("jwk",key); | |
222 }).then(function(exportedKey){ | |
223 exportedKey.ext = false; | |
224 var jwk = JSON.stringify(exportedKey) | |
225 if (wrappingKey.algorithm.name === "AES-KW") { | |
226 return aeskw(encryptKey, str2ab(jwk.slice(0,-1) + " ".repeat(jwk
.length%8 ? 8-jwk.length%8 : 0) + "}")); | |
227 } else { | |
228 return subtle.encrypt(wrapper.parameters.wrapParameters,encryptK
ey,str2ab(jwk)); | |
229 } | |
230 }); | |
231 } | |
232 | |
233 | |
234 // RSA-OAEP can only wrap relatively small payloads. AES-KW can only | |
235 // wrap payloads a multiple of 8 bytes long. | |
236 // | |
237 // Note that JWK payloads will be converted to ArrayBuffer for wrapping, | |
238 // and should automatically be padded if needed for AES-KW. | |
239 function wrappingIsPossible(exportedKey, algorithmName) { | |
240 if ("byteLength" in exportedKey && algorithmName === "AES-KW") { | |
241 return exportedKey.byteLength % 8 === 0; | |
242 } | |
243 | |
244 if ("byteLength" in exportedKey && algorithmName === "RSA-OAEP") { | |
245 // RSA-OAEP can only encrypt payloads with lengths shorter | |
246 // than modulusLength - 2*hashLength - 1 bytes long. For | |
247 // a 4096 bit modulus and SHA-256, that comes to | |
248 // 4096/8 - 2*(256/8) - 1 = 512 - 2*32 - 1 = 447 bytes. | |
249 return exportedKey.byteLength <= 446; | |
250 } | |
251 | |
252 if ("kty" in exportedKey && algorithmName === "RSA-OAEP") { | |
253 return JSON.stringify(exportedKey).length <= 478; | |
254 } | |
255 | |
256 return true; | |
257 } | |
258 | |
259 | |
260 // Helper methods follow: | |
261 | |
262 // Are two exported keys equal | |
263 function equalExport(originalExport, roundTripExport) { | |
264 if ("byteLength" in originalExport) { | |
265 return equalBuffers(originalExport, roundTripExport); | |
266 } else { | |
267 return equalJwk(originalExport, roundTripExport); | |
268 } | |
269 } | |
270 | |
271 // Are two array buffers the same? | |
272 function equalBuffers(a, b) { | |
273 if (a.byteLength !== b.byteLength) { | |
274 return false; | |
275 } | |
276 | |
277 var aBytes = new Uint8Array(a); | |
278 var bBytes = new Uint8Array(b); | |
279 | |
280 for (var i=0; i<a.byteLength; i++) { | |
281 if (aBytes[i] !== bBytes[i]) { | |
282 return false; | |
283 } | |
284 } | |
285 | |
286 return true; | |
287 } | |
288 | |
289 // Are two Jwk objects "the same"? That is, does the object returned include | |
290 // matching values for each property that was expected? It's okay if the | |
291 // returned object has extra methods; they aren't checked. | |
292 function equalJwk(expected, got) { | |
293 var fields = Object.keys(expected); | |
294 var fieldName; | |
295 | |
296 for(var i=0; i<fields.length; i++) { | |
297 fieldName = fields[i]; | |
298 if (!(fieldName in got)) { | |
299 return false; | |
300 } | |
301 if (objectToString(expected[fieldName]) !== objectToString(got[field
Name])) { | |
302 return false; | |
303 } | |
304 } | |
305 | |
306 return true; | |
307 } | |
308 | |
309 // Character representation of any object we may use as a parameter. | |
310 function objectToString(obj) { | |
311 var keyValuePairs = []; | |
312 | |
313 if (Array.isArray(obj)) { | |
314 return "[" + obj.map(function(elem){return objectToString(elem);}).j
oin(", ") + "]"; | |
315 } else if (typeof obj === "object") { | |
316 Object.keys(obj).sort().forEach(function(keyName) { | |
317 keyValuePairs.push(keyName + ": " + objectToString(obj[keyName])
); | |
318 }); | |
319 return "{" + keyValuePairs.join(", ") + "}"; | |
320 } else if (typeof obj === "undefined") { | |
321 return "undefined"; | |
322 } else { | |
323 return obj.toString(); | |
324 } | |
325 | |
326 var keyValuePairs = []; | |
327 | |
328 Object.keys(obj).sort().forEach(function(keyName) { | |
329 var value = obj[keyName]; | |
330 if (typeof value === "object") { | |
331 value = objectToString(value); | |
332 } else if (typeof value === "array") { | |
333 value = "[" + value.map(function(elem){return objectToString(ele
m);}).join(", ") + "]"; | |
334 } else { | |
335 value = value.toString(); | |
336 } | |
337 | |
338 keyValuePairs.push(keyName + ": " + value); | |
339 }); | |
340 | |
341 return "{" + keyValuePairs.join(", ") + "}"; | |
342 } | |
343 | |
344 // Can we compare key values by using them | |
345 function canCompareNonExtractableKeys(key){ | |
346 if (key.usages.indexOf("decrypt") !== -1) { | |
347 return true; | |
348 } | |
349 if (key.usages.indexOf("sign") !== -1) { | |
350 return true; | |
351 } | |
352 if (key.usages.indexOf("wrapKey") !== -1) { | |
353 return true; | |
354 } | |
355 if (key.usages.indexOf("deriveBits") !== -1) { | |
356 return true; | |
357 } | |
358 return false; | |
359 } | |
360 | |
361 // Compare two keys by using them (works for non-extractable keys) | |
362 function equalKeys(expected, got){ | |
363 if ( expected.algorithm.name !== got.algorithm.name ) { | |
364 return Promise.resolve(false); | |
365 } | |
366 | |
367 var cryptParams, signParams, wrapParams, deriveParams; | |
368 switch(expected.algorithm.name){ | |
369 case "AES-CTR" : | |
370 cryptParams = {name: "AES-CTR", counter: new Uint8Array(16), len
gth: 64}; | |
371 break; | |
372 case "AES-CBC" : | |
373 cryptParams = {name: "AES-CBC", iv: new Uint8Array(16) }; | |
374 break; | |
375 case "AES-GCM" : | |
376 cryptParams = {name: "AES-GCM", iv: new Uint8Array(16) }; | |
377 break; | |
378 case "RSA-OAEP" : | |
379 cryptParams = {name: "RSA-OAEP", label: new Uint8Array(8) }; | |
380 break; | |
381 case "RSASSA-PKCS1-v1_5" : | |
382 signParams = {name: "RSASSA-PKCS1-v1_5"}; | |
383 break; | |
384 case "RSA-PSS" : | |
385 signParams = {name: "RSA-PSS", saltLength: 32 }; | |
386 break; | |
387 case "ECDSA" : | |
388 signParams = {name: "ECDSA", hash: "SHA-256"}; | |
389 break; | |
390 case "HMAC" : | |
391 signParams = {name: "HMAC"}; | |
392 break; | |
393 case "AES-KW" : | |
394 wrapParams = {name: "AES-KW"}; | |
395 break; | |
396 case "ECDH" : | |
397 deriveParams = {name: "ECDH", public: ecdhPeerKey}; | |
398 break; | |
399 default: | |
400 throw new Error("Unsupported algorithm for key comparison"); | |
401 } | |
402 | |
403 if (cryptParams) { | |
404 return subtle.exportKey("jwk",expected) | |
405 .then(function(jwkExpectedKey){ | |
406 if (expected.algorithm.name === "RSA-OAEP") { | |
407 ["d","p","q","dp","dq","qi","oth"].forEach(function(field){
delete jwkExpectedKey[field]; }); | |
408 } | |
409 jwkExpectedKey.key_ops = ["encrypt"]; | |
410 return subtle.importKey("jwk", jwkExpectedKey, expected.algorith
m, true, ["encrypt"]); | |
411 }).then(function(expectedEncryptKey){ | |
412 return subtle.encrypt(cryptParams, expectedEncryptKey, new Uint8
Array(32)); | |
413 }).then(function(encryptedData){ | |
414 return subtle.decrypt(cryptParams, got, encryptedData); | |
415 }).then(function(decryptedData){ | |
416 var result = new Uint8Array(decryptedData); | |
417 return !result.some(x => x); | |
418 }); | |
419 } else if (signParams) { | |
420 var verifyKey; | |
421 return subtle.exportKey("jwk",expected) | |
422 .then(function(jwkExpectedKey){ | |
423 if (expected.algorithm.name === "RSA-PSS" || expected.algorithm.
name === "RSASSA-PKCS1-v1_5") { | |
424 ["d","p","q","dp","dq","qi","oth"].forEach(function(field){
delete jwkExpectedKey[field]; }); | |
425 } | |
426 if (expected.algorithm.name === "ECDSA") { | |
427 delete jwkExpectedKey["d"]; | |
428 } | |
429 jwkExpectedKey.key_ops = ["verify"]; | |
430 return subtle.importKey("jwk", jwkExpectedKey, expected.algorith
m, true, ["verify"]); | |
431 }).then(function(expectedVerifyKey){ | |
432 verifyKey = expectedVerifyKey; | |
433 return subtle.sign(signParams, got, new Uint8Array(32)); | |
434 }).then(function(signature){ | |
435 return subtle.verify(signParams, verifyKey, signature, new Uint8
Array(32)); | |
436 }); | |
437 } else if (wrapParams) { | |
438 var aKeyToWrap, wrappedWithExpected; | |
439 return subtle.importKey("raw", new Uint8Array(16), "AES-CBC", true,
["encrypt"]) | |
440 .then(function(key){ | |
441 aKeyToWrap = key; | |
442 return subtle.wrapKey("raw", aKeyToWrap, expected, wrapParams); | |
443 }).then(function(wrapResult){ | |
444 wrappedWithExpected = Array.from((new Uint8Array(wrapResult)).va
lues()); | |
445 return subtle.wrapKey("raw", aKeyToWrap, got, wrapParams); | |
446 }).then(function(wrapResult){ | |
447 var wrappedWithGot = Array.from((new Uint8Array(wrapResult)).val
ues()); | |
448 return wrappedWithGot.every((x,i) => x === wrappedWithExpected[i
]); | |
449 }); | |
450 } else { | |
451 var expectedDerivedBits; | |
452 return subtle.deriveBits(deriveParams, expected, 128) | |
453 .then(function(result){ | |
454 expectedDerivedBits = Array.from((new Uint8Array(result)).values
()); | |
455 return subtle.deriveBits(deriveParams, got, 128); | |
456 }).then(function(result){ | |
457 var gotDerivedBits = Array.from((new Uint8Array(result)).values(
)); | |
458 return gotDerivedBits.every((x,i) => x === expectedDerivedBits[i
]); | |
459 }); | |
460 } | |
461 } | |
462 | |
463 // Raw AES encryption | |
464 function aes( k, p ) { | |
465 return subtle.encrypt({name: "AES-CBC", iv: new Uint8Array(16) }, k, p).
then(function(ciphertext){return ciphertext.slice(0,16);}); | |
466 } | |
467 | |
468 // AES Key Wrap | |
469 function aeskw(key, data) { | |
470 if (data.byteLength % 8 !== 0) { | |
471 throw new Error("AES Key Wrap data must be a multiple of 8 bytes in
length"); | |
472 } | |
473 | |
474 var A = Uint8Array.from([0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
0, 0, 0, 0, 0, 0, 0, 0]), | |
475 Av = new DataView(A.buffer), | |
476 R = [], | |
477 n = data.byteLength / 8; | |
478 | |
479 for(var i = 0; i<data.byteLength; i+=8) { | |
480 R.push(new Uint8Array(data.slice(i,i+8))); | |
481 } | |
482 | |
483 function aeskw_step(j, i, final, B) { | |
484 A.set(new Uint8Array(B.slice(0,8))); | |
485 Av.setUint32(4,Av.getUint32(4) ^ (n*j+i+1)); | |
486 R[i] = new Uint8Array(B.slice(8,16)); | |
487 if (final) { | |
488 R.unshift(A.slice(0,8)); | |
489 var result = new Uint8Array(R.length * 8); | |
490 R.forEach(function(Ri,i){ result.set(Ri, i*8); }); | |
491 return result; | |
492 } else { | |
493 A.set(R[(i+1)%n],8); | |
494 return aes(key,A); | |
495 } | |
496 } | |
497 | |
498 var p = new Promise(function(resolve){ | |
499 A.set(R[0],8); | |
500 resolve(aes(key,A)); | |
501 }); | |
502 | |
503 for(var j=0;j<6;++j) { | |
504 for(var i=0;i<n;++i) { | |
505 p = p.then(aeskw_step.bind(undefined, j, i,j===5 && i===(n-1))); | |
506 } | |
507 } | |
508 | |
509 return p; | |
510 } | |
511 | |
512 function str2ab(str) { return Uint8Array.from( str.split(''), functio
n(s){return s.charCodeAt(0)} ); } | |
513 function ab2str(ab) { return String.fromCharCode.apply(null, new Uin
t8Array(ab)); } | |
514 | |
515 | |
516 } | |
OLD | NEW |