From 2e7479525f94c7fc0bb2c0d2a6e0ab367df57d10 Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Fri, 16 Jan 2015 00:13:02 +0100 Subject: [PATCH] turned crypto_secretbox nonces into counters, thereby implementing counter mode (CTR mode). we use variable size counters, max counter size is 184 bit, max implemented currently are 64 bit, multiply by 32kb and you get the maximum file size supported by pcp encryption --- TODO | 8 +++ include/pcp/crypto.h | 5 ++ include/pcp/structs.h | 4 -- libpcp/crypto.c | 152 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 163 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index d56f835..c8f9078 100644 --- a/TODO +++ b/TODO @@ -48,3 +48,11 @@ Program received signal SIGSEGV, Segmentation fault. #2 0x00000000004084ff in ptx_clean (ptx=0x801017040) at context.c:52 #3 0x0000000000402557 in main (argc=Variable "argc" is not available. ) at pcp.c:593 + + + + +_havenacl sometimes "no", even if old version is installed, followed by: + +mem.c:51: warning: implicit declaration of function 'sodium_malloc' +mem.c:51: warning: initialization makes pointer from integer without a cast diff --git a/include/pcp/crypto.h b/include/pcp/crypto.h index e6b2717..07ea2bc 100644 --- a/include/pcp/crypto.h +++ b/include/pcp/crypto.h @@ -333,6 +333,11 @@ int pcp_sodium_verify_mac(byte **cleartext, pcp_rec_t *pcp_rec_new(byte *cipher, size_t clen, pcp_key_t *secret, pcp_pubkey_t *pub); void pcp_rec_free(pcp_rec_t *r); +/* CTR mode helpers */ +byte *_gen_ctr_nonce(uint64_t ctr); +uint64_t _get_nonce_ctr(byte *nonce); + + #endif /* _HAVE_PCP_CRYPTO_H */ /**@}*/ diff --git a/include/pcp/structs.h b/include/pcp/structs.h index d70c3be..d31085d 100644 --- a/include/pcp/structs.h +++ b/include/pcp/structs.h @@ -377,8 +377,4 @@ struct _pcp_ks_bundle_t { }; typedef struct _pcp_ks_bundle_t pcp_ks_bundle_t; - - - - #endif //_HAVE_PCP_STRUCTS_H diff --git a/libpcp/crypto.c b/libpcp/crypto.c index 7311d11..269fa3d 100644 --- a/libpcp/crypto.c +++ b/libpcp/crypto.c @@ -399,6 +399,7 @@ size_t pcp_encrypt_stream_sym(PCPCTX *ptx, Pcpstream *in, Pcpstream *out, byte * crypto_generichash_state *st = NULL; byte *hash = NULL; byte head[1]; + uint64_t ctr = 1; if(in->is_buffer) { if(buffer_size(in->b) == 0) { @@ -441,7 +442,9 @@ size_t pcp_encrypt_stream_sym(PCPCTX *ptx, Pcpstream *in, Pcpstream *out, byte * cur_bufsize = ps_read(in, in_buf, PCP_BLOCK_SIZE); if(cur_bufsize <= 0) break; - buf_nonce = pcp_gennonce(); + + /* generate nonce and put current buffer counter into it */ + buf_nonce = _gen_ctr_nonce(ctr++); #ifdef PCP_CBC /* apply IV to current clear */ @@ -513,7 +516,9 @@ size_t pcp_decrypt_stream_sym(PCPCTX *ptx, Pcpstream *in, Pcpstream* out, byte * size_t out_size, cur_bufsize, es; size_t ciphersize = (PCP_BLOCK_SIZE_IN) - crypto_secretbox_NONCEBYTES; byte *in_buf = NULL; - + uint64_t ctr, pastctr; + pastctr = 0; + buf_nonce = ucmalloc(crypto_secretbox_NONCEBYTES); buf_cipher = ucmalloc(ciphersize); out_size = 0; @@ -564,6 +569,16 @@ size_t pcp_decrypt_stream_sym(PCPCTX *ptx, Pcpstream *in, Pcpstream* out, byte * memcpy(buf_nonce, in_buf, crypto_secretbox_NONCEBYTES); memcpy(buf_cipher, &in_buf[crypto_secretbox_NONCEBYTES], ciphersize); + /* extract counter from nonce and check if it is in line with previous one + TODO: save unordered buffers to disk and continue writing to out if + buffers are in order again */ + ctr = _get_nonce_ctr(buf_nonce); + if(ctr -1 != pastctr) { + fatal(ptx, "Mangled packet order, bailing out (got: %ld, expected: %ld)!\n", ctr, pastctr+1); + out_size = 0; + break; + } + pastctr = ctr; es = pcp_sodium_verify_mac(&buf_clear, buf_cipher, ciphersize, buf_nonce, symkey); #ifdef PCP_CBC @@ -684,3 +699,136 @@ void pcp_rec_free(pcp_rec_t *r) { free(r); } +/* + extract buffer counter from given nonce. + + the first byte denotes the size of the + counter in bytes (1,2,4,...). we extract + an integer of the given size from the + bytes afterwards and cast it into an uint64_t, + convert back to native endianes. + */ +uint64_t _get_nonce_ctr(byte *nonce) { + uint64_t ctr; + uint8_t i = nonce[0]; + uint16_t m16; + uint32_t m32; + + switch(i) { + case 1: + ctr = nonce[1]; + break; + case 2: + memcpy(&m16, &nonce[1], 2); + ctr = be16toh(m16); + break; + case 4: + memcpy(&m32, &nonce[1], 4); + ctr = be32toh(m32); + break; + case 8: + memcpy(&ctr, &nonce[1], 8); + ctr = be64toh(ctr); + break; + } + + return ctr; +} + +/* + generate a new random nonce and put the + given buffer counter into the front of it. + + the counter has a variable size, starting at + 1 byte. if the counter overflows, we double + the counter written to the nonce (and modify + the size indicator accordingly). it will be + converted to big endian if larger than 1 byte. + + since the nonce has a size of 24 bytes and we use + the first byte as the size indicator, the maximum + possible counter size would be a 184 bit integer. + due to the use of 32kb input buffers, encryption + is limited to files with a maximum size of + 784.637.716.923.335.095.479.473.677.900.958.302.012.794.430.558.004.314.112.000 bytes. + + sapperlot, did I really put this number in here? + + back to reality: while this protocol allows + 184 bit counters, my current implementation + is limited to 64 bit counters, which results + in a maximum file size of around 590 zetta bytes + or 590 sextillion bytes. 'nough time to learn + big integer crunching... + + why? + + we use a varable size counter, because in this + way we avoid nonces with an insufficient amount + of randomness. compare these two examples: + + 0101da63b0a8b1d4e7c32b367b7deedb8032604ed45bdf34 + + vs: + + 01000000000000000183475b2969bbb20a03ff41e8002659 + + the first nonce uses a variable sized counter + (1 byte) with the value 1 and the latter a fixed + size 64bit counter also with the value 1. as you + can see, it only contains 15 random bytes, 7 bytes + are just zeros. + + also, I assume that such large inputs will + be very rare, so in almost all cases we would end + up with just 15 or 16 random bytes. that's beyond + the idea of nacl's crypto_box, unacceptable and + doesn't look the way I'm accustomed. hence variable + size counters. + + returns the counter nonce. + */ +byte *_gen_ctr_nonce(uint64_t ctr) { + uint8_t m8 = -1; + uint16_t m16 = -1; + uint32_t m32 = -1; + uint64_t m64 = -1; + uint8_t i = 1; + + byte *nonce = pcp_gennonce(); + + if(ctr > m32) { + i = 8; + m64 = htobe64(ctr); + memcpy(&nonce[1], &m64, 8); + } + else if(ctr < m32 && ctr > m16) { + i = 4; + m32 = htobe32(ctr); + memcpy(&nonce[1], &m32, 4); + } + else if(ctr < m16 && ctr > m8) { + i = 2; + m16 = htobe16(ctr); + memcpy(&nonce[1], &m16, 2); + } + else { + i = 1; + nonce[1] = ctr; + } + nonce[0] = i; + + + m64 = htobe64(ctr); + memcpy(&nonce[1], &m64, 8); + + _dump("nonce", nonce, 24); + + return nonce; +} + +/* +TODO: how to go past 64 bits: +http://mrob.com/pub/math/int128.c.txt +http://locklessinc.com/articles/256bit_arithmetic/ +*/