You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
2.8 KiB

11 months ago
  1. const crypto = require("crypto");
  2. const { dumpENV } = require("../helpers/updateENV");
  3. // Class that is used to arbitrarily encrypt/decrypt string data via a persistent passphrase/salt that
  4. // is either user defined or is created and saved to the ENV on creation.
  5. class EncryptionManager {
  6. #keyENV = "SIG_KEY";
  7. #saltENV = "SIG_SALT";
  8. #encryptionKey;
  9. #encryptionSalt;
  10. constructor({ key = null, salt = null } = {}) {
  11. this.#loadOrCreateKeySalt(key, salt);
  12. this.key = crypto.scryptSync(this.#encryptionKey, this.#encryptionSalt, 32);
  13. this.algorithm = "aes-256-cbc";
  14. this.separator = ":";
  15. // Used to send key to collector process to be able to decrypt data since they do not share ENVs
  16. // this value should use the CommunicationKey.encrypt process before sending anywhere outside the
  17. // server process so it is never sent in its raw format.
  18. this.xPayload = this.key.toString("base64");
  19. }
  20. log(text, ...args) {
  21. console.log(`\x1b[36m[EncryptionManager]\x1b[0m ${text}`, ...args);
  22. }
  23. #loadOrCreateKeySalt(_key = null, _salt = null) {
  24. if (!!_key && !!_salt) {
  25. this.log(
  26. "Pre-assigned key & salt for encrypting arbitrary data was used."
  27. );
  28. this.#encryptionKey = _key;
  29. this.#encryptionSalt = _salt;
  30. return;
  31. }
  32. if (!process.env[this.#keyENV] || !process.env[this.#saltENV]) {
  33. this.log("Self-assigning key & salt for encrypting arbitrary data.");
  34. process.env[this.#keyENV] = crypto.randomBytes(32).toString("hex");
  35. process.env[this.#saltENV] = crypto.randomBytes(32).toString("hex");
  36. if (process.env.NODE_ENV === "production") dumpENV();
  37. } else
  38. this.log("Loaded existing key & salt for encrypting arbitrary data.");
  39. this.#encryptionKey = process.env[this.#keyENV];
  40. this.#encryptionSalt = process.env[this.#saltENV];
  41. return;
  42. }
  43. encrypt(plainTextString = null) {
  44. try {
  45. if (!plainTextString)
  46. throw new Error("Empty string is not valid for this method.");
  47. const iv = crypto.randomBytes(16);
  48. const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
  49. const encrypted = cipher.update(plainTextString, "utf8", "hex");
  50. return [
  51. encrypted + cipher.final("hex"),
  52. Buffer.from(iv).toString("hex"),
  53. ].join(this.separator);
  54. } catch (e) {
  55. this.log(e);
  56. return null;
  57. }
  58. }
  59. decrypt(encryptedString) {
  60. try {
  61. const [encrypted, iv] = encryptedString.split(this.separator);
  62. if (!iv) throw new Error("IV not found");
  63. const decipher = crypto.createDecipheriv(
  64. this.algorithm,
  65. this.key,
  66. Buffer.from(iv, "hex")
  67. );
  68. return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8");
  69. } catch (e) {
  70. this.log(e);
  71. return null;
  72. }
  73. }
  74. }
  75. module.exports = { EncryptionManager };