1 module matrix.outbound_group;
2 
3 import std.experimental.allocator : processAllocator;
4 import std.exception : assumeUnique;
5 
6 import matrix.olm : olm_error, read_random;
7 import matrix.session : cstr2dstr;
8 
9 import std.stdio; // TODO debug only!
10 
11 public class OutboundGroupSession {
12     OlmOutboundGroupSession *session;
13     public this() {
14         const len = olm_outbound_group_session_size();
15         auto mem = processAllocator.allocate(len);
16         this.session = olm_outbound_group_session(mem.ptr);
17 
18         auto rnd_len = olm_init_outbound_group_session_random_length(session);
19         auto rnd_mem = read_random(rnd_len);
20         olm_init_outbound_group_session(session, rnd_mem.ptr, rnd_mem.length);
21     }
22     /// serialize session data, locked by key
23     public string pickle(string key) {
24         char[] ret;
25         ret.length = olm_pickle_outbound_group_session_length(this.session);
26         const r = olm_pickle_outbound_group_session(this.session,
27                 key.ptr, key.length, ret.ptr, ret.length);
28         error_check(r);
29         return assumeUnique(ret);
30     }
31     /// deserialize session data, unlocked by key
32     static public OutboundGroupSession unpickle(string key, string pickle) {
33         auto a = new OutboundGroupSession();
34         char[] p = pickle.dup; // p is destroyed!
35         const r = olm_unpickle_outbound_group_session(a.session,
36                 key.ptr, key.length, p.ptr, p.length);
37         a.error_check(r);
38         return a;
39     }
40     public string encrypt(string plain) {
41         auto len = olm_group_encrypt_message_length(session, plain.length);
42         error_check(len);
43         char[] ret;
44         ret.length = len;
45         len = olm_group_encrypt(session, plain.ptr, plain.length, ret.ptr, len);
46         error_check(len);
47         return assumeUnique(ret[0..len]);
48     }
49     public string session_id() {
50         char[] ret;
51         ret.length = olm_outbound_group_session_id_length(session);
52         auto r = olm_outbound_group_session_id(session, ret.ptr, ret.length);
53         error_check(r);
54         return assumeUnique(ret);
55     }
56     public @property uint message_index() {
57         return olm_outbound_group_session_message_index(session);
58     }
59     public auto session_key() {
60         char[] ret;
61         ret.length = olm_outbound_group_session_key_length(session);
62         auto r = olm_outbound_group_session_key(session, ret.ptr, ret.length);
63         error_check(r);
64         return assumeUnique(ret);
65 
66     }
67     private void error_check(size_t x) {
68         if (x == olm_error()) {
69             auto errmsg = olm_outbound_group_session_last_error(this.session);
70             throw new Exception(cstr2dstr(errmsg));
71         }
72     }
73 }
74 
75 unittest {
76     auto igs = new OutboundGroupSession();
77     auto p = igs.pickle("foo");
78     auto dp = OutboundGroupSession.unpickle("foo", p);
79 }
80 
81 
82 extern (C):
83 // copy&pasted from outbound_group_session.h
84 
85 struct OlmOutboundGroupSession;
86 
87 /** get the size of an outbound group session, in bytes. */
88 size_t olm_outbound_group_session_size();
89 
90 /**
91  * Initialise an outbound group session object using the supplied memory
92  * The supplied memory should be at least olm_outbound_group_session_size()
93  * bytes.
94  */
95 OlmOutboundGroupSession * olm_outbound_group_session(
96     void *memory
97 );
98 
99 /**
100  * A null terminated string describing the most recent error to happen to a
101  * group session */
102 const(char)* olm_outbound_group_session_last_error(
103     const OlmOutboundGroupSession *session
104 );
105 
106 /** Clears the memory used to back this group session */
107 size_t olm_clear_outbound_group_session(
108     OlmOutboundGroupSession *session
109 );
110 
111 /** Returns the number of bytes needed to store an outbound group session */
112 size_t olm_pickle_outbound_group_session_length(
113     const OlmOutboundGroupSession *session
114 );
115 
116 /**
117  * Stores a group session as a base64 string. Encrypts the session using the
118  * supplied key. Returns the length of the session on success.
119  *
120  * Returns olm_error() on failure. If the pickle output buffer
121  * is smaller than olm_pickle_outbound_group_session_length() then
122  * olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
123  */
124 size_t olm_pickle_outbound_group_session(
125     OlmOutboundGroupSession *session,
126     const(void)* key, size_t key_length,
127     void * pickled, size_t pickled_length
128 );
129 
130 /**
131  * Loads a group session from a pickled base64 string. Decrypts the session
132  * using the supplied key.
133  *
134  * Returns olm_error() on failure. If the key doesn't match the one used to
135  * encrypt the account then olm_outbound_group_session_last_error() will be
136  * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
137  * olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
138  * pickled buffer is destroyed
139  */
140 size_t olm_unpickle_outbound_group_session(
141     OlmOutboundGroupSession *session,
142     const(void)* key, size_t key_length,
143     void * pickled, size_t pickled_length
144 );
145 
146 
147 /** The number of random bytes needed to create an outbound group session */
148 size_t olm_init_outbound_group_session_random_length(
149     const OlmOutboundGroupSession *session
150 );
151 
152 /**
153  * Start a new outbound group session. Returns olm_error() on failure. On
154  * failure last_error will be set with an error code. The last_error will be
155  * NOT_ENOUGH_RANDOM if the number of random bytes was too small.
156  */
157 size_t olm_init_outbound_group_session(
158     OlmOutboundGroupSession *session,
159     char* random, size_t random_length
160 );
161 
162 /**
163  * The number of bytes that will be created by encrypting a message
164  */
165 size_t olm_group_encrypt_message_length(
166     OlmOutboundGroupSession *session,
167     size_t plaintext_length
168 );
169 
170 /**
171  * Encrypt some plain-text. Returns the length of the encrypted message or
172  * olm_error() on failure. On failure last_error will be set with an
173  * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
174  * buffer is too small.
175  */
176 size_t olm_group_encrypt(
177     OlmOutboundGroupSession *session,
178     const(char)* plaintext, size_t plaintext_length,
179     char* message, size_t message_length
180 );
181 
182 
183 /**
184  * Get the number of bytes returned by olm_outbound_group_session_id()
185  */
186 size_t olm_outbound_group_session_id_length(
187     const OlmOutboundGroupSession *session
188 );
189 
190 /**
191  * Get a base64-encoded identifier for this session.
192  *
193  * Returns the length of the session id on success or olm_error() on
194  * failure. On failure last_error will be set with an error code. The
195  * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
196  * small.
197  */
198 size_t olm_outbound_group_session_id(
199     OlmOutboundGroupSession *session,
200     char* id, size_t id_length
201 );
202 
203 /**
204  * Get the current message index for this session.
205  *
206  * Each message is sent with an increasing index; this returns the index for
207  * the next message.
208  */
209 uint olm_outbound_group_session_message_index(
210     OlmOutboundGroupSession *session
211 );
212 
213 /**
214  * Get the number of bytes returned by olm_outbound_group_session_key()
215  */
216 size_t olm_outbound_group_session_key_length(
217     const OlmOutboundGroupSession *session
218 );
219 
220 /**
221  * Get the base64-encoded current ratchet key for this session.
222  *
223  * Each message is sent with a different ratchet key. This function returns the
224  * ratchet key that will be used for the next message.
225  *
226  * Returns the length of the ratchet key on success or olm_error() on
227  * failure. On failure last_error will be set with an error code. The
228  * last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small.
229  */
230 size_t olm_outbound_group_session_key(
231     OlmOutboundGroupSession *session,
232     char* key, size_t key_length
233 );