From 128d45cc83b1378422625ea975152e1e3c9d88f6 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 11 Jun 2018 17:48:45 -0400 Subject: add initial implementation of basic private key encryption functionality --- Makefile | 3 +- include/olm/pk.h | 148 ++++++++++++++++++++++++++ javascript/olm_pk.js | 179 ++++++++++++++++++++++++++++++++ javascript/olm_post.js | 2 + javascript/test/pk.spec.js | 64 ++++++++++++ src/pk.cpp | 253 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_pk.cpp | 104 +++++++++++++++++++ 7 files changed, 752 insertions(+), 1 deletion(-) create mode 100644 include/olm/pk.h create mode 100644 javascript/olm_pk.js create mode 100644 javascript/test/pk.spec.js create mode 100644 src/pk.cpp create mode 100644 tests/test_pk.cpp diff --git a/Makefile b/Makefile index 5012118..154954c 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json -PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h +PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h SOURCES := $(wildcard src/*.cpp) $(wildcard src/*.c) \ lib/crypto-algorithms/sha256.c \ @@ -42,6 +42,7 @@ JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS)) JS_PRE := $(wildcard javascript/*pre.js) JS_POST := javascript/olm_outbound_group_session.js \ javascript/olm_inbound_group_session.js \ + javascript/olm_pk.js \ javascript/olm_post.js DOCS := tracing/README.html \ docs/megolm.html \ diff --git a/include/olm/pk.h b/include/olm/pk.h new file mode 100644 index 0000000..a91a80d --- /dev/null +++ b/include/olm/pk.h @@ -0,0 +1,148 @@ +/* Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OLM_PK_H_ +#define OLM_PK_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OlmPkEncryption OlmPkEncryption; + +/* The size of an encryption object in bytes */ +size_t olm_pk_encryption_size(); + +/** Initialise an encryption object using the supplied memory + * The supplied memory must be at least olm_pk_encryption_size() bytes */ +OlmPkEncryption *olm_pk_encryption( + void * memory +); + +/** A null terminated string describing the most recent error to happen to an + * encryption object */ +const char * olm_pk_encryption_last_error( + OlmPkEncryption * encryption +); + +/** Clears the memory used to back this encryption object */ +size_t olm_clear_pk_encryption( + OlmPkEncryption *encryption +); + +/** Set the recipient's public key for encrypting to */ +size_t olm_pk_encryption_set_recipient_key( + OlmPkEncryption *encryption, + void const *public_key, size_t public_key_length +); + +/** Get the length of the ciphertext that will correspond to a plaintext of the + * given length. */ +size_t olm_pk_ciphertext_length( + OlmPkEncryption *encryption, + size_t plaintext_length +); + +/** Get the length of the message authentication code. */ +size_t olm_pk_mac_length( + OlmPkEncryption *encryption +); + +/** Get the length of a public or ephemeral key */ +size_t olm_pk_key_length(); + +/** The number of random bytes needed to encrypt a message. */ +size_t olm_pk_encrypt_random_length( + OlmPkEncryption *encryption +); + +/** Encrypt a plaintext for the recipient set using + * olm_pk_encryption_set_recipient_key. Returns olm_error() on failure. If the + * ciphertext, mac, or ephemeral_key buffers were too small then + * olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there + * weren't enough random bytes then olm_pk_encryption_last_error() will be + * "NOT_ENOUGH_RANDOM". */ +size_t olm_pk_encrypt( + OlmPkEncryption *encryption, + void const * plaintext, size_t plaintext_length, + void * ciphertext, size_t ciphertext_length, + void * mac, size_t mac_length, + void * ephemeral_key, size_t ephemeral_key_size, + void * random, size_t random_length +); + +typedef struct OlmPkDecryption OlmPkDecryption; + +/* The size of a decryption object in bytes */ +size_t olm_pk_decryption_size(); + +/** Initialise a decryption object using the supplied memory + * The supplied memory must be at least olm_pk_decryption_size() bytes */ +OlmPkDecryption *olm_pk_decryption( + void * memory +); + +/** A null terminated string describing the most recent error to happen to a + * decription object */ +const char * olm_pk_decryption_last_error( + OlmPkDecryption * decryption +); + +/** Clears the memory used to back this decryption object */ +size_t olm_clear_pk_decryption( + OlmPkDecryption *decryption +); + +/** The number of random bytes needed to generate a new key. */ +size_t olm_pk_generate_key_random_length(); + +/** Generate a new key to use for decrypting messages. The associated public + * key will be written to the pubkey buffer. Returns olm_error() on failure. If + * the pubkey buffer is too small then olm_pk_decryption_last_error() will be + * "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then + * olm_pk_decryption_last_error() will be "NOT_ENOUGH_RANDOM". */ +size_t olm_pk_generate_key( + OlmPkDecryption * decryption, + void * pubkey, size_t pubkey_length, + void * random, size_t random_length +); + +/** Get the length of the plaintext that will correspond to a ciphertext of the + * given length. */ +size_t olm_pk_max_plaintext_length( + OlmPkDecryption * decryption, + size_t ciphertext_length +); + +/** Decrypt a ciphertext. The input ciphertext buffer is destroyed. Returns + * the length of the plaintext on success. Returns olm_error() on failure. If + * the plaintext buffer is too small then olm_pk_encryption_last_error() will + * be "OUTPUT_BUFFER_TOO_SMALL". */ +size_t olm_pk_decrypt( + OlmPkDecryption * decrytion, + void const * ephemeral_key, size_t ephemeral_key_length, + void const * mac, size_t mac_length, + void * ciphertext, size_t ciphertext_length, + void * plaintext, size_t max_plaintext_length +); + +#ifdef __cplusplus +} +#endif + +#endif /* OLM_PK_H_ */ diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js new file mode 100644 index 0000000..e63e907 --- /dev/null +++ b/javascript/olm_pk.js @@ -0,0 +1,179 @@ +function PkEncryption() { + var size = Module['_olm_pk_encryption_size'](); + this.buf = malloc(size); + this.ptr = Module['_olm_pk_encryption'](this.buf); +} + +function pk_encryption_method(wrapped) { + return function() { + var result = wrapped.apply(this, arguments); + if (result === OLM_ERROR) { + var message = Pointer_stringify( + Module['_olm_pk_encryption_last_error'](arguments[0]) + ); + throw new Error("OLM." + message); + } + return result; + } +} + +PkEncryption.prototype['free'] = function() { + Module['_olm_clear_pk_encryption'](this.ptr); + free(this.ptr); +} + +PkEncryption.prototype['set_recipient_key'] = restore_stack(function(key) { + var key_array = array_from_string(key); + var key_buffer = stack(key_array); + pk_encryption_method(Module['_olm_pk_encryption_set_recipient_key'])( + this.ptr, key_buffer, key_array.length + ); +}); + +PkEncryption.prototype['encrypt'] = restore_stack(function( + plaintext +) { + var plaintext_buffer, ciphertext_buffer, plaintext_length; + try { + plaintext_length = Module['lengthBytesUTF8'](plaintext) + plaintext_buffer = malloc(plaintext_length + 1); + Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); + var random_length = pk_encryption_method( + Module['_olm_pk_encrypt_random_length'] + )(); + var random = random_stack(random_length); + var ciphertext_length = pk_encryption_method( + Module['_olm_pk_ciphertext_length'] + )(this.ptr, plaintext_length); + ciphertext_buffer = malloc(ciphertext_length + NULL_BYTE_PADDING_LENGTH); + var mac_length = pk_encryption_method( + Module['_olm_pk_mac_length'] + )(this.ptr); + var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH); + Module['setValue']( + mac_buffer+mac_length, + 0, "i8" + ); + var ephemeral_length = pk_encryption_method( + Module['_olm_pk_key_length'] + )(); + var ephemeral_buffer = stack(ephemeral_length + NULL_BYTE_PADDING_LENGTH); + Module['setValue']( + ephemeral_buffer+ephemeral_length, + 0, "i8" + ); + pk_encryption_method(Module['_olm_pk_encrypt'])( + this.ptr, + plaintext_buffer, plaintext_length, + ciphertext_buffer, ciphertext_length, + mac_buffer, mac_length, + ephemeral_buffer, ephemeral_length, + random, random_length + ); + // UTF8ToString requires a null-terminated argument, so add the + // null terminator. + Module['setValue']( + ciphertext_buffer+ciphertext_length, + 0, "i8" + ); + return { + "ciphertext": Module['UTF8ToString'](ciphertext_buffer), + "mac": Pointer_stringify(mac_buffer), + "ephemeral": Pointer_stringify(ephemeral_buffer) + }; + } finally { + if (plaintext_buffer !== undefined) { + // don't leave a copy of the plaintext in the heap. + bzero(plaintext_buffer, plaintext_length + 1); + free(plaintext_buffer); + } + if (ciphertext_buffer !== undefined) { + free(ciphertext_buffer); + } + } +}); + + +function PkDecryption() { + var size = Module['_olm_pk_decryption_size'](); + this.buf = malloc(size); + this.ptr = Module['_olm_pk_decryption'](this.buf); +} + +function pk_decryption_method(wrapped) { + return function() { + var result = wrapped.apply(this, arguments); + if (result === OLM_ERROR) { + var message = Pointer_stringify( + Module['_olm_pk_decryption_last_error'](arguments[0]) + ); + throw new Error("OLM." + message); + } + return result; + } +} + +PkDecryption.prototype['free'] = function() { + Module['_olm_clear_pk_decryption'](this.ptr); + free(this.ptr); +} + +PkDecryption.prototype['generate_key'] = restore_stack(function () { + var random_length = pk_decryption_method( + Module['_olm_pk_key_length'] // FIXME: wrong method + )(); + var random_buffer = random_stack(random_length); + var pubkey_length = pk_encryption_method( + Module['_olm_pk_key_length'] + )(); + var pubkey_buffer = stack(pubkey_length); + pk_decryption_method(Module['_olm_pk_generate_key'])( + this.ptr, + pubkey_buffer, pubkey_length, + random_buffer, random_length + ); + return Pointer_stringify(pubkey_buffer); +}); + +PkDecryption.prototype['decrypt'] = restore_stack(function ( + ephemeral_key, mac, ciphertext +) { + var plaintext_buffer, ciphertext_buffer, plaintext_max_length; + try { + ciphertext_length = Module['lengthBytesUTF8'](ciphertext) + ciphertext_buffer = malloc(ciphertext_length + 1); + Module['stringToUTF8'](ciphertext, ciphertext_buffer, ciphertext_length + 1); + var ephemeralkey_array = array_from_string(ephemeral_key); + var ephemeralkey_buffer = stack(ephemeralkey_array); + var mac_array = array_from_string(mac); + var mac_buffer = stack(mac_array); + plaintext_max_length = pk_decryption_method(Module['_olm_pk_max_plaintext_length'])( + this.ptr, + ciphertext_length + ); + plaintext_buffer = malloc(plaintext_max_length + NULL_BYTE_PADDING_LENGTH); + var plaintext_length = pk_decryption_method(Module['_olm_pk_decrypt'])( + this.ptr, + ephemeralkey_buffer, ephemeralkey_array.length, + mac_buffer, mac_array.length, + ciphertext_buffer, ciphertext_length, + plaintext_buffer, plaintext_max_length + ); + // UTF8ToString requires a null-terminated argument, so add the + // null terminator. + Module['setValue']( + plaintext_buffer+plaintext_length, + 0, "i8" + ); + return Module['UTF8ToString'](plaintext_buffer); + } finally { + if (plaintext_buffer !== undefined) { + // don't leave a copy of the plaintext in the heap. + bzero(plaintext_buffer, plaintext_length + 1); + free(plaintext_buffer); + } + if (ciphertext_buffer !== undefined) { + free(ciphertext_buffer); + } + } +}) diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 91830fa..7a1d284 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -461,6 +461,8 @@ Utility.prototype['ed25519_verify'] = restore_stack(function( olm_exports["Account"] = Account; olm_exports["Session"] = Session; olm_exports["Utility"] = Utility; +olm_exports["PkEncryption"] = PkEncryption; +olm_exports["PkDecryption"] = PkDecryption; olm_exports["get_library_version"] = restore_stack(function() { var buf = stack(3); diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js new file mode 100644 index 0000000..9eec47e --- /dev/null +++ b/javascript/test/pk.spec.js @@ -0,0 +1,64 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +"use strict"; + +var Olm = require('../olm'); + +if (!Object.keys) { + Object.keys = function(o) { + var k=[], p; + for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p); + return k; + } +} + +describe("pk", function() { + var encryption, decryption; + + beforeEach(function() { + encryption = new Olm.PkEncryption(); + decryption = new Olm.PkDecryption(); + }); + + afterEach(function () { + if (encryption !== undefined) { + encryption.free(); + encryption = undefined; + } + if (decryption !== undefined) { + decryption.free(); + decryption = undefined; + } + }); + + it('should encrypt and decrypt', function () { + var TEST_TEXT='têst1'; + var pubkey = decryption.generate_key(); + encryption.set_recipient_key(pubkey); + var encrypted = encryption.encrypt(TEST_TEXT); + var decrypted = decryption.decrypt(encrypted.ephemeral, encrypted.mac, encrypted.ciphertext); + console.log(TEST_TEXT, "->", decrypted); + expect(decrypted).toEqual(TEST_TEXT); + + TEST_TEXT='hot beverage: ☕'; + encryption.set_recipient_key(pubkey); + encrypted = encryption.encrypt(TEST_TEXT); + decrypted = decryption.decrypt(encrypted.ephemeral, encrypted.mac, encrypted.ciphertext); + console.log(TEST_TEXT, "->", decrypted); + expect(decrypted).toEqual(TEST_TEXT); + }); +}); diff --git a/src/pk.cpp b/src/pk.cpp new file mode 100644 index 0000000..b4edf15 --- /dev/null +++ b/src/pk.cpp @@ -0,0 +1,253 @@ +/* Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "olm/pk.h" +#include "olm/cipher.h" +#include "olm/crypto.h" +#include "olm/ratchet.hh" +#include "olm/error.h" +#include "olm/memory.hh" +#include "olm/base64.hh" + +extern "C" { + +static const std::size_t MAC_LENGTH = 8; + + const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 = + OLM_CIPHER_INIT_AES_SHA_256(""); +const struct _olm_cipher *olm_pk_cipher = + OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256); + +struct OlmPkEncryption { + OlmErrorCode last_error; + _olm_curve25519_public_key recipient_key; +}; + +const char * olm_pk_encryption_last_error( + OlmPkEncryption * encryption +) { + auto error = encryption->last_error; + return _olm_error_to_string(error); +} + +size_t olm_pk_encryption_size() { + return sizeof(OlmPkEncryption); +} + +OlmPkEncryption *olm_pk_encryption( + void * memory +) { + olm::unset(memory, sizeof(OlmPkEncryption)); + return new(memory) OlmPkEncryption; +} + +size_t olm_clear_pk_encryption( + OlmPkEncryption *encryption +) { + /* Clear the memory backing the encryption */ + olm::unset(encryption, sizeof(OlmPkEncryption)); + /* Initialise a fresh encryption object in case someone tries to use it */ + new(encryption) OlmPkEncryption(); + return sizeof(OlmPkEncryption); +} + +size_t olm_pk_encryption_set_recipient_key ( + OlmPkEncryption *encryption, + void const * key, size_t key_length +) { + if (key_length < olm_pk_key_length()) { + encryption->last_error = + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; // FIXME: + return std::size_t(-1); + } + olm::decode_base64((const uint8_t*)key, olm_pk_key_length(), (uint8_t *)encryption->recipient_key.public_key); + return 0; +} + +size_t olm_pk_ciphertext_length( + OlmPkEncryption *encryption, + size_t plaintext_length +) { + return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)); +} + +size_t olm_pk_mac_length( + OlmPkEncryption *encryption +) { + return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher)); +} + +size_t olm_pk_encrypt_random_length( + OlmPkEncryption *encryption +) { + return CURVE25519_KEY_LENGTH; +} + +size_t olm_pk_encrypt( + OlmPkEncryption *encryption, + void const * plaintext, size_t plaintext_length, + void * ciphertext, size_t ciphertext_length, + void * mac, size_t mac_length, + void * ephemeral_key, size_t ephemeral_key_size, + void * random, size_t random_length +) { + if (ciphertext_length + < olm_pk_ciphertext_length(encryption, plaintext_length) + || mac_length + < _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher) + || ephemeral_key_size + < olm_pk_key_length()) { + encryption->last_error = + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + if (random_length < olm_pk_encrypt_random_length(encryption)) { + encryption->last_error = + OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; + return std::size_t(-1); + } + + _olm_curve25519_key_pair ephemeral_keypair; + _olm_crypto_curve25519_generate_key((uint8_t *) random, &ephemeral_keypair); + olm::encode_base64((const uint8_t *)ephemeral_keypair.public_key.public_key, CURVE25519_KEY_LENGTH, (uint8_t *)ephemeral_key); + + olm::SharedKey secret; + _olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, secret); + size_t raw_ciphertext_length = _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length); + uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length; + uint8_t raw_mac[MAC_LENGTH]; + size_t result = _olm_cipher_aes_sha_256_ops.encrypt( + olm_pk_cipher, + secret, sizeof(secret), + (const uint8_t *) plaintext, plaintext_length, + (uint8_t *) ciphertext_pos, raw_ciphertext_length, + (uint8_t *) raw_mac, MAC_LENGTH + ); + if (result != std::size_t(-1)) { + olm::encode_base64(raw_mac, MAC_LENGTH, (uint8_t *)mac); + olm::encode_base64(ciphertext_pos, raw_ciphertext_length, (uint8_t *)ciphertext); + } + return result; +} + +struct OlmPkDecryption { + OlmErrorCode last_error; + _olm_curve25519_key_pair key_pair; +}; + +const char * olm_pk_decryption_last_error( + OlmPkDecryption * decryption +) { + auto error = decryption->last_error; + return _olm_error_to_string(error); +} + +size_t olm_pk_decryption_size() { + return sizeof(OlmPkDecryption); +} + +OlmPkDecryption *olm_pk_decryption( + void * memory +) { + olm::unset(memory, sizeof(OlmPkDecryption)); + return new(memory) OlmPkDecryption; +} + +size_t olm_clear_pk_decryption( + OlmPkDecryption *decryption +) { + /* Clear the memory backing the decryption */ + olm::unset(decryption, sizeof(OlmPkDecryption)); + /* Initialise a fresh decryption object in case someone tries to use it */ + new(decryption) OlmPkDecryption(); + return sizeof(OlmPkDecryption); +} + +size_t olm_pk_generate_key_random_length() { + return CURVE25519_KEY_LENGTH; +} + +size_t olm_pk_key_length() { + return olm::encode_base64_length(CURVE25519_KEY_LENGTH); +} + +size_t olm_pk_generate_key( + OlmPkDecryption * decryption, + void * pubkey, size_t pubkey_length, + void * random, size_t random_length +) { + if (pubkey_length < CURVE25519_KEY_LENGTH) { + decryption->last_error = + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + if (random_length < olm_pk_generate_key_random_length()) { + decryption->last_error = + OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; + return std::size_t(-1); + } + + _olm_crypto_curve25519_generate_key((uint8_t *) random, &decryption->key_pair); + olm::encode_base64((const uint8_t *)decryption->key_pair.public_key.public_key, CURVE25519_KEY_LENGTH, (uint8_t *)pubkey); + return 0; +} + +size_t olm_pk_max_plaintext_length( + OlmPkDecryption * decryption, + size_t ciphertext_length +) { + return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(olm_pk_cipher, olm::decode_base64_length(ciphertext_length)); +} + +size_t olm_pk_decrypt( + OlmPkDecryption * decryption, + void const * ephemeral_key, size_t ephemeral_key_length, + void const * mac, size_t mac_length, + void * ciphertext, size_t ciphertext_length, + void * plaintext, size_t max_plaintext_length +) { + if (max_plaintext_length + < olm_pk_max_plaintext_length(decryption, ciphertext_length)) { + decryption->last_error = + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + + struct _olm_curve25519_public_key ephemeral; + olm::decode_base64((const uint8_t*)ephemeral_key, ephemeral_key_length, (uint8_t *)ephemeral.public_key); + olm::SharedKey secret; + _olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret); + uint8_t raw_mac[MAC_LENGTH]; + olm::decode_base64((const uint8_t*)mac, olm::encode_base64_length(MAC_LENGTH), raw_mac); + size_t raw_ciphertext_length = olm::decode_base64_length(ciphertext_length); + olm::decode_base64((const uint8_t *)ciphertext, ciphertext_length, (uint8_t *)ciphertext); + size_t result = _olm_cipher_aes_sha_256_ops.decrypt( + olm_pk_cipher, + secret, sizeof(secret), + (uint8_t *) raw_mac, MAC_LENGTH, + (const uint8_t *) ciphertext, raw_ciphertext_length, + (uint8_t *) plaintext, max_plaintext_length + ); + if (result == std::size_t(-1)) { + // we already checked the buffer sizes, so the only error that decrypt + // will return is if the MAC is incorrect + decryption->last_error = + OlmErrorCode::OLM_BAD_MESSAGE_MAC; + return std::size_t(-1); + } else { + return result; + } +} + +} diff --git a/tests/test_pk.cpp b/tests/test_pk.cpp new file mode 100644 index 0000000..4c6fb6e --- /dev/null +++ b/tests/test_pk.cpp @@ -0,0 +1,104 @@ +#include "olm/pk.h" +#include "olm/crypto.h" +#include "olm/olm.h" + +#include "unittest.hh" + +#include + +int main() { + + +{ /* Encryption Test Case 1 */ + +TestCase test_case("Public Key Encryption/Decryption Test Case 1"); + +std::uint8_t decryption_buffer[::olm_pk_decryption_size()]; +OlmPkDecryption *decryption = olm_pk_decryption(decryption_buffer); + +std::uint8_t alice_private[32] = { + 0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D, + 0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45, + 0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A, + 0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A +}; + +/* +std::uint8_t alice_public[32] = { + 0x85, 0x20, 0xF0, 0x09, 0x89, 0x30, 0xA7, 0x54, + 0x74, 0x8B, 0x7D, 0xDC, 0xB4, 0x3E, 0xF7, 0x5A, + 0x0D, 0xBF, 0x3A, 0x0D, 0x26, 0x38, 0x1A, 0xF4, + 0xEB, 0xA4, 0xA9, 0x8E, 0xAA, 0x9B, 0x4E, 0x6A +}; +*/ + +std::uint8_t bob_private[32] = { + 0x5D, 0xAB, 0x08, 0x7E, 0x62, 0x4A, 0x8A, 0x4B, + 0x79, 0xE1, 0x7F, 0x8B, 0x83, 0x80, 0x0E, 0xE6, + 0x6F, 0x3B, 0xB1, 0x29, 0x26, 0x18, 0xB6, 0xFD, + 0x1C, 0x2F, 0x8B, 0x27, 0xFF, 0x88, 0xE0, 0xEB +}; + +/* +std::uint8_t bob_public[32] = { + 0xDE, 0x9E, 0xDB, 0x7D, 0x7B, 0x7D, 0xC1, 0xB4, + 0xD3, 0x5B, 0x61, 0xC2, 0xEC, 0xE4, 0x35, 0x37, + 0x3F, 0x83, 0x43, 0xC8, 0x5B, 0x78, 0x67, 0x4D, + 0xAD, 0xFC, 0x7E, 0x14, 0x6F, 0x88, 0x2B, 0x4F +}; +*/ + +std::uint8_t pubkey[::olm_pk_key_length()]; + +olm_pk_generate_key( + decryption, + pubkey, sizeof(pubkey), + alice_private, sizeof(alice_private) +); + +//FIXME:assert_equals(alice_public, pubkey, 32); + +std::uint8_t encryption_buffer[::olm_pk_encryption_size()]; +OlmPkEncryption *encryption = olm_pk_encryption(encryption_buffer); + +olm_pk_encryption_set_recipient_key(encryption, pubkey, sizeof(pubkey)); + +const size_t plaintext_length = 14; +const std::uint8_t *plaintext = (std::uint8_t *) "This is a test"; + +size_t ciphertext_length = olm_pk_ciphertext_length(encryption, plaintext_length); +std::uint8_t *ciphertext_buffer = (std::uint8_t *) malloc(ciphertext_length); + +std::uint8_t output_buffer[::olm_pk_mac_length(encryption)]; +std::uint8_t ephemeral_key[::olm_pk_key_length()]; + +olm_pk_encrypt( + encryption, + plaintext, plaintext_length, + ciphertext_buffer, ciphertext_length, + output_buffer, sizeof(output_buffer), + ephemeral_key, sizeof(ephemeral_key), + bob_private, sizeof(bob_private) +); + +//FIXME:assert_equals(bob_public, ephemeral_key, 32); + +size_t max_plaintext_length = olm_pk_max_plaintext_length(decryption, ciphertext_length); +std::uint8_t *plaintext_buffer = (std::uint8_t *) malloc(max_plaintext_length); + +olm_pk_decrypt( + decryption, + ephemeral_key, sizeof(ephemeral_key), + output_buffer, sizeof(output_buffer), + ciphertext_buffer, ciphertext_length, + plaintext_buffer, max_plaintext_length +); + +assert_equals(plaintext, plaintext_buffer, plaintext_length); + +free(ciphertext_buffer); +free(plaintext_buffer); + +} + +} -- cgit v1.2.3