Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
matrix-org
Olm
Commits
caaed796
Commit
caaed796
authored
May 17, 2016
by
Richard van der Hoff
Browse files
Implementation of an outbound group session
parent
68d3c7bf
Changes
11
Hide whitespace changes
Inline
Side-by-side
include/olm/error.h
View file @
caaed796
...
...
@@ -34,7 +34,6 @@ enum OlmErrorCode {
/* remember to update the list of string constants in error.c when updating
* this list. */
};
/** get a string representation of the given error code. */
...
...
include/olm/megolm.h
View file @
caaed796
...
...
@@ -47,6 +47,14 @@ typedef struct Megolm {
uint32_t
counter
;
}
Megolm
;
/**
* Get the cipher used in megolm-backed conversations
*
* (AES256 + SHA256, with keys based on an HKDF with info of MEGOLM_KEYS)
*/
const
struct
_olm_cipher
*
megolm_cipher
();
/**
* initialize the megolm ratchet. random_data should be at least
* MEGOLM_RATCHET_LENGTH bytes of randomness.
...
...
include/olm/message.h
0 → 100644
View file @
caaed796
/* Copyright 2016 OpenMarket 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.
*/
/**
* functions for encoding and decoding messages in the Olm protocol.
*
* Some of these functions have only C++ bindings, and are declared in
* message.hh; in time, they should probably be converted to plain C and
* declared here.
*/
#ifndef OLM_MESSAGE_H_
#define OLM_MESSAGE_H_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern
"C"
{
#endif
/**
* The length of the buffer needed to hold a group message.
*/
size_t
_olm_encode_group_message_length
(
size_t
group_session_id_length
,
uint32_t
chain_index
,
size_t
ciphertext_length
,
size_t
mac_length
);
/**
* Writes the message headers into the output buffer.
*
* version: version number of the olm protocol
* session_id: group session identifier
* session_id_length: length of session_id
* chain_index: message index
* ciphertext_length: length of the ciphertext
* output: where to write the output. Should be at least
* olm_encode_group_message_length() bytes long.
* ciphertext_ptr: returns the address that the ciphertext
* should be written to, followed by the MAC.
*/
void
_olm_encode_group_message
(
uint8_t
version
,
const
uint8_t
*
session_id
,
size_t
session_id_length
,
uint32_t
chain_index
,
size_t
ciphertext_length
,
uint8_t
*
output
,
uint8_t
**
ciphertext_ptr
);
#ifdef __cplusplus
}
// extern "C"
#endif
#endif
/* OLM_MESSAGE_H_ */
include/olm/message.hh
View file @
caaed796
...
...
@@ -12,6 +12,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* functions for encoding and decoding messages in the Olm protocol.
*
* Some of these functions have plain-C bindings, and are declared in
* message.h; in time, all of the functions declared here should probably be
* converted to plain C and moved to message.h.
*/
#include "message.h"
#include <cstddef>
#include <cstdint>
...
...
include/olm/olm.h
View file @
caaed796
...
...
@@ -19,6 +19,8 @@
#include <stddef.h>
#include <stdint.h>
#include "olm/outbound_group_session.h"
#ifdef __cplusplus
extern
"C"
{
#endif
...
...
include/olm/outbound_group_session.h
0 → 100644
View file @
caaed796
/* Copyright 2016 OpenMarket 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_OUTBOUND_GROUP_SESSION_H_
#define OLM_OUTBOUND_GROUP_SESSION_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern
"C"
{
#endif
typedef
struct
OlmOutboundGroupSession
OlmOutboundGroupSession
;
/** get the size of an outbound group session, in bytes. */
size_t
olm_outbound_group_session_size
();
/**
* Initialise an outbound group session object using the supplied memory
* The supplied memory should be at least olm_outbound_group_session_size()
* bytes.
*/
OlmOutboundGroupSession
*
olm_outbound_group_session
(
void
*
memory
);
/**
* A null terminated string describing the most recent error to happen to a
* group session */
const
char
*
olm_outbound_group_session_last_error
(
const
OlmOutboundGroupSession
*
session
);
/** Clears the memory used to back this group session */
size_t
olm_clear_outbound_group_session
(
OlmOutboundGroupSession
*
session
);
/** The number of random bytes needed to create an outbound group session */
size_t
olm_init_outbound_group_session_random_length
(
const
OlmOutboundGroupSession
*
session
);
/**
* Start a new outbound group session. Returns std::size_t(-1) on failure. On
* failure last_error will be set with an error code. The last_error will be
* NOT_ENOUGH_RANDOM if the number of random bytes was too small.
*/
size_t
olm_init_outbound_group_session
(
OlmOutboundGroupSession
*
session
,
uint8_t
const
*
random
,
size_t
random_length
);
/**
* The number of bytes that will be created by encrypting a message
*/
size_t
olm_group_encrypt_message_length
(
OlmOutboundGroupSession
*
session
,
size_t
plaintext_length
);
/**
* Encrypt some plain-text. Returns the length of the encrypted message or
* std::size_t(-1) on failure. On failure last_error will be set with an
* error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
* buffer is too small.
*/
size_t
olm_group_encrypt
(
OlmOutboundGroupSession
*
session
,
uint8_t
const
*
plaintext
,
size_t
plaintext_length
,
uint8_t
*
message
,
size_t
message_length
);
#ifdef __cplusplus
}
// extern "C"
#endif
#endif
/* OLM_OUTBOUND_GROUP_SESSION_H_ */
src/megolm.c
View file @
caaed796
...
...
@@ -18,8 +18,22 @@
#include <string.h>
#include "olm/cipher.h"
#include "olm/crypto.h"
const
struct
_olm_cipher
*
megolm_cipher
()
{
static
const
uint8_t
CIPHER_KDF_INFO
[]
=
"MEGOLM_KEYS"
;
static
struct
_olm_cipher
*
cipher
;
static
struct
_olm_cipher_aes_sha_256
OLM_CIPHER
;
if
(
!
cipher
)
{
cipher
=
_olm_cipher_aes_sha_256_init
(
&
OLM_CIPHER
,
CIPHER_KDF_INFO
,
sizeof
(
CIPHER_KDF_INFO
)
-
1
);
}
return
cipher
;
}
/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
*/
#define HASH_KEY_SEED_LENGTH 1
...
...
src/message.cpp
View file @
caaed796
/* Copyright 2015 OpenMarket Ltd
/* Copyright 2015
-2016
OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -325,3 +325,41 @@ void olm::decode_one_time_key_message(
unknown
=
pos
;
}
}
static
std
::
uint8_t
const
GROUP_SESSION_ID_TAG
=
052
;
size_t
_olm_encode_group_message_length
(
size_t
group_session_id_length
,
uint32_t
chain_index
,
size_t
ciphertext_length
,
size_t
mac_length
)
{
size_t
length
=
VERSION_LENGTH
;
length
+=
1
+
varstring_length
(
group_session_id_length
);
length
+=
1
+
varint_length
(
chain_index
);
length
+=
1
+
varstring_length
(
ciphertext_length
);
length
+=
mac_length
;
return
length
;
}
void
_olm_encode_group_message
(
uint8_t
version
,
const
uint8_t
*
session_id
,
size_t
session_id_length
,
uint32_t
chain_index
,
size_t
ciphertext_length
,
uint8_t
*
output
,
uint8_t
**
ciphertext_ptr
)
{
std
::
uint8_t
*
pos
=
output
;
std
::
uint8_t
*
session_id_pos
;
*
(
pos
++
)
=
version
;
pos
=
encode
(
pos
,
GROUP_SESSION_ID_TAG
,
session_id_pos
,
session_id_length
);
std
::
memcpy
(
session_id_pos
,
session_id
,
session_id_length
);
pos
=
encode
(
pos
,
COUNTER_TAG
,
chain_index
);
pos
=
encode
(
pos
,
CIPHERTEXT_TAG
,
*
ciphertext_ptr
,
ciphertext_length
);
}
src/outbound_group_session.c
0 → 100644
View file @
caaed796
/* Copyright 2016 OpenMarket 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/outbound_group_session.h"
#include <string.h>
#include <sys/time.h>
#include "olm/base64.h"
#include "olm/cipher.h"
#include "olm/error.h"
#include "olm/megolm.h"
#include "olm/message.h"
#define OLM_PROTOCOL_VERSION 3
#define SESSION_ID_RANDOM_BYTES 4
#define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES)
struct
OlmOutboundGroupSession
{
/** the Megolm ratchet providing the encryption keys */
Megolm
ratchet
;
/** unique identifier for this session */
uint8_t
session_id
[
GROUP_SESSION_ID_LENGTH
];
enum
OlmErrorCode
last_error
;
};
size_t
olm_outbound_group_session_size
()
{
return
sizeof
(
OlmOutboundGroupSession
);
}
OlmOutboundGroupSession
*
olm_outbound_group_session
(
void
*
memory
)
{
OlmOutboundGroupSession
*
session
=
memory
;
olm_clear_outbound_group_session
(
session
);
return
session
;
}
const
char
*
olm_outbound_group_session_last_error
(
const
OlmOutboundGroupSession
*
session
)
{
return
_olm_error_to_string
(
session
->
last_error
);
}
size_t
olm_clear_outbound_group_session
(
OlmOutboundGroupSession
*
session
)
{
memset
(
session
,
0
,
sizeof
(
OlmOutboundGroupSession
));
return
sizeof
(
OlmOutboundGroupSession
);
}
size_t
olm_init_outbound_group_session_random_length
(
const
OlmOutboundGroupSession
*
session
)
{
/* we need data to initialize the megolm ratchet, plus some more for the
* session id.
*/
return
MEGOLM_RATCHET_LENGTH
+
SESSION_ID_RANDOM_BYTES
;
}
size_t
olm_init_outbound_group_session
(
OlmOutboundGroupSession
*
session
,
uint8_t
const
*
random
,
size_t
random_length
)
{
if
(
random_length
<
olm_init_outbound_group_session_random_length
(
session
))
{
/* Insufficient random data for new session */
session
->
last_error
=
OLM_NOT_ENOUGH_RANDOM
;
return
(
size_t
)
-
1
;
}
megolm_init
(
&
(
session
->
ratchet
),
random
,
0
);
random
+=
MEGOLM_RATCHET_LENGTH
;
/* initialise the session id. This just has to be unique. We use the
* current time plus some random data.
*/
gettimeofday
((
struct
timeval
*
)(
session
->
session_id
),
NULL
);
memcpy
((
session
->
session_id
)
+
sizeof
(
struct
timeval
),
random
,
SESSION_ID_RANDOM_BYTES
);
return
0
;
}
static
size_t
raw_message_length
(
OlmOutboundGroupSession
*
session
,
size_t
plaintext_length
)
{
size_t
ciphertext_length
,
mac_length
;
const
struct
_olm_cipher
*
cipher
=
megolm_cipher
();
ciphertext_length
=
cipher
->
ops
->
encrypt_ciphertext_length
(
cipher
,
plaintext_length
);
mac_length
=
cipher
->
ops
->
mac_length
(
cipher
);
return
_olm_encode_group_message_length
(
GROUP_SESSION_ID_LENGTH
,
session
->
ratchet
.
counter
,
ciphertext_length
,
mac_length
);
}
size_t
olm_group_encrypt_message_length
(
OlmOutboundGroupSession
*
session
,
size_t
plaintext_length
)
{
size_t
message_length
=
raw_message_length
(
session
,
plaintext_length
);
return
_olm_encode_base64_length
(
message_length
);
}
size_t
olm_group_encrypt
(
OlmOutboundGroupSession
*
session
,
uint8_t
const
*
plaintext
,
size_t
plaintext_length
,
uint8_t
*
message
,
size_t
max_message_length
)
{
size_t
ciphertext_length
;
size_t
rawmsglen
;
size_t
result
;
uint8_t
*
ciphertext_ptr
,
*
message_pos
;
const
struct
_olm_cipher
*
cipher
=
megolm_cipher
();
rawmsglen
=
raw_message_length
(
session
,
plaintext_length
);
if
(
max_message_length
<
_olm_encode_base64_length
(
rawmsglen
))
{
session
->
last_error
=
OLM_OUTPUT_BUFFER_TOO_SMALL
;
return
(
size_t
)
-
1
;
}
ciphertext_length
=
cipher
->
ops
->
encrypt_ciphertext_length
(
cipher
,
plaintext_length
);
/* we construct the message at the end of the buffer, so that
* we have room to base64-encode it once we're done.
*/
message_pos
=
message
+
_olm_encode_base64_length
(
rawmsglen
)
-
rawmsglen
;
/* first we build the message structure, then we encrypt
* the plaintext into it.
*/
_olm_encode_group_message
(
OLM_PROTOCOL_VERSION
,
session
->
session_id
,
GROUP_SESSION_ID_LENGTH
,
session
->
ratchet
.
counter
,
ciphertext_length
,
message_pos
,
&
ciphertext_ptr
);
result
=
cipher
->
ops
->
encrypt
(
cipher
,
megolm_get_data
(
&
(
session
->
ratchet
)),
MEGOLM_RATCHET_LENGTH
,
plaintext
,
plaintext_length
,
ciphertext_ptr
,
ciphertext_length
,
message_pos
,
rawmsglen
);
if
(
result
==
(
size_t
)
-
1
)
{
return
result
;
}
megolm_advance
(
&
(
session
->
ratchet
));
return
_olm_encode_base64
(
message_pos
,
rawmsglen
,
message
);
}
tests/test_group_session.cpp
0 → 100644
View file @
caaed796
/* Copyright 2016 OpenMarket 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/outbound_group_session.h"
#include "unittest.hh"
int
main
()
{
uint8_t
random_bytes
[]
=
"0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"
;
{
TestCase
test_case
(
"Group message send/receive"
);
size_t
size
=
olm_outbound_group_session_size
();
void
*
memory
=
alloca
(
size
);
OlmOutboundGroupSession
*
session
=
olm_outbound_group_session
(
memory
);
assert_equals
((
size_t
)
132
,
olm_init_outbound_group_session_random_length
(
session
));
size_t
res
=
olm_init_outbound_group_session
(
session
,
random_bytes
,
sizeof
(
random_bytes
));
assert_equals
((
size_t
)
0
,
res
);
uint8_t
plaintext
[]
=
"Message"
;
size_t
plaintext_length
=
sizeof
(
plaintext
)
-
1
;
size_t
msglen
=
olm_group_encrypt_message_length
(
session
,
plaintext_length
);
uint8_t
*
msg
=
(
uint8_t
*
)
alloca
(
msglen
);
res
=
olm_group_encrypt
(
session
,
plaintext
,
plaintext_length
,
msg
,
msglen
);
assert_equals
(
msglen
,
res
);
// TODO: decode the message
}
}
tests/test_message.cpp
View file @
caaed796
/* Copyright 2015 OpenMarket Ltd
/* Copyright 2015
-2016
OpenMarket Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -62,4 +62,39 @@ assert_equals(message2, output, 35);
}
/* Message encode test */
{
/* group message encode test */
TestCase
test_case
(
"Group message encode test"
);
const
uint8_t
session_id
[]
=
"sessionid"
;
size_t
session_id_len
=
9
;
size_t
length
=
_olm_encode_group_message_length
(
session_id_len
,
200
,
10
,
8
);
size_t
expected_length
=
1
+
(
2
+
session_id_len
)
+
(
1
+
2
)
+
(
2
+
10
)
+
8
;
assert_equals
(
expected_length
,
length
);
uint8_t
output
[
50
];
uint8_t
*
ciphertext_ptr
;
_olm_encode_group_message
(
3
,
session_id
,
session_id_len
,
200
,
// counter
10
,
// ciphertext length
output
,
&
ciphertext_ptr
);
uint8_t
expected
[]
=
"
\x03
"
"
\x2A\x09
sessionid"
"
\x10\xc8\x01
"
"
\x22\x0a
"
;
assert_equals
(
expected
,
output
,
sizeof
(
expected
)
-
1
);
assert_equals
(
output
+
sizeof
(
expected
)
-
1
,
ciphertext_ptr
);
}
/* group message encode test */
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment