diff --git a/include/olm/megolm.h b/include/olm/megolm.h
index 5cae353099aaede4ef5b45fcc8283358a5d10632..831c6fbcba0e56b21e54468f986179c51bc3f00c 100644
--- a/include/olm/megolm.h
+++ b/include/olm/megolm.h
@@ -61,6 +61,21 @@ const struct _olm_cipher *megolm_cipher();
  */
 void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter);
 
+/** Returns the number of bytes needed to store a megolm */
+size_t megolm_pickle_length(const Megolm *megolm);
+
+/**
+ * Pickle the megolm. Returns a pointer to the next free space in the buffer.
+ */
+uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos);
+
+/**
+ * Unpickle the megolm. Returns a pointer to the next item in the buffer.
+ */
+const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
+                                const uint8_t *end);
+
+
 /** advance the ratchet by one step */
 void megolm_advance(Megolm *megolm);
 
diff --git a/include/olm/outbound_group_session.h b/include/olm/outbound_group_session.h
index 6c0237094359364605871b588f2044080216d324..27991acb844fd6edb5c2a9df7c71833cd096f112 100644
--- a/include/olm/outbound_group_session.h
+++ b/include/olm/outbound_group_session.h
@@ -48,6 +48,42 @@ size_t olm_clear_outbound_group_session(
     OlmOutboundGroupSession *session
 );
 
+/** Returns the number of bytes needed to store an outbound group session */
+size_t olm_pickle_outbound_group_session_length(
+    const OlmOutboundGroupSession *session
+);
+
+/**
+ * Stores a group session as a base64 string. Encrypts the session using the
+ * supplied key. Returns the length of the session on success.
+ *
+ * Returns olm_error() on failure. If the pickle output buffer
+ * is smaller than olm_pickle_outbound_group_session_length() then
+ * olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
+ */
+size_t olm_pickle_outbound_group_session(
+    OlmOutboundGroupSession *session,
+    void const * key, size_t key_length,
+    void * pickled, size_t pickled_length
+);
+
+/**
+ * Loads a group session from a pickled base64 string. Decrypts the session
+ * using the supplied key.
+ *
+ * Returns olm_error() on failure. If the key doesn't match the one used to
+ * encrypt the account then olm_outbound_group_session_last_error() will be
+ * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
+ * olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
+ * pickled buffer is destroyed
+ */
+size_t olm_unpickle_outbound_group_session(
+    OlmOutboundGroupSession *session,
+    void const * key, size_t key_length,
+    void * pickled, size_t pickled_length
+);
+
+
 /** The number of random bytes needed to create an outbound group session */
 size_t olm_init_outbound_group_session_random_length(
     const OlmOutboundGroupSession *session
diff --git a/src/megolm.c b/src/megolm.c
index 58fe725325dd29b768748ba348a06ccd29ac1dca..7567894665ed58e3350d761cf5e321dac06e162a 100644
--- a/src/megolm.c
+++ b/src/megolm.c
@@ -20,6 +20,7 @@
 
 #include "olm/cipher.h"
 #include "olm/crypto.h"
+#include "olm/pickle.h"
 
 const struct _olm_cipher *megolm_cipher() {
     static const uint8_t CIPHER_KDF_INFO[] = "MEGOLM_KEYS";
@@ -59,12 +60,32 @@ static void rehash_part(
 
 
 
-void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter)
-{
+void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) {
     megolm->counter = counter;
     memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH);
 }
 
+size_t megolm_pickle_length(const Megolm *megolm) {
+    size_t length = 0;
+    length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
+    length += _olm_pickle_uint32_length(megolm->counter);
+    return length;
+
+}
+
+uint8_t * megolm_pickle(const Megolm *megolm,  uint8_t *pos) {
+    pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
+    pos = _olm_pickle_uint32(pos, megolm->counter);
+    return pos;
+}
+
+const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
+                                const uint8_t *end) {
+    pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data),
+                             MEGOLM_RATCHET_LENGTH);
+    pos = _olm_unpickle_uint32(pos, end, &megolm->counter);
+    return pos;
+}
 
 /* simplistic implementation for a single step */
 void megolm_advance(Megolm *megolm) {
diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c
index a23f684992f2b340fe7054617984dae2e00f3dff..8dc1cd1a400d69f5bc7fa8aba33340578aa12d06 100644
--- a/src/outbound_group_session.c
+++ b/src/outbound_group_session.c
@@ -23,10 +23,13 @@
 #include "olm/error.h"
 #include "olm/megolm.h"
 #include "olm/message.h"
+#include "olm/pickle.h"
+#include "olm/pickle_encoding.h"
 
 #define OLM_PROTOCOL_VERSION     3
 #define SESSION_ID_RANDOM_BYTES  4
 #define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES)
+#define PICKLE_VERSION           1
 
 struct OlmOutboundGroupSession {
     /** the Megolm ratchet providing the encryption keys */
@@ -64,6 +67,80 @@ size_t olm_clear_outbound_group_session(
     return sizeof(OlmOutboundGroupSession);
 }
 
+static size_t raw_pickle_length(
+    const OlmOutboundGroupSession *session
+) {
+    size_t length = 0;
+    length += _olm_pickle_uint32_length(PICKLE_VERSION);
+    length += megolm_pickle_length(&(session->ratchet));
+    length += _olm_pickle_bytes_length(session->session_id,
+                                       GROUP_SESSION_ID_LENGTH);
+    return length;
+}
+
+size_t olm_pickle_outbound_group_session_length(
+    const OlmOutboundGroupSession *session
+) {
+    return _olm_enc_output_length(raw_pickle_length(session));
+}
+
+size_t olm_pickle_outbound_group_session(
+    OlmOutboundGroupSession *session,
+    void const * key, size_t key_length,
+    void * pickled, size_t pickled_length
+) {
+    size_t raw_length = raw_pickle_length(session);
+    uint8_t *pos;
+
+    if (pickled_length < _olm_enc_output_length(raw_length)) {
+        session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return (size_t)-1;
+    }
+
+    pos = _olm_enc_output_pos(pickled, raw_length);
+    pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
+    pos = megolm_pickle(&(session->ratchet), pos);
+    pos = _olm_pickle_bytes(pos, session->session_id, GROUP_SESSION_ID_LENGTH);
+
+    return _olm_enc_output(key, key_length, pickled, raw_length);
+}
+
+size_t olm_unpickle_outbound_group_session(
+    OlmOutboundGroupSession *session,
+    void const * key, size_t key_length,
+    void * pickled, size_t pickled_length
+) {
+    const uint8_t *pos;
+    const uint8_t *end;
+    uint32_t pickle_version;
+
+    size_t raw_length = _olm_enc_input(
+        key, key_length, pickled, pickled_length, &(session->last_error)
+    );
+    if (raw_length == (size_t)-1) {
+        return raw_length;
+    }
+
+    pos = pickled;
+    end = pos + raw_length;
+    pos = _olm_unpickle_uint32(pos, end, &pickle_version);
+    if (pickle_version != PICKLE_VERSION) {
+        session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
+        return (size_t)-1;
+    }
+    pos = megolm_unpickle(&(session->ratchet), pos, end);
+    pos = _olm_unpickle_bytes(pos, end, session->session_id, GROUP_SESSION_ID_LENGTH);
+
+    if (end != pos) {
+        /* We had the wrong number of bytes in the input. */
+        session->last_error = OLM_CORRUPTED_PICKLE;
+        return (size_t)-1;
+    }
+
+    return pickled_length;
+}
+
+
 size_t olm_init_outbound_group_session_random_length(
     const OlmOutboundGroupSession *session
 ) {
diff --git a/tests/test_group_session.cpp b/tests/test_group_session.cpp
index 9081293240b0c1afead2fdbbcd0cb4d139971a85..b9fe1ef78eec1f3fdec786313fdc8dbd319d8450 100644
--- a/tests/test_group_session.cpp
+++ b/tests/test_group_session.cpp
@@ -18,16 +18,50 @@
 
 int main() {
 
-uint8_t random_bytes[] =
-    "0123456789ABDEF0123456789ABCDEF"
-    "0123456789ABDEF0123456789ABCDEF"
-    "0123456789ABDEF0123456789ABCDEF"
-    "0123456789ABDEF0123456789ABCDEF"
-    "0123456789ABDEF0123456789ABCDEF";
+{
+
+    TestCase test_case("Pickle outbound group");
+
+    size_t size = olm_outbound_group_session_size();
+    void *memory = alloca(size);
+    OlmOutboundGroupSession *session = olm_outbound_group_session(memory);
+
+    size_t pickle_length = olm_pickle_outbound_group_session_length(session);
+    uint8_t pickle1[pickle_length];
+    olm_pickle_outbound_group_session(session,
+                                      "secret_key", 10,
+                                      pickle1, pickle_length);
+    uint8_t pickle2[pickle_length];
+    memcpy(pickle2, pickle1, pickle_length);
+
+    uint8_t buffer2[size];
+    OlmOutboundGroupSession *session2 = olm_outbound_group_session(buffer2);
+    size_t res = olm_unpickle_outbound_group_session(session2,
+                                                     "secret_key", 10,
+                                                     pickle2, pickle_length);
+    assert_not_equals((size_t)-1, res);
+    assert_equals(pickle_length,
+                  olm_pickle_outbound_group_session_length(session2));
+    olm_pickle_outbound_group_session(session2,
+                                      "secret_key", 10,
+                                      pickle2, pickle_length);
+
+    assert_equals(pickle1, pickle2, pickle_length);
+}
+
 
 {
     TestCase test_case("Group message send/receive");
 
+    uint8_t random_bytes[] =
+        "0123456789ABDEF0123456789ABCDEF"
+        "0123456789ABDEF0123456789ABCDEF"
+        "0123456789ABDEF0123456789ABCDEF"
+        "0123456789ABDEF0123456789ABCDEF"
+        "0123456789ABDEF0123456789ABCDEF";
+
+
+
     size_t size = olm_outbound_group_session_size();
     void *memory = alloca(size);
     OlmOutboundGroupSession *session = olm_outbound_group_session(memory);