@breakside/jskit 2023.19.0 → 2023.21.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.
Files changed (59) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/JSURLResponse.js +1 -1
  7. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  8. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  9. package/Frameworks/SecurityKit.jsframework/JS/SECCipher+HTML.js +31 -44
  10. package/Frameworks/SecurityKit.jsframework/JS/SECCipher+Node.js +16 -38
  11. package/Frameworks/SecurityKit.jsframework/JS/SECCipher.js +33 -3
  12. package/Frameworks/SecurityKit.jsframework/JS/SECHMAC.js +17 -0
  13. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  14. package/Info.json +2 -2
  15. package/Node/HTMLBuilder.js +76 -20
  16. package/Node/io.breakside.jskit-bundle.js +2 -2
  17. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  18. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  19. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  20. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  21. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  22. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  23. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  24. package/Root/Frameworks/DOM/Info.yaml +1 -1
  25. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  26. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  27. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  28. package/Root/Frameworks/Foundation/JSURLResponse.js +1 -1
  29. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  30. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  31. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  32. package/Root/Frameworks/NotificationKit/Info.yaml +2 -1
  33. package/Root/Frameworks/NotificationKit/NKHTMLUserNotificationCenter.js +59 -18
  34. package/Root/Frameworks/NotificationKit/NKUserNotification.js +12 -1
  35. package/Root/Frameworks/NotificationKit/NKUserNotificationCenter.js +5 -0
  36. package/Root/Frameworks/NotificationKit/NKWebPushService.js +145 -38
  37. package/Root/Frameworks/NotificationKit/NotificationKit+Node.js +19 -0
  38. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  39. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  40. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  41. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  42. package/Root/Frameworks/SecurityKit/SECCipher+HTML.js +31 -44
  43. package/Root/Frameworks/SecurityKit/SECCipher+Node.js +16 -38
  44. package/Root/Frameworks/SecurityKit/SECCipher.js +33 -3
  45. package/Root/Frameworks/SecurityKit/SECHMAC.js +17 -0
  46. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  47. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  48. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  49. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  50. package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +1 -1
  51. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +8 -1
  52. package/Root/Frameworks/UIKit/UIMenuView.js +4 -0
  53. package/Root/Frameworks/UIKit/UIWindowServer.js +7 -13
  54. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  55. package/Root/Templates/html/${PROJECT_NAME}/www/${PROJECT_NAME}.webmanifest +9 -0
  56. package/Root/Templates/html/${PROJECT_NAME}/www/HTMLAppBootstrapper.js +7 -1
  57. package/Root/Templates/html/${PROJECT_NAME}/www/index.html +1 -0
  58. package/Root/Templates/html/${PROJECT_NAME}/www/service-worker.js +25 -1
  59. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
4
- "JSBundleVersion": "2023.19.0",
4
+ "JSBundleVersion": "2023.21.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "DOM+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
12
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
13
13
  }
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.DOM",
6
- "JSBundleVersion": "2023.19.0",
6
+ "JSBundleVersion": "2023.21.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.DOM'] = {
11
11
  "node": "DOM+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
14
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
4
- "JSBundleVersion": "2023.19.0",
4
+ "JSBundleVersion": "2023.21.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
8
8
  "html": "FontKit+HTML.js"
9
9
  },
10
10
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
11
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
11
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
12
12
  }
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.JSKit.FontKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.FontKit",
6
- "JSBundleVersion": "2023.19.0",
6
+ "JSBundleVersion": "2023.21.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
10
10
  "html": "FontKit+HTML.js"
11
11
  },
12
12
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
13
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
13
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
14
14
  },
15
15
  "Resources": [],
16
16
  "ResourceLookup": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
4
- "JSBundleVersion": "2023.19.0",
4
+ "JSBundleVersion": "2023.21.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "Foundation+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
12
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
13
13
  }
@@ -175,7 +175,7 @@ JSURLResponse.error = function(statusCode, object){
175
175
 
176
176
  JSGlobalObject.JSURLResponseError = function(response){
177
177
  if (this === undefined){
178
- return new JSURLResponseError();
178
+ return new JSURLResponseError(response);
179
179
  }
180
180
  this.name = "JSURLResponseError";
181
181
  if (response instanceof JSURLResponseError){
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.Foundation",
6
- "JSBundleVersion": "2023.19.0",
6
+ "JSBundleVersion": "2023.21.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
11
11
  "node": "Foundation+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
14
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
15
15
  },
16
16
  "Resources": [
17
17
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "JSBundleType": "framework",
3
3
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
4
- "JSBundleVersion": "2023.19.0",
4
+ "JSBundleVersion": "2023.21.0",
5
5
  "JSDevelopmentLanguage": "en",
6
6
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
7
7
  "JSBundleEnvironments": {
@@ -9,5 +9,5 @@
9
9
  "node": "SecurityKit+Node.js"
10
10
  },
11
11
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
12
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
12
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
13
13
  }
@@ -195,14 +195,6 @@ SECCipherAESCounter.definePropertiesFromExtensions({
195
195
  counter: JSData.initWithLength(16),
196
196
  length: 64
197
197
  };
198
- this.decryptedMessageId =
199
- (nonce[6] << 48)
200
- | (nonce[5] << 40)
201
- | (nonce[4] << 32)
202
- | (nonce[3] << 24)
203
- | (nonce[2] << 16)
204
- | (nonce[1] << 8)
205
- | (nonce[0]);
206
198
  nonce.copyTo(algorithm.counter, 0);
207
199
  return algorithm;
208
200
  },
@@ -282,55 +274,41 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
282
274
 
283
275
  htmlAlgorithmName: HTMLCryptoAlgorithmNames.aesGCM,
284
276
 
285
- _getHTMLEncryptAlgorithm: function(){
286
- var nonce = JSData.initWithArray([
287
- 1,
288
- ((this.encryptedMessageId / 0x100000000) >> 16) & 0xFF,
289
- ((this.encryptedMessageId / 0x100000000) >> 8) & 0xFF,
290
- (this.encryptedMessageId / 0x100000000) & 0xFF,
291
- (this.encryptedMessageId >> 24) & 0xFF,
292
- (this.encryptedMessageId >> 16) & 0xFF,
293
- (this.encryptedMessageId >> 8) & 0xFF,
294
- this.encryptedMessageId & 0xF
295
- ]);
277
+ _getHTMLEncryptAlgorithm: function(nonce, _ivLength){
278
+ if (_ivLength === undefined){
279
+ _ivLength = nonce.length;
280
+ }
296
281
  var algorithm = {
297
282
  name: HTMLCryptoAlgorithmNames.aesGCM,
298
- iv: JSData.initWithLength(16),
283
+ iv: JSData.initWithLength(_ivLength),
299
284
  tagLength: 128
300
285
  };
301
286
  nonce.copyTo(algorithm.iv, 0);
302
287
  return algorithm;
303
288
  },
304
289
 
305
- _getHTMLDecryptAlgorithm: function(prefixedData){
306
- var nonce = prefixedData.subdataInRange(JSRange(0, 8));
290
+ _getHTMLDecryptAlgorithm: function(prefixedData, nonceLength, _ivLength){
291
+ if (nonceLength === undefined){
292
+ nonceLength = 8;
293
+ }
294
+ if (_ivLength === undefined){
295
+ _ivLength = nonceLength;
296
+ }
297
+ var nonce = prefixedData.subdataInRange(JSRange(0, nonceLength));
307
298
  var algorithm = {
308
299
  name: HTMLCryptoAlgorithmNames.aesGCM,
309
- iv: JSData.initWithLength(16),
300
+ iv: JSData.initWithLength(_ivLength),
310
301
  tagLength: 128
311
302
  };
312
- this.decryptedMessageId =
313
- (nonce[6] << 48)
314
- | (nonce[5] << 40)
315
- | (nonce[4] << 32)
316
- | (nonce[3] << 24)
317
- | (nonce[2] << 16)
318
- | (nonce[1] << 8)
319
- | (nonce[0]);
320
303
  nonce.copyTo(algorithm.iv, 0);
321
304
  return algorithm;
322
305
  },
323
306
 
324
- encrypt: function(data, key, completion, target){
307
+ encryptWithNonce: function(nonce, data, key, completion, target, _ivLength){
325
308
  if (!completion){
326
309
  completion = Promise.completion(Promise.resolveNonNull);
327
310
  }
328
- if (!this.ensureUniqueMessageID()){
329
- JSRunLoop.main.schedule(completion, target, null);
330
- return completion.promise;
331
- }
332
- var algorithm = this._getHTMLEncryptAlgorithm();
333
- var nonce = algorithm.iv.truncatedToLength(8);
311
+ var algorithm = this._getHTMLEncryptAlgorithm(nonce, _ivLength);
334
312
  crypto.subtle.encrypt(algorithm, key.htmlKey, data).then(function(encrypted){
335
313
  var noncePrefixed = JSData.initWithChunks([nonce, JSData.initWithBuffer(encrypted)]);
336
314
  completion.call(target, noncePrefixed);
@@ -340,12 +318,12 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
340
318
  return completion.promise;
341
319
  },
342
320
 
343
- decrypt: function(data, key, completion, target){
321
+ decryptWithNonceLength: function(nonceLength, data, key, completion, target, _ivLength){
344
322
  if (!completion){
345
323
  completion = Promise.completion(Promise.resolveNonNull);
346
324
  }
347
- var algorithm = this._getHTMLDecryptAlgorithm(data);
348
- var encrypted = data.subdataInRange(JSRange(8, data.length - 8));
325
+ var algorithm = this._getHTMLDecryptAlgorithm(data, nonceLength, _ivLength);
326
+ var encrypted = data.subdataInRange(JSRange(nonceLength, data.length - nonceLength));
349
327
  crypto.subtle.decrypt(algorithm, key.htmlKey, encrypted).then(function(decrypted){
350
328
  var decryptedData = JSData.initWithBuffer(decrypted);
351
329
  completion.call(target, decryptedData);
@@ -363,8 +341,17 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
363
341
  JSRunLoop.main.schedule(completion, target, null);
364
342
  return completion.promise;
365
343
  }
366
- var algorithm = this._getHTMLEncryptAlgorithm();
367
- var nonce = algorithm.iv.truncatedToLength(8);
344
+ var nonce = JSData.initWithArray([
345
+ 1,
346
+ ((this.encryptedMessageId / 0x100000000) >> 16) & 0xFF,
347
+ ((this.encryptedMessageId / 0x100000000) >> 8) & 0xFF,
348
+ (this.encryptedMessageId / 0x100000000) & 0xFF,
349
+ (this.encryptedMessageId >> 24) & 0xFF,
350
+ (this.encryptedMessageId >> 16) & 0xFF,
351
+ (this.encryptedMessageId >> 8) & 0xFF,
352
+ this.encryptedMessageId & 0xF
353
+ ]);
354
+ var algorithm = this._getHTMLEncryptAlgorithm(nonce, 16);
368
355
  crypto.subtle.wrapKey("raw", key.htmlKey, wrappingKey.htmlKey, algorithm).then(function(bytes){
369
356
  var noncePrefixed = JSData.initWithChunks([nonce, JSData.initWithBuffer(bytes)]);
370
357
  completion.call(target, noncePrefixed);
@@ -378,7 +365,7 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
378
365
  if (!completion){
379
366
  completion = Promise.completion(Promise.resolveNonNull);
380
367
  }
381
- var algorithm = this._getHTMLDecryptAlgorithm(wrappedKeyData);
368
+ var algorithm = this._getHTMLDecryptAlgorithm(wrappedKeyData, 8, 16);
382
369
  var unwrappedKeyHTMLAlgorithm = HTMLCryptoAlgorithmNames.fromAlgorithm(unwrappedKeyAlgorithm);
383
370
  wrappedKeyData = wrappedKeyData.subdataInRange(JSRange(8, wrappedKeyData.length - 8));
384
371
  crypto.subtle.unwrapKey("raw", wrappedKeyData, wrappingKey.htmlKey, algorithm, unwrappedKeyHTMLAlgorithm, true, ["encrypt", "decrypt"]).then(function(key){
@@ -182,14 +182,6 @@ SECCipherAESCounter.definePropertiesFromExtensions({
182
182
  return completion.promise;
183
183
  }
184
184
  var nonce = data.subdataInRange(JSRange(0, 8));
185
- this.decryptedMessageId =
186
- (nonce[6] << 48)
187
- | (nonce[5] << 40)
188
- | (nonce[4] << 32)
189
- | (nonce[3] << 24)
190
- | (nonce[2] << 16)
191
- | (nonce[1] << 8)
192
- | (nonce[0]);
193
185
  var iv = JSData.initWithLength(16);
194
186
  nonce.copyTo(iv, 0);
195
187
  var cipher = crypto.createDecipheriv(name, key.keyData, iv);
@@ -217,41 +209,31 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
217
209
  return null;
218
210
  },
219
211
 
220
- encrypt: function(data, key, completion, target){
212
+ encryptWithNonce: function(nonce, data, key, completion, target, _ivLength){
221
213
  if (!completion){
222
214
  completion = Promise.completion(Promise.resolveNonNull);
223
215
  }
224
- if (!this.ensureUniqueMessageID()){
225
- JSRunLoop.main.schedule(completion, target, null);
226
- return completion.promise;
227
- }
228
216
  var name = this._cipherNameForKey(key);
229
217
  if (name === null){
230
218
  JSRunLoop.main.schedule(completion, target, null);
231
219
  return completion.promise;
232
220
  }
233
- var nonce = JSData.initWithArray([
234
- 1,
235
- ((this.encryptedMessageId / 0x100000000) >> 16) & 0xFF,
236
- ((this.encryptedMessageId / 0x100000000) >> 8) & 0xFF,
237
- (this.encryptedMessageId / 0x100000000) & 0xFF,
238
- (this.encryptedMessageId >> 24) & 0xFF,
239
- (this.encryptedMessageId >> 16) & 0xFF,
240
- (this.encryptedMessageId >> 8) & 0xFF,
241
- this.encryptedMessageId & 0xF
242
- ]);
243
- var iv = JSData.initWithLength(16);
221
+ if (_ivLength === undefined){
222
+ _ivLength = nonce.length;
223
+ }
224
+ var iv = JSData.initWithLength(_ivLength);
244
225
  nonce.copyTo(iv, 0);
245
226
  var cipher = crypto.createCipheriv(name, key.keyData, iv);
246
227
  var chunks = [nonce, cipher.update(data), cipher.final()];
247
- var tag = JSData.initWithLength(16);
228
+ var tagLength = 16;
229
+ var tag = JSData.initWithLength(tagLength);
248
230
  cipher.getAuthTag().copyTo(tag, 0);
249
231
  chunks.push(tag);
250
232
  JSRunLoop.main.schedule(completion, target, JSData.initWithChunks(chunks));
251
233
  return completion.promise;
252
234
  },
253
235
 
254
- decrypt: function(data, key, completion, target){
236
+ decryptWithNonceLength: function(nonceLength, data, key, completion, target, _ivLength){
255
237
  if (!completion){
256
238
  completion = Promise.completion(Promise.resolveNonNull);
257
239
  }
@@ -261,21 +243,17 @@ SECCipherAESGaloisCounterMode.definePropertiesFromExtensions({
261
243
  JSRunLoop.main.schedule(completion, target, null);
262
244
  return completion.promise;
263
245
  }
264
- var nonce = data.subdataInRange(JSRange(0, 8));
265
- this.decryptedMessageId =
266
- (nonce[6] << 48)
267
- | (nonce[5] << 40)
268
- | (nonce[4] << 32)
269
- | (nonce[3] << 24)
270
- | (nonce[2] << 16)
271
- | (nonce[1] << 8)
272
- | (nonce[0]);
273
- var iv = JSData.initWithLength(16);
246
+ var nonce = data.subdataInRange(JSRange(0, nonceLength));
247
+ if (_ivLength === undefined){
248
+ _ivLength = nonceLength;
249
+ }
250
+ var iv = JSData.initWithLength(_ivLength);
274
251
  nonce.copyTo(iv, 0);
275
252
  var cipher = crypto.createDecipheriv(name, key.keyData, iv);
276
- var tag = data.subdataInRange(JSRange(data.length - 16, 16));
253
+ var tagLength = 16;
254
+ var tag = data.subdataInRange(JSRange(data.length - tagLength, tagLength));
277
255
  cipher.setAuthTag(tag);
278
- var chunks = [cipher.update(data.subdataInRange(JSRange(8, data.length - 8 - 16))), cipher.final()];
256
+ var chunks = [cipher.update(data.subdataInRange(JSRange(nonceLength, data.length - nonceLength - tagLength))), cipher.final()];
279
257
  JSRunLoop.main.schedule(completion, target, JSData.initWithChunks(chunks));
280
258
  }catch(e){
281
259
  JSRunLoop.main.schedule(completion, target, null);
@@ -190,7 +190,6 @@ JSClass("SECCipherAESCipherBlockChaining", SECCipher, {
190
190
  JSClass("SECCipherAESCounter", SECCipher, {
191
191
 
192
192
  encryptedMessageId: 0,
193
- decryptedMessageId: 0,
194
193
 
195
194
  ensureUniqueMessageID: function(){
196
195
  if (this.encryptedMessageId == 9007199254740991){
@@ -205,7 +204,6 @@ JSClass("SECCipherAESCounter", SECCipher, {
205
204
  JSClass("SECCipherAESGaloisCounterMode", SECCipher, {
206
205
 
207
206
  encryptedMessageId: 0,
208
- decryptedMessageId: 0,
209
207
 
210
208
  ensureUniqueMessageID: function(){
211
209
  if (this.encryptedMessageId == 9007199254740991){
@@ -213,7 +211,39 @@ JSClass("SECCipherAESGaloisCounterMode", SECCipher, {
213
211
  }
214
212
  ++this.encryptedMessageId;
215
213
  return true;
216
- }
214
+ },
215
+
216
+ encrypt: function(data, key, completion, target){
217
+ if (!completion){
218
+ completion = Promise.completion(Promise.resolveNonNull);
219
+ }
220
+ if (!this.ensureUniqueMessageID()){
221
+ JSRunLoop.main.schedule(completion, target, null);
222
+ return completion.promise;
223
+ }
224
+ var nonce = JSData.initWithArray([
225
+ 1,
226
+ ((this.encryptedMessageId / 0x100000000) >> 16) & 0xFF,
227
+ ((this.encryptedMessageId / 0x100000000) >> 8) & 0xFF,
228
+ (this.encryptedMessageId / 0x100000000) & 0xFF,
229
+ (this.encryptedMessageId >> 24) & 0xFF,
230
+ (this.encryptedMessageId >> 16) & 0xFF,
231
+ (this.encryptedMessageId >> 8) & 0xFF,
232
+ this.encryptedMessageId & 0xF
233
+ ]);
234
+ this.encryptWithNonce(nonce, data, key, completion, target, 16);
235
+ return completion.promise;
236
+ },
237
+
238
+ decrypt: function(data, key, completion, target){
239
+ return this.decryptWithNonceLength(8, data, key, completion, target, 16);
240
+ },
241
+
242
+ encryptWithNonce: function(nonce, data, key, completion, target){
243
+ },
244
+
245
+ decryptWithNonceLength: function(nonceLength, data, key, completion, target){
246
+ },
217
247
 
218
248
  });
219
249
 
@@ -64,4 +64,21 @@ SECHMAC.Algorithm = {
64
64
  sha256: "sha256",
65
65
  sha384: "sha384",
66
66
  sha512: "sha512"
67
+ };
68
+
69
+ SECHMAC.digest = function(algorithm, keyData, data, completion, target){
70
+ if (!completion){
71
+ completion = Promise.completion(Promise.resolveNonNull);
72
+ }
73
+ var hmac = SECHMAC.initWithAlgorithm(algorithm);
74
+ hmac.createKeyWithData(keyData, function(key){
75
+ if (key === null){
76
+ completion.call(target, null);
77
+ return;
78
+ }
79
+ hmac.key = key;
80
+ hmac.update(data);
81
+ hmac.sign(completion, target);
82
+ });
83
+ return completion.promise;
67
84
  };
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
3
3
  "Info": {
4
4
  "JSBundleType": "framework",
5
5
  "JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
6
- "JSBundleVersion": "2023.19.0",
6
+ "JSBundleVersion": "2023.21.0",
7
7
  "JSDevelopmentLanguage": "en",
8
8
  "JSCopyright": "Copyright © 2020 Breakside Inc.",
9
9
  "JSBundleEnvironments": {
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
11
11
  "node": "SecurityKit+Node.js"
12
12
  },
13
13
  "JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
14
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
14
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
15
15
  },
16
16
  "Resources": [],
17
17
  "ResourceLookup": {
package/Info.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "JSBundleType": "node",
3
3
  "JSBundleIdentifier": "io.breakside.jskit",
4
- "JSBundleVersion": "2023.19.0",
4
+ "JSBundleVersion": "2023.21.0",
5
5
  "JSExecutableName": "jskit",
6
6
  "NPMOrganization": "breakside",
7
7
  "JSResources": [
8
8
  "Tests/HTMLTestRunner.js",
9
9
  "Tests/NodeTestRunner.js"
10
10
  ],
11
- "GitRevision": "1dc0150a5a81da24491bad16f195a3683e48f654"
11
+ "GitRevision": "af89e2c41e4ddad1c493c9ccfd577f76dde0520b"
12
12
  }
@@ -484,7 +484,8 @@ JSClass("HTMLBuilder", Builder, {
484
484
  wwwCSSPaths: null,
485
485
  wwwPaths: null,
486
486
  uncachedWWWPaths: null,
487
- manifestURL: null,
487
+ webApplicationManifestURL: null,
488
+ legacyAppcacheManifestURL: null,
488
489
  preflightId: null,
489
490
  preflightURL: null,
490
491
  preflightFeatures: null,
@@ -497,14 +498,14 @@ JSClass("HTMLBuilder", Builder, {
497
498
  // As of Dec 2019...
498
499
  // Browsers are in the process of removing the appcache API soon (Chrome
499
500
  // says it'll be gone by April 2020), so there's no point to contunue
500
- // supporting it. Keeping this `manifestURL` `null` will disable the
501
+ // supporting it. Keeping this `legacyAppcacheManifestURL` `null` will disable the
501
502
  // appcache behavior in the built application, but has the advantage of
502
503
  // keeping our builder code otherwise as-is should we need to reference
503
504
  // it or turn it back on (like for IE 11 support). The way we use
504
505
  // service workers (the appcache replacement API) is to essentially
505
506
  // mimic the way appcache worked because although others didn't like it,
506
507
  // appcache was perfect for our use case.
507
- // this.manifestURL = this.wwwURL.appendingPathComponent("manifest.appcache");
508
+ // this.legacyAppcacheManifestURL = this.wwwURL.appendingPathComponent("manifest.appcache");
508
509
 
509
510
  await this.buildCSS();
510
511
  await this.buildPreflight();
@@ -552,7 +553,8 @@ JSClass("HTMLBuilder", Builder, {
552
553
  }
553
554
  }
554
555
 
555
- await this.buildManifest();
556
+ await this.buildLegacyAppcacheManifest();
557
+ await this.updateWebApplicationManifest();
556
558
  },
557
559
 
558
560
  _copyWWWEntry: async function(entry, wwwPath, manifestConfiguration){
@@ -612,6 +614,11 @@ JSClass("HTMLBuilder", Builder, {
612
614
  }else if (element.localName == 'title' && element.parentNode.localName == 'head'){
613
615
  let title = this.project.getInfoString('UIApplicationTitle', this.resources);
614
616
  element.appendChild(document.createTextNode(title));
617
+ }else if (element.localName == 'link' && element.parentNode.localName == 'head' && element.getAttribute("rel") == "manifest"){
618
+ let href = element.getAttribute("href");
619
+ if (href !== "" && href !== null){
620
+ this.webApplicationManifestURL = JSURL.initWithString(href, toURL);
621
+ }
615
622
  }else if (element.localName == 'script' && element.getAttribute('type') == 'text/javascript'){
616
623
  if (element.hasAttribute("jskit")){
617
624
  let params = {
@@ -653,8 +660,8 @@ JSClass("HTMLBuilder", Builder, {
653
660
  }
654
661
  }
655
662
  }
656
- if (this.manifestURL){
657
- document.documentElement.setAttribute('manifest', this.manifestURL.encodedStringRelativeTo(this.wwwURL));
663
+ if (this.legacyAppcacheManifestURL){
664
+ document.documentElement.setAttribute('manifest', this.legacyAppcacheManifestURL.encodedStringRelativeTo(this.wwwURL));
658
665
  }
659
666
  let serializer = new XMLSerializer();
660
667
  html = serializer.serializeToString(document);
@@ -785,8 +792,24 @@ JSClass("HTMLBuilder", Builder, {
785
792
  await this.fileManager.createFileAtURL(this.preflightURL, contents);
786
793
  },
787
794
 
788
- buildManifest: async function(){
789
- if (!this.manifestURL){
795
+ updateWebApplicationManifest: async function(){
796
+ if (!this.webApplicationManifestURL){
797
+ return;
798
+ }
799
+ let data = await this.fileManager.contentsAtURL(this.webApplicationManifestURL);
800
+ let manifest = JSON.parse(data.stringByDecodingUTF8());
801
+ if (!manifest.name){
802
+ manifest.name = this.project.getInfoString('UIApplicationTitle', this.resources);
803
+ }
804
+ if (!manifest.icons){
805
+ manifest.icons = this.manifestIcons();
806
+ }
807
+ data = JSON.stringify(manifest).utf8();
808
+ await this.fileManager.createFileAtURL(this.webApplicationManifestURL, data);
809
+ },
810
+
811
+ buildLegacyAppcacheManifest: async function(){
812
+ if (!this.legacyAppcacheManifestURL){
790
813
  return;
791
814
  }
792
815
  this.printer.setStatus("Creating manifest.appcache...");
@@ -827,7 +850,7 @@ JSClass("HTMLBuilder", Builder, {
827
850
  lines.push("*");
828
851
  lines.push("");
829
852
  let txt = lines.join("\n");
830
- await this.fileManager.createFileAtURL(this.manifestURL, txt.utf8());
853
+ await this.fileManager.createFileAtURL(this.legacyAppcacheManifestURL, txt.utf8());
831
854
  },
832
855
 
833
856
  applicationIcons: function(){
@@ -840,15 +863,48 @@ JSClass("HTMLBuilder", Builder, {
840
863
  let images = contents.images;
841
864
  for (let i = 0, l = images.length; i < l; ++i){
842
865
  let image = images[i];
843
- let metadata = this.resources.getMetadata(image.filename, setName);
844
- let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
845
- icons.push({
846
- rel: image.mask ? "mask-icon" : "icon",
847
- href: bundledURL.encodedStringRelativeTo(this.wwwURL),
848
- type: metadata.mimetype,
849
- sizes: image.vector ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
850
- color: image.color || null
851
- });
866
+ if (image.use === undefined || image.use === "web-icon" || image.use == "app-icon-base"){
867
+ let metadata = this.resources.getMetadata(image.filename, setName);
868
+ let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
869
+ let rel = "icon";
870
+ if (image.use === "app-icon-base"){
871
+ rel = "apple-touch-icon";
872
+ }else if (image.mask){
873
+ rel = "mask-icon";
874
+ }
875
+ icons.push({
876
+ rel: rel,
877
+ href: bundledURL.encodedStringRelativeTo(this.wwwURL),
878
+ type: metadata.mimetype,
879
+ sizes: (image.vector || metadata.image.vector) ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
880
+ color: image.color || null
881
+ });
882
+ }
883
+ }
884
+ }
885
+ return icons;
886
+ },
887
+
888
+ manifestIcons: function(){
889
+ var icons = [];
890
+ let setName = this.project.info.UIApplicationIcon;
891
+ if (setName){
892
+ setName += '.imageset';
893
+ let metadata = this.resources.getMetadata('Contents.json', setName);
894
+ let contents = metadata.value;
895
+ let images = contents.images;
896
+ for (let i = 0, l = images.length; i < l; ++i){
897
+ let image = images[i];
898
+ if (image.use === "app-icon" || image.use === "app-icon-base"){
899
+ let metadata = this.resources.getMetadata(image.filename, setName);
900
+ let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
901
+ icons.push({
902
+ src: bundledURL.encodedStringRelativeTo(this.wwwURL),
903
+ type: metadata.mimetype,
904
+ sizes: metadata.image.vector ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
905
+ purpose: image.use === "app-icon-base" ? "maskable" : "any"
906
+ });
907
+ }
852
908
  }
853
909
  }
854
910
  return icons;
@@ -948,8 +1004,8 @@ JSClass("HTMLBuilder", Builder, {
948
1004
  indexPath,
949
1005
  bootstrapperPath
950
1006
  ];
951
- if (this.manifestURL !== null){
952
- manifestPath = this.manifestURL.encodedStringRelativeTo(this.wwwURL);
1007
+ if (this.legacyAppcacheManifestURL !== null){
1008
+ manifestPath = this.legacyAppcacheManifestURL.encodedStringRelativeTo(this.wwwURL);
953
1009
  excludes.push(manifestPath);
954
1010
  }
955
1011
  if (this.serviceWorkerURL !== null){