Skip to content
Snippets Groups Projects
Verified Commit 6b5265df authored by Joe Donofry's avatar Joe Donofry
Browse files

Update key export / import to follow unstable spec. Fix some

memory leaks in the implementation.
parent 74c5b9e4
No related branches found
No related tags found
No related merge requests found
......@@ -106,10 +106,11 @@ set_package_properties(Boost PROPERTIES
TYPE REQUIRED
)
add_library(matrix_client
add_library(matrix_client SHARED
lib/http/client.cpp
lib/http/session.cpp
lib/crypto/client.cpp
lib/crypto/utils.cpp
lib/utils.cpp
lib/structs/common.cpp
lib/structs/errors.cpp
......
......@@ -80,13 +80,18 @@ from_json(const nlohmann::json &obj, ExportedSession &s)
inline void
to_json(nlohmann::json &obj, const ExportedSessionKeys &keys)
{
obj["sessions"] = keys.sessions;
obj = keys.sessions;
}
inline void
from_json(const nlohmann::json &obj, ExportedSessionKeys &keys)
{
keys.sessions = obj.at("sessions").get<std::vector<ExportedSession>>();
try {
keys.sessions = obj.get<std::vector<ExportedSession>>();
// might be the old format.
} catch (const nlohmann::json::exception&) {
keys.sessions = obj.at("sessions").get<std::vector<ExportedSession>>();
}
}
inline void
......
......@@ -3,12 +3,14 @@
#include <string>
#include <vector>
#include <algorithm>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <boost/algorithm/string.hpp>
namespace mtx {
namespace crypto {
......@@ -16,13 +18,28 @@ namespace crypto {
//! Data representation used to interact with libolm.
using BinaryBuf = std::vector<uint8_t>;
const std::string HEADER_LINE ( "-----BEGIN MEGOLM SESSION DATA-----");
const std::string TRAILER_LINE ( "-----END MEGOLM SESSION DATA-----");
//! 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 AES_CTR_256_Encrypt(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv);
BinaryBuf AES_CTR_256_Decrypt(const std::string ciphertext, const BinaryBuf aes256Key, BinaryBuf iv);
BinaryBuf HMAC_SHA256(const BinaryBuf hmacKey, const BinaryBuf data);
//! Translates the data back into the binary buffer, taking care
//! to remove the header and footer elements.
std::string unpack_key_file(const std::string &data);
template<typename T>
void remove_substrs(std::basic_string<T>& s,
const std::basic_string<T>& p);
void uint8_to_uint32(uint8_t b[4], uint32_t &u32);
void uint32_to_uint8 (uint8_t b[4], uint32_t u32);
void print_binary_buf(const BinaryBuf buf) ;
......
......@@ -561,7 +561,7 @@ mtx::crypto::encrypt_exported_sessions(const mtx::crypto::ExportedSessionKeys &k
BinaryBuf hmac256 = BinaryBuf(buf.begin() + 32, buf.begin() + (2 * 32));
auto ciphertext = mtx::crypto::AES_CTR_256(plaintext,
auto ciphertext = mtx::crypto::AES_CTR_256_Encrypt(plaintext,
aes256,
nonce);
......@@ -594,31 +594,87 @@ mtx::crypto::encrypt_exported_sessions(const mtx::crypto::ExportedSessionKeys &k
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"};
// Parse the data into a base64 string without the header and footer
std::string unpacked = mtx::crypto::unpack_key_file(data);
const auto nonce_start = data.begin();
const auto nonce_end = nonce_start + crypto_secretbox_NONCEBYTES;
auto nonce = BinaryBuf(nonce_start, nonce_end);
std::string binary_str = base642bin(unpacked);
const auto salt_end = nonce_end + crypto_pwhash_SALTBYTES;
auto salt = BinaryBuf(nonce_end, salt_end);
const auto binary_start = binary_str.begin();
const auto binary_end = binary_str.end();
auto ciphertext = BinaryBuf(salt_end, data.end());
auto decrypted = create_buffer(ciphertext.size() - crypto_secretbox_MACBYTES);
// Format version 0x01, 1 byte
const auto format_end = binary_start + 1;
auto format = BinaryBuf(binary_start, format_end);
auto key = derive_key(pass, salt);
// Salt, 16 bytes
const auto salt_end = format_end + crypto_pwhash_SALTBYTES;
auto salt = BinaryBuf(format_end, salt_end);
if (crypto_secretbox_open_easy(decrypted.data(),
reinterpret_cast<const unsigned char *>(ciphertext.data()),
ciphertext.size(),
nonce.data(),
reinterpret_cast<const unsigned char *>(key.data())) != 0)
throw sodium_exception{"crypto_secretbox_open_easy", "failed to decrypt"};
// IV, 16 bytes
const auto iv_end = salt_end + AES_BLOCK_SIZE;
auto iv = BinaryBuf(salt_end, iv_end);
return json::parse(std::string(decrypted.begin(), decrypted.end()));
// Number of rounds, 4 bytes
const auto rounds_end = iv_end + sizeof(uint32_t);
auto rounds_buff = BinaryBuf(iv_end, rounds_end);
uint8_t rounds_arr[4];
std::copy(rounds_buff.begin(), rounds_buff.end(), rounds_arr);
uint32_t rounds;
mtx::crypto::uint8_to_uint32(rounds_arr, rounds);
// Variable-length JSON object...
const auto json_end = binary_end - SHA256_DIGEST_LENGTH;
auto json = BinaryBuf(rounds_end, json_end);
// HMAC of the above, 32 bytes
auto hmac = BinaryBuf(json_end, binary_end);
// derive the keys
auto buf = mtx::crypto::PBKDF2_HMAC_SHA_512(pass,
salt,
rounds);
BinaryBuf aes256 = BinaryBuf(buf.begin(), buf.begin() + 32);
BinaryBuf hmac256 = BinaryBuf(buf.begin() + 32, buf.begin() + (2 * 32));
// get hmac and verify they match
auto hmacSha256 = mtx::crypto::HMAC_SHA256(hmac256, BinaryBuf(binary_start, json_end));
if (hmacSha256 != hmac) {
throw sodium_exception{"decrypt_exported_sessions", "HMAC doesn't match"};
}
const std::string ciphertext(json.begin(), json.end());
auto decrypted = mtx::crypto::AES_CTR_256_Decrypt(ciphertext, aes256, iv);
// TODO: Move this to a helper function for the 'old' format that
// nheko enabled pre-e2e key spec.
// 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"};
// const auto nonce_start = data.begin();
// const auto nonce_end = nonce_start + crypto_secretbox_NONCEBYTES;
// auto nonce = BinaryBuf(nonce_start, nonce_end);
// const auto salt_end = nonce_end + crypto_pwhash_SALTBYTES;
// auto salt = BinaryBuf(nonce_end, salt_end);
// auto ciphertext = BinaryBuf(salt_end, data.end());
// auto decrypted = create_buffer(ciphertext.size() - crypto_secretbox_MACBYTES);
// auto key = derive_key(pass, salt);
// if (crypto_secretbox_open_easy(decrypted.data(),
// reinterpret_cast<const unsigned char *>(ciphertext.data()),
// ciphertext.size(),
// nonce.data(),
// reinterpret_cast<const unsigned char *>(key.data())) != 0)
// throw sodium_exception{"crypto_secretbox_open_easy", "failed to decrypt"};
std::string plaintext(decrypted.begin(), decrypted.end());
return json::parse(plaintext);
}
std::string
......
......@@ -16,14 +16,14 @@ PBKDF2_HMAC_SHA_512(const std::string pass, const BinaryBuf salt, uint32_t itera
}
BinaryBuf
AES_CTR_256(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv) {
AES_CTR_256_Encrypt(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv) {
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
unsigned char *cipher;
unsigned char *cipher = new unsigned char[plaintext.size()];
uint8_t *iv_data = iv.data();
// need to set bit 63 to 0
......@@ -58,10 +58,85 @@ AES_CTR_256(const std::string plaintext, const BinaryBuf aes256Key, BinaryBuf iv
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return BinaryBuf(reinterpret_cast<uint8_t *>(cipher), cipher + ciphertext_len);
BinaryBuf encrypted(reinterpret_cast<uint8_t *>(cipher), cipher + ciphertext_len);
delete [] cipher;
return encrypted;
}
BinaryBuf
AES_CTR_256_Decrypt(const std::string ciphertext, const BinaryBuf aes256Key, BinaryBuf iv)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
unsigned char *plaintext = new unsigned char[ciphertext.size()];
/* Create and initialise the context */
if (!(ctx = EVP_CIPHER_CTX_new())) {
// handleErrors();
}
/* Initialise the decryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* In this example we are using 256 bit AES (i.e. a 256 bit key). The
* IV size for *most* modes is the same as the block size. For AES this
* is 128 bits */
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, aes256Key.data(), iv.data())) {
// handleErrors();
}
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, reinterpret_cast<const unsigned char*>(&ciphertext.data()[0]), ciphertext.size())) {
// handleErrors();
}
plaintext_len = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
*/
if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
// handleErrors();
}
plaintext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
BinaryBuf decrypted(reinterpret_cast<uint8_t *>(plaintext), plaintext + plaintext_len);
delete[] plaintext;
return decrypted;
}
template<typename T>
void remove_substrs(std::basic_string<T>& s,
const std::basic_string<T>& p) {
auto n = p.length();
for (auto i = s.find(p);
i != std::basic_string<T>::npos;
i = s.find(p))
s.erase(i, n);
}
std::string
unpack_key_file(const std::string &data) {
std::string unpacked(data);
remove_substrs(unpacked, HEADER_LINE);
remove_substrs(unpacked, TRAILER_LINE);
remove_substrs(unpacked, std::string("\n"));
return unpacked;
}
BinaryBuf
HMAC_SHA256 (const BinaryBuf hmacKey, const BinaryBuf data) {
unsigned int len = SHA256_DIGEST_LENGTH;
......@@ -79,6 +154,10 @@ print_binary_buf(const BinaryBuf buf) {
std::cout << std::endl;
}
void uint8_to_uint32(uint8_t b[4], uint32_t &u32) {
u32 = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
}
void uint32_to_uint8 (uint8_t b[4], uint32_t u32) {
b[3] = (uint8_t)u32;
b[2] = (uint8_t)(u32>>=8);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment