aes.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. export class AESECBCipher {
  2. constructor() {
  3. this._key = null;
  4. }
  5. get algorithm() {
  6. return { name: "AES-ECB" };
  7. }
  8. static async importKey(key, _algorithm, extractable, keyUsages) {
  9. const cipher = new AESECBCipher;
  10. await cipher._importKey(key, extractable, keyUsages);
  11. return cipher;
  12. }
  13. async _importKey(key, extractable, keyUsages) {
  14. this._key = await window.crypto.subtle.importKey(
  15. "raw", key, {name: "AES-CBC"}, extractable, keyUsages);
  16. }
  17. async encrypt(_algorithm, plaintext) {
  18. const x = new Uint8Array(plaintext);
  19. if (x.length % 16 !== 0 || this._key === null) {
  20. return null;
  21. }
  22. const n = x.length / 16;
  23. for (let i = 0; i < n; i++) {
  24. const y = new Uint8Array(await window.crypto.subtle.encrypt({
  25. name: "AES-CBC",
  26. iv: new Uint8Array(16),
  27. }, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
  28. x.set(y, i * 16);
  29. }
  30. return x;
  31. }
  32. }
  33. export class AESEAXCipher {
  34. constructor() {
  35. this._rawKey = null;
  36. this._ctrKey = null;
  37. this._cbcKey = null;
  38. this._zeroBlock = new Uint8Array(16);
  39. this._prefixBlock0 = this._zeroBlock;
  40. this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
  41. this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
  42. }
  43. get algorithm() {
  44. return { name: "AES-EAX" };
  45. }
  46. async _encryptBlock(block) {
  47. const encrypted = await window.crypto.subtle.encrypt({
  48. name: "AES-CBC",
  49. iv: this._zeroBlock,
  50. }, this._cbcKey, block);
  51. return new Uint8Array(encrypted).slice(0, 16);
  52. }
  53. async _initCMAC() {
  54. const k1 = await this._encryptBlock(this._zeroBlock);
  55. const k2 = new Uint8Array(16);
  56. const v = k1[0] >>> 6;
  57. for (let i = 0; i < 15; i++) {
  58. k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);
  59. k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);
  60. }
  61. const lut = [0x0, 0x87, 0x0e, 0x89];
  62. k2[14] ^= v >>> 1;
  63. k2[15] = (k1[15] << 2) ^ lut[v];
  64. k1[15] = (k1[15] << 1) ^ lut[v >> 1];
  65. this._k1 = k1;
  66. this._k2 = k2;
  67. }
  68. async _encryptCTR(data, counter) {
  69. const encrypted = await window.crypto.subtle.encrypt({
  70. name: "AES-CTR",
  71. counter: counter,
  72. length: 128
  73. }, this._ctrKey, data);
  74. return new Uint8Array(encrypted);
  75. }
  76. async _decryptCTR(data, counter) {
  77. const decrypted = await window.crypto.subtle.decrypt({
  78. name: "AES-CTR",
  79. counter: counter,
  80. length: 128
  81. }, this._ctrKey, data);
  82. return new Uint8Array(decrypted);
  83. }
  84. async _computeCMAC(data, prefixBlock) {
  85. if (prefixBlock.length !== 16) {
  86. return null;
  87. }
  88. const n = Math.floor(data.length / 16);
  89. const m = Math.ceil(data.length / 16);
  90. const r = data.length - n * 16;
  91. const cbcData = new Uint8Array((m + 1) * 16);
  92. cbcData.set(prefixBlock);
  93. cbcData.set(data, 16);
  94. if (r === 0) {
  95. for (let i = 0; i < 16; i++) {
  96. cbcData[n * 16 + i] ^= this._k1[i];
  97. }
  98. } else {
  99. cbcData[(n + 1) * 16 + r] = 0x80;
  100. for (let i = 0; i < 16; i++) {
  101. cbcData[(n + 1) * 16 + i] ^= this._k2[i];
  102. }
  103. }
  104. let cbcEncrypted = await window.crypto.subtle.encrypt({
  105. name: "AES-CBC",
  106. iv: this._zeroBlock,
  107. }, this._cbcKey, cbcData);
  108. cbcEncrypted = new Uint8Array(cbcEncrypted);
  109. const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);
  110. return mac;
  111. }
  112. static async importKey(key, _algorithm, _extractable, _keyUsages) {
  113. const cipher = new AESEAXCipher;
  114. await cipher._importKey(key);
  115. return cipher;
  116. }
  117. async _importKey(key) {
  118. this._rawKey = key;
  119. this._ctrKey = await window.crypto.subtle.importKey(
  120. "raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
  121. this._cbcKey = await window.crypto.subtle.importKey(
  122. "raw", key, {name: "AES-CBC"}, false, ["encrypt"]);
  123. await this._initCMAC();
  124. }
  125. async encrypt(algorithm, message) {
  126. const ad = algorithm.additionalData;
  127. const nonce = algorithm.iv;
  128. const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
  129. const encrypted = await this._encryptCTR(message, nCMAC);
  130. const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
  131. const mac = await this._computeCMAC(encrypted, this._prefixBlock2);
  132. for (let i = 0; i < 16; i++) {
  133. mac[i] ^= nCMAC[i] ^ adCMAC[i];
  134. }
  135. const res = new Uint8Array(16 + encrypted.length);
  136. res.set(encrypted);
  137. res.set(mac, encrypted.length);
  138. return res;
  139. }
  140. async decrypt(algorithm, data) {
  141. const encrypted = data.slice(0, data.length - 16);
  142. const ad = algorithm.additionalData;
  143. const nonce = algorithm.iv;
  144. const mac = data.slice(data.length - 16);
  145. const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
  146. const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
  147. const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);
  148. for (let i = 0; i < 16; i++) {
  149. computedMac[i] ^= nCMAC[i] ^ adCMAC[i];
  150. }
  151. if (computedMac.length !== mac.length) {
  152. return null;
  153. }
  154. for (let i = 0; i < mac.length; i++) {
  155. if (computedMac[i] !== mac[i]) {
  156. return null;
  157. }
  158. }
  159. const res = await this._decryptCTR(encrypted, nCMAC);
  160. return res;
  161. }
  162. }