rncryptor.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. var RNCryptor = {};
  2. /*
  3. Takes password string and salt WordArray
  4. Returns key bitArray
  5. */
  6. RNCryptor.KeyForPassword = function(password, salt) {
  7. var hmacSHA1 = function (key) {
  8. var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1);
  9. this.encrypt = function () {
  10. return hasher.encrypt.apply(hasher, arguments);
  11. };
  12. };
  13. return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA1);
  14. }
  15. /*
  16. Takes password string and plaintext bitArray
  17. options:
  18. iv
  19. encryption_salt
  20. html_salt
  21. Returns ciphertext bitArray
  22. */
  23. RNCryptor.Encrypt = function(password, plaintext, options) {
  24. options = options || {}
  25. var encryption_salt = options["encryption_salt"] || sjcl.random.randomWords(8 / 4); // FIXME: Need to seed PRNG
  26. var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
  27. var hmac_salt = options["hmac_salt"] || sjcl.random.randomWords(8 / 4);
  28. var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
  29. var iv = options["iv"] || sjcl.random.randomWords(16 / 4);
  30. var version = sjcl.codec.hex.toBits("03");
  31. var options = sjcl.codec.hex.toBits("01");
  32. var message = sjcl.bitArray.concat(version, options);
  33. message = sjcl.bitArray.concat(message, encryption_salt);
  34. message = sjcl.bitArray.concat(message, hmac_salt);
  35. message = sjcl.bitArray.concat(message, iv);
  36. var aes = new sjcl.cipher.aes(encryption_key);
  37. sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
  38. var encrypted = sjcl.mode.cbc.encrypt(aes, plaintext, iv);
  39. message = sjcl.bitArray.concat(message, encrypted);
  40. var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message);
  41. message = sjcl.bitArray.concat(message, hmac);
  42. return message;
  43. }
  44. /*
  45. Takes password string and message (ciphertext) bitArray
  46. options:
  47. iv
  48. encryption_salt
  49. html_salt
  50. Returns plaintext bitArray
  51. */
  52. RNCryptor.Decrypt = function(password, message, options) {
  53. options = options || {}
  54. var version = sjcl.bitArray.extract(message, 0 * 8, 8);
  55. var options = sjcl.bitArray.extract(message, 1 * 8, 8);
  56. var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8);
  57. var encryption_key = RNCryptor.KeyForPassword(password, encryption_salt);
  58. var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8);
  59. var hmac_key = RNCryptor.KeyForPassword(password, hmac_salt);
  60. var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8);
  61. var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8);
  62. var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end);
  63. var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end);
  64. var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end));
  65. // .equal is of consistent time
  66. if (! sjcl.bitArray.equal(hmac, expected_hmac)) {
  67. throw new sjcl.exception.corrupt("HMAC mismatch or bad password.");
  68. }
  69. var aes = new sjcl.cipher.aes(encryption_key);
  70. sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]();
  71. var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);
  72. return decrypted;
  73. }