1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * crypt.c: Generic encryption support.
12  */
13 #include "vim.h"
14 
15 #if defined(FEAT_CRYPT) || defined(PROTO)
16 /*
17  * Optional encryption support.
18  * Mohsin Ahmed, mosh@sasi.com, 1998-09-24
19  * Based on zip/crypt sources.
20  * Refactored by David Leadbeater, 2014.
21  *
22  * NOTE FOR USA: Since 2000 exporting this code from the USA is allowed to
23  * most countries.  There are a few exceptions, but that still should not be a
24  * problem since this code was originally created in Europe and India.
25  *
26  * Blowfish addition originally made by Mohsin Ahmed,
27  * http://www.cs.albany.edu/~mosh 2010-03-14
28  * Based on blowfish by Bruce Schneier (http://www.schneier.com/blowfish.html)
29  * and sha256 by Christophe Devine.
30  */
31 
32 typedef struct {
33     char    *name;	// encryption name as used in 'cryptmethod'
34     char    *magic;	// magic bytes stored in file header
35     int	    salt_len;	// length of salt, or 0 when not using salt
36     int	    seed_len;	// length of seed, or 0 when not using seed
37 #ifdef CRYPT_NOT_INPLACE
38     int	    works_inplace; // encryption/decryption can be done in-place
39 #endif
40     int	    whole_undofile; // whole undo file is encrypted
41 
42     // Optional function pointer for a self-test.
43     int (* self_test_fn)();
44 
45     // Function pointer for initializing encryption/decryption.
46     int (* init_fn)(cryptstate_T *state, char_u *key,
47 		      char_u *salt, int salt_len, char_u *seed, int seed_len);
48 
49     // Function pointers for encoding/decoding from one buffer into another.
50     // Optional, however, these or the _buffer ones should be configured.
51     void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len,
52 							char_u *to, int last);
53     void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len,
54 							char_u *to, int last);
55 
56     // Function pointers for encoding and decoding, can buffer data if needed.
57     // Optional (however, these or the above should be configured).
58     long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len,
59 						    char_u **newptr, int last);
60     long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len,
61 						    char_u **newptr, int last);
62 
63     // Function pointers for in-place encoding and decoding, used for
64     // crypt_*_inplace(). "from" and "to" arguments will be equal.
65     // These may be the same as decode_fn and encode_fn above, however an
66     // algorithm may implement them in a way that is not interchangeable with
67     // the crypt_(en|de)code() interface (for example because it wishes to add
68     // padding to files).
69     // This method is used for swap and undo files which have a rigid format.
70     void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len,
71 							char_u *p2, int last);
72     void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len,
73 							char_u *p2, int last);
74 } cryptmethod_T;
75 
76 // index is method_nr of cryptstate_T, CRYPT_M_*
77 static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = {
78     // PK_Zip; very weak
79     {
80 	"zip",
81 	"VimCrypt~01!",
82 	0,
83 	0,
84 #ifdef CRYPT_NOT_INPLACE
85 	TRUE,
86 #endif
87 	FALSE,
88 	NULL,
89 	crypt_zip_init,
90 	crypt_zip_encode, crypt_zip_decode,
91 	NULL, NULL,
92 	crypt_zip_encode, crypt_zip_decode,
93     },
94 
95     // Blowfish/CFB + SHA-256 custom key derivation; implementation issues.
96     {
97 	"blowfish",
98 	"VimCrypt~02!",
99 	8,
100 	8,
101 #ifdef CRYPT_NOT_INPLACE
102 	TRUE,
103 #endif
104 	FALSE,
105 	blowfish_self_test,
106 	crypt_blowfish_init,
107 	crypt_blowfish_encode, crypt_blowfish_decode,
108 	NULL, NULL,
109 	crypt_blowfish_encode, crypt_blowfish_decode,
110     },
111 
112     // Blowfish/CFB + SHA-256 custom key derivation; fixed.
113     {
114 	"blowfish2",
115 	"VimCrypt~03!",
116 	8,
117 	8,
118 #ifdef CRYPT_NOT_INPLACE
119 	TRUE,
120 #endif
121 	TRUE,
122 	blowfish_self_test,
123 	crypt_blowfish_init,
124 	crypt_blowfish_encode, crypt_blowfish_decode,
125 	NULL, NULL,
126 	crypt_blowfish_encode, crypt_blowfish_decode,
127     },
128 
129     // XChaCha20 using libsodium
130     {
131 	"xchacha20",
132 	"VimCrypt~04!",
133 #ifdef FEAT_SODIUM
134 	crypto_pwhash_argon2id_SALTBYTES, // 16
135 #else
136 	16,
137 #endif
138 	8,
139 #ifdef CRYPT_NOT_INPLACE
140 	FALSE,
141 #endif
142 	FALSE,
143 	NULL,
144 	crypt_sodium_init,
145 	NULL, NULL,
146 	crypt_sodium_buffer_encode, crypt_sodium_buffer_decode,
147 	NULL, NULL,
148     },
149 
150     // NOTE: when adding a new method, use some random bytes for the magic key,
151     // to avoid that a text file is recognized as encrypted.
152 };
153 
154 #ifdef FEAT_SODIUM
155 typedef struct {
156     size_t	    count;
157     unsigned char   key[crypto_box_SEEDBYTES];
158 		  // 32, same as crypto_secretstream_xchacha20poly1305_KEYBYTES
159     crypto_secretstream_xchacha20poly1305_state
160 		    state;
161 } sodium_state_T;
162 #endif
163 
164 #define CRYPT_MAGIC_LEN	12	// cannot change
165 static char	crypt_magic_head[] = "VimCrypt~";
166 
167 /*
168  * Return int value for crypt method name.
169  * 0 for "zip", the old method.  Also for any non-valid value.
170  * 1 for "blowfish".
171  * 2 for "blowfish2".
172  */
173     int
crypt_method_nr_from_name(char_u * name)174 crypt_method_nr_from_name(char_u *name)
175 {
176     int i;
177 
178     for (i = 0; i < CRYPT_M_COUNT; ++i)
179 	if (STRCMP(name, cryptmethods[i].name) == 0)
180 	    return i;
181     return 0;
182 }
183 
184 /*
185  * Get the crypt method used for a file from "ptr[len]", the magic text at the
186  * start of the file.
187  * Returns -1 when no encryption used.
188  */
189     int
crypt_method_nr_from_magic(char * ptr,int len)190 crypt_method_nr_from_magic(char *ptr, int len)
191 {
192     int i;
193 
194     if (len < CRYPT_MAGIC_LEN)
195 	return -1;
196 
197     for (i = 0; i < CRYPT_M_COUNT; i++)
198 	if (memcmp(ptr, cryptmethods[i].magic, CRYPT_MAGIC_LEN) == 0)
199 	    return i;
200 
201     i = (int)STRLEN(crypt_magic_head);
202     if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0)
203 	emsg(_("E821: File is encrypted with unknown method"));
204 
205     return -1;
206 }
207 
208 #ifdef CRYPT_NOT_INPLACE
209 /*
210  * Return TRUE if the crypt method for "method_nr" can be done in-place.
211  */
212     int
crypt_works_inplace(cryptstate_T * state)213 crypt_works_inplace(cryptstate_T *state)
214 {
215     return cryptmethods[state->method_nr].works_inplace;
216 }
217 #endif
218 
219 /*
220  * Get the crypt method for buffer "buf" as a number.
221  */
222     int
crypt_get_method_nr(buf_T * buf)223 crypt_get_method_nr(buf_T *buf)
224 {
225     return crypt_method_nr_from_name(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm);
226 }
227 
228 /*
229  * Return TRUE when the buffer uses an encryption method that encrypts the
230  * whole undo file, not only the text.
231  */
232     int
crypt_whole_undofile(int method_nr)233 crypt_whole_undofile(int method_nr)
234 {
235     return cryptmethods[method_nr].whole_undofile;
236 }
237 
238 /*
239  * Get crypt method specific length of the file header in bytes.
240  */
241     int
crypt_get_header_len(int method_nr)242 crypt_get_header_len(int method_nr)
243 {
244     return CRYPT_MAGIC_LEN
245 	+ cryptmethods[method_nr].salt_len
246 	+ cryptmethods[method_nr].seed_len;
247 }
248 
249 
250 /*
251  * Get maximum crypt method specific length of the file header in bytes.
252  */
253     int
crypt_get_max_header_len()254 crypt_get_max_header_len()
255 {
256     int i;
257     int max = 0;
258     int temp = 0;
259 
260     for (i = 0; i < CRYPT_M_COUNT; ++i)
261     {
262 	temp = crypt_get_header_len(i);
263 	if (temp > max)
264 	    max = temp;
265     }
266     return max;
267 }
268 
269 /*
270  * Set the crypt method for buffer "buf" to "method_nr" using the int value as
271  * returned by crypt_method_nr_from_name().
272  */
273     void
crypt_set_cm_option(buf_T * buf,int method_nr)274 crypt_set_cm_option(buf_T *buf, int method_nr)
275 {
276     free_string_option(buf->b_p_cm);
277     buf->b_p_cm = vim_strsave((char_u *)cryptmethods[method_nr].name);
278 }
279 
280 /*
281  * If the crypt method for the current buffer has a self-test, run it and
282  * return OK/FAIL.
283  */
284     int
crypt_self_test(void)285 crypt_self_test(void)
286 {
287     int method_nr = crypt_get_method_nr(curbuf);
288 
289     if (cryptmethods[method_nr].self_test_fn == NULL)
290 	return OK;
291     return cryptmethods[method_nr].self_test_fn();
292 }
293 
294 /*
295  * Allocate a crypt state and initialize it.
296  * Return NULL for failure.
297  */
298     cryptstate_T *
crypt_create(int method_nr,char_u * key,char_u * salt,int salt_len,char_u * seed,int seed_len)299 crypt_create(
300     int		method_nr,
301     char_u	*key,
302     char_u	*salt,
303     int		salt_len,
304     char_u	*seed,
305     int		seed_len)
306 {
307     cryptstate_T *state = ALLOC_ONE(cryptstate_T);
308 
309     if (state == NULL)
310 	return state;
311 
312     state->method_nr = method_nr;
313     if (cryptmethods[method_nr].init_fn(
314 	state, key, salt, salt_len, seed, seed_len) == FAIL)
315     {
316         vim_free(state);
317         return NULL;
318     }
319     return state;
320 }
321 
322 /*
323  * Allocate a crypt state from a file header and initialize it.
324  * Assumes that header contains at least the number of bytes that
325  * crypt_get_header_len() returns for "method_nr".
326  */
327     cryptstate_T *
crypt_create_from_header(int method_nr,char_u * key,char_u * header)328 crypt_create_from_header(
329     int		method_nr,
330     char_u	*key,
331     char_u	*header)
332 {
333     char_u	*salt = NULL;
334     char_u	*seed = NULL;
335     int		salt_len = cryptmethods[method_nr].salt_len;
336     int		seed_len = cryptmethods[method_nr].seed_len;
337 
338     if (salt_len > 0)
339 	salt = header + CRYPT_MAGIC_LEN;
340     if (seed_len > 0)
341 	seed = header + CRYPT_MAGIC_LEN + salt_len;
342 
343     return crypt_create(method_nr, key, salt, salt_len, seed, seed_len);
344 }
345 
346 /*
347  * Read the crypt method specific header data from "fp".
348  * Return an allocated cryptstate_T or NULL on error.
349  */
350     cryptstate_T *
crypt_create_from_file(FILE * fp,char_u * key)351 crypt_create_from_file(FILE *fp, char_u *key)
352 {
353     int		method_nr;
354     int		header_len;
355     char	magic_buffer[CRYPT_MAGIC_LEN];
356     char_u	*buffer;
357     cryptstate_T *state;
358 
359     if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1)
360 	return NULL;
361     method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN);
362     if (method_nr < 0)
363 	return NULL;
364 
365     header_len = crypt_get_header_len(method_nr);
366     if ((buffer = alloc(header_len)) == NULL)
367 	return NULL;
368     mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN);
369     if (header_len > CRYPT_MAGIC_LEN
370 	    && fread(buffer + CRYPT_MAGIC_LEN,
371 				    header_len - CRYPT_MAGIC_LEN, 1, fp) != 1)
372     {
373 	vim_free(buffer);
374 	return NULL;
375     }
376 
377     state = crypt_create_from_header(method_nr, key, buffer);
378     vim_free(buffer);
379     return state;
380 }
381 
382 /*
383  * Allocate a cryptstate_T for writing and initialize it with "key".
384  * Allocates and fills in the header and stores it in "header", setting
385  * "header_len".  The header may include salt and seed, depending on
386  * cryptmethod.  Caller must free header.
387  * Returns the state or NULL on failure.
388  */
389     cryptstate_T *
crypt_create_for_writing(int method_nr,char_u * key,char_u ** header,int * header_len)390 crypt_create_for_writing(
391     int	    method_nr,
392     char_u  *key,
393     char_u  **header,
394     int	    *header_len)
395 {
396     int	    len = crypt_get_header_len(method_nr);
397     char_u  *salt = NULL;
398     char_u  *seed = NULL;
399     int	    salt_len = cryptmethods[method_nr].salt_len;
400     int	    seed_len = cryptmethods[method_nr].seed_len;
401     cryptstate_T *state;
402 
403     *header_len = len;
404     *header = alloc(len);
405     if (*header == NULL)
406 	return NULL;
407 
408     mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN);
409     if (salt_len > 0 || seed_len > 0)
410     {
411 	if (salt_len > 0)
412 	    salt = *header + CRYPT_MAGIC_LEN;
413 	if (seed_len > 0)
414 	    seed = *header + CRYPT_MAGIC_LEN + salt_len;
415 
416 	// TODO: Should this be crypt method specific? (Probably not worth
417 	// it).  sha2_seed is pretty bad for large amounts of entropy, so make
418 	// that into something which is suitable for anything.
419 #ifdef FEAT_SODIUM
420 	if (sodium_init() >= 0)
421 	{
422 	    if (salt_len > 0)
423 		randombytes_buf(salt, salt_len);
424 	    if (seed_len > 0)
425 		randombytes_buf(seed, seed_len);
426 	}
427 	else
428 #endif
429 	    sha2_seed(salt, salt_len, seed, seed_len);
430     }
431     state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len);
432     if (state == NULL)
433 	VIM_CLEAR(*header);
434     return state;
435 }
436 
437 /*
438  * Free the crypt state.
439  */
440     void
crypt_free_state(cryptstate_T * state)441 crypt_free_state(cryptstate_T *state)
442 {
443 #ifdef FEAT_SODIUM
444     if (state->method_nr == CRYPT_M_SOD)
445     {
446 	sodium_munlock(((sodium_state_T *)state->method_state)->key,
447 							 crypto_box_SEEDBYTES);
448 	sodium_memzero(state->method_state, sizeof(sodium_state_T));
449 	sodium_free(state->method_state);
450     }
451     else
452 #endif
453 	vim_free(state->method_state);
454     vim_free(state);
455 }
456 
457 #ifdef CRYPT_NOT_INPLACE
458 /*
459  * Encode "from[len]" and store the result in a newly allocated buffer, which
460  * is stored in "newptr".
461  * Return number of bytes in "newptr", 0 for need more or -1 on error.
462  */
463     long
crypt_encode_alloc(cryptstate_T * state,char_u * from,size_t len,char_u ** newptr,int last)464 crypt_encode_alloc(
465     cryptstate_T *state,
466     char_u	*from,
467     size_t	len,
468     char_u	**newptr,
469     int		last)
470 {
471     cryptmethod_T *method = &cryptmethods[state->method_nr];
472 
473     if (method->encode_buffer_fn != NULL)
474 	// Has buffer function, pass through.
475 	return method->encode_buffer_fn(state, from, len, newptr, last);
476     if (len == 0)
477 	// Not buffering, just return EOF.
478 	return (long)len;
479 
480     *newptr = alloc(len + 50);
481     if (*newptr == NULL)
482 	return -1;
483     method->encode_fn(state, from, len, *newptr, last);
484     return (long)len;
485 }
486 
487 /*
488  * Decrypt "ptr[len]" and store the result in a newly allocated buffer, which
489  * is stored in "newptr".
490  * Return number of bytes in "newptr", 0 for need more or -1 on error.
491  */
492     long
crypt_decode_alloc(cryptstate_T * state,char_u * ptr,long len,char_u ** newptr,int last)493 crypt_decode_alloc(
494     cryptstate_T *state,
495     char_u	*ptr,
496     long	len,
497     char_u      **newptr,
498     int		last)
499 {
500     cryptmethod_T *method = &cryptmethods[state->method_nr];
501 
502     if (method->decode_buffer_fn != NULL)
503 	// Has buffer function, pass through.
504 	return method->decode_buffer_fn(state, ptr, len, newptr, last);
505 
506     if (len == 0)
507 	// Not buffering, just return EOF.
508 	return len;
509 
510     *newptr = alloc(len);
511     if (*newptr == NULL)
512 	return -1;
513     method->decode_fn(state, ptr, len, *newptr, last);
514     return len;
515 }
516 #endif
517 
518 /*
519  * Encrypting "from[len]" into "to[len]".
520  */
521     void
crypt_encode(cryptstate_T * state,char_u * from,size_t len,char_u * to,int last)522 crypt_encode(
523     cryptstate_T *state,
524     char_u	*from,
525     size_t	len,
526     char_u	*to,
527     int		last)
528 {
529     cryptmethods[state->method_nr].encode_fn(state, from, len, to, last);
530 }
531 
532 #if 0  // unused
533 /*
534  * decrypting "from[len]" into "to[len]".
535  */
536     void
537 crypt_decode(
538     cryptstate_T *state,
539     char_u	*from,
540     size_t	len,
541     char_u	*to,
542     int		last)
543 {
544     cryptmethods[state->method_nr].decode_fn(state, from, len, to, last);
545 }
546 #endif
547 
548 /*
549  * Simple inplace encryption, modifies "buf[len]" in place.
550  */
551     void
crypt_encode_inplace(cryptstate_T * state,char_u * buf,size_t len,int last)552 crypt_encode_inplace(
553     cryptstate_T *state,
554     char_u	*buf,
555     size_t	len,
556     int         last)
557 {
558     cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len,
559 								    buf, last);
560 }
561 
562 /*
563  * Simple inplace decryption, modifies "buf[len]" in place.
564  */
565     void
crypt_decode_inplace(cryptstate_T * state,char_u * buf,size_t len,int last)566 crypt_decode_inplace(
567     cryptstate_T *state,
568     char_u	*buf,
569     size_t	len,
570     int		last)
571 {
572     cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len,
573 								    buf, last);
574 }
575 
576 /*
577  * Free an allocated crypt key.  Clear the text to make sure it doesn't stay
578  * in memory anywhere.
579  */
580     void
crypt_free_key(char_u * key)581 crypt_free_key(char_u *key)
582 {
583     char_u *p;
584 
585     if (key != NULL)
586     {
587 	for (p = key; *p != NUL; ++p)
588 	    *p = 0;
589 	vim_free(key);
590     }
591 }
592 
593 /*
594  * Check the crypt method and give a warning if it's outdated.
595  */
596     void
crypt_check_method(int method)597 crypt_check_method(int method)
598 {
599     if (method < CRYPT_M_BF2)
600     {
601 	msg_scroll = TRUE;
602 	msg(_("Warning: Using a weak encryption method; see :help 'cm'"));
603     }
604 }
605 
606 #ifdef FEAT_SODIUM
607     static void
crypt_check_swapfile_curbuf(void)608 crypt_check_swapfile_curbuf(void)
609 {
610     int method = crypt_get_method_nr(curbuf);
611     if (method == CRYPT_M_SOD)
612     {
613 	// encryption uses padding and MAC, that does not work very well with
614 	// swap and undo files, so disable them
615 	mf_close_file(curbuf, TRUE);	// remove the swap file
616 	set_option_value((char_u *)"swf", 0, NULL, OPT_LOCAL);
617 	msg_scroll = TRUE;
618 	msg(_("Note: Encryption of swapfile not supported, disabling swap file"));
619     }
620 }
621 #endif
622 
623     void
crypt_check_current_method(void)624 crypt_check_current_method(void)
625 {
626     crypt_check_method(crypt_get_method_nr(curbuf));
627 }
628 
629 /*
630  * Ask the user for a crypt key.
631  * When "store" is TRUE, the new key is stored in the 'key' option, and the
632  * 'key' option value is returned: Don't free it.
633  * When "store" is FALSE, the typed key is returned in allocated memory.
634  * Returns NULL on failure.
635  */
636     char_u *
crypt_get_key(int store,int twice)637 crypt_get_key(
638     int		store,
639     int		twice)	    // Ask for the key twice.
640 {
641     char_u	*p1, *p2 = NULL;
642     int		round;
643 
644     for (round = 0; ; ++round)
645     {
646 	cmdline_star = TRUE;
647 	cmdline_row = msg_row;
648 	p1 = getcmdline_prompt(NUL, round == 0
649 		? (char_u *)_("Enter encryption key: ")
650 		: (char_u *)_("Enter same key again: "), 0, EXPAND_NOTHING,
651 		NULL);
652 	cmdline_star = FALSE;
653 
654 	if (p1 == NULL)
655 	    break;
656 
657 	if (round == twice)
658 	{
659 	    if (p2 != NULL && STRCMP(p1, p2) != 0)
660 	    {
661 		msg(_("Keys don't match!"));
662 		crypt_free_key(p1);
663 		crypt_free_key(p2);
664 		p2 = NULL;
665 		round = -1;		// do it again
666 		continue;
667 	    }
668 
669 	    if (store)
670 	    {
671 		set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL);
672 		crypt_free_key(p1);
673 		p1 = curbuf->b_p_key;
674 #ifdef FEAT_SODIUM
675 		crypt_check_swapfile_curbuf();
676 #endif
677 	    }
678 	    break;
679 	}
680 	p2 = p1;
681     }
682 
683     // since the user typed this, no need to wait for return
684     if (crypt_get_method_nr(curbuf) != CRYPT_M_SOD)
685     {
686 	if (msg_didout)
687 	    msg_putchar('\n');
688 	need_wait_return = FALSE;
689 	msg_didout = FALSE;
690     }
691 
692     crypt_free_key(p2);
693     return p1;
694 }
695 
696 
697 /*
698  * Append a message to IObuff for the encryption/decryption method being used.
699  */
700     void
crypt_append_msg(buf_T * buf)701 crypt_append_msg(
702     buf_T *buf)
703 {
704     if (crypt_get_method_nr(buf) == 0)
705 	STRCAT(IObuff, _("[crypted]"));
706     else
707     {
708 	STRCAT(IObuff, "[");
709 	STRCAT(IObuff, *buf->b_p_cm == NUL ? p_cm : buf->b_p_cm);
710 	STRCAT(IObuff, "]");
711     }
712 }
713 
714     int
crypt_sodium_init(cryptstate_T * state UNUSED,char_u * key UNUSED,char_u * salt UNUSED,int salt_len UNUSED,char_u * seed UNUSED,int seed_len UNUSED)715 crypt_sodium_init(
716     cryptstate_T	*state UNUSED,
717     char_u		*key UNUSED,
718     char_u		*salt UNUSED,
719     int			salt_len UNUSED,
720     char_u		*seed UNUSED,
721     int			seed_len UNUSED)
722 {
723 # ifdef FEAT_SODIUM
724     // crypto_box_SEEDBYTES ==  crypto_secretstream_xchacha20poly1305_KEYBYTES
725     unsigned char	dkey[crypto_box_SEEDBYTES]; // 32
726     sodium_state_T	*sd_state;
727     int			retval = 0;
728 
729     if (sodium_init() < 0)
730 	return FAIL;
731 
732     sd_state = (sodium_state_T *)sodium_malloc(sizeof(sodium_state_T));
733     sodium_memzero(sd_state, sizeof(sodium_state_T));
734 
735     // derive a key from the password
736     if (crypto_pwhash(dkey, sizeof(dkey), (const char *)key, STRLEN(key), salt,
737 	crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE,
738 	crypto_pwhash_ALG_DEFAULT) != 0)
739     {
740 	// out of memory
741 	sodium_free(sd_state);
742 	return FAIL;
743     }
744     memcpy(sd_state->key, dkey, crypto_box_SEEDBYTES);
745 
746     retval += sodium_mlock(sd_state->key, crypto_box_SEEDBYTES);
747     retval += sodium_mlock(key, STRLEN(key));
748 
749     if (retval < 0)
750     {
751 	emsg(_(e_encryption_sodium_mlock_failed));
752 	sodium_free(sd_state);
753 	return FAIL;
754     }
755     sd_state->count = 0;
756     state->method_state = sd_state;
757 
758     return OK;
759 # else
760     emsg(e_libsodium_not_built_in);
761     return FAIL;
762 # endif
763 }
764 
765 /*
766  * Encrypt "from[len]" into "to[len]".
767  * "from" and "to" can be equal to encrypt in place.
768  * Call needs to ensure that there is enough space in to (for the header)
769  */
770 #if 0  // Currently unused
771     void
772 crypt_sodium_encode(
773     cryptstate_T *state UNUSED,
774     char_u	*from UNUSED,
775     size_t	len UNUSED,
776     char_u	*to UNUSED,
777     int		last UNUSED)
778 {
779 # ifdef FEAT_SODIUM
780     // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES
781     sodium_state_T *sod_st = state->method_state;
782     unsigned char  tag = last
783 			? crypto_secretstream_xchacha20poly1305_TAG_FINAL  : 0;
784 
785     if (sod_st->count == 0)
786     {
787 	if (len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES)
788 	{
789 	    emsg(e_libsodium_cannot_encrypt_header);
790 	    return;
791 	}
792 	crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state,
793 							      to, sod_st->key);
794 	to += crypto_secretstream_xchacha20poly1305_HEADERBYTES;
795     }
796 
797     if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES)
798     {
799 	emsg(e_libsodium_cannot_encrypt_buffer);
800 	return;
801     }
802 
803     crypto_secretstream_xchacha20poly1305_push(&sod_st->state, to, NULL,
804 						      from, len, NULL, 0, tag);
805 
806     sod_st->count++;
807 # endif
808 }
809 #endif
810 
811 /*
812  * Decrypt "from[len]" into "to[len]".
813  * "from" and "to" can be equal to encrypt in place.
814  */
815 #if 0  // Currently unused
816     void
817 crypt_sodium_decode(
818     cryptstate_T *state UNUSED,
819     char_u	*from UNUSED,
820     size_t	len UNUSED,
821     char_u	*to UNUSED,
822     int		last UNUSED)
823 {
824 # ifdef FEAT_SODIUM
825     // crypto_box_SEEDBYTES ==  crypto_secretstream_xchacha20poly1305_KEYBYTES
826     sodium_state_T *sod_st = state->method_state;
827     unsigned char  tag;
828     unsigned long long buf_len;
829     char_u *p1 = from;
830     char_u *p2 = to;
831     char_u *buf_out;
832 
833     if (sod_st->count == 0
834 		   && len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES)
835     {
836 	emsg(e_libsodium_cannot_decrypt_header);
837 	return;
838     }
839 
840     buf_out = (char_u *)alloc(len);
841 
842     if (buf_out == NULL)
843     {
844 	emsg(e_libsodium_cannot_allocate_buffer);
845 	return;
846     }
847     if (sod_st->count == 0)
848     {
849 	if (crypto_secretstream_xchacha20poly1305_init_pull(
850 				       &sod_st->state, from, sod_st->key) != 0)
851 	{
852 	    emsg(e_libsodium_decryption_failed_header_incomplete);
853 	    goto fail;
854 	}
855 
856 	from += crypto_secretstream_xchacha20poly1305_HEADERBYTES;
857 	len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES;
858 
859 	if (p1 == p2)
860 	    to += crypto_secretstream_xchacha20poly1305_HEADERBYTES;
861     }
862 
863     if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES)
864     {
865 	emsg(e_libsodium_cannot_decrypt_buffer);
866 	goto fail;
867     }
868     if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state,
869 			     buf_out, &buf_len, &tag, from, len, NULL, 0) != 0)
870     {
871 	emsg(e_libsodium_decryption_failed);
872 	goto fail;
873     }
874     sod_st->count++;
875 
876     if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last)
877     {
878 	emsg(e_libsodium_decryption_failed_premature);
879 	goto fail;
880     }
881     if (p1 == p2)
882 	mch_memmove(p2, buf_out, buf_len);
883 
884 fail:
885     vim_free(buf_out);
886 # endif
887 }
888 #endif
889 
890 /*
891  * Encrypt "from[len]" into "to[len]".
892  * "from" and "to" can be equal to encrypt in place.
893  */
894     long
crypt_sodium_buffer_encode(cryptstate_T * state UNUSED,char_u * from UNUSED,size_t len UNUSED,char_u ** buf_out UNUSED,int last UNUSED)895 crypt_sodium_buffer_encode(
896     cryptstate_T *state UNUSED,
897     char_u	*from UNUSED,
898     size_t	len UNUSED,
899     char_u	**buf_out UNUSED,
900     int		last UNUSED)
901 {
902 # ifdef FEAT_SODIUM
903     // crypto_box_SEEDBYTES ==  crypto_secretstream_xchacha20poly1305_KEYBYTES
904     unsigned long long	out_len;
905     char_u		*ptr;
906     unsigned char	tag = last
907 			? crypto_secretstream_xchacha20poly1305_TAG_FINAL  : 0;
908     int			length;
909     sodium_state_T	*sod_st = state->method_state;
910     int			first = (sod_st->count == 0);
911 
912     length = (int)len + crypto_secretstream_xchacha20poly1305_ABYTES
913 	     + (first ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0);
914     *buf_out = alloc_clear(length);
915     if (*buf_out == NULL)
916     {
917 	emsg(e_libsodium_cannot_allocate_buffer);
918 	return -1;
919     }
920     ptr = *buf_out;
921 
922     if (first)
923     {
924 	crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state,
925 		ptr, sod_st->key);
926 	ptr += crypto_secretstream_xchacha20poly1305_HEADERBYTES;
927     }
928 
929     crypto_secretstream_xchacha20poly1305_push(&sod_st->state, ptr,
930 	    &out_len, from, len, NULL, 0, tag);
931 
932     sod_st->count++;
933     return out_len + (first
934 		      ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0);
935 # else
936     return -1;
937 # endif
938 }
939 
940 /*
941  * Decrypt "from[len]" into "to[len]".
942  * "from" and "to" can be equal to encrypt in place.
943  */
944     long
crypt_sodium_buffer_decode(cryptstate_T * state UNUSED,char_u * from UNUSED,size_t len UNUSED,char_u ** buf_out UNUSED,int last UNUSED)945 crypt_sodium_buffer_decode(
946     cryptstate_T *state UNUSED,
947     char_u	*from UNUSED,
948     size_t	len UNUSED,
949     char_u	**buf_out UNUSED,
950     int		last UNUSED)
951 {
952 # ifdef FEAT_SODIUM
953     // crypto_box_SEEDBYTES ==  crypto_secretstream_xchacha20poly1305_KEYBYTES
954     sodium_state_T *sod_st = state->method_state;
955     unsigned char  tag;
956     unsigned long long out_len;
957     *buf_out = alloc_clear(len);
958     if (*buf_out == NULL)
959     {
960 	emsg(e_libsodium_cannot_allocate_buffer);
961 	return -1;
962     }
963 
964     if (sod_st->count == 0)
965     {
966 	if (crypto_secretstream_xchacha20poly1305_init_pull(&sod_st->state,
967 						       from, sod_st->key) != 0)
968 	{
969 	    emsg(e_libsodium_decryption_failed_header_incomplete);
970 	    return -1;
971 	}
972 	from += crypto_secretstream_xchacha20poly1305_HEADERBYTES;
973 	len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES;
974 	sod_st->count++;
975     }
976     if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state,
977 			    *buf_out, &out_len, &tag, from, len, NULL, 0) != 0)
978     {
979 	emsg(e_libsodium_decryption_failed);
980 	return -1;
981     }
982 
983     if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last)
984 	emsg(e_libsodium_decryption_failed_premature);
985     return (long) out_len;
986 # else
987     return -1;
988 # endif
989 }
990 
991 #endif // FEAT_CRYPT
992