EncryptionHandler.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. export default class EncryptionHandler {
  2. constructor(key, salt){
  3. this._key = key;
  4. }
  5. static async create(password, salt){
  6. const key = await this.generateEncryptionKey(password, salt);
  7. return new EncryptionHandler(key, salt);
  8. }
  9. static async hashPassword(password, salt){
  10. const iterations = 100000;
  11. const hash = "SHA-256";
  12. const encoder = new TextEncoder();
  13. const baseKey = await crypto.subtle.importKey(
  14. "raw",
  15. encoder.encode(password),
  16. {name: "PBKDF2"},
  17. false,
  18. ["deriveBits"]
  19. );
  20. const derivedBits = await crypto.subtle.deriveBits(
  21. {
  22. name: "PBKDF2",
  23. salt: encoder.encode(salt),
  24. iterations,
  25. hash
  26. },
  27. baseKey,
  28. 256
  29. );
  30. return btoa(String.fromCharCode(...new Uint8Array(derivedBits)));
  31. }
  32. static generateSalt(){
  33. const length = 16;
  34. const salt = new Uint8Array(length);
  35. crypto.getRandomValues(salt);
  36. return btoa(String.fromCharCode(...salt));
  37. }
  38. static generateIv(){
  39. const length = 12;
  40. const iv = new Uint8Array(length);
  41. crypto.getRandomValues(iv);
  42. return btoa(String.fromCharCode(...iv));
  43. }
  44. stringToBuffer(str){
  45. return Uint8Array.from(atob(str), c => c.charCodeAt(0));
  46. }
  47. async generateEncryptionKey(password, salt){
  48. const passwordKey = await crypto.subtle.importKey(
  49. "raw",
  50. new TextEncoder().encode(password),
  51. "PBKDF2",
  52. false,
  53. ["deriveKey"]
  54. );
  55. return crypto.subtle.deriveKey(
  56. {
  57. name: "PBKDF2",
  58. salt: salt,
  59. iterations: 100000,
  60. hash: "SHA-256"
  61. },
  62. passwordKey,
  63. {
  64. name: "AES-GCM",
  65. length: 256
  66. },
  67. true,
  68. ["encrypt", "decrypt"]
  69. );
  70. }
  71. async encrypt(data, iv){
  72. const json = JSON.stringify(data);
  73. const buff = new TextEncoder().encode(str);
  74. const encrypted = await crypto.subtle.encrypt(
  75. {
  76. name: "AES-GCM",
  77. iv
  78. },
  79. this._key,
  80. data
  81. );
  82. return btoa(String.fromCharCode(...new Uint8Array(encrypted)));
  83. }
  84. async decrypt(data, key, ivString){
  85. const encrypted = stringToBuffer(data);
  86. const iv = stringToBuffer(ivString);
  87. const decrypted = await crypto.subtle.decrypt(
  88. {
  89. name: "AES-GCM",
  90. iv,
  91. },
  92. key,
  93. encrypted
  94. );
  95. const json = new TextDecoder().decode(decrypted);
  96. return JSON.parse(json);
  97. }
  98. }