From 74c5b9e48deba3e552505f1ffe54a5691cafbc35 Mon Sep 17 00:00:00 2001
From: redsky17 <joedonofry@gmail.com>
Date: Wed, 20 Mar 2019 23:04:50 +0000
Subject: [PATCH] Update E2E Key Export to follow specification at:
 https://matrix.org/docs/spec/client_server/unstable.html#key-export-format

Currently, importing does not work.  Do not use this commit
if you intend to keep import/export working.  I also have not yet
cleaned up the code, so there might be some nasties that have
not been discovered.
---
 include/mtxclient/crypto/client.hpp |  1 +
 include/mtxclient/crypto/utils.hpp  | 31 ++++++++++
 lib/crypto/client.cpp               | 58 ++++++++++++++-----
 lib/crypto/utils.cpp                | 90 +++++++++++++++++++++++++++++
 4 files changed, 166 insertions(+), 14 deletions(-)
 create mode 100644 include/mtxclient/crypto/utils.hpp
 create mode 100644 lib/crypto/utils.cpp

diff --git a/include/mtxclient/crypto/client.hpp b/include/mtxclient/crypto/client.hpp
index f9a354bd7..2f9e35f47 100644
--- a/include/mtxclient/crypto/client.hpp
+++ b/include/mtxclient/crypto/client.hpp
@@ -14,6 +14,7 @@
 
 #include "mtxclient/crypto/objects.hpp"
 #include "mtxclient/crypto/types.hpp"
+#include "mtxclient/crypto/utils.hpp"
 
 namespace mtx {
 namespace crypto {
diff --git a/include/mtxclient/crypto/utils.hpp b/include/mtxclient/crypto/utils.hpp
new file mode 100644
index 000000000..fb9c37448
--- /dev/null
+++ b/include/mtxclient/crypto/utils.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+
+#include <string>
+#include <vector>
+
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+
+namespace mtx {
+namespace crypto {
+
+//! Data representation used to interact with libolm.
+using BinaryBuf = std::vector<uint8_t>;
+
+//! Simple wrapper around the OpenSSL PKCS5_PBKDF2_HMAC function
+BinaryBuf PBKDF2_HMAC_SHA_512(const std::string pass, const BinaryBuf salt, uint32_t iterations);
+
+BinaryBuf AES_CTR_256(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv);
+
+BinaryBuf HMAC_SHA256(const BinaryBuf hmacKey, const BinaryBuf data);
+
+void uint32_to_uint8 (uint8_t b[4], uint32_t u32);
+
+void print_binary_buf(const BinaryBuf buf) ;
+
+} // namespace crypto
+} // namespace mtx
\ No newline at end of file
diff --git a/lib/crypto/client.cpp b/lib/crypto/client.cpp
index 5af207530..fe3bd50d1 100644
--- a/lib/crypto/client.cpp
+++ b/lib/crypto/client.cpp
@@ -2,8 +2,9 @@
 
 #include "mtxclient/crypto/client.hpp"
 #include "mtxclient/crypto/types.hpp"
+#include "mtxclient/crypto/utils.hpp"
 
-#include "sodium.h"
+#include <sodium.h>
 
 using json = nlohmann::json;
 using namespace mtx::crypto;
@@ -536,35 +537,64 @@ mtx::crypto::encrypt_exported_sessions(const mtx::crypto::ExportedSessionKeys &k
                                        std::string pass)
 {
         const auto plaintext      = json(keys).dump();
-        const auto msg_len        = plaintext.size();
-        const auto ciphertext_len = crypto_secretbox_MACBYTES + msg_len;
 
-        auto nonce      = create_buffer(crypto_secretbox_NONCEBYTES);
-        auto ciphertext = create_buffer(ciphertext_len);
+        auto nonce      = create_buffer(AES_BLOCK_SIZE);
 
         auto salt = create_buffer(crypto_pwhash_SALTBYTES);
-        auto key  = derive_key(pass, salt);
 
-        crypto_secretbox_easy(reinterpret_cast<unsigned char *>(ciphertext.data()),
-                              reinterpret_cast<const unsigned char *>(plaintext.data()),
-                              msg_len,
-                              nonce.data(),
-                              reinterpret_cast<const unsigned char *>(key.data()));
+        //auto key  = derive_key(pass, salt);
+        auto buf = create_buffer(64U);
 
-        // Format of the output buffer: (nonce + salt + ciphertext)
-        BinaryBuf output{nonce};
+
+        //crypto_secretbox_easy(reinterpret_cast<unsigned char *>(ciphertext.data()),
+        //                      reinterpret_cast<const unsigned char *>(plaintext.data()),
+        //                      msg_len,
+        //                      nonce.data(),
+        //                      reinterpret_cast<const unsigned char *>(key.data()));
+        uint32_t iterations = 100000;
+        buf = mtx::crypto::PBKDF2_HMAC_SHA_512(pass,
+                                   salt,
+                                   iterations);
+
+        BinaryBuf aes256 = BinaryBuf(buf.begin(), buf.begin() + 32);
+
+
+        BinaryBuf hmac256 = BinaryBuf(buf.begin() + 32, buf.begin() + (2 * 32));
+
+        auto ciphertext = mtx::crypto::AES_CTR_256(plaintext,
+                                aes256,
+                                nonce);
+
+        uint8_t iterationsArr[4];
+        mtx::crypto::uint32_to_uint8(iterationsArr, iterations);
+
+        // Format of the output buffer: (0x01 + salt + IV + number of rounds + ciphertext + hmac-sha-256)
+        BinaryBuf output{0x01};
         output.insert(
           output.end(), std::make_move_iterator(salt.begin()), std::make_move_iterator(salt.end()));
+        output.insert(output.end(),
+                      std::make_move_iterator(nonce.begin()),
+                      std::make_move_iterator(nonce.end()));
+        output.insert(output.end(), &iterationsArr[0], &iterationsArr[4]);
         output.insert(output.end(),
                       std::make_move_iterator(ciphertext.begin()),
                       std::make_move_iterator(ciphertext.end()));
 
-        return std::string(output.begin(), output.end());
+        // Need to hmac-sha256 our string so far, and then use that to finish making the output.
+        auto hmacSha256 = mtx::crypto::HMAC_SHA256(hmac256, output);
+
+        output.insert(output.end(),
+                      std::make_move_iterator(hmacSha256.begin()),
+                      std::make_move_iterator(hmacSha256.end()));
+        auto encrypted = std::string(output.begin(), output.end());
+
+        return encrypted;
 }
 
 mtx::crypto::ExportedSessionKeys
 mtx::crypto::decrypt_exported_sessions(const std::string &data, std::string pass)
 {
+        std::cout << "decrypt_exported_sessions data: " << data << std::endl;
         if (data.size() <
             crypto_secretbox_MACBYTES + crypto_secretbox_NONCEBYTES + crypto_pwhash_SALTBYTES)
                 throw sodium_exception{"decrypt_exported_sessions", "ciphertext too small"};
diff --git a/lib/crypto/utils.cpp b/lib/crypto/utils.cpp
new file mode 100644
index 000000000..8800a0fe8
--- /dev/null
+++ b/lib/crypto/utils.cpp
@@ -0,0 +1,90 @@
+#include "mtxclient/crypto/utils.hpp"
+
+#include <iostream>
+
+namespace mtx {
+namespace crypto {
+
+BinaryBuf
+PBKDF2_HMAC_SHA_512(const std::string pass, const BinaryBuf salt, uint32_t iterations) {
+    uint8_t out[SHA512_DIGEST_LENGTH];
+    PKCS5_PBKDF2_HMAC(&pass[0], pass.size(), salt.data(), salt.size(), iterations, EVP_sha512(), SHA512_DIGEST_LENGTH, out);
+    
+    BinaryBuf output(out, out + SHA512_DIGEST_LENGTH);
+
+    return output;
+}
+
+BinaryBuf
+AES_CTR_256(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv) {
+    EVP_CIPHER_CTX *ctx;
+    
+    int len;
+
+    int ciphertext_len;
+
+    unsigned char *cipher;
+
+    uint8_t *iv_data = iv.data();
+    // need to set bit 63 to 0
+    *(iv_data) &= ~(1UL << 63);
+
+    /* Create and initialise the context */
+    if(!(ctx = EVP_CIPHER_CTX_new())) {
+        //handleErrors();
+    }
+
+    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, aes256Key.data(), iv_data)) {
+        //handleErrors();
+    }
+
+    /* Provide the message to be encrypted, and obtain the encrypted output.
+    * EVP_EncryptUpdate can be called multiple times if necessary
+    */
+    if(1 != EVP_EncryptUpdate(ctx, cipher, &len, reinterpret_cast<const unsigned char*>(&plaintext.c_str()[0]), plaintext.size())) {
+        //handleErrors();
+    }
+    ciphertext_len = len;
+
+    /* Finalise the encryption. Further ciphertext bytes may be written at
+    * this stage.
+    */
+    if(1 != EVP_EncryptFinal_ex(ctx, cipher + len, &len)) {
+        //handleErrors();
+    }
+
+    ciphertext_len += len;
+
+    /* Clean up */
+    EVP_CIPHER_CTX_free(ctx);
+
+    return BinaryBuf(reinterpret_cast<uint8_t *>(cipher), cipher + ciphertext_len);
+
+}
+
+BinaryBuf
+HMAC_SHA256 (const BinaryBuf hmacKey, const BinaryBuf data) {
+    unsigned int len = SHA256_DIGEST_LENGTH;
+    unsigned char digest[SHA256_DIGEST_LENGTH];
+    HMAC(EVP_sha256(), hmacKey.data(), hmacKey.size(), data.data(), data.size(), digest, &len);
+    BinaryBuf output(digest, digest + SHA256_DIGEST_LENGTH);
+    return output;
+}
+
+void
+print_binary_buf(const BinaryBuf buf) {
+    for (uint8_t val : buf) {
+        std::cout << std::to_string(val) << ",";
+    }
+    std::cout << std::endl;
+}
+
+void uint32_to_uint8 (uint8_t b[4], uint32_t u32) {
+    b[3] = (uint8_t)u32;
+    b[2] = (uint8_t)(u32>>=8);
+    b[1] = (uint8_t)(u32>>=8);
+    b[0] = (uint8_t)(u32>>=8);
+}
+
+} // namespace crypto
+} // namespace mtx
-- 
GitLab