@@ -36,12 +36,12 @@ constexpr size_t ENCRYPT_KEY_SIZE = 32;
36
36
// retrieved via oxen-storage-server onion requests, and its padded size is the maximum attachment
37
37
// size allowed by the storage server. (Technically this value was chosen as it is the largest
38
38
// unencrypted data size that has the same padded+encrypted size as a 10'000'000B file).
39
- constexpr size_t ENCRYPT_MAX_SIZE =
39
+ constexpr size_t MAX_REGULAR_SIZE =
40
40
10218286 ; // == 10223616 after stream mac+tag and (1-byte) padding
41
41
42
42
// Returns the amount of padding to add to an attachment to obfuscate the true size, given
43
- // crypto_secretstream encryption with a 32kiB chunk size. We determine the padded size as follows,
44
- // given an input size N:
43
+ // crypto_secretstream encryption with a 32kiB chunk size. We determine the padded size as
44
+ // follows, given an input size N:
45
45
//
46
46
// - compute the total raw size M as N plus:
47
47
// - 1 for the 'S' prefix (outside the encryption)
@@ -65,11 +65,24 @@ constexpr size_t ENCRYPT_MAX_SIZE =
65
65
// + 31 × 17 -- embedded mac+tags after every 32kiB of file stream data
66
66
// = 1015808 final output.
67
67
//
68
- // (Note that we always including at least one padding byte, and there are some complications in the
69
- // calculation as padding values get large enough to start inducing additional mac+tags; see the
70
- // implementation for details).
68
+ // (Note that we always including at least one padding byte, and there are some complications in
69
+ // the calculation as padding values get large enough to start inducing additional mac+tags; see
70
+ // the implementation for details).
71
71
size_t encrypted_padding (size_t data_size);
72
72
73
+ // / API: crypto/attachment::encrypted_size
74
+ // /
75
+ // / Returns the exact final encrypted (including any overhead and padding) of an input of
76
+ // / `plaintext_size`.
77
+ size_t encrypted_size (size_t plaintext_size);
78
+
79
+ // / API: crypto/attachment::decrypted_max_size
80
+ // /
81
+ // / Returns the maximum possible decrypted size of encrypted data of length `encrypted_size`. The
82
+ // / actual size can be (and usually is) less than this depending on how much padding is in the data.
83
+ // / Returns std::nullopt if the input is too small to be a valid encrypted attachment.
84
+ std::optional<size_t > decrypted_max_size (size_t encrypted_size);
85
+
73
86
// / API: crypto/attachment::encrypt
74
87
// /
75
88
// / Encrypt an attachment for storage on the file server and distribution to other users using
@@ -118,9 +131,105 @@ std::pair<std::vector<std::byte>, std::array<std::byte, ENCRYPT_KEY_SIZE>> encry
118
131
Domain domain,
119
132
bool allow_large = false );
120
133
134
+ // / API: crypto/attachment::encrypt
135
+ // /
136
+ // / Similar to the above `encrypt` except that instead of allocating and returning a vector it
137
+ // / writes the encrypted result directly into a given output span. The output span *must* be
138
+ // / exactly `encrypted_size()` bytes long (but this is checked via assertion in debug builds).
139
+ // /
140
+ // / Inputs:
141
+ // / - `seed` -- as above
142
+ // / - `data` -- as above
143
+ // / - `domain` -- as above
144
+ // / - `out` -- writeable span into which the encrypted data will be written. This span must be
145
+ // / exactly `encrypted_size(data.size())` bytes long.
146
+ // / - `allow_large` -- as above.
147
+ // /
148
+ // / Outputs:
149
+ // / - 32 byte decryption key
150
+ // /
151
+ // / Throws std::invalid_argument if `seed` is shorter than 32 bytes, or if data is larger than
152
+ // / MAX_REGULAR_SIZE (unless `allow_large` is true).
153
+ std::array<std::byte, ENCRYPT_KEY_SIZE> encrypt (
154
+ std::span<const std::byte> seed,
155
+ std::span<const std::byte> data,
156
+ Domain domain,
157
+ std::span<std::byte> out,
158
+ bool allow_large = false );
159
+
160
+ // / API: crypto/attachment::encrypt
161
+ // /
162
+ // / Encrypts the contents of a file on disk into a buffer. This requires reading the file twice
163
+ // / (once in order to generate the deterministic encryption key and nonce, and then a second time
164
+ // / for the actual encryption), but does not require holding the file contents in memory.
165
+ // /
166
+ // / Inputs:
167
+ // / - `seed`, `domain`, `allow_large` -- see above.
168
+ // / - `file` -- path to the file to encrypt.
169
+ // /
170
+ // / Outputs:
171
+ // / - Pair of values: the padded+encrypted data, and the decryption key (32 bytes), both in raw
172
+ // / bytes.
173
+ // /
174
+ // / Throws std::invalid_argument if `seed` is shorter than 32 bytes, or if the file is larger than
175
+ // / MAX_REGULAR_SIZE.
176
+ std::pair<std::vector<std::byte>, std::array<std::byte, ENCRYPT_KEY_SIZE>> encrypt (
177
+ std::span<const std::byte> seed,
178
+ const std::filesystem::path& file,
179
+ Domain domain,
180
+ bool allow_large = false );
181
+
182
+ // / API: crypto/attachment::encrypt
183
+ // /
184
+ // / Encrypts the contents of a file on disk into a buffer. This method is a more general version of
185
+ // / the above that allows allocation of the encrypted buffer via a callback once the size is
186
+ // / determined from the file.
187
+ // /
188
+ // / Inputs:
189
+ // / - `seed`, `domain`, `allow_large` -- see above.
190
+ // / - `file` -- path to the file to encrypt.
191
+ // / - `make_buffer` -- callback that is invoked with the exact required encrypted size for the file
192
+ // / that must return a byte span of that exact file where the encrypted data will be written.
193
+ // /
194
+ // / Outputs:
195
+ // / - The 32-byte decryption key, in raw bytes.
196
+ // /
197
+ // / Throws std::invalid_argument if `seed` is shorter than 32 bytes, or if the file is larger than
198
+ // / MAX_REGULAR_SIZE.
199
+ // / Throws std::runtime_error if the file size changes between first and second passes.
200
+ std::array<std::byte, ENCRYPT_KEY_SIZE> encrypt (
201
+ std::span<const std::byte> seed,
202
+ const std::filesystem::path& file,
203
+ Domain domain,
204
+ std::function<std::span<std::byte>(size_t enc_size)> make_buffer,
205
+ bool allow_large = false);
206
+
207
+ // / API: crypto/attachment::encrypt
208
+ // /
209
+ // / Encrypts the contents of a plaintext buffer, writing the encrypted data to a file. The file
210
+ // / will be overwritten.
211
+ // /
212
+ // / Inputs:
213
+ // / - `seed`, `domain`, `allow_large` -- see above.
214
+ // / - `data` -- the buffer of data to encrypt.
215
+ // / - `file` -- path to the file to write to.
216
+ // /
217
+ // / Outputs:
218
+ // / - The 32-byte decryption key, in raw bytes.
219
+ // /
220
+ // / Throws std::invalid_argument if `seed` is shorter than 32 bytes, or if data is larger than
221
+ // / MAX_REGULAR_SIZE (unless `allow_large` is given). Throws on I/O error. If decryption fails
222
+ // / then any partially written output file will be removed.
223
+ std::array<std::byte, ENCRYPT_KEY_SIZE> encrypt (
224
+ std::span<const std::byte> seed,
225
+ std::span<const std::byte> data,
226
+ Domain domain,
227
+ const std::filesystem::path& file,
228
+ bool allow_large = false );
229
+
121
230
// / API: crypto/attachment::decrypt
122
231
// /
123
- // / Decrypts an attachment allegedly produced by attachment::encrypt to a single in-memory buffer .
232
+ // / Decrypts an attachment allegedly produced by attachment::encrypt to an in-memory byte vector .
124
233
// /
125
234
// / Inputs:
126
235
// / - `data` -- in-memory buffer of data to decrypt.
@@ -133,6 +242,28 @@ std::pair<std::vector<std::byte>, std::array<std::byte, ENCRYPT_KEY_SIZE>> encry
133
242
std::vector<std::byte> decrypt (
134
243
std::span<const std::byte> encrypted, std::span<const std::byte, ENCRYPT_KEY_SIZE> key);
135
244
245
+ // / API: crypto/attachment::decrypt
246
+ // /
247
+ // / Decrypts an attachment allegedly produced by attachment::encrypt to a single in-memory,
248
+ // / caller-provided buffer. This version writes into a given output span rather than allocating a
249
+ // / new vector.
250
+ // /
251
+ // / Inputs:
252
+ // / - `data` -- in-memory buffer of data to decrypt.
253
+ // / - `key` -- the 32-byte decryption key
254
+ // / - `out` -- writeable output span in which the decrypted value should be written. The given span
255
+ // / must be at least `decrypted_max_size(data.size())` bytes large.
256
+ // /
257
+ // / Outputs:
258
+ // / - size_t -- the actual decrypted data size written into `out` which could be (and often is, due
259
+ // / to padding) less than `out.size()`.
260
+ // /
261
+ // / Throws std::runtime_error if decryption fails.
262
+ size_t decrypt (
263
+ std::span<const std::byte> encrypted,
264
+ std::span<const std::byte, ENCRYPT_KEY_SIZE> key,
265
+ std::span<std::byte> out);
266
+
136
267
// / API: crypto/attachment::Decryptor
137
268
// /
138
269
// / Object-based interfaced to streaming decryption. The basic usage is to construct the object
@@ -205,4 +336,67 @@ void decrypt(
205
336
std::span<const std::byte, ENCRYPT_KEY_SIZE> key,
206
337
const std::filesystem::path& filename);
207
338
339
+ // / API: crypto/attachment::decrypt
340
+ // /
341
+ // / Decrypts an encrypted attachment stored in an input file into a byte vector.
342
+ // /
343
+ // / Inputs:
344
+ // / - `filename` -- path to encrypted file.
345
+ // / - `key` -- the 32-byte decryption key.
346
+ // /
347
+ // / Outputs:
348
+ // / - vector of decrypted content.
349
+ // /
350
+ // / Throws std::runtime_error if decryption fails; can throw I/O exceptions if reading the file
351
+ // / fails.
352
+ std::vector<std::byte> decrypt (
353
+ const std::filesystem::path& encrypted_file,
354
+ std::span<const std::byte, ENCRYPT_KEY_SIZE> key);
355
+
356
+ // / API: crypto/attachment::decrypt
357
+ // /
358
+ // / Decrypts an encrypted attachment stored in an input file into a provided memory buffer.
359
+ // /
360
+ // / Inputs:
361
+ // / - `filename` -- path to encrypted file.
362
+ // / - `key` -- the 32-byte decryption key.
363
+ // / - `make_buffer` -- callback that is invoked to allocate the buffer into which the content should
364
+ // / be written. This is passed the required buffer size. Note that this buffer may not be
365
+ // / completely filled: the return value of `decrypt()` indicates the actual amount of the buffer
366
+ // / that was written.
367
+ // /
368
+ // / Outputs:
369
+ // / - size_t the actual decrypted size. Can be less than the value passed to `decrypted.size()`
370
+ // / because of padding.
371
+ // /
372
+ // / Throws std::runtime_error if decryption fails; can throw I/O exceptions if reading the file
373
+ // / fails.
374
+ size_t decrypt (
375
+ const std::filesystem::path& encrypted_file,
376
+ std::span<const std::byte, ENCRYPT_KEY_SIZE> key,
377
+ std::function<std::span<std::byte>(size_t dec_size)> make_buffer);
378
+
379
+ // / API: crypto/attachment::decrypt
380
+ // /
381
+ // / Decrypts an attachment allegedly produced by attachment::encrypt stored in a file to another
382
+ // / output file. Overwrites the destination file if it already exists.
383
+ // /
384
+ // / Unlike the various decrypt functions above, this version does not need to hold more than a few
385
+ // / kB of the input/output file in memory at a time, regardless of the size of the input or output
386
+ // / files.
387
+ // /
388
+ // / Inputs:
389
+ // / - `file_in` -- filename containing the data to decrypt.
390
+ // / - `key` -- the 32-byte decryption key.
391
+ // / - `file_out` -- where to write the output file.
392
+ // /
393
+ // / Outputs: None.
394
+ // /
395
+ // / Throws std::runtime_error if decryption fails or if writing to the file fails. Upon exception a
396
+ // / partially written file will be deleted.
397
+ void decrypt (
398
+ const std::filesystem::path& file_in,
399
+ std::span<const std::byte, ENCRYPT_KEY_SIZE> key,
400
+ const std::filesystem::path& file_out);
401
+
208
402
} // namespace session::attachment
0 commit comments