Commit b6e248c9 authored by Mark Haines's avatar Mark Haines
Browse files

Output simpler JSON for the account keys, don't sign the JSON but instead...

Output simpler JSON for the account keys, don't sign the JSON but instead provide a olm_account_sign method so that the user of the library can sign the JSON themselves
parent 373acefd
...@@ -56,57 +56,45 @@ struct Account { ...@@ -56,57 +56,45 @@ struct Account {
); );
/** Number of bytes needed to output the identity keys for this account */ /** Number of bytes needed to output the identity keys for this account */
std::size_t get_identity_json_length( std::size_t get_identity_json_length();
std::size_t user_id_length,
std::size_t device_id_length,
std::uint64_t valid_after_ts,
std::uint64_t valid_until_ts
);
/** Output the identity keys for this account as JSON in the following /** Output the identity keys for this account as JSON in the following
* format: * format:
* *
* {"algorithms": * {"curve25519":"<43 base64 characters>"
* ["m.olm.curve25519-aes-sha256" * ,"ed25519":"<43 base64 characters>"
* ]
* ,"device_id":"<device identifier>"
* ,"keys":
* {"curve25519:<key id>":"<base64 characters>"
* ,"ed25519:<key id>":"<base64 characters>"
* }
* ,"user_id":"<user identifier>"
* ,"valid_after_ts":<digits>
* ,"valid_until_ts":<digits>
* ,"signatures":
* {"<user identifier>/<device identifier>":
* {"ed25519:<key id>":"<base64 characters>"
* }
* }
* } * }
* *
* The user_id and device_id must not contain 0x00-0x1F, '\"' or '\\'.
* The JSON up to but not including the "signatures" key will be signed
* using the account's ed25519 key. That signature is then included under
* the "signatures" key.
* *
* Returns the size of the JSON written or std::size_t(-1) on error. * Returns the size of the JSON written or std::size_t(-1) on error.
* If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL. */ * If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL. */
std::size_t get_identity_json( std::size_t get_identity_json(
std::uint8_t const * user_id, std::size_t user_id_length,
std::uint8_t const * device_id, std::size_t device_id_length,
std::uint64_t valid_after_ts,
std::uint64_t valid_until_ts,
std::uint8_t * identity_json, std::size_t identity_json_length std::uint8_t * identity_json, std::size_t identity_json_length
); );
/**
* The length of an ed25519 signature in bytes.
*/
std::size_t signature_length();
/**
* Signs a message with the ed25519 key for this account.
*/
std::size_t sign(
std::uint8_t const * message, std::size_t message_length,
std::uint8_t * signature, std::size_t signature_length
);
/** Number of bytes needed to output the one time keys for this account */ /** Number of bytes needed to output the one time keys for this account */
std::size_t get_one_time_keys_json_length(); std::size_t get_one_time_keys_json_length();
/** Output the one time keys that haven't been published yet as JSON: /** Output the one time keys that haven't been published yet as JSON:
* *
* {"curve25519:<key id>":"<base64 characters>" * {"curve25519":
* ,"curve25519:<key_id>":"<base64 characters>" * ["<6 byte key id>":"<43 base64 characters>"
* ,"<6 byte key id>":"<43 base64 characters>"
* ... * ...
* ]
* } * }
* *
* Returns the size of the JSON written or std::size_t(-1) on error. * Returns the size of the JSON written or std::size_t(-1) on error.
......
...@@ -132,11 +132,7 @@ size_t olm_create_account( ...@@ -132,11 +132,7 @@ size_t olm_create_account(
/** The size of the output buffer needed to hold the identity keys */ /** The size of the output buffer needed to hold the identity keys */
size_t olm_account_identity_keys_length( size_t olm_account_identity_keys_length(
OlmAccount * account, OlmAccount * account
size_t user_id_length,
size_t device_id_length,
uint64_t valid_after_ts,
uint64_t valid_until_ts
); );
/** Writes the public parts of the identity keys for the account into the /** Writes the public parts of the identity keys for the account into the
...@@ -145,13 +141,24 @@ size_t olm_account_identity_keys_length( ...@@ -145,13 +141,24 @@ size_t olm_account_identity_keys_length(
* "OUTPUT_BUFFER_TOO_SMALL". */ * "OUTPUT_BUFFER_TOO_SMALL". */
size_t olm_account_identity_keys( size_t olm_account_identity_keys(
OlmAccount * account, OlmAccount * account,
void const * user_id, size_t user_id_length,
void const * device_id, size_t device_id_length,
uint64_t valid_after_ts,
uint64_t valid_until_ts,
void * identity_keys, size_t identity_key_length void * identity_keys, size_t identity_key_length
); );
/** The length of an ed25519 signature encoded as base64. */
size_t olm_account_signature_length(
OlmAccount * account
);
/** Signs a message with the ed25519 key for this account. Returns olm_error()
* on failure. If the signature buffer was too small then
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
size_t olm_account_sign(
OlmAccount * account,
void const * message, size_t message_length,
void * signature, size_t signature_length
);
/** The size of the output buffer needed to hold the one time keys */ /** The size of the output buffer needed to hold the one time keys */
size_t olm_account_one_time_keys_length( size_t olm_account_one_time_keys_length(
OlmAccount * account OlmAccount * account
......
...@@ -19,8 +19,8 @@ document.addEventListener("DOMContentLoaded", function (event) { ...@@ -19,8 +19,8 @@ document.addEventListener("DOMContentLoaded", function (event) {
return {start:start, done:done}; return {start:start, done:done};
} }
var alice = new Olm.Account(); window.alice = new Olm.Account();
var bob = new Olm.Account(); window.bob = new Olm.Account();
var a_session = new Olm.Session(); var a_session = new Olm.Session();
var b_session = new Olm.Session(); var b_session = new Olm.Session();
var message_1; var message_1;
...@@ -32,19 +32,12 @@ document.addEventListener("DOMContentLoaded", function (event) { ...@@ -32,19 +32,12 @@ document.addEventListener("DOMContentLoaded", function (event) {
bob.generate_one_time_keys(1); bob.generate_one_time_keys(1);
}]); }]);
tasks.push(["alice", "Create outbound session", function() { tasks.push(["alice", "Create outbound session", function() {
var bobs_id_keys = JSON.parse(bob.identity_keys("bob", "bob_device", 0, 0)); var bobs_id_keys = JSON.parse(bob.identity_keys());
var bobs_id_key; var bobs_id_key = bobs_id_keys.curve25519;
for (key in bobs_id_keys.keys) {
if (key.startsWith("curve25519:")) {
bobs_id_key = bobs_id_keys.keys[key];
}
}
var bobs_ot_keys = JSON.parse(bob.one_time_keys()); var bobs_ot_keys = JSON.parse(bob.one_time_keys());
var bobs_ot_key; var bobs_ot_key;
for (key in bobs_ot_keys) { for (key in bobs_ot_keys.curve25519) {
if (key.startsWith("curve25519:")) { bobs_ot_key = bobs_ot_keys.curve25519[key];
bobs_ot_key = bobs_ot_keys[key];
}
} }
a_session.create_outbound(alice, bobs_id_key, bobs_ot_key); a_session.create_outbound(alice, bobs_id_key, bobs_ot_key);
}]); }]);
...@@ -104,7 +97,7 @@ document.addEventListener("DOMContentLoaded", function (event) { ...@@ -104,7 +97,7 @@ document.addEventListener("DOMContentLoaded", function (event) {
task[2](); task[2]();
p.done(); p.done();
window.setTimeout(do_tasks, 50, next); window.setTimeout(do_tasks, 50, next);
}, 0) }, 50)
} else { } else {
next(); next();
} }
......
...@@ -63,32 +63,32 @@ Account.prototype['create'] = restore_stack(function() { ...@@ -63,32 +63,32 @@ Account.prototype['create'] = restore_stack(function() {
); );
}); });
Account.prototype['identity_keys'] = restore_stack(function( Account.prototype['identity_keys'] = restore_stack(function() {
user_id, device_id, valid_after, valid_until
) {
var user_id_array = array_from_string(user_id);
var device_id_array = array_from_string(device_id);
var keys_length = account_method( var keys_length = account_method(
Module['_olm_account_identity_keys_length'] Module['_olm_account_identity_keys_length']
)( )(this.ptr);
this.ptr, user_id_array.length, device_id_array.length,
valid_after, valid_after / Math.pow(2, 32),
valid_until, valid_until / Math.pow(2, 32)
);
var user_id_buffer = stack(user_id_array);
var device_id_buffer = stack(device_id_array);
var keys = stack(keys_length); var keys = stack(keys_length);
account_method(Module['_olm_account_identity_keys'])( account_method(Module['_olm_account_identity_keys'])(
this.ptr, this.ptr, keys, keys_length
user_id_buffer, user_id_array.length,
device_id_buffer, device_id_array.length,
valid_after, valid_after / Math.pow(2, 32),
valid_until, valid_until / Math.pow(2, 32),
keys, keys_length
); );
return Pointer_stringify(keys, keys_length); return Pointer_stringify(keys, keys_length);
}); });
Account.prototype['sign'] = restore_stack(function(message) {
var signature_length = account_method(
Module['_olm_account_signature_length']
)(this.ptr);
var message_array = array_from_string(message);
var message_buffer = stack(message_array);
var signature_buffer = stack(signature_length);
account_method(Module['_olm_account_sign'])(
this.ptr,
message_buffer, message_array.length,
signature_buffer, signature_length
);
return Pointer_stringify(signature_buffer, signature_length);
});
Account.prototype['one_time_keys'] = restore_stack(function() { Account.prototype['one_time_keys'] = restore_stack(function() {
var keys_length = account_method( var keys_length = account_method(
Module['_olm_account_one_time_keys_length'] Module['_olm_account_one_time_keys_length']
......
...@@ -48,15 +48,10 @@ account_function( ...@@ -48,15 +48,10 @@ account_function(
) )
account_function(lib.olm_create_account_random_length) account_function(lib.olm_create_account_random_length)
account_function(lib.olm_create_account, c_void_p, c_size_t) account_function(lib.olm_create_account, c_void_p, c_size_t)
account_function( account_function(lib.olm_account_identity_keys_length)
lib.olm_account_identity_keys_length, account_function(lib.olm_account_identity_keys, c_void_p, c_size_t)
c_size_t, c_size_t, c_uint64, c_uint64 account_function(lib.olm_account_signature_length)
) account_function(lib.olm_account_sign, c_void_p, c_size_t, c_void_p, c_size_t)
account_function(
lib.olm_account_identity_keys,
c_void_p, c_size_t, c_void_p, c_size_t, c_uint64, c_uint64,
c_void_p, c_size_t
)
account_function(lib.olm_account_one_time_keys_length) account_function(lib.olm_account_one_time_keys_length)
account_function(lib.olm_account_one_time_keys, c_void_p, c_size_t) account_function(lib.olm_account_one_time_keys, c_void_p, c_size_t)
account_function(lib.olm_account_mark_keys_as_published) account_function(lib.olm_account_mark_keys_as_published)
...@@ -66,7 +61,7 @@ account_function( ...@@ -66,7 +61,7 @@ account_function(
c_size_t c_size_t
) )
account_function( account_function(
lib.olm_account_generate_one_time_keys lib.olm_account_generate_one_time_keys,
c_size_t, c_size_t,
c_void_p, c_size_t c_void_p, c_size_t
) )
...@@ -103,22 +98,24 @@ class Account(object): ...@@ -103,22 +98,24 @@ class Account(object):
self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) self.ptr, key_buffer, len(key), pickle_buffer, len(pickle)
) )
def identity_keys(self, user_id, device_id, valid_after, valid_until): def identity_keys(self):
out_length = lib.olm_account_identity_keys_length( out_length = lib.olm_account_identity_keys_length(self.ptr)
self.ptr, len(user_id), len(device_id), valid_after, valid_until
)
user_id_buffer = create_string_buffer(user_id)
device_id_buffer = create_string_buffer(device_id)
out_buffer = create_string_buffer(out_length) out_buffer = create_string_buffer(out_length)
lib.olm_account_identity_keys( lib.olm_account_identity_keys(
self.ptr, self.ptr,
user_id_buffer, len(user_id),
device_id_buffer, len(device_id),
valid_after, valid_until,
out_buffer, out_length out_buffer, out_length
) )
return json.loads(out_buffer.raw) return json.loads(out_buffer.raw)
def sign(self, message):
out_length = lib.olm_account_signature_length(self.ptr)
message_buffer = create_string_buffer(message)
out_buffer = create_string_buffer(out_length)
lib.olm_account_sign(
self.ptr, message_buffer, len(message), out_buffer, out_length
)
return out_buffer.raw
def one_time_keys(self): def one_time_keys(self):
out_length = lib.olm_account_one_time_keys_length(self.ptr) out_length = lib.olm_account_one_time_keys_length(self.ptr)
out_buffer = create_string_buffer(out_length) out_buffer = create_string_buffer(out_length)
...@@ -128,7 +125,6 @@ class Account(object): ...@@ -128,7 +125,6 @@ class Account(object):
def mark_keys_as_published(self): def mark_keys_as_published(self):
lib.olm_account_mark_keys_as_published(self.ptr) lib.olm_account_mark_keys_as_published(self.ptr)
def max_number_of_one_time_keys(self): def max_number_of_one_time_keys(self):
return lib.olm_account_max_number_of_one_time_keys(self.ptr) return lib.olm_account_max_number_of_one_time_keys(self.ptr)
...@@ -326,10 +322,6 @@ if __name__ == '__main__': ...@@ -326,10 +322,6 @@ if __name__ == '__main__':
create_account.set_defaults(func=do_create_account) create_account.set_defaults(func=do_create_account)
keys = commands.add_parser("keys", help="List public keys for an account") keys = commands.add_parser("keys", help="List public keys for an account")
keys.add_argument("--user-id", default="@user:example.com")
keys.add_argument("--device-id", default="default_device_id")
keys.add_argument("--valid-after", default=0, type=int)
keys.add_argument("--valid-until", default=0, type=int)
keys.add_argument("account_file", help="Local account file") keys.add_argument("account_file", help="Local account file")
def do_keys(args): def do_keys(args):
...@@ -337,10 +329,7 @@ if __name__ == '__main__': ...@@ -337,10 +329,7 @@ if __name__ == '__main__':
with open(args.account_file, "rb") as f: with open(args.account_file, "rb") as f:
account.unpickle(args.key, f.read()) account.unpickle(args.key, f.read())
result1 = { result1 = {
"device_keys": account.identity_keys( "account_keys": account.identity_keys(),
args.user_id, args.device_id,
args.valid_after, args.valid_until,
),
"one_time_keys": account.one_time_keys(), "one_time_keys": account.one_time_keys(),
} }
try: try:
...@@ -350,6 +339,23 @@ if __name__ == '__main__': ...@@ -350,6 +339,23 @@ if __name__ == '__main__':
keys.set_defaults(func=do_keys) keys.set_defaults(func=do_keys)
sign = commands.add_parser("sign", help="Sign a message")
sign.add_argument("account_file", help="Local account file")
sign.add_argument("message_file", help="Message to sign")
sign.add_argument("signature_file", help="Signature to output")
def do_sign(args):
account = Account()
with open(args.account_file, "rb") as f:
account.unpickle(args.key, f.read())
with open_in(args.message_file) as f:
message = f.read()
signature = account.sign(message)
with open_out(args.signature_file) as f:
f.write(signature)
sign.set_defaults(func=do_sign)
outbound = commands.add_parser("outbound", help="Create an outbound session") outbound = commands.add_parser("outbound", help="Create an outbound session")
outbound.add_argument("account_file", help="Local account file") outbound.add_argument("account_file", help="Local account file")
outbound.add_argument("session_file", help="Local session file") outbound.add_argument("session_file", help="Local session file")
......
...@@ -69,22 +69,12 @@ std::size_t olm::Account::new_account( ...@@ -69,22 +69,12 @@ std::size_t olm::Account::new_account(
namespace { namespace {
static const uint8_t IDENTITY_JSON_PART_0[] =
"{\"algorithms\":" namespace {
"[\"m.olm.curve25519-aes-sha256\"" uint8_t KEY_JSON_ED25519[] = "\"ed25519\":";
"],\"device_id\":\""; uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":";
static const uint8_t IDENTITY_JSON_PART_1[] = "\",\"keys\":{\"curve25519:"; }
static const uint8_t IDENTITY_JSON_PART_2[] = "\":\"";
static const uint8_t IDENTITY_JSON_PART_3[] = "\",\"ed25519:";
static const uint8_t IDENTITY_JSON_PART_4[] = "\":\"";
static const uint8_t IDENTITY_JSON_PART_5[] = "\"},\"user_id\":\"";
static const uint8_t IDENTITY_JSON_PART_6[] = "\",\"valid_after_ts\":";
static const uint8_t IDENTITY_JSON_PART_7[] = ",\"valid_until_ts\":";
static const uint8_t IDENTITY_JSON_PART_8[] = ",\"signatures\":{\"";
static const uint8_t IDENTITY_JSON_PART_9[] = "/";
static const uint8_t IDENTITY_JSON_PART_A[] = "\":{\"ed25519:";
static const uint8_t IDENTITY_JSON_PART_B[] = "\":\"";
static const uint8_t IDENTITY_JSON_PART_C[] = "\"}}}";
std::size_t count_digits( std::size_t count_digits(
std::uint64_t value std::uint64_t value
...@@ -130,104 +120,79 @@ std::uint8_t * write_digits( ...@@ -130,104 +120,79 @@ std::uint8_t * write_digits(
} }
std::size_t olm::Account::get_identity_json_length( std::size_t olm::Account::get_identity_json_length() {
std::size_t user_id_length,
std::size_t device_id_length,
std::uint64_t valid_after_ts,
std::uint64_t valid_until_ts
) {
std::size_t length = 0; std::size_t length = 0;
length += sizeof(IDENTITY_JSON_PART_0) - 1; length += 1; /* { */
length += device_id_length; length += sizeof(KEY_JSON_CURVE25519) - 1;
length += sizeof(IDENTITY_JSON_PART_1) - 1; length += 1; /* " */
length += olm::encode_base64_length(3);
length += sizeof(IDENTITY_JSON_PART_2) - 1;
length += olm::encode_base64_length( length += olm::encode_base64_length(
sizeof(identity_keys.curve25519_key.public_key) sizeof(identity_keys.curve25519_key.public_key)
); );
length += sizeof(IDENTITY_JSON_PART_3) - 1; length += 2; /* ", */
length += olm::encode_base64_length(3); length += sizeof(KEY_JSON_ED25519) - 1;
length += sizeof(IDENTITY_JSON_PART_4) - 1; length += 1; /* " */
length += olm::encode_base64_length( length += olm::encode_base64_length(
sizeof(identity_keys.ed25519_key.public_key) sizeof(identity_keys.ed25519_key.public_key)
); );
length += sizeof(IDENTITY_JSON_PART_5) - 1; length += 2; /* "} */
length += user_id_length;
length += sizeof(IDENTITY_JSON_PART_6) - 1;
length += count_digits(valid_after_ts);
length += sizeof(IDENTITY_JSON_PART_7) - 1;
length += count_digits(valid_until_ts);
length += sizeof(IDENTITY_JSON_PART_8) - 1;
length += user_id_length;
length += sizeof(IDENTITY_JSON_PART_9) - 1;
length += device_id_length;
length += sizeof(IDENTITY_JSON_PART_A) - 1;
length += olm::encode_base64_length(3);
length += sizeof(IDENTITY_JSON_PART_B) - 1;
length += olm::encode_base64_length(64);
length += sizeof(IDENTITY_JSON_PART_C) - 1;
return length; return length;
} }
std::size_t olm::Account::get_identity_json( std::size_t olm::Account::get_identity_json(
std::uint8_t const * user_id, std::size_t user_id_length,
std::uint8_t const * device_id, std::size_t device_id_length,
std::uint64_t valid_after_ts,
std::uint64_t valid_until_ts,
std::uint8_t * identity_json, std::size_t identity_json_length std::uint8_t * identity_json, std::size_t identity_json_length
) { ) {
std::uint8_t * pos = identity_json; std::uint8_t * pos = identity_json;
std::uint8_t signature[64]; std::uint8_t signature[64];
size_t expected_length = get_identity_json_length( size_t expected_length = get_identity_json_length();
user_id_length, device_id_length, valid_after_ts, valid_until_ts
);
if (identity_json_length < expected_length) { if (identity_json_length < expected_length) {
last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1); return std::size_t(-1);
} }
pos = write_string(pos, IDENTITY_JSON_PART_0); *(pos++) = '{';
pos = write_string(pos, device_id, device_id_length); pos = write_string(pos, KEY_JSON_CURVE25519);
pos = write_string(pos, IDENTITY_JSON_PART_1); *(pos++) = '\"';
pos = encode_base64(identity_keys.curve25519_key.public_key, 3, pos); pos = olm::encode_base64(
pos = write_string(pos, IDENTITY_JSON_PART_2); identity_keys.curve25519_key.public_key,
pos = encode_base64(identity_keys.curve25519_key.public_key, 32, pos); sizeof(identity_keys.curve25519_key.public_key),
pos = write_string(pos, IDENTITY_JSON_PART_3); pos
pos = encode_base64(identity_keys.ed25519_key.public_key, 3, pos);
pos = write_string(pos, IDENTITY_JSON_PART_4);
pos = encode_base64(identity_keys.ed25519_key.public_key, 32, pos);
pos = write_string(pos, IDENTITY_JSON_PART_5);
pos = write_string(pos, user_id, user_id_length);
pos = write_string(pos, IDENTITY_JSON_PART_6);
pos = write_digits(pos, valid_after_ts);
pos = write_string(pos, IDENTITY_JSON_PART_7);
pos = write_digits(pos, valid_until_ts);
*pos = '}';
// Sign the JSON up to written up to this point.
ed25519_sign(
identity_keys.ed25519_key,
identity_json, 1 + pos - identity_json,
signature
); );
// Append the signature to the end of the JSON. *(pos++) = '\"'; *(pos++) = ',';
pos = write_string(pos, IDENTITY_JSON_PART_8); pos = write_string(pos, KEY_JSON_ED25519);
pos = write_string(pos, user_id, user_id_length); *(pos++) = '\"';
pos = write_string(pos, IDENTITY_JSON_PART_9); pos = olm::encode_base64(
pos = write_string(pos, device_id, device_id_length); identity_keys.ed25519_key.public_key,
pos = write_string(pos, IDENTITY_JSON_PART_A); sizeof(identity_keys.ed25519_key.public_key),
pos = encode_base64(identity_keys.ed25519_key.public_key, 3, pos); pos
pos = write_string(pos, IDENTITY_JSON_PART_B); );
pos = encode_base64(signature, 64, pos); *(pos++) = '\"'; *(pos++) = '}';
pos = write_string(pos, IDENTITY_JSON_PART_C);
return pos - identity_json; return pos - identity_json;
} }
namespace {
uint8_t ONE_TIME_KEY_JSON_ALG[] = "curve25519"; std::size_t olm::Account::signature_length(
) {
return 64;
} }
std::size_t olm::Account::sign(
std::uint8_t const * message, std::size_t message_length,
std::uint8_t * signature, std::size_t signature_length
) {
if (signature_length < this->signature_length()) {