FIX: Encrypt drafts using AES.

FIX: Encrypt drafts using AES.

diff --git a/assets/javascripts/discourse/initializers/hook-composer.js.es6 b/assets/javascripts/discourse/initializers/hook-composer.js.es6
index 0792618..b3b5f3c 100644
--- a/assets/javascripts/discourse/initializers/hook-composer.js.es6
+++ b/assets/javascripts/discourse/initializers/hook-composer.js.es6
@@ -2,13 +2,13 @@ import { observes, on } from "ember-addons/ember-computed-decorators";
 import { ajax } from "discourse/lib/ajax";
 import Composer from "discourse/models/composer";
 import {
-  decrypt,
-  rsaDecrypt
+  importKey,
+  decrypt
 } from "discourse/plugins/discourse-encrypt/lib/keys";
 import {
   ENCRYPT_ACTIVE,
   getEncryptionStatus,
-  getPrivateKey,
+  getRsaKey,
   getTopicKey,
   getTopicTitle,
   hasTopicKey
@@ -125,11 +125,23 @@ export default {
             decrypt(key, model.reply)
           );
         } else {
-          const pk = getPrivateKey();
-          decTitle =
-            model.title && pk.then(key => rsaDecrypt(key, model.title));
-          decReply =
-            model.reply && pk.then(key => rsaDecrypt(key, model.reply));
+          const pos = model.reply ? model.reply.indexOf("\n") : -1;
+          if (pos !== -1) {
+            const topicKey = model.reply.substr(0, pos);
+            model.reply = model.reply.substr(pos + 1);
+
+            const decKey = getRsaKey().then(keyPair =>
+              importKey(topicKey, keyPair[1])
+            );
+
+            decTitle = model.title
+              ? decKey.then(key => decrypt(key, model.title))
+              : "";
+
+            decReply = model.reply
+              ? decKey.then(key => decrypt(key, model.reply))
+              : "";
+          }
         }
       }
 
diff --git a/assets/javascripts/discourse/initializers/hook-decrypt-post.js.es6 b/assets/javascripts/discourse/initializers/hook-decrypt-post.js.es6
index f1302d8..1e0f19b 100644
--- a/assets/javascripts/discourse/initializers/hook-decrypt-post.js.es6
+++ b/assets/javascripts/discourse/initializers/hook-decrypt-post.js.es6
@@ -9,7 +9,7 @@ import { decrypt } from "discourse/plugins/discourse-encrypt/lib/keys";
 import {
   ENCRYPT_DISABLED,
   getEncryptionStatus,
-  getPrivateKey,
+  getRsaKey,
   getTopicKey,
   hasTopicKey,
   hasTopicTitle
@@ -41,7 +41,7 @@ export default {
             state.encrypted = ciphertext;
             state.decrypting = true;
 
-            getPrivateKey()
+            getRsaKey()
               .then(() =>
                 getTopicKey(topicId)
                   .then(key => decrypt(key, ciphertext))
diff --git a/assets/javascripts/discourse/initializers/hook-draft.js.es6 b/assets/javascripts/discourse/initializers/hook-draft.js.es6
index 93763a8..8265834 100644
--- a/assets/javascripts/discourse/initializers/hook-draft.js.es6
+++ b/assets/javascripts/discourse/initializers/hook-draft.js.es6
@@ -1,12 +1,16 @@
 import Composer from "discourse/models/composer";
 import Draft from "discourse/models/draft";
 import {
+  encrypt,
+  exportKey,
+  generateKey
+} from "discourse/plugins/discourse-encrypt/lib/keys";
+import {
   hasTopicKey,
-  getPublicKey,
+  getRsaKey,
   getEncryptionStatus,
   ENCRYPT_ACTIVE
 } from "discourse/plugins/discourse-encrypt/lib/discourse";
-import { rsaEncrypt } from "discourse/plugins/discourse-encrypt/lib/keys";
 import { filterObjectKeys } from "discourse/plugins/discourse-encrypt/lib/utils";
 
 const ALLOWED_DRAFT_FIELDS = [
@@ -36,7 +40,7 @@ export default {
       save(draftKey, sequence, data) {
         // TODO: https://github.com/emberjs/ember.js/issues/15291
         let { _super } = this;
-        let encrypted, encTitle, encReply;
+        let encrypted;
 
         if (draftKey === Composer.NEW_PRIVATE_MESSAGE_KEY) {
           const controller = container.lookup("controller:composer");
@@ -48,15 +52,28 @@ export default {
 
         if (encrypted) {
           data = filterObjectKeys(data, ALLOWED_DRAFT_FIELDS);
+          if (!data.title && !data.reply) {
+            return _super.call(this, draftKey, sequence, data);
+          }
+
+          const topicKey = generateKey();
+
+          const encKey = Ember.RSVP.Promise.all([topicKey, getRsaKey()]).then(
+            ([key, keyPair]) => exportKey(key, keyPair[0])
+          );
+
+          const encTitle = data.title
+            ? topicKey.then(key => encrypt(key, data.title))
+            : "";
 
-          const pk = getPublicKey();
-          encTitle = data.title && pk.then(key => rsaEncrypt(key, data.title));
-          encReply = data.reply && pk.then(key => rsaEncrypt(key, data.reply));
+          const encReply = data.reply
+            ? topicKey.then(key => encrypt(key, data.reply))
+            : "";
 
-          return Ember.RSVP.Promise.all([encTitle, encReply]).then(
-            ([title, reply]) => {
+          return Ember.RSVP.Promise.all([encTitle, encReply, encKey]).then(
+            ([title, reply, key]) => {
               data.title = title;
-              data.reply = reply;
+              data.reply = key + "\n" + reply;
               return _super.call(this, draftKey, sequence, data);
             }
           );
diff --git a/assets/javascripts/lib/discourse.js.es6 b/assets/javascripts/lib/discourse.js.es6
index afc37c6..7c5e0a3 100644
--- a/assets/javascripts/lib/discourse.js.es6
+++ b/assets/javascripts/lib/discourse.js.es6
@@ -30,14 +30,9 @@ export const PACKED_KEY_FOOTER =
   "=============== END EXPORTED DISCOURSE ENCRYPT KEY PAIR ===============";
 
 /**
- * @var User's public key used to encrypt topic keys and drafts for private message.
+ * @var Array of public and private key.
  */
-let publicKey;
-
-/**
- * @var User's private key used to decrypt topic keys.
- */
-let privateKey;
+let rsaKey;
 
 /**
  * @var Dictionary of all topic keys (topic_id => key).
@@ -52,39 +47,14 @@ const topicTitles = {};
 /**
  * Gets a user's key pair from the database and caches it for future usage.
  *
- * @return Tuple of two public and private CryptoKey.
+ * @return Tuple of public and private `CryptoKey`.
  */
-export function getKeyPair() {
-  return loadKeyPairFromIndexedDb().then(keyPair => {
-    if (!keyPair || !keyPair[0] || !keyPair[1]) {
-      return Ember.RSVP.Promise.reject();
-    }
-
-    [publicKey, privateKey] = keyPair;
-    return keyPair;
-  });
-}
-
-/**
- * Gets user's public key.
- *
- * @return CryptoKey
- */
-export function getPublicKey() {
-  return publicKey
-    ? Ember.RSVP.Promise.resolve(publicKey)
-    : getKeyPair().then(keyPair => keyPair[0]);
-}
+export function getRsaKey() {
+  if (!rsaKey) {
+    rsaKey = loadKeyPairFromIndexedDb();
+  }
 
-/**
- * Gets user's private key.
- *
- * @return CryptoKey
- */
-export function getPrivateKey() {
-  return privateKey
-    ? Ember.RSVP.Promise.resolve(privateKey)
-    : getKeyPair().then(keyPair => keyPair[1]);
+  return rsaKey;
 }
 
 /**
@@ -116,8 +86,8 @@ export function getTopicKey(topicId) {
   } else if (key instanceof CryptoKey) {
     return Ember.RSVP.Promise.resolve(key);
   } else if (!(key instanceof Promise || key instanceof Ember.RSVP.Promise)) {
-    topicKeys[topicId] = getPrivateKey().then(privKey =>
-      importKey(key, privKey)
+    topicKeys[topicId] = getRsaKey().then(keyPair =>
+      importKey(key, keyPair[1])
     );
   }

GitHub sha: efcef2f2

1 Like