Source: encrypt/algo/aes-algorithm.js

  1. /**
  2. * Copyright (C) 2015-2018 Regents of the University of California.
  3. * @author: Jeff Thompson <jefft0@remap.ucla.edu>
  4. * @author: From ndn-group-encrypt src/algo/aes https://github.com/named-data/ndn-group-encrypt
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. * A copy of the GNU Lesser General Public License is in the file COPYING.
  19. */
  20. // (This is ported from ndn::gep::algo::Aes, and named AesAlgorithm because
  21. // "Aes" is very short and not all the Common Client Libraries have namespaces.)
  22. /** @ignore */
  23. var Crypto = require('../../crypto.js'); /** @ignore */
  24. var Blob = require('../../util/blob.js').Blob; /** @ignore */
  25. var DecryptKey = require('../decrypt-key.js').DecryptKey; /** @ignore */
  26. var EncryptKey = require('../encrypt-key.js').EncryptKey; /** @ignore */
  27. var EncryptAlgorithmType = require('./encrypt-params.js').EncryptAlgorithmType; /** @ignore */
  28. var UseSubtleCrypto = require('../../use-subtle-crypto-node.js').UseSubtleCrypto; /** @ignore */
  29. var SyncPromise = require('../../util/sync-promise.js').SyncPromise;
  30. /**
  31. * The AesAlgorithm class provides static methods to manipulate keys, encrypt
  32. * and decrypt using the AES symmetric key cipher.
  33. * @note This class is an experimental feature. The API may change.
  34. * @constructor
  35. */
  36. var AesAlgorithm = function AesAlgorithm()
  37. {
  38. };
  39. exports.AesAlgorithm = AesAlgorithm;
  40. /**
  41. * Generate a new random decrypt key for AES based on the given params.
  42. * @param {AesKeyParams} params The key params with the key size (in bits).
  43. * @return {DecryptKey} The new decrypt key.
  44. */
  45. AesAlgorithm.generateKey = function(params)
  46. {
  47. // Convert the key bit size to bytes.
  48. var key = Crypto.randomBytes(params.getKeySize() / 8);
  49. var decryptKey = new DecryptKey(new Blob(key, false));
  50. return decryptKey;
  51. };
  52. /**
  53. * Derive a new encrypt key from the given decrypt key value.
  54. * @param {Blob} keyBits The key value of the decrypt key.
  55. * @return {EncryptKey} The new encrypt key.
  56. */
  57. AesAlgorithm.deriveEncryptKey = function(keyBits)
  58. {
  59. return new EncryptKey(keyBits);
  60. };
  61. /**
  62. * Decrypt the encryptedData using the keyBits according the encrypt params.
  63. * @param {Blob} keyBits The key value.
  64. * @param {Blob} encryptedData The data to decrypt.
  65. * @param {EncryptParams} params This decrypts according to
  66. * params.getAlgorithmType() and other params as needed such as
  67. * params.getInitialVector().
  68. * @param {boolean} useSync (optional) If true then return a SyncPromise which
  69. * is already fulfilled. If omitted or false, this may return a SyncPromise or
  70. * an async Promise.
  71. * @return {Promise|SyncPromise} A promise which returns the decrypted Blob.
  72. */
  73. AesAlgorithm.decryptPromise = function(keyBits, encryptedData, params, useSync)
  74. {
  75. if (UseSubtleCrypto() && !useSync &&
  76. // Crypto.subtle doesn't implement ECB.
  77. params.getAlgorithmType() != EncryptAlgorithmType.AesEcb) {
  78. if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
  79. return crypto.subtle.importKey
  80. ("raw", keyBits.buf(), { name: "AES-CBC" }, false,
  81. ["encrypt", "decrypt"])
  82. .then(function(key) {
  83. return crypto.subtle.decrypt
  84. ({ name: "AES-CBC", iv: params.getInitialVector().buf() },
  85. key, encryptedData.buf());
  86. })
  87. .then(function(result) {
  88. return Promise.resolve(new Blob(new Uint8Array(result), false));
  89. });
  90. }
  91. else
  92. return Promise.reject(new Error("unsupported encryption mode"));
  93. }
  94. else {
  95. if (params.getAlgorithmType() == EncryptAlgorithmType.AesEcb) {
  96. try {
  97. // ECB ignores the initial vector.
  98. var cipher = Crypto.createDecipheriv
  99. (keyBits.size() == 32 ? "aes-256-ecb" : "aes-128-ecb",
  100. keyBits.buf(), "");
  101. return SyncPromise.resolve(new Blob
  102. (Buffer.concat([cipher.update(encryptedData.buf()), cipher.final()]),
  103. false));
  104. } catch (err) {
  105. return SyncPromise.reject(err);
  106. }
  107. }
  108. else if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
  109. try {
  110. var cipher = Crypto.createDecipheriv
  111. (keyBits.size() == 32 ? "aes-256-cbc" : "aes-128-cbc",
  112. keyBits.buf(), params.getInitialVector().buf());
  113. return SyncPromise.resolve(new Blob
  114. (Buffer.concat([cipher.update(encryptedData.buf()), cipher.final()]),
  115. false));
  116. } catch (err) {
  117. return SyncPromise.reject(err);
  118. }
  119. }
  120. else
  121. return SyncPromise.reject(new Error("unsupported encryption mode"));
  122. }
  123. };
  124. /**
  125. * Decrypt the encryptedData using the keyBits according the encrypt params.
  126. * @param {Blob} keyBits The key value.
  127. * @param {Blob} encryptedData The data to decrypt.
  128. * @param {EncryptParams} params This decrypts according to
  129. * params.getAlgorithmType() and other params as needed such as
  130. * params.getInitialVector().
  131. * @return {Blob} The decrypted data.
  132. * @throws Error If decryptPromise doesn't return a SyncPromise which is
  133. * already fulfilled.
  134. */
  135. AesAlgorithm.decrypt = function(keyBits, encryptedData, params)
  136. {
  137. return SyncPromise.getValue(this.decryptPromise
  138. (keyBits, encryptedData, params, true));
  139. };
  140. /**
  141. * Encrypt the plainData using the keyBits according the encrypt params.
  142. * @param {Blob} keyBits The key value.
  143. * @param {Blob} plainData The data to encrypt.
  144. * @param {EncryptParams} params This encrypts according to
  145. * params.getAlgorithmType() and other params as needed such as
  146. * params.getInitialVector().
  147. * @param {boolean} useSync (optional) If true then return a SyncPromise which
  148. * is already fulfilled. If omitted or false, this may return a SyncPromise or
  149. * an async Promise.
  150. * @return {Promise|SyncPromise} A promise which returns the encrypted Blob.
  151. */
  152. AesAlgorithm.encryptPromise = function(keyBits, plainData, params, useSync)
  153. {
  154. if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
  155. if (params.getInitialVector().size() != AesAlgorithm.BLOCK_SIZE)
  156. return SyncPromise.reject(new Error("incorrect initial vector size"));
  157. }
  158. if (UseSubtleCrypto() && !useSync &&
  159. // Crypto.subtle doesn't implement ECB.
  160. params.getAlgorithmType() != EncryptAlgorithmType.AesEcb) {
  161. if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
  162. return crypto.subtle.importKey
  163. ("raw", keyBits.buf(), { name: "AES-CBC" }, false,
  164. ["encrypt", "decrypt"])
  165. .then(function(key) {
  166. return crypto.subtle.encrypt
  167. ({ name: "AES-CBC", iv: params.getInitialVector().buf() },
  168. key, plainData.buf());
  169. })
  170. .then(function(result) {
  171. return Promise.resolve(new Blob(new Uint8Array(result), false));
  172. });
  173. }
  174. else
  175. return Promise.reject(new Error("unsupported encryption mode"));
  176. }
  177. else {
  178. if (params.getAlgorithmType() == EncryptAlgorithmType.AesEcb) {
  179. // ECB ignores the initial vector.
  180. var cipher = Crypto.createCipheriv
  181. (keyBits.size() == 32 ? "aes-256-ecb" : "aes-128-ecb", keyBits.buf(), "");
  182. return SyncPromise.resolve(new Blob
  183. (Buffer.concat([cipher.update(plainData.buf()), cipher.final()]),
  184. false));
  185. }
  186. else if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
  187. var cipher = Crypto.createCipheriv
  188. (keyBits.size() == 32 ? "aes-256-cbc" : "aes-128-cbc",
  189. keyBits.buf(), params.getInitialVector().buf());
  190. return SyncPromise.resolve(new Blob
  191. (Buffer.concat([cipher.update(plainData.buf()), cipher.final()]),
  192. false));
  193. }
  194. else
  195. return SyncPromise.reject(new Error("unsupported encryption mode"));
  196. }
  197. };
  198. /**
  199. * Encrypt the plainData using the keyBits according the encrypt params.
  200. * @param {Blob} keyBits The key value.
  201. * @param {Blob} plainData The data to encrypt.
  202. * @param {EncryptParams} params This encrypts according to
  203. * params.getAlgorithmType() and other params as needed such as
  204. * params.getInitialVector().
  205. * @return {Blob} The encrypted data.
  206. * @throws Error If encryptPromise doesn't return a SyncPromise which is
  207. * already fulfilled.
  208. */
  209. AesAlgorithm.encrypt = function(keyBits, plainData, params)
  210. {
  211. return SyncPromise.getValue(this.encryptPromise
  212. (keyBits, plainData, params, true));
  213. };
  214. AesAlgorithm.BLOCK_SIZE = 16;