From 8dd3c182ee568b9121a336b106aaaceedd1cc470 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 30 Jun 2016 11:27:25 +0100 Subject: [PATCH] Make space in the session pickle for chain index Keeping track of the chain index is a useful thing to do, but is only required if we've enabled diagnostics. Extend the session pickle format to make a space for it, so that pickles can be transferred between the logging_enabled branch and the master branch without loss of information. Also add some tests for session pickling which explicitly check that we can unpickle both formats of pickle. --- include/olm/ratchet.hh | 3 +- src/ratchet.cpp | 18 +++++- src/session.cpp | 22 +++++-- tests/test_session.cpp | 144 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 tests/test_session.cpp diff --git a/include/olm/ratchet.hh b/include/olm/ratchet.hh index aaa3579..13f7097 100644 --- a/include/olm/ratchet.hh +++ b/include/olm/ratchet.hh @@ -169,7 +169,8 @@ std::uint8_t * pickle( std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, - Ratchet & value + Ratchet & value, + bool includes_chain_index ); diff --git a/src/ratchet.cpp b/src/ratchet.cpp index dd1d42c..671d260 100644 --- a/src/ratchet.cpp +++ b/src/ratchet.cpp @@ -363,6 +363,10 @@ std::size_t olm::pickle_length( length += olm::pickle_length(value.sender_chain); length += olm::pickle_length(value.receiver_chains); length += olm::pickle_length(value.skipped_message_keys); + + // the logging_enabled branch includes a 'chain_index' field + length += olm::pickle_length(std::uint32_t(0)); + return length; } @@ -374,18 +378,30 @@ std::uint8_t * olm::pickle( pos = pickle(pos, value.sender_chain); pos = pickle(pos, value.receiver_chains); pos = pickle(pos, value.skipped_message_keys); + + // the logging_enabled branch includes a 'chain_index' field; for us, it is + // empty. + pos = pickle(pos, std::uint32_t(0)); + return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ratchet & value + olm::Ratchet & value, + bool includes_chain_index ) { pos = unpickle(pos, end, value.root_key); pos = unpickle(pos, end, value.sender_chain); pos = unpickle(pos, end, value.receiver_chains); pos = unpickle(pos, end, value.skipped_message_keys); + + // pickle v2 includes a chain index; pickle v1 did not. + if (includes_chain_index) { + std::uint32_t dummy; + pos = unpickle(pos, end, dummy); + } return pos; } diff --git a/src/session.cpp b/src/session.cpp index c148c97..b76a6b8 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -396,7 +396,7 @@ std::size_t olm::Session::decrypt( } namespace { -static const std::uint32_t SESSION_PICKLE_VERSION = 1; +static const std::uint32_t SESSION_PICKLE_VERSION = 2; } std::size_t olm::pickle_length( @@ -433,14 +433,26 @@ std::uint8_t const * olm::unpickle( ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); - if (pickle_version != SESSION_PICKLE_VERSION) { - value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; - return end; + + bool includes_chain_index; + switch (pickle_version) { + case 1: + includes_chain_index = false; + break; + + case 2: + includes_chain_index = true; + break; + + default: + value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; + return end; } + pos = olm::unpickle(pos, end, value.received_message); pos = olm::unpickle(pos, end, value.alice_identity_key); pos = olm::unpickle(pos, end, value.alice_base_key); pos = olm::unpickle(pos, end, value.bob_one_time_key); - pos = olm::unpickle(pos, end, value.ratchet); + pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); return pos; } diff --git a/tests/test_session.cpp b/tests/test_session.cpp new file mode 100644 index 0000000..a0c1f6a --- /dev/null +++ b/tests/test_session.cpp @@ -0,0 +1,144 @@ +#include "olm/session.hh" +#include "olm/pickle_encoding.h" + +#include "unittest.hh" + +/* decode into a buffer, which is returned */ +std::uint8_t *decode_hex( + const char * input +) { + static std::uint8_t buf[256]; + std::uint8_t *p = buf; + while (*input != '\0') { + char high = *(input++); + char low = *(input++); + if (high >= 'a') high -= 'a' - ('9' + 1); + if (low >= 'a') low -= 'a' - ('9' + 1); + uint8_t value = ((high - '0') << 4) | (low - '0'); + *p++ = value; + } + return buf; +} + +void check_session(const olm::Session &session) { + assert_equals( + decode_hex("49d640dc96b80176694af69fc4b8ca9fac49aecbd697d01fd8bee1ed2693b6c9"), + session.ratchet.root_key, 32 + ); + + assert_equals( + std::size_t(1), + session.ratchet.sender_chain.size() + ); + + assert_equals( + decode_hex("f77a03eaa9b301fa7d2a5aa6b50286906de12cc96044f526dbbcb12839ad7003"), + session.ratchet.sender_chain[0].ratchet_key.public_key, 32 + ); + + assert_equals( + decode_hex("d945c6ed4c7c277117adf11fb133a7936d287afe97c0b3ac989644b4490d4f31"), + session.ratchet.sender_chain[0].ratchet_key.private_key, 32 + ); + + assert_equals( + std::uint32_t(0), + session.ratchet.sender_chain[0].chain_key.index + ); + + assert_equals( + std::size_t(0), + session.ratchet.receiver_chains.size() + ); + + assert_equals( + std::size_t(0), + session.ratchet.skipped_message_keys.size() + ); + + assert_equals(OLM_SUCCESS, session.last_error); + assert_equals(false, session.received_message); + + assert_equals( + decode_hex("7326b58623a3f7bd8da11a1bab51f432c02a7430241b326e9fc8916a21eb257e"), + session.alice_identity_key.public_key, 32 + ); + + assert_equals( + decode_hex("0ab4b30bde20bd374ceccc72861660f0fd046f7516900796c3e5de41c598316c"), + session.alice_base_key.public_key, 32 + ); + + assert_equals( + decode_hex("585dba930b10d90d81702c715f4085d07c42b0cd2d676010bb6086c86c4cc618"), + session.bob_one_time_key.public_key, 32 + ); +} + +int main() { + +{ + TestCase test_case("V1 session pickle"); + + const uint8_t *PICKLE_KEY=(uint8_t *)"secret_key"; + uint8_t pickled[] = + "wkEpwMgiAqD7B1/Lw2cKYYDcUZVOd9QHes7ZroWxr/Rp/nWEAySgRsIu/a54YhO67rwitr" + "Lpos7tFxxK9IZ7pKB1qrR1coVWIt78V9lp9WgmBAvxHBSY+tu1lkL/JjLi963/yFdPancZ" + "+WHMVfaKlV3gWGpo7EfNK6qAOxI1Ea/eCsE2sYrsHEDvLLGlKAA9E56rmmoe2w6TKzsQjs" + "ZM2/XT2eJ82EgMO9pL02iLElXWmGNv72Ut7DouR0pQIT50HIEEKcFxYcoTb3WCfJD76Coe" + "sE4kx+TA6d45Xu1bwQNNkTGF+nCCu/GmKY+sECXbz9U6WhxG0YdF9Z4T8YkWYAgpKNS0FW" + "RV"; + size_t pickle_len = _olm_enc_input( + PICKLE_KEY, strlen((char *)PICKLE_KEY), + pickled, strlen((char *)pickled), NULL + ); + + olm::Session session; + const uint8_t *unpickle_res = olm::unpickle(pickled, pickled+sizeof(pickled), session); + assert_equals( + pickle_len, (size_t)(unpickle_res - pickled) + ); + + check_session(session); + +#if 0 + size_t rawlen = olm::pickle_length(session); + uint8_t *r1 = _olm_enc_output_pos(pickled, rawlen); + olm::pickle(r1, session); + _olm_enc_output( + PICKLE_KEY, strlen((char *)PICKLE_KEY), + pickled, rawlen); + printf("%s\n", pickled); +#endif +} + +{ + TestCase test_case("V2 session pickle"); + + const uint8_t *PICKLE_KEY=(uint8_t *)"secret_key"; + uint8_t pickled[1024] = + "XTc0/aHUZIVjq257h6sCI1f4/EFJ42QYSOp6SDn88FSXRkO9dfrxGk8gPvZiNCnXFYWggX" + "UkTkaiSd9MFuj6kB49COlLU1zKw3caJmEIgud7umbYrAXhaBMCTl8T3XFVJK5gXThOE3sI" + "QCRy4RP4zAVJMTwnvNTzobi3N0aCxEDC+RIol6cEv+bV04zs8wbivdBDVJ7WIDVBF42dzQ" + "qPhL4KC5TLB9FYn/eUvrn56tHa5B3udjm1dbPynkjzkRy7Mbp5zUe6XhHDesKo0VxaHhOl" + "t117bSOKdf72wK/bStaWTpNdAA9h0AFuj2Rgp7E42yJTHY0tv23lMH1Hd+IHdsMsgMcfvL" + "Si"; + + size_t pickle_len = _olm_enc_input( + PICKLE_KEY, strlen((char *)PICKLE_KEY), + pickled, strlen((char *)pickled), NULL + ); + + olm::Session session; + const uint8_t *unpickle_res = olm::unpickle(pickled, pickled+sizeof(pickled), session); + assert_equals( + pickle_len, (size_t)(unpickle_res - pickled) + ); + + check_session(session); +} + + + +return 0; +}