Mercurial > vim
diff src/fileio.c @ 6122:18ac55444b37 v7.4.399
updated for version 7.4.399
Problem: Encryption implementation is messy. Blowfish encryption has a
weakness.
Solution: Refactor the encryption, store the state in an allocated struct
instead of using a save/restore mechanism. Introduce the
"blowfish2" method, which does not have the weakness and encrypts
the whole undo file. (largely by David Leadbeater)
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sun, 10 Aug 2014 13:38:34 +0200 |
parents | f7bc601823e5 |
children | 86fb698a38d5 |
line wrap: on
line diff
--- a/src/fileio.c +++ b/src/fileio.c @@ -24,20 +24,6 @@ #define BUFSIZE 8192 /* size of normal write buffer */ #define SMBUFSIZE 256 /* size of emergency write buffer */ -#ifdef FEAT_CRYPT -/* crypt_magic[0] is pkzip crypt, crypt_magic[1] is sha2+blowfish */ -static char *crypt_magic[] = {"VimCrypt~01!", "VimCrypt~02!"}; -static char crypt_magic_head[] = "VimCrypt~"; -# define CRYPT_MAGIC_LEN 12 /* must be multiple of 4! */ - -/* For blowfish, after the magic header, we store 8 bytes of salt and then 8 - * bytes of seed (initialisation vector). */ -static int crypt_salt_len[] = {0, 8}; -static int crypt_seed_len[] = {0, 8}; -#define CRYPT_SALT_LEN_MAX 8 -#define CRYPT_SEED_LEN_MAX 8 -#endif - /* Is there any system that doesn't have access()? */ #define USE_MCH_ACCESS @@ -55,7 +41,6 @@ static char_u *readfile_charconvert __AR static void check_marks_read __ARGS((void)); #endif #ifdef FEAT_CRYPT -static int crypt_method_from_magic __ARGS((char *ptr, int len)); static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, char_u *fname, int *did_ask)); #endif #ifdef UNIX @@ -116,6 +101,9 @@ struct bw_info #ifdef HAS_BW_FLAGS int bw_flags; /* FIO_ flags */ #endif +#ifdef FEAT_CRYPT + buf_T *bw_buffer; /* buffer being written */ +#endif #ifdef FEAT_MBYTE char_u bw_rest[CONV_RESTLEN]; /* not converted bytes */ int bw_restlen; /* nr of bytes in bw_rest[] */ @@ -250,7 +238,6 @@ readfile(fname, sfname, from, lines_to_s #ifdef FEAT_CRYPT char_u *cryptkey = NULL; int did_ask_for_key = FALSE; - int crypt_method_used; #endif #ifdef FEAT_PERSISTENT_UNDO context_sha256_T sha_ctx; @@ -966,13 +953,6 @@ retry: #endif } -#ifdef FEAT_CRYPT - if (cryptkey != NULL) - /* Need to reset the state, but keep the key, don't want to ask for it - * again. */ - crypt_pop_state(); -#endif - /* * When retrying with another "fenc" and the first time "fileformat" * will be reset. @@ -1175,6 +1155,15 @@ retry: if (read_undo_file) sha256_start(&sha_ctx); #endif +#ifdef FEAT_CRYPT + if (curbuf->b_cryptstate != NULL) + { + /* Need to free the state, but keep the key, don't want to ask for + * it again. */ + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; + } +#endif } while (!error && !got_int) @@ -1339,6 +1328,76 @@ retry: size = read_eintr(fd, ptr, size); } +#ifdef FEAT_CRYPT + /* + * At start of file: Check for magic number of encryption. + */ + if (filesize == 0 && size > 0) + cryptkey = check_for_cryptkey(cryptkey, ptr, &size, + &filesize, newfile, sfname, + &did_ask_for_key); + /* + * Decrypt the read bytes. This is done before checking for + * EOF because the crypt layer may be buffering. + */ + if (cryptkey != NULL && size > 0) + { + if (crypt_works_inplace(curbuf->b_cryptstate)) + { + crypt_decode_inplace(curbuf->b_cryptstate, ptr, size); + } + else + { + char_u *newptr = NULL; + int decrypted_size; + + decrypted_size = crypt_decode_alloc( + curbuf->b_cryptstate, ptr, size, &newptr); + + /* If the crypt layer is buffering, not producing + * anything yet, need to read more. */ + if (size > 0 && decrypted_size == 0) + continue; + + if (linerest == 0) + { + /* Simple case: reuse returned buffer (may be + * NULL, checked later). */ + new_buffer = newptr; + } + else + { + long_u new_size; + + /* Need new buffer to add bytes carried over. */ + new_size = (long_u)(decrypted_size + linerest + 1); + new_buffer = lalloc(new_size, FALSE); + if (new_buffer == NULL) + { + do_outofmem_msg(new_size); + error = TRUE; + break; + } + + mch_memmove(new_buffer, buffer, linerest); + if (newptr != NULL) + mch_memmove(new_buffer + linerest, newptr, + decrypted_size); + } + + if (new_buffer != NULL) + { + vim_free(buffer); + buffer = new_buffer; + new_buffer = NULL; + line_start = buffer; + ptr = buffer + linerest; + } + size = decrypted_size; + } + } +#endif + if (size <= 0) { if (size < 0) /* read error */ @@ -1403,21 +1462,6 @@ retry: } #endif } - -#ifdef FEAT_CRYPT - /* - * At start of file: Check for magic number of encryption. - */ - if (filesize == 0) - cryptkey = check_for_cryptkey(cryptkey, ptr, &size, - &filesize, newfile, sfname, - &did_ask_for_key); - /* - * Decrypt the read bytes. - */ - if (cryptkey != NULL && size > 0) - crypt_decode(ptr, size); -#endif } skip_read = FALSE; @@ -1430,10 +1474,9 @@ retry: */ if ((filesize == 0 # ifdef FEAT_CRYPT - || (filesize == (CRYPT_MAGIC_LEN - + crypt_salt_len[use_crypt_method] - + crypt_seed_len[use_crypt_method]) - && cryptkey != NULL) + || (cryptkey != NULL + && filesize == crypt_get_header_len( + crypt_get_method_nr(curbuf))) # endif ) && (fio_flags == FIO_UCSBOM @@ -2262,15 +2305,15 @@ failed: save_file_ff(curbuf); /* remember the current file format */ #ifdef FEAT_CRYPT - crypt_method_used = use_crypt_method; - if (cryptkey != NULL) - { - crypt_pop_state(); - if (cryptkey != curbuf->b_p_key) - free_crypt_key(cryptkey); - /* don't set cryptkey to NULL, it's used below as a flag that - * encryption was used */ - } + if (curbuf->b_cryptstate != NULL) + { + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; + } + if (cryptkey != NULL && cryptkey != curbuf->b_p_key) + crypt_free_key(cryptkey); + /* Don't set cryptkey to NULL, it's used below as a flag that + * encryption was used. */ #endif #ifdef FEAT_MBYTE @@ -2457,10 +2500,7 @@ failed: #ifdef FEAT_CRYPT if (cryptkey != NULL) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); + crypt_append_msg(curbuf); c = TRUE; } #endif @@ -2489,9 +2529,7 @@ failed: #ifdef FEAT_CRYPT if (cryptkey != NULL) msg_add_lines(c, (long)linecnt, filesize - - CRYPT_MAGIC_LEN - - crypt_salt_len[use_crypt_method] - - crypt_seed_len[use_crypt_method]); + - crypt_get_header_len(crypt_get_method_nr(curbuf))); else #endif msg_add_lines(c, (long)linecnt, filesize); @@ -2882,33 +2920,6 @@ check_marks_read() #if defined(FEAT_CRYPT) || defined(PROTO) /* - * Get the crypt method used for a file from "ptr[len]", the magic text at the - * start of the file. - * Returns -1 when no encryption used. - */ - static int -crypt_method_from_magic(ptr, len) - char *ptr; - int len; -{ - int i; - - for (i = 0; i < (int)(sizeof(crypt_magic) / sizeof(crypt_magic[0])); i++) - { - if (len < (CRYPT_MAGIC_LEN + crypt_salt_len[i] + crypt_seed_len[i])) - continue; - if (memcmp(ptr, crypt_magic[i], CRYPT_MAGIC_LEN) == 0) - return i; - } - - i = (int)STRLEN(crypt_magic_head); - if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0) - EMSG(_("E821: File is encrypted with unknown method")); - - return -1; -} - -/* * Check for magic number used for encryption. Applies to the current buffer. * If found, the magic number is removed from ptr[*sizep] and *sizep and * *filesizep are updated. @@ -2924,7 +2935,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, char_u *fname; /* file name to display */ int *did_ask; /* flag: whether already asked for key */ { - int method = crypt_method_from_magic((char *)ptr, *sizep); + int method = crypt_method_nr_from_magic((char *)ptr, *sizep); int b_p_ro = curbuf->b_p_ro; if (method >= 0) @@ -2933,9 +2944,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, * Avoids accidentally overwriting the file with garbage. */ curbuf->b_p_ro = TRUE; - set_crypt_method(curbuf, method); - if (method > 0) - (void)blowfish_self_test(); + crypt_set_cm_option(curbuf, method); if (cryptkey == NULL && !*did_ask) { if (*curbuf->b_p_key) @@ -2948,7 +2957,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, * Happens when retrying to detect encoding. */ smsg((char_u *)_(need_key_msg), fname); msg_scroll = TRUE; - cryptkey = get_crypt_key(newfile, FALSE); + cryptkey = crypt_get_key(newfile, FALSE); *did_ask = TRUE; /* check if empty key entered */ @@ -2963,24 +2972,18 @@ check_for_cryptkey(cryptkey, ptr, sizep, if (cryptkey != NULL) { - int seed_len = crypt_seed_len[method]; - int salt_len = crypt_salt_len[method]; - - crypt_push_state(); - use_crypt_method = method; - if (method == 0) - crypt_init_keys(cryptkey); - else - { - bf_key_init(cryptkey, ptr + CRYPT_MAGIC_LEN, salt_len); - bf_cfb_init(ptr + CRYPT_MAGIC_LEN + salt_len, seed_len); - } - - /* Remove magic number from the text */ - *filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len; - *sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len; - mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len, - (size_t)*sizep); + int header_len; + + curbuf->b_cryptstate = crypt_create_from_header( + method, cryptkey, ptr); + crypt_set_cm_option(curbuf, method); + + /* Remove cryptmethod specific header from the text. */ + header_len = crypt_get_header_len(method); + *filesizep += header_len; + *sizep -= header_len; + mch_memmove(ptr, ptr + header_len, (size_t)*sizep); + /* Restore the read-only flag. */ curbuf->b_p_ro = b_p_ro; } @@ -2992,85 +2995,6 @@ check_for_cryptkey(cryptkey, ptr, sizep, return cryptkey; } - -/* - * Check for magic number used for encryption. Applies to the current buffer. - * If found and decryption is possible returns OK; - */ - int -prepare_crypt_read(fp) - FILE *fp; -{ - int method; - char_u buffer[CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2]; - - if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) - return FAIL; - method = crypt_method_from_magic((char *)buffer, - CRYPT_MAGIC_LEN + - CRYPT_SEED_LEN_MAX + - CRYPT_SALT_LEN_MAX); - if (method < 0 || method != get_crypt_method(curbuf)) - return FAIL; - - crypt_push_state(); - if (method == 0) - crypt_init_keys(curbuf->b_p_key); - else - { - int salt_len = crypt_salt_len[method]; - int seed_len = crypt_seed_len[method]; - - if (fread(buffer, salt_len + seed_len, 1, fp) != 1) - return FAIL; - bf_key_init(curbuf->b_p_key, buffer, salt_len); - bf_cfb_init(buffer + salt_len, seed_len); - } - return OK; -} - -/* - * Prepare for writing encrypted bytes for buffer "buf". - * Returns a pointer to an allocated header of length "*lenp". - * When out of memory returns NULL. - * Otherwise calls crypt_push_state(), call crypt_pop_state() later. - */ - char_u * -prepare_crypt_write(buf, lenp) - buf_T *buf; - int *lenp; -{ - char_u *header; - int seed_len = crypt_seed_len[get_crypt_method(buf)]; - int salt_len = crypt_salt_len[get_crypt_method(buf)]; - char_u *salt; - char_u *seed; - - header = alloc_clear(CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2); - if (header != NULL) - { - crypt_push_state(); - use_crypt_method = get_crypt_method(buf); /* select zip or blowfish */ - vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method], - CRYPT_MAGIC_LEN); - if (use_crypt_method == 0) - crypt_init_keys(buf->b_p_key); - else - { - /* Using blowfish, add salt and seed. */ - salt = header + CRYPT_MAGIC_LEN; - seed = salt + salt_len; - sha2_seed(salt, salt_len, seed, seed_len); - bf_key_init(buf->b_p_key, salt, salt_len); - bf_cfb_init(seed, seed_len); - } - } - *lenp = CRYPT_MAGIC_LEN + salt_len + seed_len; - return header; -} - #endif /* FEAT_CRYPT */ #ifdef UNIX @@ -3224,9 +3148,6 @@ buf_write(buf, fname, sfname, start, end int write_undo_file = FALSE; context_sha256_T sha_ctx; #endif -#ifdef FEAT_CRYPT - int crypt_method_used; -#endif if (fname == NULL || *fname == NUL) /* safety check */ return FAIL; @@ -3262,6 +3183,9 @@ buf_write(buf, fname, sfname, start, end write_info.bw_iconv_fd = (iconv_t)-1; # endif #endif +#ifdef FEAT_CRYPT + write_info.bw_buffer = buf; +#endif /* After writing a file changedtick changes but we don't want to display * the line. */ @@ -4505,17 +4429,17 @@ restore_backup: #ifdef FEAT_CRYPT if (*buf->b_p_key != NUL && !filtering) { - char_u *header; - int header_len; - - header = prepare_crypt_write(buf, &header_len); - if (header == NULL) + char_u *header; + int header_len; + + buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf), + buf->b_p_key, &header, &header_len); + if (buf->b_cryptstate == NULL || header == NULL) end = 0; else { - /* Write magic number, so that Vim knows that this file is - * encrypted when reading it again. This also undergoes utf-8 to - * ucs-2/4 conversion when needed. */ + /* Write magic number, so that Vim knows how this file is + * encrypted when reading it back. */ write_info.bw_buf = header; write_info.bw_len = header_len; write_info.bw_flags = FIO_NOCONVERT; @@ -4769,11 +4693,12 @@ restore_backup: mch_set_acl(wfname, acl); #endif #ifdef FEAT_CRYPT - crypt_method_used = use_crypt_method; - if (wb_flags & FIO_ENCRYPTED) - crypt_pop_state(); -#endif - + if (buf->b_cryptstate != NULL) + { + crypt_free_state(buf->b_cryptstate); + buf->b_cryptstate = NULL; + } +#endif #if defined(FEAT_MBYTE) && defined(FEAT_EVAL) if (wfname != fname) @@ -4924,10 +4849,7 @@ restore_backup: #ifdef FEAT_CRYPT if (wb_flags & FIO_ENCRYPTED) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); + crypt_append_msg(buf); c = TRUE; } #endif @@ -5740,8 +5662,26 @@ buf_write_bytes(ip) #endif /* FEAT_MBYTE */ #ifdef FEAT_CRYPT - if (flags & FIO_ENCRYPTED) /* encrypt the data */ - crypt_encode(buf, len, buf); + if (flags & FIO_ENCRYPTED) + { + /* Encrypt the data. Do it in-place if possible, otherwise use an + * allocated buffer. */ + if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) + { + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); + } + else + { + char_u *outbuf; + + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + if (len == 0) + return OK; /* Crypt layer is buffering, will flush later. */ + wlen = write_eintr(ip->bw_fd, outbuf, len); + vim_free(outbuf); + return (wlen < len) ? FAIL : OK; + } + } #endif wlen = write_eintr(ip->bw_fd, buf, len);