commit cec50f7a274d09d95ca9e00020b6b59c44f336a8
parent a147d1b9bec2045f5d08065c98400bf8aef81586
Author: MTRNord <mtrnord1@gmail.com>
Date: Mon, 14 Apr 2025 16:13:59 +0200
Add rest of pickling, clang-format and fix fuzzer
Diffstat:
9 files changed, 878 insertions(+), 391 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,54 @@
+# Generated from CLion C/C++ Code Style settings
+---
+Language: Cpp
+BasedOnStyle: LLVM
+AccessModifierOffset: -4
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignOperands: false
+AlignTrailingComments: false
+AlwaysBreakTemplateDeclarations: Yes
+BraceWrapping:
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: false
+ BeforeCatch: true
+ BeforeElse: true
+ BeforeLambdaBody: true
+ BeforeWhile: true
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBraces: Custom
+BreakConstructorInitializers: AfterColon
+BreakConstructorInitializersBeforeComma: false
+ColumnLimit: 120
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+IncludeCategories:
+ - Regex: '^<.*'
+ Priority: 1
+ - Regex: '^".*'
+ Priority: 2
+ - Regex: '.*'
+ Priority: 3
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IndentCaseBlocks: true
+IndentWidth: 4
+InsertNewlineAtEOF: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+SpaceInEmptyParentheses: false
+SpacesInAngles: false
+SpacesInConditionalStatement: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+TabWidth: 4
+...
diff --git a/fuzz/StandaloneFuzzTargetMain.c b/fuzz/StandaloneFuzzTargetMain.c
@@ -18,12 +18,12 @@
#include <stdlib.h>
#include <string.h>
-extern int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size);
-extern int LLVMFuzzerInitialize(int* argc, char*** argv);
+extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
+extern int LLVMFuzzerInitialize(int *argc, char ***argv);
-int main(int argc, char** argv)
+int main(int argc, char **argv)
{
- const char* progname;
+ const char *progname;
if ((progname = strrchr(argv[0], '/')))
progname++;
else
@@ -33,13 +33,13 @@ int main(int argc, char** argv)
for (int i = 1; i < argc; i++)
{
fprintf(stderr, "Running: %s\n", argv[i]);
- FILE* f = fopen(argv[i], "r+");
+ FILE *f = fopen(argv[i], "r+");
assert(f);
fseek(f, 0, SEEK_END);
- long len = ftell(f);
+ const long len = ftell(f);
fseek(f, 0, SEEK_SET);
- unsigned char* buf = (unsigned char*)malloc(len);
- size_t n_read = fread(buf, 1, len, f);
+ unsigned char *buf = (unsigned char *)malloc(len);
+ const size_t n_read = fread(buf, 1, len, f);
fclose(f);
assert(n_read == len);
LLVMFuzzerTestOneInput(buf, len);
diff --git a/fuzz/olm_sign_fuzzer.cpp b/fuzz/olm_sign_fuzzer.cpp
@@ -1,18 +1,14 @@
+#include <botan/auto_rng.h>
#include <cassert>
-#include <cstdint>
#include <cstddef>
-#include <botan/auto_rng.h>
+#include <cstdint>
#include "account.hpp"
#include "botan/pubkey.h"
// Just needed for compiling reasons
-extern "C" int
-LLVMFuzzerInitialize(int* argc, char*** argv)
-{
- return 0;
-}
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { return 0; }
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
if (Size == 0)
{
@@ -23,8 +19,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
Botan::AutoSeeded_RNG rng;
account.new_account(rng);
- const std::string_view message(reinterpret_cast<const char*>(Data), Size);
- auto signature = account.sign(message);
+ const std::string_view message(reinterpret_cast<const char *>(Data), Size);
+ auto signature = account.sign(rng, message);
// Verify the signature
Botan::PK_Verifier verifier(account.identity_keys->ed25519_key, "Ed25519ph");
diff --git a/include/megolm.hpp b/include/megolm.hpp
@@ -0,0 +1,60 @@
+#pragma once
+#include <array>
+#include <botan/rng.h>
+
+/**
+ * number of bytes in each part of the ratchet; this should be the same as
+ * the length of the hash function used in the HMAC (32 bytes for us, as we
+ * use HMAC-SHA-256)
+ */
+#define MEGOLM_RATCHET_PART_LENGTH 32 /* SHA256_OUTPUT_LENGTH */
+
+/**
+ * number of parts in the ratchet; the advance() implementations rely on
+ * this being 4.
+ */
+#define MEGOLM_RATCHET_PARTS 4
+
+#define MEGOLM_RATCHET_LENGTH (MEGOLM_RATCHET_PARTS * MEGOLM_RATCHET_PART_LENGTH)
+
+namespace spank_olm
+{
+ struct Megolm
+ {
+ std::array<std::array<std::uint8_t, MEGOLM_RATCHET_PART_LENGTH>, MEGOLM_RATCHET_PARTS> data;
+ std::uint32_t counter;
+
+
+ /**
+ * \brief Initialize the megolm ratchet.
+ */
+ void init(Botan::RandomNumberGenerator &rng, unsigned int counter);
+
+ /**
+ * \brief Returns the number of bytes needed to store a megolm
+ */
+ [[nodiscard]] size_t pickle_length() const;
+
+ /**
+ * \brief Pickle the megolm.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos) const;
+
+ /**
+ * \brief Unpickle the megolm.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, const std::uint8_t *end);
+
+ /**
+ * \brief Advance the megolm ratchet by one step.
+ */
+ void advance();
+
+ /**
+ * \brief Advance the megolm ratchet by a given number of steps.
+ */
+ void advance(unsigned int advance_to);
+
+ [[nodiscard]] const uint8_t *get_data() const { return reinterpret_cast<const uint8_t *>(data.data()); }
+ };
+} // namespace spank_olm
diff --git a/include/pickle.hpp b/include/pickle.hpp
@@ -0,0 +1,181 @@
+#pragma once
+#include <account.hpp>
+
+namespace spank_olm
+{
+
+ /**
+ * Serializes a 32-bit unsigned integer into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The 32-bit unsigned integer to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, std::uint32_t value);
+
+ /**
+ * Deserializes a 32-bit unsigned integer from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the 32-bit unsigned integer to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or
+ * nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::uint32_t &value);
+
+ /**
+ * Serializes a boolean value into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The boolean value to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, bool value);
+
+ /**
+ * Deserializes a boolean value from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the boolean value to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, bool &value);
+
+ /**
+ * Serializes a Botan::secure_vector<uint8_t> into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The Botan::secure_vector<uint8_t> to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const Botan::secure_vector<uint8_t> &value);
+
+ /**
+ * Deserializes a Botan::secure_vector<uint8_t> from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end,
+ Botan::secure_vector<uint8_t> &value);
+
+ /**
+ * Serializes a std::vector<uint8_t> into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The Botan::secure_vector<uint8_t> to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::vector<uint8_t> &value);
+
+ /**
+ * Deserializes a std::vector<uint8_t> from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::vector<uint8_t> &value);
+
+ /**
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value A OneTimeKey object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::optional<OneTimeKey> &value);
+
+ /**
+ * Deserializes a OneTimeKey object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @return A pair containing the pointer to the position in the byte array after the deserialized data and the
+ * deserialized OneTimeKey object.
+ */
+ static std::pair<std::uint8_t const *, std::optional<OneTimeKey>> unpickle_otk(std::uint8_t const *pos,
+ std::uint8_t const *end);
+
+ /**
+ * Serializes a FixedSizeArray object into a byte array.
+ *
+ * @tparam T The type of elements in the FixedSizeArray.
+ * @tparam max_size The maximum size of the FixedSizeArray.
+ * @param pos Pointer to the current position in the byte array.
+ * @param list The FixedSizeArray object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ template <typename T, std::size_t max_size>
+ std::uint8_t *pickle(std::uint8_t *pos, FixedSizeArray<T, max_size> const &list);
+
+ /**
+ * Deserializes a FixedSizeArray object from a byte array.
+ *
+ * @tparam max_size The maximum size of the FixedSizeArray.
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param list Reference to the FixedSizeArray object to store the deserialized values.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ template <std::size_t max_size>
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end,
+ FixedSizeArray<OneTimeKey, max_size> &list);
+
+ /**
+ * Serializes a uint8_t value into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The uint8_t value to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, std::uint8_t value);
+
+ /**
+ * Deserializes a uint8_t value from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the uint8_t value to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::uint8_t &value);
+
+ /**
+ * Serializes an optional IdentityKeys object into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The optional IdentityKeys object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::optional<IdentityKeys> &value);
+
+ /**
+ * Deserializes an optional IdentityKeys object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the optional IdentityKeys object to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::optional<IdentityKeys> &value);
+
+ /**
+ * Deserializes an optional OneTimeKey object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the optional OneTimeKey object to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::optional<OneTimeKey> &value);
+
+ std::uint8_t *pickle_bytes(std::uint8_t *pos, const std::uint8_t *bytes, const std::size_t bytes_length);
+
+ std::uint8_t const *unpickle_bytes(const std::uint8_t *pos, const std::uint8_t *end, std::uint8_t *bytes,
+ const std::size_t bytes_length);
+} // namespace spank_olm
diff --git a/meson.build b/meson.build
@@ -27,7 +27,7 @@ is_wasm = host_machine.system() == 'emscripten'
# Add specific arguments for WASM
if is_wasm
add_project_arguments('-flto', language : 'cpp')
- add_project_link_arguments('-flto', '-lembind', '-sEMBIND_AOT=1', '-sEXPORT_ES6=1', '-sMODULARIZE=1', '-sENVIRONMENT=web', '-sEXPORT_NAME=SpankOlmLibrarys', '-sFILESYSTEM=0', '-sEXPORT_ALL=1', '--emit-tsd=interface.d.ts', language : 'cpp')
+ add_project_link_arguments('-flto', '-lembind', '-sEMBIND_AOT=1', '-sEXPORT_ES6=1', '-sMODULARIZE=1', '-sENVIRONMENT=web,worker', '-sEXPORT_NAME=SpankOlmLibrarys', '-sFILESYSTEM=0', '-sEXPORT_ALL=1', '--emit-tsd=interface.d.ts', language : 'cpp')
endif
# Cmake doesnt work with meson, so we need to require pkg-config
@@ -47,7 +47,11 @@ spank_olm_deps = [botan_dep]
incdir = include_directories('include')
# List of source files
-src_files = files('src/spank-olm.cpp', 'src/account.cpp')
+src_files = files(
+ 'src/spank-olm.cpp',
+ 'src/account.cpp',
+ 'src/megolm.cpp',
+ 'src/pickle.cpp', )
if is_wasm
spank_olm = executable('spank_olm', src_files, install : true, dependencies : spank_olm_deps, include_directories : incdir, override_options : ['b_lto=false'])
diff --git a/src/account.cpp b/src/account.cpp
@@ -1,371 +1,27 @@
#include "account.hpp"
#include "errors.hpp"
+#include "pickle.hpp"
#include <botan/pubkey.h>
#include <botan/rng.h>
-/* Convenience macro for checking the return value of internal unpickling
- * functions and returning early on failure. */
-#ifndef UNPICKLE_OK
-#define UNPICKLE_OK(x) do { if (!(x)) return nullptr; } while(0)
-#endif
-
-
namespace spank_olm
{
- /**
- * Serializes a 32-bit unsigned integer into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The 32-bit unsigned integer to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(std::uint8_t* pos, std::uint32_t value)
- {
- for (int i = 3; i >= 0; --i)
- {
- pos[i] = value & 0xFF;
- value >>= 8;
- }
- return pos + 4;
- }
-
- /**
- * Deserializes a 32-bit unsigned integer from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the 32-bit unsigned integer to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(std::uint8_t const* pos, std::uint8_t const* end, std::uint32_t& value)
- {
- if (!pos || end < pos + 4) return nullptr;
- value = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];
- return pos + 4;
- }
-
- /**
- * Serializes a boolean value into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The boolean value to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(std::uint8_t* pos, const bool value)
- {
- *(pos++) = value ? 1 : 0;
- return pos;
- }
-
- /**
- * Deserializes a boolean value from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the boolean value to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(std::uint8_t const* pos, std::uint8_t const* end, bool& value)
- {
- if (!pos || end <= pos) return nullptr;
- value = *(pos++) != 0;
- return pos;
- }
-
- /**
- * Serializes a Botan::secure_vector<uint8_t> into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The Botan::secure_vector<uint8_t> to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(std::uint8_t* pos, const Botan::secure_vector<uint8_t>& value)
- {
- pos = pickle(pos, static_cast<std::uint32_t>(value.size()));
- for (const auto byte : value)
- {
- *(pos++) = byte;
- }
- return pos;
- }
-
- /**
- * Deserializes a Botan::secure_vector<uint8_t> from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(std::uint8_t const* pos, std::uint8_t const* end, Botan::secure_vector<uint8_t>& value)
- {
- std::uint32_t size;
- pos = unpickle(pos, end, size);
- if (!pos || end < pos + size) return nullptr;
- value.assign(pos, pos + size);
- return pos + size;
- }
-
- /**
- * Serializes a std::vector<uint8_t> into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The Botan::secure_vector<uint8_t> to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(std::uint8_t* pos, const std::vector<uint8_t>& value)
+ void Account::new_account(Botan::RandomNumberGenerator &rng)
{
- pos = pickle(pos, static_cast<std::uint32_t>(value.size()));
- for (const auto byte : value)
- {
- *(pos++) = byte;
- }
- return pos;
- }
-
- /**
- * Deserializes a std::vector<uint8_t> from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(std::uint8_t const* pos, std::uint8_t const* end, std::vector<uint8_t>& value)
- {
- std::uint32_t size;
- pos = unpickle(pos, end, size);
- if (!pos || end < pos + size) return nullptr;
- value.assign(pos, pos + size);
- return pos + size;
- }
-
- std::uint8_t* pickle(
- std::uint8_t* pos,
- const std::optional<OneTimeKey>& value)
- {
- pos = pickle(pos, value->id);
- pos = pickle(pos, value->published);
- return pickle(pos, value->key.raw_private_key_bits());
- }
-
- /**
- * Deserializes a OneTimeKey object from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @return A pair containing the pointer to the position in the byte array after the deserialized data and the deserialized OneTimeKey object.
- */
- static std::pair<std::uint8_t const*, std::optional<OneTimeKey>> unpickle_otk(
- std::uint8_t const* pos, std::uint8_t const* end
- )
- {
- std::uint32_t id; ///< The unique identifier for the one-time key.
- bool published; ///< Indicates whether the key has been published.
-
- pos = unpickle(pos, end, id);
- if (!pos || !id)
- {
- return {nullptr, std::nullopt};
- }
- pos = unpickle(pos, end, published);
- if (!pos) return {nullptr, std::nullopt};
-
- Botan::secure_vector<uint8_t> key_bits;
- pos = unpickle(pos, end, key_bits);
- if (!pos) return {nullptr, std::nullopt};
-
- auto otk = OneTimeKey{id, published, Botan::X25519_PrivateKey(key_bits)};
-
- return {pos, otk};
- }
-
- /**
- * Serializes a FixedSizeArray object into a byte array.
- *
- * @tparam T The type of elements in the FixedSizeArray.
- * @tparam max_size The maximum size of the FixedSizeArray.
- * @param pos Pointer to the current position in the byte array.
- * @param list The FixedSizeArray object to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- template <typename T, std::size_t max_size>
- std::uint8_t* pickle(
- std::uint8_t* pos,
- FixedSizeArray<T, max_size> const& list
- )
- {
- pos = pickle(pos, static_cast<std::uint32_t>(list.size()));
- for (auto const& value : list)
- {
- pos = pickle(pos, *value);
- }
- return pos;
- }
-
- /**
- * Deserializes a FixedSizeArray object from a byte array.
- *
- * @tparam max_size The maximum size of the FixedSizeArray.
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param list Reference to the FixedSizeArray object to store the deserialized values.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- template <std::size_t max_size>
- std::uint8_t const* unpickle(
- std::uint8_t const* pos, std::uint8_t const* end,
- FixedSizeArray<OneTimeKey, max_size>& list
- )
- {
- std::uint32_t size;
- pos = unpickle(pos, end, size);
- if (!pos)
- {
- return nullptr;
- }
-
- while (size-- && pos != end)
- {
- auto [temp_pos, value] = unpickle_otk(pos, end);
- if (!((pos = temp_pos))) return nullptr;
- list.insert(value.value());
- }
-
- return pos;
- }
-
- /**
- * Serializes a uint8_t value into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The uint8_t value to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(
- std::uint8_t* pos,
- const std::uint8_t value
- )
- {
- *(pos++) = value;
- return pos;
- }
-
- /**
- * Deserializes a uint8_t value from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the uint8_t value to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(
- std::uint8_t const* pos, std::uint8_t const* end,
- std::uint8_t& value
- )
- {
- if (!pos || pos == end) return nullptr;
- value = *(pos++);
- return pos;
- }
-
- /**
- * Serializes an optional IdentityKeys object into a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param value The optional IdentityKeys object to serialize.
- * @return Pointer to the position in the byte array after the serialized data.
- */
- std::uint8_t* pickle(
- std::uint8_t* pos,
- const std::optional<IdentityKeys>& value)
- {
- pos = pickle(pos, value->ed25519_key.public_key()->raw_public_key_bits());
- pos = pickle(pos, value->ed25519_key.raw_private_key_bits());
- pos = pickle(pos, value->curve25519_key.public_key()->raw_public_key_bits());
- return pickle(pos, value->curve25519_key.raw_private_key_bits());
- }
-
- /**
- * Deserializes an optional IdentityKeys object from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the optional IdentityKeys object to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(
- std::uint8_t const* pos, std::uint8_t const* end,
- std::optional<IdentityKeys>& value)
- {
- if (!pos || pos == end) return nullptr;
- Botan::secure_vector<uint8_t> ed25519_public_key_bits;
- Botan::secure_vector<uint8_t> curve25519_public_key_bits;
- Botan::secure_vector<uint8_t> ed25519_private_key_bits;
- Botan::secure_vector<uint8_t> curve25519_private_key_bits;
-
- pos = unpickle(pos, end, ed25519_public_key_bits);
- UNPICKLE_OK(pos);
- pos = unpickle(pos, end, ed25519_private_key_bits);
- UNPICKLE_OK(pos);
- pos = unpickle(pos, end, curve25519_public_key_bits);
- UNPICKLE_OK(pos);
- pos = unpickle(pos, end, curve25519_private_key_bits);
- UNPICKLE_OK(pos);
- value = IdentityKeys{
- Botan::Ed25519_PrivateKey(ed25519_private_key_bits),
- Botan::X25519_PrivateKey(curve25519_private_key_bits)
- };
- return pos;
- }
-
- /**
- * Deserializes an optional OneTimeKey object from a byte array.
- *
- * @param pos Pointer to the current position in the byte array.
- * @param end Pointer to the end of the byte array.
- * @param value Reference to the optional OneTimeKey object to store the deserialized value.
- * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
- */
- std::uint8_t const* unpickle(
- std::uint8_t const* pos, std::uint8_t const* end,
- std::optional<OneTimeKey>& value)
- {
- if (!pos || pos == end) return nullptr;
- std::uint32_t id;
- bool published;
- Botan::secure_vector<uint8_t> key_bits;
- pos = unpickle(pos, end, id);
- UNPICKLE_OK(pos);
- pos = unpickle(pos, end, published);
- UNPICKLE_OK(pos);
- pos = unpickle(pos, end, key_bits);
- UNPICKLE_OK(pos);
- value = OneTimeKey{id, published, Botan::X25519_PrivateKey(key_bits)};
- return pos;
- }
-
- void Account::new_account(Botan::RandomNumberGenerator& rng)
- {
- identity_keys = IdentityKeys{
- Botan::Ed25519_PrivateKey(rng),
- Botan::X25519_PrivateKey(rng)
- };
+ identity_keys = IdentityKeys{Botan::Ed25519_PrivateKey(rng), Botan::X25519_PrivateKey(rng)};
// Make sure we check the key pairs.
- if (!identity_keys->ed25519_key.check_key(rng, false) ||
- !identity_keys->curve25519_key.check_key(rng, false) ||
+ if (!identity_keys->ed25519_key.check_key(rng, false) || !identity_keys->curve25519_key.check_key(rng, false) ||
!identity_keys->ed25519_key.public_key()->check_key(rng, false) ||
- !identity_keys->curve25519_key.public_key()->check_key(rng, false)
- )
+ !identity_keys->curve25519_key.public_key()->check_key(rng, false))
{
throw SpankOlmErrorKeyGeneration();
}
}
- std::vector<uint8_t> Account::sign(Botan::RandomNumberGenerator& rng, const std::string_view message) const
+ std::vector<uint8_t> Account::sign(Botan::RandomNumberGenerator &rng, const std::string_view message) const
{
// According to https://botan.randombit.net/handbook/api_ref/pubkey.html#ed25519-ed448-variants
const std::string padding_scheme = "Ed25519ph";
@@ -383,7 +39,7 @@ namespace spank_olm
std::size_t Account::mark_keys_as_published()
{
auto count = 0;
- for (const auto& key : one_time_keys)
+ for (const auto &key : one_time_keys)
{
if (!key->published)
{
@@ -396,7 +52,7 @@ namespace spank_olm
return count;
}
- void Account::generate_one_time_keys(Botan::RandomNumberGenerator& rng, const std::size_t number_of_keys)
+ void Account::generate_one_time_keys(Botan::RandomNumberGenerator &rng, const std::size_t number_of_keys)
{
for (std::size_t i = 0; i < number_of_keys; ++i)
{
@@ -404,7 +60,7 @@ namespace spank_olm
}
}
- void Account::generate_fallback_key(Botan::RandomNumberGenerator& rng)
+ void Account::generate_fallback_key(Botan::RandomNumberGenerator &rng)
{
prev_fallback_key = current_fallback_key;
current_fallback_key = OneTimeKey{++next_one_time_key_id, false, Botan::X25519_PrivateKey(rng)};
@@ -419,32 +75,32 @@ namespace spank_olm
}
}
- std::optional<OneTimeKey const*> Account::lookup_key(Botan::Public_Key const& key) const
+ std::optional<OneTimeKey const *> Account::lookup_key(Botan::Public_Key const &key) const
{
- for (const auto& one_time_key : one_time_keys)
+ for (const auto &one_time_key : one_time_keys)
{
if (one_time_key->key.public_key()->raw_public_key_bits() == key.raw_public_key_bits())
{
return one_time_key;
}
}
- if (current_fallback_key && current_fallback_key->key.public_key()->raw_public_key_bits() == key.
- raw_public_key_bits())
+ if (current_fallback_key &&
+ current_fallback_key->key.public_key()->raw_public_key_bits() == key.raw_public_key_bits())
{
return ¤t_fallback_key.value();
}
- if (prev_fallback_key && prev_fallback_key->key.public_key()->raw_public_key_bits() == key.
- raw_public_key_bits())
+ if (prev_fallback_key &&
+ prev_fallback_key->key.public_key()->raw_public_key_bits() == key.raw_public_key_bits())
{
return ¤t_fallback_key.value();
}
return std::nullopt;
}
- void Account::remove_key(Botan::Public_Key const& key)
+ void Account::remove_key(Botan::Public_Key const &key)
{
// Use iterator to find and remove the key.
- for (const auto& one_time_key : one_time_keys)
+ for (const auto &one_time_key : one_time_keys)
{
if (one_time_key->key.public_key()->raw_public_key_bits() == key.raw_public_key_bits())
{
@@ -461,19 +117,20 @@ namespace spank_olm
* \brief The current version of the account pickle format.
*
* \details
- * - Version 1 used only 32 bytes for the ed25519 private key. Any keys thus used should be considered compromised.
+ * - Version 1 used only 32 bytes for the ed25519 private key. Any keys thus used should be considered
+ * compromised.
* - Version 2 does not have fallback keys.
* - Version 3 does not store whether the current fallback key is published.
*/
constexpr std::uint32_t ACCOUNT_PICKLE_VERSION = 4;
- }
+ } // namespace
/**
- * Serializes the Account object into a byte array.
- *
- * @return A vector of uint8_t containing the serialized data.
- */
+ * Serializes the Account object into a byte array.
+ *
+ * @return A vector of uint8_t containing the serialized data.
+ */
std::vector<uint8_t> Account::pickle() const
{
std::vector<uint8_t> buffer(1024); // Initial buffer size, can be adjusted
@@ -487,8 +144,10 @@ namespace spank_olm
// Calculate the number of fallback keys
std::uint8_t fallback_key_count = 0;
- if (current_fallback_key && current_fallback_key->published) fallback_key_count++;
- if (prev_fallback_key && prev_fallback_key->published) fallback_key_count++;
+ if (current_fallback_key && current_fallback_key->published)
+ fallback_key_count++;
+ if (prev_fallback_key && prev_fallback_key->published)
+ fallback_key_count++;
// Serialize the fallback key count
pos = spank_olm::pickle(pos, fallback_key_count);
@@ -518,7 +177,7 @@ namespace spank_olm
* @throws SpankOlmErrorUnknownPickleVersion if the pickle version is unknown.
* @throws SpankOlmErrorCorruptedAccountPickle if the pickle data is corrupted.
*/
- Account Account::unpickle(std::vector<uint8_t> const& data)
+ Account Account::unpickle(std::vector<uint8_t> const &data)
{
Account value;
auto pos = data.data();
@@ -605,4 +264,4 @@ namespace spank_olm
return value;
}
-}
+} // namespace spank_olm
diff --git a/src/megolm.cpp b/src/megolm.cpp
@@ -0,0 +1,168 @@
+#include "megolm.hpp"
+#include "pickle.hpp"
+
+#include <botan/auto_rng.h>
+#include <botan/hash.h>
+#include <botan/mac.h>
+
+
+/* Convenience macro for checking the return value of internal unpickling
+ * functions and returning early on failure. */
+#ifndef UNPICKLE_OK
+#define UNPICKLE_OK(x) \
+ do \
+ { \
+ if (!(x)) \
+ return nullptr; \
+ } \
+ while (0)
+#endif
+
+/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
+ */
+#define HASH_KEY_SEED_LENGTH 1
+static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = {{0x00}, {0x01}, {0x02}, {0x03}};
+
+
+namespace spank_olm
+{
+ constexpr size_t UINT32_LENGTH = 4;
+
+ std::uint8_t *_olm_pickle_uint32(std::uint8_t *pos, uint32_t const value) { return pickle(pos, value); }
+
+
+ std::uint8_t const *_olm_unpickle_uint32(std::uint8_t const *pos, std::uint8_t const *end, std::uint32_t *value)
+ {
+ return unpickle(pos, end, *value);
+ }
+
+
+ std::uint8_t *_olm_pickle_bytes(std::uint8_t *pos, std::uint8_t const *bytes, size_t bytes_length)
+ {
+ return pickle_bytes(pos, bytes, bytes_length);
+ }
+
+ std::uint8_t const *_olm_unpickle_bytes(std::uint8_t const *pos, std::uint8_t const *end, std::uint8_t *bytes,
+ size_t bytes_length)
+ {
+ return unpickle_bytes(pos, end, bytes, bytes_length);
+ }
+
+
+ static void
+ rehash_part(std::array<std::array<std::uint8_t, MEGOLM_RATCHET_PART_LENGTH>, MEGOLM_RATCHET_PARTS> &data,
+ const int rehash_from_part, const int rehash_to_part)
+ {
+ const auto hmac = Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+
+ hmac->set_key(HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH);
+ hmac->update(data[rehash_from_part].data(), MEGOLM_RATCHET_PART_LENGTH);
+ hmac->final(data[rehash_to_part].data());
+ }
+
+
+ void Megolm::init(Botan::RandomNumberGenerator &rng, const unsigned int counter)
+ {
+ this->counter = counter;
+ for (auto &part : data)
+ {
+ rng.randomize(part.data(), part.size());
+ }
+ }
+
+ size_t Megolm::pickle_length() const { return data.size() * data[0].size() + UINT32_LENGTH; }
+
+ std::uint8_t *Megolm::pickle(std::uint8_t *pos) const
+ {
+
+ pos = _olm_pickle_bytes(pos, get_data(), MEGOLM_RATCHET_LENGTH);
+ pos = _olm_pickle_uint32(pos, counter);
+ return pos;
+ }
+
+ std::uint8_t const *Megolm::unpickle(std::uint8_t const *pos, const std::uint8_t *end)
+ {
+ pos = _olm_unpickle_bytes(pos, end, const_cast<std::uint8_t *>(get_data()), MEGOLM_RATCHET_LENGTH);
+ UNPICKLE_OK(pos);
+
+ pos = _olm_unpickle_uint32(pos, end, &counter);
+ UNPICKLE_OK(pos);
+
+ return pos;
+ }
+
+ void Megolm::advance()
+ {
+ counter++;
+ uint32_t mask = 0x00FFFFFF;
+ int h = 0;
+
+ /* figure out how much we need to rekey */
+ while (h < MEGOLM_RATCHET_PARTS && (counter & mask))
+ {
+ h++;
+ mask >>= 8;
+ }
+
+ /* now update R(h)...R(3) based on R(h) */
+ for (int i = MEGOLM_RATCHET_PARTS - 1; i >= h; i--)
+ {
+ rehash_part(data, h, i);
+ }
+ }
+
+ void Megolm::advance(const unsigned int advance_to)
+ {
+ /* starting with R0, see if we need to update each part of the hash */
+ for (int j = 0; j < MEGOLM_RATCHET_PARTS; j++)
+ {
+ const int shift = (MEGOLM_RATCHET_PARTS - j - 1) * 8;
+ const uint32_t mask = (~static_cast<uint32_t>(0)) << shift;
+
+
+ /* how many times do we need to rehash this part?
+ *
+ * '& 0xff' ensures we handle integer wraparound correctly
+ */
+ unsigned int steps = ((advance_to >> shift) - (counter >> shift)) & 0xff;
+
+ if (steps == 0)
+ {
+ /* deal with the edge case where megolm->counter is slightly larger
+ * than advance_to. This should only happen for R(0), and implies
+ * that advance_to has wrapped around and we need to advance R(0)
+ * 256 times.
+ */
+ if (advance_to < counter)
+ {
+ steps = 0x100;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ /* for all but the last step, we can just bump R(j) without regard
+ * to R(j+1)...R(3).
+ */
+ while (steps-- > 1)
+ {
+ rehash_part(data, j, j);
+ }
+
+ /* on the last step we also need to bump R(j+1)...R(3).
+ *
+ * (Theoretically, we could skip bumping R(j+2) if we're going to bump
+ * R(j+1) again, but the code to figure that out is a bit baroque and
+ * doesn't save us much).
+ */
+ for (int k = 3; k >= j; k--)
+ {
+ rehash_part(data, j, k);
+ }
+ counter = advance_to & mask;
+ }
+ }
+
+} // namespace spank_olm
diff --git a/src/pickle.cpp b/src/pickle.cpp
@@ -0,0 +1,365 @@
+#include "pickle.hpp"
+
+#include <botan/x25519.h>
+
+/* Convenience macro for checking the return value of internal unpickling
+ * functions and returning early on failure. */
+#ifndef UNPICKLE_OK
+#define UNPICKLE_OK(x) \
+ do \
+ { \
+ if (!(x)) \
+ return nullptr; \
+ } \
+ while (0)
+#endif
+
+
+namespace spank_olm
+{
+
+ /**
+ * Serializes a 32-bit unsigned integer into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The 32-bit unsigned integer to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, std::uint32_t value)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ pos[i] = value & 0xFF;
+ value >>= 8;
+ }
+ return pos + 4;
+ }
+
+ /**
+ * Deserializes a 32-bit unsigned integer from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the 32-bit unsigned integer to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::uint32_t &value)
+ {
+ if (!pos || end < pos + 4)
+ return nullptr;
+ value = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];
+ return pos + 4;
+ }
+
+ /**
+ * Serializes a boolean value into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The boolean value to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const bool value)
+ {
+ *(pos++) = value ? 1 : 0;
+ return pos;
+ }
+
+ /**
+ * Deserializes a boolean value from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the boolean value to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, bool &value)
+ {
+ if (!pos || end <= pos)
+ return nullptr;
+ value = *(pos++) != 0;
+ return pos;
+ }
+
+ /**
+ * Serializes a Botan::secure_vector<uint8_t> into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The Botan::secure_vector<uint8_t> to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const Botan::secure_vector<uint8_t> &value)
+ {
+ pos = pickle(pos, static_cast<std::uint32_t>(value.size()));
+ for (const auto byte : value)
+ {
+ *(pos++) = byte;
+ }
+ return pos;
+ }
+
+ /**
+ * Deserializes a Botan::secure_vector<uint8_t> from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, Botan::secure_vector<uint8_t> &value)
+ {
+ std::uint32_t size;
+ pos = unpickle(pos, end, size);
+ if (!pos || end < pos + size)
+ return nullptr;
+ value.assign(pos, pos + size);
+ return pos + size;
+ }
+
+
+ /**
+ * Serializes a std::vector<uint8_t> into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The Botan::secure_vector<uint8_t> to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::vector<uint8_t> &value)
+ {
+ pos = pickle(pos, static_cast<std::uint32_t>(value.size()));
+ for (const auto byte : value)
+ {
+ *(pos++) = byte;
+ }
+ return pos;
+ }
+
+ /**
+ * Deserializes a std::vector<uint8_t> from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the Botan::secure_vector<uint8_t> to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::vector<uint8_t> &value)
+ {
+ std::uint32_t size;
+ pos = unpickle(pos, end, size);
+ if (!pos || end < pos + size)
+ return nullptr;
+ value.assign(pos, pos + size);
+ return pos + size;
+ }
+
+ /**
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value A OneTimeKey object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::optional<OneTimeKey> &value)
+ {
+ pos = pickle(pos, value->id);
+ pos = pickle(pos, value->published);
+ return pickle(pos, value->key.raw_private_key_bits());
+ }
+
+ /**
+ * Deserializes a OneTimeKey object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @return A pair containing the pointer to the position in the byte array after the deserialized data and the
+ * deserialized OneTimeKey object.
+ */
+ static std::pair<std::uint8_t const *, std::optional<OneTimeKey>> unpickle_otk(std::uint8_t const *pos,
+ std::uint8_t const *end)
+ {
+ std::uint32_t id; ///< The unique identifier for the one-time key.
+ bool published; ///< Indicates whether the key has been published.
+
+ pos = unpickle(pos, end, id);
+ if (!pos || !id)
+ {
+ return {nullptr, std::nullopt};
+ }
+ pos = unpickle(pos, end, published);
+ if (!pos)
+ return {nullptr, std::nullopt};
+
+ Botan::secure_vector<uint8_t> key_bits;
+ pos = unpickle(pos, end, key_bits);
+ if (!pos)
+ return {nullptr, std::nullopt};
+
+ auto otk = OneTimeKey{id, published, Botan::X25519_PrivateKey(key_bits)};
+
+ return {pos, otk};
+ }
+
+ /**
+ * Serializes a FixedSizeArray object into a byte array.
+ *
+ * @tparam T The type of elements in the FixedSizeArray.
+ * @tparam max_size The maximum size of the FixedSizeArray.
+ * @param pos Pointer to the current position in the byte array.
+ * @param list The FixedSizeArray object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ template <typename T, std::size_t max_size>
+ std::uint8_t *pickle(std::uint8_t *pos, FixedSizeArray<T, max_size> const &list)
+ {
+ pos = pickle(pos, static_cast<std::uint32_t>(list.size()));
+ for (auto const &value : list)
+ {
+ pos = pickle(pos, *value);
+ }
+ return pos;
+ }
+
+ /**
+ * Deserializes a FixedSizeArray object from a byte array.
+ *
+ * @tparam max_size The maximum size of the FixedSizeArray.
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param list Reference to the FixedSizeArray object to store the deserialized values.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ template <std::size_t max_size>
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end,
+ FixedSizeArray<OneTimeKey, max_size> &list)
+ {
+ std::uint32_t size;
+ pos = unpickle(pos, end, size);
+ if (!pos)
+ {
+ return nullptr;
+ }
+
+ while (size-- && pos != end)
+ {
+ auto [temp_pos, value] = unpickle_otk(pos, end);
+ if (!((pos = temp_pos)))
+ return nullptr;
+ list.insert(value.value());
+ }
+
+ return pos;
+ }
+
+ /**
+ * Serializes a uint8_t value into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The uint8_t value to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::uint8_t value)
+ {
+ *(pos++) = value;
+ return pos;
+ }
+
+ /**
+ * Deserializes a uint8_t value from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the uint8_t value to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::uint8_t &value)
+ {
+ if (!pos || pos == end)
+ return nullptr;
+ value = *(pos++);
+ return pos;
+ }
+
+ /**
+ * Serializes an optional IdentityKeys object into a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param value The optional IdentityKeys object to serialize.
+ * @return Pointer to the position in the byte array after the serialized data.
+ */
+ std::uint8_t *pickle(std::uint8_t *pos, const std::optional<IdentityKeys> &value)
+ {
+ pos = pickle(pos, value->ed25519_key.public_key()->raw_public_key_bits());
+ pos = pickle(pos, value->ed25519_key.raw_private_key_bits());
+ pos = pickle(pos, value->curve25519_key.public_key()->raw_public_key_bits());
+ return pickle(pos, value->curve25519_key.raw_private_key_bits());
+ }
+
+ /**
+ * Deserializes an optional IdentityKeys object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the optional IdentityKeys object to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::optional<IdentityKeys> &value)
+ {
+ if (!pos || pos == end)
+ return nullptr;
+ Botan::secure_vector<uint8_t> ed25519_public_key_bits;
+ Botan::secure_vector<uint8_t> curve25519_public_key_bits;
+ Botan::secure_vector<uint8_t> ed25519_private_key_bits;
+ Botan::secure_vector<uint8_t> curve25519_private_key_bits;
+
+ pos = unpickle(pos, end, ed25519_public_key_bits);
+ UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, ed25519_private_key_bits);
+ UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, curve25519_public_key_bits);
+ UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, curve25519_private_key_bits);
+ UNPICKLE_OK(pos);
+ value = IdentityKeys{Botan::Ed25519_PrivateKey::from_bytes(ed25519_private_key_bits),
+ Botan::X25519_PrivateKey(curve25519_private_key_bits)};
+ return pos;
+ }
+
+ /**
+ * Deserializes an optional OneTimeKey object from a byte array.
+ *
+ * @param pos Pointer to the current position in the byte array.
+ * @param end Pointer to the end of the byte array.
+ * @param value Reference to the optional OneTimeKey object to store the deserialized value.
+ * @return Pointer to the position in the byte array after the deserialized data, or nullptr on failure.
+ */
+ std::uint8_t const *unpickle(std::uint8_t const *pos, std::uint8_t const *end, std::optional<OneTimeKey> &value)
+ {
+ if (!pos || pos == end)
+ return nullptr;
+ std::uint32_t id;
+ bool published;
+ Botan::secure_vector<uint8_t> key_bits;
+ pos = unpickle(pos, end, id);
+ UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, published);
+ UNPICKLE_OK(pos);
+ pos = unpickle(pos, end, key_bits);
+ UNPICKLE_OK(pos);
+ value = OneTimeKey{id, published, Botan::X25519_PrivateKey(key_bits)};
+ return pos;
+ }
+
+ std::uint8_t *pickle_bytes(std::uint8_t *pos, const std::uint8_t *bytes, const std::size_t bytes_length)
+ {
+ std::memcpy(pos, bytes, bytes_length);
+ return pos + bytes_length;
+ }
+
+ std::uint8_t const *unpickle_bytes(const std::uint8_t *pos, const std::uint8_t *end, std::uint8_t *bytes,
+ const std::size_t bytes_length)
+ {
+ if (!pos || end < pos + bytes_length)
+ return nullptr;
+ std::memcpy(bytes, pos, bytes_length);
+ return pos + bytes_length;
+ }
+
+} // namespace spank_olm