forked from aniani/vim
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)
This commit is contained in:
305
src/blowfish.c
305
src/blowfish.c
@@ -9,17 +9,25 @@
|
||||
* Blowfish encryption for Vim; in Blowfish cipher feedback mode.
|
||||
* Contributed by Mohsin Ahmed, http://www.cs.albany.edu/~mosh
|
||||
* Based on http://www.schneier.com/blowfish.html by Bruce Schneier.
|
||||
*
|
||||
* There are two variants:
|
||||
* - The old one "blowfish" has a flaw which makes it much easier to crack the
|
||||
* key. To see this, make a text file with one line of 1000 "x" characters
|
||||
* and write it encrypted. Use "xxd" to inspect the bytes in the file. You
|
||||
* will see that a block of 8 bytes repeats 8 times.
|
||||
* - The new one "blowfish2" is better. It uses an 8 byte CFB to avoid the
|
||||
* repeats.
|
||||
*/
|
||||
|
||||
#include "vim.h"
|
||||
|
||||
#if defined(FEAT_CRYPT)
|
||||
#if defined(FEAT_CRYPT) || defined(PROTO)
|
||||
|
||||
#define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0]))
|
||||
|
||||
#define BF_BLOCK 8
|
||||
#define BF_BLOCK_MASK 7
|
||||
#define BF_CFB_LEN (8*(BF_BLOCK))
|
||||
#define BF_MAX_CFB_LEN (8 * BF_BLOCK)
|
||||
|
||||
typedef union {
|
||||
UINT32_T ul[2];
|
||||
@@ -37,14 +45,26 @@ typedef union {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void bf_e_block __ARGS((UINT32_T *p_xl, UINT32_T *p_xr));
|
||||
static void bf_e_cblock __ARGS((char_u *block));
|
||||
static int bf_check_tables __ARGS((UINT32_T a_ipa[18], UINT32_T a_sbi[4][256], UINT32_T val));
|
||||
/* The state of encryption, referenced by cryptstate_T. */
|
||||
typedef struct {
|
||||
UINT32_T pax[18]; /* P-array */
|
||||
UINT32_T sbx[4][256]; /* S-boxes */
|
||||
int randbyte_offset;
|
||||
int update_offset;
|
||||
char_u cfb_buffer[BF_MAX_CFB_LEN]; /* up to 64 bytes used */
|
||||
int cfb_len; /* size of cfb_buffer actually used */
|
||||
} bf_state_T;
|
||||
|
||||
|
||||
static void bf_e_block __ARGS((bf_state_T *state, UINT32_T *p_xl, UINT32_T *p_xr));
|
||||
static void bf_e_cblock __ARGS((bf_state_T *state, char_u *block));
|
||||
static int bf_check_tables __ARGS((UINT32_T pax[18], UINT32_T sbx[4][256], UINT32_T val));
|
||||
static int bf_self_test __ARGS((void));
|
||||
static void bf_key_init __ARGS((bf_state_T *state, char_u *password, char_u *salt, int salt_len));
|
||||
static void bf_cfb_init __ARGS((bf_state_T *state, char_u *seed, int seed_len));
|
||||
|
||||
/* Blowfish code */
|
||||
static UINT32_T pax[18];
|
||||
static UINT32_T ipa[18] = {
|
||||
static UINT32_T pax_init[18] = {
|
||||
0x243f6a88u, 0x85a308d3u, 0x13198a2eu,
|
||||
0x03707344u, 0xa4093822u, 0x299f31d0u,
|
||||
0x082efa98u, 0xec4e6c89u, 0x452821e6u,
|
||||
@@ -53,8 +73,7 @@ static UINT32_T ipa[18] = {
|
||||
0xb5470917u, 0x9216d5d9u, 0x8979fb1bu
|
||||
};
|
||||
|
||||
static UINT32_T sbx[4][256];
|
||||
static UINT32_T sbi[4][256] = {
|
||||
static UINT32_T sbx_init[4][256] = {
|
||||
{0xd1310ba6u, 0x98dfb5acu, 0x2ffd72dbu, 0xd01adfb7u,
|
||||
0xb8e1afedu, 0x6a267e96u, 0xba7c9045u, 0xf12c7f99u,
|
||||
0x24a19947u, 0xb3916cf7u, 0x0801f2e2u, 0x858efc16u,
|
||||
@@ -314,33 +333,40 @@ static UINT32_T sbi[4][256] = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define F1(i) \
|
||||
xl ^= pax[i]; \
|
||||
xr ^= ((sbx[0][xl >> 24] + \
|
||||
sbx[1][(xl & 0xFF0000) >> 16]) ^ \
|
||||
sbx[2][(xl & 0xFF00) >> 8]) + \
|
||||
sbx[3][xl & 0xFF];
|
||||
xl ^= bfs->pax[i]; \
|
||||
xr ^= ((bfs->sbx[0][xl >> 24] + \
|
||||
bfs->sbx[1][(xl & 0xFF0000) >> 16]) ^ \
|
||||
bfs->sbx[2][(xl & 0xFF00) >> 8]) + \
|
||||
bfs->sbx[3][xl & 0xFF];
|
||||
|
||||
#define F2(i) \
|
||||
xr ^= pax[i]; \
|
||||
xl ^= ((sbx[0][xr >> 24] + \
|
||||
sbx[1][(xr & 0xFF0000) >> 16]) ^ \
|
||||
sbx[2][(xr & 0xFF00) >> 8]) + \
|
||||
sbx[3][xr & 0xFF];
|
||||
|
||||
xr ^= bfs->pax[i]; \
|
||||
xl ^= ((bfs->sbx[0][xr >> 24] + \
|
||||
bfs->sbx[1][(xr & 0xFF0000) >> 16]) ^ \
|
||||
bfs->sbx[2][(xr & 0xFF00) >> 8]) + \
|
||||
bfs->sbx[3][xr & 0xFF];
|
||||
|
||||
static void
|
||||
bf_e_block(p_xl, p_xr)
|
||||
bf_e_block(bfs, p_xl, p_xr)
|
||||
bf_state_T *bfs;
|
||||
UINT32_T *p_xl;
|
||||
UINT32_T *p_xr;
|
||||
{
|
||||
UINT32_T temp, xl = *p_xl, xr = *p_xr;
|
||||
UINT32_T temp;
|
||||
UINT32_T xl = *p_xl;
|
||||
UINT32_T xr = *p_xr;
|
||||
|
||||
F1(0) F2(1) F1(2) F2(3) F1(4) F2(5) F1(6) F2(7)
|
||||
F1(8) F2(9) F1(10) F2(11) F1(12) F2(13) F1(14) F2(15)
|
||||
xl ^= pax[16];
|
||||
xr ^= pax[17];
|
||||
F1(0) F2(1)
|
||||
F1(2) F2(3)
|
||||
F1(4) F2(5)
|
||||
F1(6) F2(7)
|
||||
F1(8) F2(9)
|
||||
F1(10) F2(11)
|
||||
F1(12) F2(13)
|
||||
F1(14) F2(15)
|
||||
xl ^= bfs->pax[16];
|
||||
xr ^= bfs->pax[17];
|
||||
temp = xl;
|
||||
xl = xr;
|
||||
xr = temp;
|
||||
@@ -348,22 +374,6 @@ bf_e_block(p_xl, p_xr)
|
||||
*p_xr = xr;
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
static void
|
||||
bf_d_block(p_xl, p_xr)
|
||||
UINT32_T *p_xl;
|
||||
UINT32_T *p_xr;
|
||||
{
|
||||
UINT32_T temp, xl = *p_xl, xr = *p_xr;
|
||||
F1(17) F2(16) F1(15) F2(14) F1(13) F2(12) F1(11) F2(10)
|
||||
F1(9) F2(8) F1(7) F2(6) F1(5) F2(4) F1(3) F2(2)
|
||||
xl ^= pax[1];
|
||||
xr ^= pax[0];
|
||||
temp = xl; xl = xr; xr = temp;
|
||||
*p_xl = xl; *p_xr = xr;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
# define htonl2(x) \
|
||||
@@ -374,7 +384,8 @@ bf_d_block(p_xl, p_xr)
|
||||
#endif
|
||||
|
||||
static void
|
||||
bf_e_cblock(block)
|
||||
bf_e_cblock(bfs, block)
|
||||
bf_state_T *bfs;
|
||||
char_u *block;
|
||||
{
|
||||
block8 bk;
|
||||
@@ -382,35 +393,22 @@ bf_e_cblock(block)
|
||||
memcpy(bk.uc, block, 8);
|
||||
htonl2(bk.ul[0]);
|
||||
htonl2(bk.ul[1]);
|
||||
bf_e_block(&bk.ul[0], &bk.ul[1]);
|
||||
bf_e_block(bfs, &bk.ul[0], &bk.ul[1]);
|
||||
htonl2(bk.ul[0]);
|
||||
htonl2(bk.ul[1]);
|
||||
memcpy(block, bk.uc, 8);
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
void
|
||||
bf_d_cblock(block)
|
||||
char_u *block;
|
||||
{
|
||||
block8 bk;
|
||||
memcpy(bk.uc, block, 8);
|
||||
htonl2(bk.ul[0]); htonl2(bk.ul[1]);
|
||||
bf_d_block(&bk.ul[0], &bk.ul[1]);
|
||||
htonl2(bk.ul[0]); htonl2(bk.ul[1]);
|
||||
memcpy(block, bk.uc, 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize the crypt method using "password" as the encryption key and
|
||||
* "salt[salt_len]" as the salt.
|
||||
*/
|
||||
void
|
||||
bf_key_init(password, salt, salt_len)
|
||||
char_u *password;
|
||||
char_u *salt;
|
||||
int salt_len;
|
||||
static void
|
||||
bf_key_init(bfs, password, salt, salt_len)
|
||||
bf_state_T *bfs;
|
||||
char_u *password;
|
||||
char_u *salt;
|
||||
int salt_len;
|
||||
{
|
||||
int i, j, keypos = 0;
|
||||
unsigned u;
|
||||
@@ -418,7 +416,7 @@ bf_key_init(password, salt, salt_len)
|
||||
char_u *key;
|
||||
int keylen;
|
||||
|
||||
/* Process the key 1000 times.
|
||||
/* Process the key 1001 times.
|
||||
* See http://en.wikipedia.org/wiki/Key_strengthening. */
|
||||
key = sha256_key(password, salt, salt_len);
|
||||
for (i = 0; i < 1000; i++)
|
||||
@@ -437,52 +435,54 @@ bf_key_init(password, salt, salt_len)
|
||||
key[i] = u;
|
||||
}
|
||||
|
||||
mch_memmove(sbx, sbi, 4 * 4 * 256);
|
||||
/* Use "key" to initialize the P-array ("pax") and S-boxes ("sbx") of
|
||||
* Blowfish. */
|
||||
mch_memmove(bfs->sbx, sbx_init, 4 * 4 * 256);
|
||||
|
||||
for (i = 0; i < 18; ++i)
|
||||
{
|
||||
val = 0;
|
||||
for (j = 0; j < 4; ++j)
|
||||
val = (val << 8) | key[keypos++ % keylen];
|
||||
pax[i] = ipa[i] ^ val;
|
||||
bfs->pax[i] = pax_init[i] ^ val;
|
||||
}
|
||||
|
||||
data_l = data_r = 0;
|
||||
for (i = 0; i < 18; i += 2)
|
||||
{
|
||||
bf_e_block(&data_l, &data_r);
|
||||
pax[i + 0] = data_l;
|
||||
pax[i + 1] = data_r;
|
||||
bf_e_block(bfs, &data_l, &data_r);
|
||||
bfs->pax[i + 0] = data_l;
|
||||
bfs->pax[i + 1] = data_r;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
for (j = 0; j < 256; j += 2)
|
||||
{
|
||||
bf_e_block(&data_l, &data_r);
|
||||
sbx[i][j + 0] = data_l;
|
||||
sbx[i][j + 1] = data_r;
|
||||
bf_e_block(bfs, &data_l, &data_r);
|
||||
bfs->sbx[i][j + 0] = data_l;
|
||||
bfs->sbx[i][j + 1] = data_r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BF Self test for corrupted tables or instructions
|
||||
* Blowfish self-test for corrupted tables or instructions.
|
||||
*/
|
||||
static int
|
||||
bf_check_tables(a_ipa, a_sbi, val)
|
||||
UINT32_T a_ipa[18];
|
||||
UINT32_T a_sbi[4][256];
|
||||
bf_check_tables(pax, sbx, val)
|
||||
UINT32_T pax[18];
|
||||
UINT32_T sbx[4][256];
|
||||
UINT32_T val;
|
||||
{
|
||||
int i, j;
|
||||
UINT32_T c = 0;
|
||||
|
||||
for (i = 0; i < 18; i++)
|
||||
c ^= a_ipa[i];
|
||||
c ^= pax[i];
|
||||
for (i = 0; i < 4; i++)
|
||||
for (j = 0; j < 256; j++)
|
||||
c ^= a_sbi[i][j];
|
||||
c ^= sbx[i][j];
|
||||
return c == val;
|
||||
}
|
||||
|
||||
@@ -520,6 +520,10 @@ bf_self_test()
|
||||
int err = 0;
|
||||
block8 bk;
|
||||
UINT32_T ui = 0xffffffffUL;
|
||||
bf_state_T state;
|
||||
|
||||
vim_memset(&state, 0, sizeof(bf_state_T));
|
||||
state.cfb_len = BF_MAX_CFB_LEN;
|
||||
|
||||
/* We can't simply use sizeof(UINT32_T), it would generate a compiler
|
||||
* warning. */
|
||||
@@ -528,21 +532,21 @@ bf_self_test()
|
||||
EMSG(_("E820: sizeof(uint32_t) != 4"));
|
||||
}
|
||||
|
||||
if (!bf_check_tables(ipa, sbi, 0x6ffa520a))
|
||||
if (!bf_check_tables(pax_init, sbx_init, 0x6ffa520a))
|
||||
err++;
|
||||
|
||||
bn = ARRAY_LENGTH(bf_test_data);
|
||||
for (i = 0; i < bn; i++)
|
||||
{
|
||||
bf_key_init((char_u *)(bf_test_data[i].password),
|
||||
bf_key_init(&state, (char_u *)(bf_test_data[i].password),
|
||||
bf_test_data[i].salt,
|
||||
(int)STRLEN(bf_test_data[i].salt));
|
||||
if (!bf_check_tables(pax, sbx, bf_test_data[i].keysum))
|
||||
if (!bf_check_tables(state.pax, state.sbx, bf_test_data[i].keysum))
|
||||
err++;
|
||||
|
||||
/* Don't modify bf_test_data[i].plaintxt, self test is idempotent. */
|
||||
memcpy(bk.uc, bf_test_data[i].plaintxt, 8);
|
||||
bf_e_cblock(bk.uc);
|
||||
bf_e_cblock(&state, bk.uc);
|
||||
if (memcmp(bk.uc, bf_test_data[i].cryptxt, 8) != 0)
|
||||
{
|
||||
if (err == 0 && memcmp(bk.uc, bf_test_data[i].badcryptxt, 8) == 0)
|
||||
@@ -554,43 +558,43 @@ bf_self_test()
|
||||
return err > 0 ? FAIL : OK;
|
||||
}
|
||||
|
||||
/* Cipher feedback mode. */
|
||||
static int randbyte_offset = 0;
|
||||
static int update_offset = 0;
|
||||
static char_u cfb_buffer[BF_CFB_LEN]; /* 64 bytes */
|
||||
/*
|
||||
* CFB: Cipher Feedback Mode.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize with seed "iv[iv_len]".
|
||||
* Initialize with seed "seed[seed_len]".
|
||||
*/
|
||||
void
|
||||
bf_cfb_init(iv, iv_len)
|
||||
char_u *iv;
|
||||
int iv_len;
|
||||
static void
|
||||
bf_cfb_init(bfs, seed, seed_len)
|
||||
bf_state_T *bfs;
|
||||
char_u *seed;
|
||||
int seed_len;
|
||||
{
|
||||
int i, mi;
|
||||
|
||||
randbyte_offset = update_offset = 0;
|
||||
vim_memset(cfb_buffer, 0, BF_CFB_LEN);
|
||||
if (iv_len > 0)
|
||||
bfs->randbyte_offset = bfs->update_offset = 0;
|
||||
vim_memset(bfs->cfb_buffer, 0, bfs->cfb_len);
|
||||
if (seed_len > 0)
|
||||
{
|
||||
mi = iv_len > BF_CFB_LEN ? iv_len : BF_CFB_LEN;
|
||||
mi = seed_len > bfs->cfb_len ? seed_len : bfs->cfb_len;
|
||||
for (i = 0; i < mi; i++)
|
||||
cfb_buffer[i % BF_CFB_LEN] ^= iv[i % iv_len];
|
||||
bfs->cfb_buffer[i % bfs->cfb_len] ^= seed[i % seed_len];
|
||||
}
|
||||
}
|
||||
|
||||
#define BF_CFB_UPDATE(c) { \
|
||||
cfb_buffer[update_offset] ^= (char_u)c; \
|
||||
if (++update_offset == BF_CFB_LEN) \
|
||||
update_offset = 0; \
|
||||
#define BF_CFB_UPDATE(bfs, c) { \
|
||||
bfs->cfb_buffer[bfs->update_offset] ^= (char_u)c; \
|
||||
if (++bfs->update_offset == bfs->cfb_len) \
|
||||
bfs->update_offset = 0; \
|
||||
}
|
||||
|
||||
#define BF_RANBYTE(t) { \
|
||||
if ((randbyte_offset & BF_BLOCK_MASK) == 0) \
|
||||
bf_e_cblock(&cfb_buffer[randbyte_offset]); \
|
||||
t = cfb_buffer[randbyte_offset]; \
|
||||
if (++randbyte_offset == BF_CFB_LEN) \
|
||||
randbyte_offset = 0; \
|
||||
#define BF_RANBYTE(bfs, t) { \
|
||||
if ((bfs->randbyte_offset & BF_BLOCK_MASK) == 0) \
|
||||
bf_e_cblock(bfs, &(bfs->cfb_buffer[bfs->randbyte_offset])); \
|
||||
t = bfs->cfb_buffer[bfs->randbyte_offset]; \
|
||||
if (++bfs->randbyte_offset == bfs->cfb_len) \
|
||||
bfs->randbyte_offset = 0; \
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -598,90 +602,69 @@ bf_cfb_init(iv, iv_len)
|
||||
* "from" and "to" can be equal to encrypt in place.
|
||||
*/
|
||||
void
|
||||
bf_crypt_encode(from, len, to)
|
||||
crypt_blowfish_encode(state, from, len, to)
|
||||
cryptstate_T *state;
|
||||
char_u *from;
|
||||
size_t len;
|
||||
char_u *to;
|
||||
{
|
||||
bf_state_T *bfs = state->method_state;
|
||||
size_t i;
|
||||
int ztemp, t;
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
ztemp = from[i];
|
||||
BF_RANBYTE(t);
|
||||
BF_CFB_UPDATE(ztemp);
|
||||
BF_RANBYTE(bfs, t);
|
||||
BF_CFB_UPDATE(bfs, ztemp);
|
||||
to[i] = t ^ ztemp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrypt "ptr[len]" in place.
|
||||
* Decrypt "from[len]" into "to[len]".
|
||||
*/
|
||||
void
|
||||
bf_crypt_decode(ptr, len)
|
||||
char_u *ptr;
|
||||
long len;
|
||||
crypt_blowfish_decode(state, from, len, to)
|
||||
cryptstate_T *state;
|
||||
char_u *from;
|
||||
size_t len;
|
||||
char_u *to;
|
||||
{
|
||||
char_u *p;
|
||||
bf_state_T *bfs = state->method_state;
|
||||
size_t i;
|
||||
int t;
|
||||
|
||||
for (p = ptr; p < ptr + len; ++p)
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
BF_RANBYTE(t);
|
||||
*p ^= t;
|
||||
BF_CFB_UPDATE(*p);
|
||||
BF_RANBYTE(bfs, t);
|
||||
to[i] = from[i] ^ t;
|
||||
BF_CFB_UPDATE(bfs, to[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the encryption keys and the random header according to
|
||||
* the given password.
|
||||
*/
|
||||
void
|
||||
bf_crypt_init_keys(passwd)
|
||||
char_u *passwd; /* password string with which to modify keys */
|
||||
crypt_blowfish_init(state, key, salt, salt_len, seed, seed_len)
|
||||
cryptstate_T *state;
|
||||
char_u* key;
|
||||
char_u* salt;
|
||||
int salt_len;
|
||||
char_u* seed;
|
||||
int seed_len;
|
||||
{
|
||||
char_u *p;
|
||||
bf_state_T *bfs = (bf_state_T *)alloc_clear(sizeof(bf_state_T));
|
||||
|
||||
for (p = passwd; *p != NUL; ++p)
|
||||
{
|
||||
BF_CFB_UPDATE(*p);
|
||||
}
|
||||
}
|
||||
state->method_state = bfs;
|
||||
|
||||
static int save_randbyte_offset;
|
||||
static int save_update_offset;
|
||||
static char_u save_cfb_buffer[BF_CFB_LEN];
|
||||
static UINT32_T save_pax[18];
|
||||
static UINT32_T save_sbx[4][256];
|
||||
/* "blowfish" uses a 64 byte buffer, causing it to repeat 8 byte groups 8
|
||||
* times. "blowfish2" uses a 8 byte buffer to avoid repeating. */
|
||||
bfs->cfb_len = state->method_nr == CRYPT_M_BF ? BF_MAX_CFB_LEN : BF_BLOCK;
|
||||
|
||||
/*
|
||||
* Save the current crypt state. Can only be used once before
|
||||
* bf_crypt_restore().
|
||||
*/
|
||||
void
|
||||
bf_crypt_save()
|
||||
{
|
||||
save_randbyte_offset = randbyte_offset;
|
||||
save_update_offset = update_offset;
|
||||
mch_memmove(save_cfb_buffer, cfb_buffer, BF_CFB_LEN);
|
||||
mch_memmove(save_pax, pax, 4 * 18);
|
||||
mch_memmove(save_sbx, sbx, 4 * 4 * 256);
|
||||
}
|
||||
if (blowfish_self_test() == FAIL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Restore the current crypt state. Can only be used after
|
||||
* bf_crypt_save().
|
||||
*/
|
||||
void
|
||||
bf_crypt_restore()
|
||||
{
|
||||
randbyte_offset = save_randbyte_offset;
|
||||
update_offset = save_update_offset;
|
||||
mch_memmove(cfb_buffer, save_cfb_buffer, BF_CFB_LEN);
|
||||
mch_memmove(pax, save_pax, 4 * 18);
|
||||
mch_memmove(sbx, save_sbx, 4 * 4 * 256);
|
||||
bf_key_init(bfs, key, salt, salt_len);
|
||||
bf_cfb_init(bfs, seed, seed_len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user