webcrypto.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. "use strict";
  2. /*
  3. Copyright 2021-2022 The Matrix.org Foundation C.I.C.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  16. return new (P || (P = Promise))(function (resolve, reject) {
  17. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  18. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  19. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  20. step((generator = generator.apply(thisArg, _arguments || [])).next());
  21. });
  22. };
  23. var __generator = (this && this.__generator) || function (thisArg, body) {
  24. var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
  25. return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
  26. function verb(n) { return function (v) { return step([n, v]); }; }
  27. function step(op) {
  28. if (f) throw new TypeError("Generator is already executing.");
  29. while (_) try {
  30. if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
  31. if (y = 0, t) op = [op[0] & 2, t.value];
  32. switch (op[0]) {
  33. case 0: case 1: t = op; break;
  34. case 4: _.label++; return { value: op[1], done: false };
  35. case 5: _.label++; y = op[1]; op = [0]; continue;
  36. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  37. default:
  38. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  39. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  40. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  41. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  42. if (t[2]) _.ops.pop();
  43. _.trys.pop(); continue;
  44. }
  45. op = body.call(thisArg, _);
  46. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  47. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  48. }
  49. };
  50. // Object.defineProperty(exports, "__esModule", { value: true });
  51. // exports.decodeBase64 = exports.encodeBase64 = exports.decryptAttachment = exports.encryptAttachment = void 0;
  52. function encryptAttachment(plaintextBuffer) {
  53. return __awaiter(this, void 0, void 0, function () {
  54. var ivArray, cryptoKey, exportedKey, ciphertextBuffer, sha256Buffer;
  55. return __generator(this, function (_a) {
  56. switch (_a.label) {
  57. case 0:
  58. ivArray = new Uint8Array(16);
  59. window.crypto.getRandomValues(ivArray.subarray(0, 8));
  60. return [4 /*yield*/, window.crypto.subtle.generateKey({ 'name': 'AES-CTR', 'length': 256 }, true, ['encrypt', 'decrypt'])];
  61. case 1:
  62. cryptoKey = _a.sent();
  63. return [4 /*yield*/, window.crypto.subtle.exportKey('jwk', cryptoKey)];
  64. case 2:
  65. exportedKey = _a.sent();
  66. return [4 /*yield*/, window.crypto.subtle.encrypt({ name: 'AES-CTR', counter: ivArray, length: 64 }, cryptoKey, plaintextBuffer)];
  67. case 3:
  68. ciphertextBuffer = _a.sent();
  69. return [4 /*yield*/, window.crypto.subtle.digest('SHA-256', ciphertextBuffer)];
  70. case 4:
  71. sha256Buffer = _a.sent();
  72. return [2 /*return*/, {
  73. data: ciphertextBuffer,
  74. info: {
  75. v: 'v2',
  76. key: exportedKey,
  77. iv: encodeBase64(ivArray),
  78. hashes: {
  79. sha256: encodeBase64(new Uint8Array(sha256Buffer)),
  80. },
  81. },
  82. }];
  83. }
  84. });
  85. });
  86. }
  87. // exports.encryptAttachment = encryptAttachment;
  88. function decryptAttachment(ciphertextBuffer, info) {
  89. return __awaiter(this, void 0, void 0, function () {
  90. var ivArray, expectedSha256base64, cryptoKey, digestResult, counterLength;
  91. return __generator(this, function (_a) {
  92. switch (_a.label) {
  93. case 0:
  94. if (info === undefined || info.key === undefined || info.iv === undefined
  95. || info.hashes === undefined || info.hashes.sha256 === undefined) {
  96. throw new Error('Invalid info. Missing info.key, info.iv or info.hashes.sha256 key');
  97. }
  98. if (info.v && !info.v.match(/^v[1-2]$/)) {
  99. throw new Error("Unsupported protocol version: " + info.v);
  100. }
  101. ivArray = decodeBase64(info.iv);
  102. expectedSha256base64 = info.hashes.sha256;
  103. return [4 /*yield*/, window.crypto.subtle.importKey('jwk', info.key, { 'name': 'AES-CTR' }, false, ['encrypt', 'decrypt'])];
  104. case 1:
  105. cryptoKey = _a.sent();
  106. return [4 /*yield*/, window.crypto.subtle.digest('SHA-256', ciphertextBuffer)];
  107. case 2:
  108. digestResult = _a.sent();
  109. if (encodeBase64(new Uint8Array(digestResult)) != expectedSha256base64) {
  110. throw new Error('Mismatched SHA-256 digest');
  111. }
  112. if (info.v == 'v1' || info.v == 'v2') {
  113. // Version 1 and 2 use a 64 bit counter.
  114. counterLength = 64;
  115. }
  116. else {
  117. // Version 0 uses a 128 bit counter.
  118. counterLength = 128;
  119. }
  120. return [2 /*return*/, window.crypto.subtle.decrypt({ name: 'AES-CTR', counter: ivArray, length: counterLength }, cryptoKey, ciphertextBuffer)];
  121. }
  122. });
  123. });
  124. }
  125. // exports.decryptAttachment = decryptAttachment;
  126. function encodeBase64(uint8Array) {
  127. // Misinterpt the Uint8Array as Latin-1.
  128. // window.btoa expects a unicode string with codepoints in the range 0-255.
  129. var latin1String = String.fromCharCode.apply(null, uint8Array);
  130. // Use the builtin base64 encoder.
  131. var paddedBase64 = window.btoa(latin1String);
  132. // Calculate the unpadded length.
  133. var inputLength = uint8Array.length;
  134. var outputLength = 4 * Math.floor((inputLength + 2) / 3) + (inputLength + 2) % 3 - 2;
  135. // Return the unpadded base64.
  136. return paddedBase64.slice(0, outputLength);
  137. }
  138. // exports.encodeBase64 = encodeBase64;
  139. function decodeBase64(base64) {
  140. // Pad the base64 up to the next multiple of 4.
  141. var paddedBase64 = base64 + '==='.slice(0, (4 - base64.length % 4) % 4);
  142. // Decode the base64 as a misinterpreted Latin-1 string.
  143. // window.atob returns a unicode string with codepoints in the range 0-255.
  144. var latin1String = window.atob(paddedBase64);
  145. // Encode the string as a Uint8Array as Latin-1.
  146. var uint8Array = new Uint8Array(latin1String.length);
  147. for (var i = 0; i < latin1String.length; i++) {
  148. uint8Array[i] = latin1String.charCodeAt(i);
  149. }
  150. return uint8Array;
  151. }
  152. // exports.decodeBase64 = decodeBase64;
  153. // exports.default = {
  154. // encryptAttachment: encryptAttachment,
  155. // decryptAttachment: decryptAttachment,
  156. // encodeBase64: encodeBase64,
  157. // decodeBase64: decodeBase64,
  158. // }
  159. ;
  160. //# sourceMappingURL=webcrypto.js.map