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