crypto.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. * vim: ts=4:sw=4:expandtab
  3. */
  4. ;(function(){
  5. 'use strict';
  6. var encrypt = libsignal.crypto.encrypt;
  7. var decrypt = libsignal.crypto.decrypt;
  8. var calculateMAC = libsignal.crypto.calculateMAC;
  9. var verifyMAC = libsignal.crypto.verifyMAC;
  10. function verifyDigest(data, theirDigest) {
  11. return crypto.subtle.digest({name: 'SHA-256'}, data).then(function(ourDigest) {
  12. var a = new Uint8Array(ourDigest);
  13. var b = new Uint8Array(theirDigest);
  14. var result = 0;
  15. for (var i=0; i < theirDigest.byteLength; ++i) {
  16. result = result | (a[i] ^ b[i]);
  17. }
  18. if (result !== 0) {
  19. throw new Error('Bad digest');
  20. }
  21. });
  22. }
  23. function calculateDigest(data) {
  24. return crypto.subtle.digest({name: 'SHA-256'}, data);
  25. }
  26. window.textsecure = window.textsecure || {};
  27. window.textsecure.crypto = {
  28. // Decrypts message into a raw string
  29. decryptWebsocketMessage: function(message, signaling_key) {
  30. var decodedMessage = message.toArrayBuffer();
  31. if (signaling_key.byteLength != 52) {
  32. throw new Error("Got invalid length signaling_key");
  33. }
  34. if (decodedMessage.byteLength < 1 + 16 + 10) {
  35. throw new Error("Got invalid length message");
  36. }
  37. if (new Uint8Array(decodedMessage)[0] != 1) {
  38. throw new Error("Got bad version number: " + decodedMessage[0]);
  39. }
  40. var aes_key = signaling_key.slice(0, 32);
  41. var mac_key = signaling_key.slice(32, 32 + 20);
  42. var iv = decodedMessage.slice(1, 1 + 16);
  43. var ciphertext = decodedMessage.slice(1 + 16, decodedMessage.byteLength - 10);
  44. var ivAndCiphertext = decodedMessage.slice(0, decodedMessage.byteLength - 10);
  45. var mac = decodedMessage.slice(decodedMessage.byteLength - 10, decodedMessage.byteLength);
  46. return verifyMAC(ivAndCiphertext, mac_key, mac, 10).then(function() {
  47. return decrypt(aes_key, ciphertext, iv);
  48. });
  49. },
  50. decryptAttachment: function(encryptedBin, keys, theirDigest) {
  51. if (keys.byteLength != 64) {
  52. throw new Error("Got invalid length attachment keys");
  53. }
  54. if (encryptedBin.byteLength < 16 + 32) {
  55. throw new Error("Got invalid length attachment");
  56. }
  57. var aes_key = keys.slice(0, 32);
  58. var mac_key = keys.slice(32, 64);
  59. var iv = encryptedBin.slice(0, 16);
  60. var ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32);
  61. var ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32);
  62. var mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength);
  63. return verifyMAC(ivAndCiphertext, mac_key, mac, 32).then(function() {
  64. if (theirDigest !== undefined) {
  65. return verifyDigest(encryptedBin, theirDigest);
  66. }
  67. }).then(function() {
  68. return decrypt(aes_key, ciphertext, iv);
  69. });
  70. },
  71. encryptAttachment: function(plaintext, keys, iv) {
  72. if (keys.byteLength != 64) {
  73. throw new Error("Got invalid length attachment keys");
  74. }
  75. if (iv.byteLength != 16) {
  76. throw new Error("Got invalid length attachment iv");
  77. }
  78. var aes_key = keys.slice(0, 32);
  79. var mac_key = keys.slice(32, 64);
  80. return encrypt(aes_key, plaintext, iv).then(function(ciphertext) {
  81. var ivAndCiphertext = new Uint8Array(16 + ciphertext.byteLength);
  82. ivAndCiphertext.set(new Uint8Array(iv));
  83. ivAndCiphertext.set(new Uint8Array(ciphertext), 16);
  84. return calculateMAC(mac_key, ivAndCiphertext.buffer).then(function(mac) {
  85. var encryptedBin = new Uint8Array(16 + ciphertext.byteLength + 32);
  86. encryptedBin.set(ivAndCiphertext);
  87. encryptedBin.set(new Uint8Array(mac), 16 + ciphertext.byteLength);
  88. return calculateDigest(encryptedBin.buffer).then(function(digest) {
  89. return { ciphertext: encryptedBin.buffer, digest: digest };
  90. });
  91. });
  92. });
  93. },
  94. getRandomBytes: function(size) {
  95. return libsignal.crypto.getRandomBytes(size);
  96. }
  97. };
  98. })();