Merge branch 'rav/ed25519_fix'
This commit is contained in:
commit
214e932806
16 changed files with 96 additions and 172 deletions
|
@ -1,3 +1,15 @@
|
|||
Changes in `1.1.0 <http://matrix.org/git/olm/commit/?h=1.1.0>`_
|
||||
===============================================================
|
||||
|
||||
This release includes a fix to a bug which caused Ed25519 keypairs to be
|
||||
generated and used insecurely. Any Ed25519 keys generated by libolm 1.0.0
|
||||
should be considered compromised.
|
||||
|
||||
The fix necessitates a change to the format of the OlmAccount pickle; since
|
||||
existing OlmAccounts should in any case be considered compromised (as above),
|
||||
the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE.
|
||||
|
||||
|
||||
Changes in `1.0.0 <http://matrix.org/git/olm/commit/?h=1.0.0>`_
|
||||
===============================================================
|
||||
|
||||
|
|
21
Makefile
21
Makefile
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
MAJOR := 1
|
||||
MINOR := 0
|
||||
MINOR := 1
|
||||
PATCH := 0
|
||||
VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
||||
PREFIX ?= /usr/local
|
||||
|
@ -15,9 +15,7 @@ EMCC = emcc
|
|||
AFL_CC = afl-gcc
|
||||
AFL_CXX = afl-g++
|
||||
RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION)
|
||||
RELEASE_SYMLINKS := $(BUILD_DIR)/libolm.so.$(MAJOR) $(BUILD_DIR)/libolm.so
|
||||
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION)
|
||||
DEBUG_SYMLINKS := $(BUILD_DIR)/libolm_debug.so.$(MAJOR) $(BUILD_DIR)/libolm_debug.so
|
||||
JS_TARGET := javascript/olm.js
|
||||
|
||||
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
||||
|
@ -49,7 +47,10 @@ DOCS := tracing/README.html \
|
|||
README.html \
|
||||
CHANGELOG.html
|
||||
|
||||
CPPFLAGS += -Iinclude -Ilib
|
||||
CPPFLAGS += -Iinclude -Ilib \
|
||||
-DOLMLIB_VERSION_MAJOR=$(MAJOR) -DOLMLIB_VERSION_MINOR=$(MINOR) \
|
||||
-DOLMLIB_VERSION_PATCH=$(PATCH)
|
||||
|
||||
# we rely on <stdint.h>, which was introduced in C99
|
||||
CFLAGS += -Wall -Werror -std=c99 -fPIC
|
||||
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
|
||||
|
@ -98,7 +99,7 @@ $(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
|
|||
|
||||
### top-level targets
|
||||
|
||||
lib: $(RELEASE_TARGET) $(RELEASE_SYMLINKS)
|
||||
lib: $(RELEASE_TARGET)
|
||||
.PHONY: lib
|
||||
|
||||
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
||||
|
@ -106,11 +107,9 @@ $(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
|||
-Wl,-soname,libolm.so.$(MAJOR) \
|
||||
-Wl,--version-script,version_script.ver \
|
||||
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
|
||||
ldconfig -l $@
|
||||
|
||||
$(RELEASE_SYMLINKS):
|
||||
ln -s libolm.so.$(VERSION) $@
|
||||
|
||||
debug: $(DEBUG_TARGET) $(DEBUG_SYMLINKS)
|
||||
debug: $(DEBUG_TARGET)
|
||||
.PHONY: debug
|
||||
|
||||
$(DEBUG_TARGET): $(DEBUG_OBJECTS)
|
||||
|
@ -118,9 +117,7 @@ $(DEBUG_TARGET): $(DEBUG_OBJECTS)
|
|||
-Wl,-soname,libolm_debug.so.$(MAJOR) \
|
||||
-Wl,--version-script,version_script.ver \
|
||||
$(OUTPUT_OPTION) $(DEBUG_OBJECTS)
|
||||
|
||||
$(DEBUG_SYMLINKS):
|
||||
ln -s libolm_debug.so.$(VERSION) $@
|
||||
ldconfig -l $@
|
||||
|
||||
js: $(JS_TARGET)
|
||||
.PHONY: js
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
namespace olm {
|
||||
|
||||
static const std::size_t ED25519_PRIVATE_KEY_LENGTH = 64;
|
||||
static const std::size_t KEY_LENGTH = 32;
|
||||
static const std::size_t SIGNATURE_LENGTH = 64;
|
||||
static const std::size_t IV_LENGTH = 16;
|
||||
|
@ -45,7 +46,7 @@ struct Ed25519PublicKey {
|
|||
|
||||
|
||||
struct Ed25519KeyPair : public Ed25519PublicKey {
|
||||
std::uint8_t private_key[KEY_LENGTH];
|
||||
std::uint8_t private_key[ED25519_PRIVATE_KEY_LENGTH];
|
||||
};
|
||||
|
||||
|
||||
|
@ -65,24 +66,6 @@ void curve25519_shared_secret(
|
|||
);
|
||||
|
||||
|
||||
/** Signs the message using our private key.
|
||||
* The output buffer must be at least 64 bytes long. */
|
||||
void curve25519_sign(
|
||||
Curve25519KeyPair const & our_key,
|
||||
std::uint8_t const * message, std::size_t message_length,
|
||||
std::uint8_t * output
|
||||
);
|
||||
|
||||
|
||||
/** Verify their message using their public key.
|
||||
* The signature input buffer must be 64 bytes long.
|
||||
* Returns true if the signature is valid. */
|
||||
bool curve25519_verify(
|
||||
Curve25519PublicKey const & their_key,
|
||||
std::uint8_t const * message, std::size_t message_length,
|
||||
std::uint8_t const * signature
|
||||
);
|
||||
|
||||
/** Generate a curve25519 key pair from 32 random bytes. */
|
||||
void ed25519_generate_key(
|
||||
std::uint8_t const * random_32_bytes,
|
||||
|
|
|
@ -39,6 +39,13 @@ enum OlmErrorCode {
|
|||
* known session key.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Attempt to unpickle an account which uses pickle version 1 (which did
|
||||
* not save enough space for the Ed25519 key; the key should be considered
|
||||
* compromised. We don't let the user reload the account.
|
||||
*/
|
||||
OLM_BAD_LEGACY_ACCOUNT_PICKLE = 13,
|
||||
|
||||
/* remember to update the list of string constants in error.c when updating
|
||||
* this list. */
|
||||
};
|
||||
|
|
|
@ -33,6 +33,11 @@ typedef struct OlmAccount OlmAccount;
|
|||
typedef struct OlmSession OlmSession;
|
||||
typedef struct OlmUtility OlmUtility;
|
||||
|
||||
/** Get the version number of the library.
|
||||
* Arguments will be updated if non-null.
|
||||
*/
|
||||
void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch);
|
||||
|
||||
/** The size of an account object in bytes */
|
||||
size_t olm_account_size();
|
||||
|
||||
|
|
|
@ -402,4 +402,14 @@ Utility.prototype['ed25519_verify'] = restore_stack(function(
|
|||
olm_exports["Account"] = Account;
|
||||
olm_exports["Session"] = Session;
|
||||
olm_exports["Utility"] = Utility;
|
||||
|
||||
olm_exports["get_library_version"] = restore_stack(function() {
|
||||
var buf = stack(3);
|
||||
Module['_olm_get_library_version'](buf, buf+1, buf+2);
|
||||
return [
|
||||
getValue(buf, 'i8'),
|
||||
getValue(buf+1, 'i8'),
|
||||
getValue(buf+2, 'i8'),
|
||||
];
|
||||
});
|
||||
}();
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
void convert_curve25519_to_ed25519(
|
||||
unsigned char * public_key,
|
||||
unsigned char * signature
|
||||
) {
|
||||
fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one;
|
||||
fe one;
|
||||
fe ed_y;
|
||||
|
||||
fe_frombytes(mont_x, public_key);
|
||||
fe_1(one);
|
||||
fe_sub(mont_x_minus_one, mont_x, one);
|
||||
fe_add(mont_x_plus_one, mont_x, one);
|
||||
fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
|
||||
fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
|
||||
fe_tobytes(public_key, ed_y);
|
||||
|
||||
public_key[31] &= 0x7F;
|
||||
public_key[31] |= (signature[63] & 0x80);
|
||||
signature[63] &= 0x7F;
|
||||
}
|
||||
|
||||
|
||||
void convert_ed25519_to_curve25519(
|
||||
unsigned char const * public_key,
|
||||
unsigned char * signature
|
||||
) {
|
||||
unsigned char sign_bit = public_key[31] & 0x80;
|
||||
signature[63] &= 0x7F;
|
||||
signature[63] |= sign_bit;
|
||||
}
|
||||
|
||||
|
||||
void ed25519_keypair(
|
||||
unsigned char * private_key,
|
||||
unsigned char * public_key
|
||||
) {
|
||||
ge_p3 A;
|
||||
private_key[0] &= 248;
|
||||
private_key[31] &= 63;
|
||||
private_key[31] |= 64;
|
||||
ge_scalarmult_base(&A, private_key);
|
||||
ge_p3_tobytes(public_key, &A);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef ED25519_ADDITIONS_H
|
||||
#define ED25519_ADDITIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void convert_curve25519_to_ed25519(
|
||||
unsigned char * public_key,
|
||||
unsigned char * signature);
|
||||
|
||||
void convert_ed25519_to_curve25519(
|
||||
unsigned char const * public_key,
|
||||
unsigned char * signature);
|
||||
|
||||
void ed25519_keypair(
|
||||
unsigned char * private_key,
|
||||
unsigned char * public_key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -7,7 +7,7 @@ def read_random(n):
|
|||
return f.read(n)
|
||||
|
||||
lib = cdll.LoadLibrary(os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "build", "libolm.so")
|
||||
os.path.dirname(__file__), "..", "..", "build", "libolm.so.1")
|
||||
)
|
||||
|
||||
lib.olm_error.argtypes = []
|
||||
|
|
|
@ -326,7 +326,9 @@ static std::uint8_t const * unpickle(
|
|||
} // namespace olm
|
||||
|
||||
namespace {
|
||||
static const std::uint32_t ACCOUNT_PICKLE_VERSION = 1;
|
||||
// pickle version 1 used only 32 bytes for the ed25519 private key.
|
||||
// Any keys thus used should be considered compromised.
|
||||
static const std::uint32_t ACCOUNT_PICKLE_VERSION = 2;
|
||||
}
|
||||
|
||||
|
||||
|
@ -360,9 +362,15 @@ std::uint8_t const * olm::unpickle(
|
|||
) {
|
||||
uint32_t pickle_version;
|
||||
pos = olm::unpickle(pos, end, pickle_version);
|
||||
if (pickle_version != ACCOUNT_PICKLE_VERSION) {
|
||||
value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
|
||||
return end;
|
||||
switch (pickle_version) {
|
||||
case ACCOUNT_PICKLE_VERSION:
|
||||
break;
|
||||
case 1:
|
||||
value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
|
||||
return end;
|
||||
default:
|
||||
value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
|
||||
return end;
|
||||
}
|
||||
pos = olm::unpickle(pos, end, value.identity_keys);
|
||||
pos = olm::unpickle(pos, end, value.one_time_keys);
|
||||
|
|
|
@ -25,7 +25,6 @@ extern "C" {
|
|||
}
|
||||
|
||||
#include "ed25519/src/ed25519.h"
|
||||
#include "ed25519_additions.h"
|
||||
#include "curve25519-donna.h"
|
||||
|
||||
namespace {
|
||||
|
@ -121,48 +120,14 @@ void olm::curve25519_shared_secret(
|
|||
}
|
||||
|
||||
|
||||
void olm::curve25519_sign(
|
||||
olm::Curve25519KeyPair const & our_key,
|
||||
std::uint8_t const * message, std::size_t message_length,
|
||||
std::uint8_t * output
|
||||
) {
|
||||
std::uint8_t private_key[KEY_LENGTH];
|
||||
std::uint8_t public_key[KEY_LENGTH];
|
||||
std::memcpy(private_key, our_key.private_key, KEY_LENGTH);
|
||||
::ed25519_keypair(private_key, public_key);
|
||||
::ed25519_sign(
|
||||
output,
|
||||
message, message_length,
|
||||
public_key, private_key
|
||||
);
|
||||
::convert_ed25519_to_curve25519(public_key, output);
|
||||
}
|
||||
|
||||
|
||||
bool olm::curve25519_verify(
|
||||
olm::Curve25519PublicKey const & their_key,
|
||||
std::uint8_t const * message, std::size_t message_length,
|
||||
std::uint8_t const * signature
|
||||
) {
|
||||
std::uint8_t public_key[KEY_LENGTH];
|
||||
std::uint8_t signature_buffer[SIGNATURE_LENGTH];
|
||||
std::memcpy(public_key, their_key.public_key, KEY_LENGTH);
|
||||
std::memcpy(signature_buffer, signature, SIGNATURE_LENGTH);
|
||||
::convert_curve25519_to_ed25519(public_key, signature_buffer);
|
||||
return 0 != ::ed25519_verify(
|
||||
signature,
|
||||
message, message_length,
|
||||
public_key
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void olm::ed25519_generate_key(
|
||||
std::uint8_t const * random_32_bytes,
|
||||
olm::Ed25519KeyPair & key_pair
|
||||
) {
|
||||
std::memcpy(key_pair.private_key, random_32_bytes, KEY_LENGTH);
|
||||
::ed25519_keypair(key_pair.private_key, key_pair.public_key);
|
||||
::ed25519_create_keypair(
|
||||
key_pair.public_key, key_pair.private_key,
|
||||
random_32_bytes
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "ed25519/src/fe.c"
|
||||
#include "ed25519/src/sc.c"
|
||||
#include "ed25519/src/ge.c"
|
||||
#include "ed25519/src/keypair.c"
|
||||
#include "ed25519/src/sha512.c"
|
||||
#include "ed25519/src/verify.c"
|
||||
#include "ed25519/src/sign.c"
|
||||
#include "ed25519_additions.c"
|
||||
|
|
|
@ -29,6 +29,7 @@ static const char * ERRORS[] = {
|
|||
"CORRUPTED_PICKLE",
|
||||
"BAD_SESSION_KEY",
|
||||
"UNKNOWN_MESSAGE_INDEX",
|
||||
"BAD_LEGACY_ACCOUNT_PICKLE",
|
||||
};
|
||||
|
||||
const char * _olm_error_to_string(enum OlmErrorCode error)
|
||||
|
|
|
@ -98,6 +98,11 @@ std::size_t b64_input(
|
|||
|
||||
extern "C" {
|
||||
|
||||
void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) {
|
||||
if (major != NULL) *major = OLMLIB_VERSION_MAJOR;
|
||||
if (minor != NULL) *minor = OLMLIB_VERSION_MINOR;
|
||||
if (patch != NULL) *patch = OLMLIB_VERSION_PATCH;
|
||||
}
|
||||
|
||||
size_t olm_error() {
|
||||
return std::size_t(-1);
|
||||
|
|
|
@ -83,35 +83,6 @@ assert_equals(expected_agreement, actual_agreement, 32);
|
|||
} /* Curve25529 Test Case 1 */
|
||||
|
||||
|
||||
{ /* Curve25519 Signature Test Case 1 */
|
||||
TestCase test_case("Curve25519 Signature Test Case 1");
|
||||
|
||||
std::uint8_t private_key[33] = "This key is a string of 32 bytes";
|
||||
std::uint8_t message[] = "message";
|
||||
std::size_t message_length = sizeof(message) - 1;
|
||||
|
||||
olm::Curve25519KeyPair key_pair;
|
||||
olm::curve25519_generate_key(private_key, key_pair);
|
||||
|
||||
std::uint8_t signature[64];
|
||||
|
||||
olm::curve25519_sign(
|
||||
key_pair, message, message_length, signature
|
||||
);
|
||||
|
||||
bool result = olm::curve25519_verify(
|
||||
key_pair, message, message_length, signature
|
||||
);
|
||||
assert_equals(true, result);
|
||||
|
||||
message[0] = 'n';
|
||||
result = olm::curve25519_verify(
|
||||
key_pair, message, message_length, signature
|
||||
);
|
||||
assert_equals(false, result);
|
||||
|
||||
} /* Curve25519 Signature Test Case 1 */
|
||||
|
||||
{
|
||||
TestCase test_case("Ed25519 Signature Test Case 1");
|
||||
std::uint8_t private_key[33] = "This key is a string of 32 bytes";
|
||||
|
|
|
@ -65,6 +65,33 @@ assert_equals(pickle1, pickle2, pickle_length);
|
|||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestCase test_case("Old account unpickle test");
|
||||
|
||||
// this uses the old pickle format, which did not use enough space
|
||||
// for the Ed25519 key. We should reject it.
|
||||
std::uint8_t pickle[] =
|
||||
"x3h9er86ygvq56pM1yesdAxZou4ResPQC9Rszk/fhEL9JY/umtZ2N/foL/SUgVXS"
|
||||
"v0IxHHZTafYjDdzJU9xr8dQeBoOTGfV9E/lCqDGBnIlu7SZndqjEKXtzGyQr4sP4"
|
||||
"K/A/8TOu9iK2hDFszy6xETiousHnHgh2ZGbRUh4pQx+YMm8ZdNZeRnwFGLnrWyf9"
|
||||
"O5TmXua1FcU";
|
||||
|
||||
std::uint8_t account_buffer[::olm_account_size()];
|
||||
::OlmAccount *account = ::olm_account(account_buffer);
|
||||
assert_equals(
|
||||
std::size_t(-1),
|
||||
::olm_unpickle_account(
|
||||
account, "", 0, pickle, sizeof(pickle)-1
|
||||
)
|
||||
);
|
||||
assert_equals(
|
||||
std::string("BAD_LEGACY_ACCOUNT_PICKLE"),
|
||||
std::string(::olm_account_last_error(account))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
{ /** Pickle session test */
|
||||
|
||||
TestCase test_case("Pickle session test");
|
||||
|
|
Loading…
Reference in a new issue