add initial implementation of basic private key encryption functionality
This commit is contained in:
parent
6a2a2741e8
commit
128d45cc83
7 changed files with 752 additions and 1 deletions
3
Makefile
3
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 \
|
||||
|
|
148
include/olm/pk.h
Normal file
148
include/olm/pk.h
Normal file
|
@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_ */
|
179
javascript/olm_pk.js
Normal file
179
javascript/olm_pk.js
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
})
|
|
@ -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);
|
||||
|
|
64
javascript/test/pk.spec.js
Normal file
64
javascript/test/pk.spec.js
Normal file
|
@ -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);
|
||||
});
|
||||
});
|
253
src/pk.cpp
Normal file
253
src/pk.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
104
tests/test_pk.cpp
Normal file
104
tests/test_pk.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "olm/pk.h"
|
||||
#include "olm/crypto.h"
|
||||
#include "olm/olm.h"
|
||||
|
||||
#include "unittest.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue