summaryrefslogtreecommitdiffstats
path: root/src/blowfish.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2014-08-10 13:38:34 +0200
committerBram Moolenaar <Bram@vim.org>2014-08-10 13:38:34 +0200
commit8f4ac01544b44bdd906d241e4f203de7496e5ac8 (patch)
tree52ee7ff7368d7953f2baa3d7d015c539b11a345e /src/blowfish.c
parent0106b4b89127b043eddf711c750364b487deb794 (diff)
updated for version 7.4.399v7.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)
Diffstat (limited to 'src/blowfish.c')
-rw-r--r--src/blowfish.c307
1 files changed, 145 insertions, 162 deletions
diff --git a/src/blowfish.c b/src/blowfish.c
index 3d9ba5566a..6bf2482f0e 100644
--- a/src/blowfish.c
+++ b/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;
-
- 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];
+ 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 ^= 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);
}
/*