diff options
-rw-r--r-- | include/olm/pk.h | 44 | ||||
-rw-r--r-- | javascript/olm_pk.js | 89 | ||||
-rw-r--r-- | javascript/olm_post.js | 1 | ||||
-rw-r--r-- | javascript/test/olm.spec.js | 1 | ||||
-rw-r--r-- | javascript/test/pk.spec.js | 34 | ||||
-rw-r--r-- | src/pk.cpp | 86 | ||||
-rw-r--r-- | tests/test_pk.cpp | 70 |
7 files changed, 320 insertions, 5 deletions
diff --git a/include/olm/pk.h b/include/olm/pk.h index c46baa0..aed14ef 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -1,4 +1,4 @@ -/* Copyright 2018 New Vector Ltd +/* Copyright 2018, 2019 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. @@ -207,6 +207,48 @@ size_t olm_pk_get_private_key( void *private_key, size_t private_key_length ); +typedef struct OlmPkSigning OlmPkSigning; + +/* The size of a signing object in bytes */ +size_t olm_pk_signing_size(void); + +/** Initialise a signing object using the supplied memory + * The supplied memory must be at least olm_pk_sign_size() bytes */ +OlmPkSigning *olm_pk_signing( + void * memory +); + +/** A null terminated string describing the most recent error to happen to a + * signing object */ +const char * olm_pk_signing_last_error( + OlmPkSigning * sign +); + +/** Clears the memory used to back this signing object */ +size_t olm_clear_pk_signing( + OlmPkSigning *sign +); + +/** + * Initialise the signing object with a public/private keypair from a seed + */ +size_t olm_pk_signing_key_from_seed( + OlmPkSigning * sign, + void * pubkey, size_t pubkey_length, + void * seed, size_t seed_length +); + +size_t olm_pk_sign_seed_length(void); +size_t olm_pk_sign_public_key_length(void); + +size_t olm_pk_signature_length(); + +size_t olm_pk_sign( + OlmPkSigning *sign, + uint8_t const * message, size_t message_length, + uint8_t * signature, size_t signature_length +); + #ifdef __cplusplus } #endif diff --git a/javascript/olm_pk.js b/javascript/olm_pk.js index 81dbad4..9159fbf 100644 --- a/javascript/olm_pk.js +++ b/javascript/olm_pk.js @@ -271,3 +271,92 @@ PkDecryption.prototype['decrypt'] = restore_stack(function ( } } }) + + +function PkSigning() { + var size = Module['_olm_pk_signing_size'](); + this.buf = malloc(size); + this.ptr = Module['_olm_pk_signing'](this.buf); +} + +function pk_signing_method(wrapped) { + return function() { + var result = wrapped.apply(this, arguments); + if (result === OLM_ERROR) { + var message = Pointer_stringify( + Module['_olm_pk_signing_last_error'](arguments[0]) + ); + throw new Error("OLM." + message); + } + return result; + } +} + +PkSigning.prototype['free'] = function() { + Module['_olm_clear_pk_signing'](this.ptr); + free(this.ptr); +} + +PkSigning.prototype['init_with_seed'] = restore_stack(function (seed) { + var seed_buffer = stack(seed.length); + Module['HEAPU8'].set(seed, seed_buffer); + + var pubkey_length = pk_decryption_method( + Module['_olm_pk_sign_public_key_length'] + )(); + var pubkey_buffer = stack(pubkey_length + NULL_BYTE_PADDING_LENGTH); + try { + pk_signing_method(Module['_olm_pk_signing_key_from_seed'])( + this.ptr, + pubkey_buffer, pubkey_length, + seed_buffer, seed.length + ); + } finally { + // clear out our copy of the seed + bzero(seed_buffer, seed.length); + } + return Pointer_stringify(pubkey_buffer); +}); + +PkSigning.prototype['generate_seed'] = restore_stack(function () { + var random_length = pk_decryption_method( + Module['_olm_pk_sign_seed_length'] + )(); + var random_buffer = random_stack(random_length); + var key_arr = new Uint8Array( + new Uint8Array(Module['HEAPU8'].buffer, random_buffer, random_length) + ); + bzero(random_buffer, random_length); + return key_arr; +}); + +PkSigning.prototype['sign'] = restore_stack(function (message) { + // XXX: Should be able to sign any bytes rather than just strings, + // but this is consistent with encrypt for now. + //var message_buffer = stack(message.length); + //Module['HEAPU8'].set(message, message_buffer); + var message_buffer, message_length; + + try { + message_length = lengthBytesUTF8(message) + message_buffer = malloc(message_length + 1); + stringToUTF8(message, message_buffer, message_length + 1); + + var sig_length = pk_decryption_method( + Module['_olm_pk_signature_length'] + )(); + var sig_buffer = stack(sig_length + NULL_BYTE_PADDING_LENGTH); + pk_signing_method(Module['_olm_pk_sign'])( + this.ptr, + message_buffer, message_length, + sig_buffer, sig_length + ); + return Pointer_stringify(sig_buffer); + } finally { + if (message_buffer !== undefined) { + // don't leave a copy of the plaintext in the heap. + bzero(message_buffer, message_length + 1); + free(message_buffer); + } + } +}); diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 3bf0d66..a3a3ef4 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -534,6 +534,7 @@ olm_exports["Session"] = Session; olm_exports["Utility"] = Utility; olm_exports["PkEncryption"] = PkEncryption; olm_exports["PkDecryption"] = PkDecryption; +olm_exports["PkSigning"] = PkSigning; olm_exports["SAS"] = SAS; olm_exports["get_library_version"] = restore_stack(function() { diff --git a/javascript/test/olm.spec.js b/javascript/test/olm.spec.js index 77dd712..a698314 100644 --- a/javascript/test/olm.spec.js +++ b/javascript/test/olm.spec.js @@ -34,7 +34,6 @@ describe("olm", function() { beforeEach(function(done) { // This should really be in a beforeAll, but jasmine-node // doesn't support that - debugger; Olm.init().then(function() { aliceAccount = new Olm.Account(); bobAccount = new Olm.Account(); diff --git a/javascript/test/pk.spec.js b/javascript/test/pk.spec.js index b4b119e..f212b96 100644 --- a/javascript/test/pk.spec.js +++ b/javascript/test/pk.spec.js @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 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. @@ -19,12 +19,13 @@ limitations under the License. var Olm = require('../olm'); describe("pk", function() { - var encryption, decryption; + var encryption, decryption, signing; beforeEach(function(done) { Olm.init().then(function() { encryption = new Olm.PkEncryption(); decryption = new Olm.PkDecryption(); + signing = new Olm.PkSigning(); done(); }); @@ -39,6 +40,10 @@ describe("pk", function() { decryption.free(); decryption = undefined; } + if (signing !== undefined) { + signing.free(); + signing = undefined; + } }); it('should import & export keys from private parts', function () { @@ -89,4 +94,29 @@ describe("pk", function() { expect(decrypted).toEqual(TEST_TEXT); new_decryption.free(); }); + + it('should sign and verify', function () { + var seed = new Uint8Array([ + 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 + ]); + + var TEST_TEXT = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness."; + //var seed = signing.generate_seed(); + var pubkey = signing.init_with_seed(seed); + var sig = signing.sign(TEST_TEXT); + + var util = new Olm.Utility(); + util.ed25519_verify(pubkey, TEST_TEXT, sig); + var verifyFailure; + try { + util.ed25519_verify(pubkey, TEST_TEXT, 'p' + sig.slice(1)); + } catch (e) { + verifyFailure = e; + } + expect(verifyFailure).not.toBeNull(); + util.free(); + }); }); @@ -1,4 +1,4 @@ -/* Copyright 2018 New Vector Ltd +/* Copyright 2018, 2019 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. @@ -409,4 +409,88 @@ size_t olm_pk_get_private_key( return olm_pk_private_key_length(); } +struct OlmPkSigning { + OlmErrorCode last_error; + _olm_ed25519_key_pair key_pair; +}; + +size_t olm_pk_signing_size(void) { + return sizeof(OlmPkSigning); +} + +OlmPkSigning *olm_pk_signing(void * memory) { + olm::unset(memory, sizeof(OlmPkSigning)); + return new(memory) OlmPkSigning; +} + +const char * olm_pk_signing_last_error(OlmPkSigning * sign) { + auto error = sign->last_error; + return _olm_error_to_string(error); +} + +size_t olm_clear_pk_signing(OlmPkSigning *sign) { + /* Clear the memory backing the signing */ + olm::unset(sign, sizeof(OlmPkSigning)); + /* Initialise a fresh signing object in case someone tries to use it */ + new(sign) OlmPkSigning(); + return sizeof(OlmPkSigning); +} + +size_t olm_pk_sign_seed_length(void) { + return ED25519_RANDOM_LENGTH; +} + +size_t olm_pk_sign_public_key_length(void) { + return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH); +} + +size_t olm_pk_signing_key_from_seed( + OlmPkSigning * signing, + void * pubkey, size_t pubkey_length, + void * seed, size_t seed_length +) { + if (pubkey_length < olm_pk_sign_public_key_length()) { + signing->last_error = + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + if (seed_length < olm_pk_sign_seed_length()) { + signing->last_error = + OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + + _olm_crypto_ed25519_generate_key((uint8_t *) seed, &signing->key_pair); + olm::encode_base64( + (const uint8_t *)signing->key_pair.public_key.public_key, + ED25519_PUBLIC_KEY_LENGTH, + (uint8_t *)pubkey + ); + return 0; +} + +size_t olm_pk_signature_length() { + return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH); +} + +#include "olm/utility.hh" + +size_t olm_pk_sign( + OlmPkSigning *signing, + uint8_t const * message, size_t message_length, + uint8_t * signature, size_t signature_length +) { + if (signature_length < olm_pk_signature_length()) { + signing->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; + return std::size_t(-1); + } + uint8_t *raw_sig = signature + olm_pk_signature_length() - ED25519_SIGNATURE_LENGTH; + _olm_crypto_ed25519_sign( + &signing->key_pair, + message, message_length, raw_sig + ); + olm::encode_base64(raw_sig, ED25519_SIGNATURE_LENGTH, signature); + return olm_pk_signature_length(); +} + } diff --git a/tests/test_pk.cpp b/tests/test_pk.cpp index 42cc8c9..874710b 100644 --- a/tests/test_pk.cpp +++ b/tests/test_pk.cpp @@ -163,4 +163,74 @@ free(ciphertext); free(plaintext_buffer); } + +{ /* Signing Test Case 1 */ + +TestCase test_case("Public Key Signing"); + +std::uint8_t signing_buffer[olm_pk_signing_size()]; +OlmPkSigning *signing = olm_pk_signing(signing_buffer); + +std::uint8_t seed[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 +}; + +//const std::uint8_t *pub_key = (std::uint8_t *) "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmoK"; + +char pubkey[olm_pk_sign_public_key_length() + 1]; + +olm_pk_signing_key_from_seed( + signing, + pubkey, sizeof(pubkey), + seed, sizeof(seed) +); + +printf("pubkey: %s\n", pubkey); + +char *message = strdup("We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness."); + +std::uint8_t *sig_buffer = (std::uint8_t *) malloc(olm_pk_signature_length() + 1); + +olm_pk_sign( + signing, + (const uint8_t *)message, strlen(message), + sig_buffer, olm_pk_signature_length() +); + +printf("sig: %s\n", sig_buffer); + +void * utility_buffer = malloc(::olm_utility_size()); +::OlmUtility * utility = ::olm_utility(utility_buffer); + +size_t result; + +result = ::olm_ed25519_verify( + utility, + pubkey, olm_pk_sign_public_key_length(), + message, strlen(message), + sig_buffer, olm_pk_signature_length() +); + +assert_equals((size_t)0, result); + +sig_buffer[5] = 'm'; + +result = ::olm_ed25519_verify( + utility, + pubkey, olm_pk_sign_public_key_length(), + message, strlen(message), + sig_buffer, olm_pk_signature_length() +); + +assert_equals((size_t)-1, result); + +free(message); +free(sig_buffer); + +olm_clear_pk_signing(signing); + +} } |