From 1e10661a513b3468a79578b36b48d70a5ee07fcd Mon Sep 17 00:00:00 2001 From: Max Lv Date: Tue, 22 Sep 2015 16:52:44 +0800 Subject: [PATCH 1/2] Refine the authentication for true CCA --- src/encrypt.c | 86 ++++++++++++++++++++++++++++++++------------------- src/encrypt.h | 13 ++++++-- src/local.c | 9 +++--- src/local.h | 3 -- src/redir.c | 2 +- src/redir.h | 3 -- src/server.c | 33 ++++++++++++-------- src/server.h | 3 +- src/tunnel.c | 2 +- src/tunnel.h | 3 -- 10 files changed, 92 insertions(+), 65 deletions(-) diff --git a/src/encrypt.c b/src/encrypt.c index 3f397f2a..6aab823d 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -1478,53 +1478,77 @@ int enc_init(const char *pass, const char *method) return m; } -int ss_check_hash(char *buf, ssize_t *buf_len, char *hash_buf, ssize_t *hash_idx) +int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf_size) { - int i, j; + int i, j, k; + char *buf = *buf_ptr; ssize_t blen = *buf_len; - ssize_t cidx = *hash_idx; + uint32_t cidx = chunk->idx; - for (i = 0, j = 0; i < blen; i++) { - if (cidx < HASH_BUF_LEN) { - buf[j] = buf[i]; - j++; + if (chunk->buf == NULL) { + chunk->buf = (char *)malloc(buf_size); + chunk->len = buf_size - AUTH_BYTES; + } + + int size = max(chunk->len + blen, buf_size); + if (buf_size < size) { + buf = realloc(buf, size); + } + + for (i = 0, j = 0, k = 0; i < blen; i++) { + + chunk->buf[cidx++] = buf[k++]; + + if (cidx == CLEN_BYTES) { + uint16_t clen = ntohs(*((uint16_t *)chunk->buf)); + + if (chunk->len < clen) { + chunk->buf = realloc(chunk->buf, clen + AUTH_BYTES); + } + chunk->len = clen; } - hash_buf[cidx] = buf[i]; - cidx++; - if (cidx == HASH_BUF_LEN + HASH_BYTES) { - uint8_t hash[HASH_BYTES]; - crypto_generichash(hash, HASH_BYTES, (uint8_t *)hash_buf, HASH_BUF_LEN, NULL, 0); - if (memcmp(hash, hash_buf + HASH_BUF_LEN, HASH_BYTES) != 0) return 0; + + if (cidx == chunk->len + AUTH_BYTES) { + // Compare hash + uint8_t *hash = (uint8_t *)malloc(chunk->len); + crypto_generichash(hash, HASH_BYTES, (uint8_t *)chunk->buf + AUTH_BYTES, chunk->len, NULL, 0); + + if (memcmp(hash, chunk->buf + CLEN_BYTES, HASH_BYTES) != 0) return 0; + + // Copy chunk back to buffer + memmove(buf + j + chunk->len, buf + k, blen - i - 1); + memcpy(buf + j, chunk->buf + AUTH_BYTES, chunk->len); + + // Reset the base offset + j += chunk->len; + k = j; cidx = 0; } } + + *buf_ptr = buf; *buf_len = j; - *hash_idx = cidx; + chunk->idx = cidx; return 1; } -char *ss_gen_hash(char *buf, ssize_t *buf_len, char *hash_buf, ssize_t *hash_idx, int buf_size) +char *ss_gen_hash(char *buf, ssize_t *buf_len, int buf_size) { - int i, j; ssize_t blen = *buf_len; - ssize_t cidx = *hash_idx; - int size = max((blen / HASH_BUF_LEN + 1) * HASH_BYTES + blen, buf_size); + int size = max(AUTH_BYTES + blen, buf_size); if (buf_size < size) { buf = realloc(buf, size); } - for (i = 0, j = 0; i < blen; i++, j++) { - if (cidx == HASH_BUF_LEN) { - uint8_t hash[HASH_BYTES]; - crypto_generichash(hash, HASH_BYTES, (uint8_t *)hash_buf, HASH_BUF_LEN, NULL, 0); - memmove(buf + j + HASH_BYTES, buf + j, blen - i); - memcpy(buf + j, hash, HASH_BYTES); - j += HASH_BYTES; cidx = 0; - } - hash_buf[cidx] = buf[j]; - cidx++; - } - *buf_len = j; - *hash_idx = cidx; + + uint16_t chunk_len = htons((uint16_t)blen); + uint8_t hash[HASH_BYTES]; + crypto_generichash(hash, HASH_BYTES, (uint8_t *)buf, blen, NULL, 0); + + memmove(buf + AUTH_BYTES, buf, blen); + memcpy(buf + CLEN_BYTES, hash, HASH_BYTES); + memcpy(buf, &chunk_len, CLEN_BYTES); + + *buf_len = blen + AUTH_BYTES; return buf; } diff --git a/src/encrypt.h b/src/encrypt.h index 3210230e..43bc6552 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -146,12 +146,19 @@ typedef struct { #define ONETIMEAUTH_FLAG 0x10 #define ADDRTYPE_MASK 0xF -#define HASH_BUF_LEN 128 #define HASH_BYTES 4 +#define CLEN_BYTES 2 +#define AUTH_BYTES (HASH_BYTES + CLEN_BYTES) #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) +struct chunk { + uint32_t idx; + uint32_t len; + char *buf; +}; + struct enc_ctx { uint8_t init; uint64_t counter; @@ -173,7 +180,7 @@ unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); int ss_onetimeauth(char *auth, char *msg, int msg_len, struct enc_ctx *ctx); int ss_onetimeauth_verify(char *auth, char *msg, int msg_len, struct enc_ctx *ctx); -int ss_check_hash(char *buf, ssize_t *buf_len, char *hash_buf, ssize_t *hash_idx); -char * ss_gen_hash(char *buf, ssize_t *buf_len, char *hash_buf, ssize_t *hash_idx, int buf_size); +int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf_size); +char *ss_gen_hash(char *buf, ssize_t *buf_len, int buf_size); #endif // _ENCRYPT_H diff --git a/src/local.c b/src/local.c index c3b7a7b3..4bc6ee34 100644 --- a/src/local.c +++ b/src/local.c @@ -245,7 +245,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (!remote->direct && remote->send_ctx->connected && auth) { - remote->buf = ss_gen_hash(remote->buf, &r, remote->hash_buf, &remote->hash_idx, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); } // insert shadowsocks header @@ -483,11 +483,10 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) memcpy(remote->buf, ss_addr_to_send, addr_len); - if (auth) { - buf = ss_gen_hash(buf, &r, remote->hash_buf, &remote->hash_idx, BUF_SIZE); - } - if (r > 0) { + if (auth) { + buf = ss_gen_hash(buf, &r, BUF_SIZE); + } memcpy(remote->buf + addr_len, buf, r); } r += addr_len; diff --git a/src/local.h b/src/local.h index 6972f4f0..27588910 100644 --- a/src/local.h +++ b/src/local.h @@ -81,9 +81,6 @@ struct remote { struct server *server; struct sockaddr_storage addr; int addr_len; - - ssize_t hash_idx; - char hash_buf[HASH_BUF_LEN]; }; #endif // _LOCAL_H diff --git a/src/redir.c b/src/redir.c index 266fd031..38b2ab01 100644 --- a/src/redir.c +++ b/src/redir.c @@ -185,7 +185,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (auth) { - remote->buf = ss_gen_hash(remote->buf, &r, remote->hash_buf, &remote->hash_idx, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); } remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); diff --git a/src/redir.h b/src/redir.h index ec5e32ce..57a7f57c 100644 --- a/src/redir.h +++ b/src/redir.h @@ -70,9 +70,6 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; - - ssize_t hash_idx; - char hash_buf[HASH_BUF_LEN]; }; #endif // _LOCAL_H diff --git a/src/server.c b/src/server.c index 31c5fc4b..72940335 100644 --- a/src/server.c +++ b/src/server.c @@ -495,8 +495,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) // handshake and transmit data if (server->stage == 5) { - if (server->auth - && !ss_check_hash(remote->buf, &r, server->hash_buf, &server->hash_idx)) { + if (server->auth && !ss_check_hash(&remote->buf, &r, server->chunk, BUF_SIZE)) { LOGE("hash error"); report_addr(server->fd); close_and_free_server(EV_A_ server); @@ -541,13 +540,13 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) */ /* - * Shadowsocks TCP Request Payload CRC (Optional, no hash check for response's payload): + * Shadowsocks TCP Request Chunk (Optional, no hash check for response's payload): * - * +------+---------+------+---------+------+ - * | DATA | BLAKE2b | DATA | BLAKE2b | ... - * +------+---------+------+---------+------+ - * | 128 | 4 | 128 | 4 | ... - * +------+---------+------+---------+------+ + * +------+---------+-------------+------+ + * | LEN | BLAKE2b | DATA | ... + * +------+---------+-------------+------+ + * | 2 | 4 | Variable | ... + * +------+---------+-------------+------+ */ int offset = 0; @@ -680,11 +679,10 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) // XXX: should handle buffer carefully if (r > offset) { server->buf_len = r - offset; - server->buf_idx = offset; + memmove(server->buf, server->buf + offset, server->buf_len); } - if (server->auth - && !ss_check_hash(server->buf + server->buf_idx, &server->buf_len, server->hash_buf, &server->hash_idx)) { + if (server->auth && !ss_check_hash(&server->buf, &server->buf_len, server->chunk, BUF_SIZE)) { LOGE("hash error"); report_addr(server->fd); close_and_free_server(EV_A_ server); @@ -704,8 +702,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) // XXX: should handle buffer carefully if (server->buf_len > 0) { - memcpy(remote->buf, server->buf + server->buf_idx, - server->buf_len); + memcpy(remote->buf, server->buf + server->buf_idx, server->buf_len); remote->buf_len = server->buf_len; remote->buf_idx = 0; server->buf_len = 0; @@ -1123,6 +1120,9 @@ static struct server * new_server(int fd, struct listen_ctx *listener) server->buf_idx = 0; server->remote = NULL; + server->chunk = (struct chunk *)malloc(sizeof(struct chunk)); + memset(server->chunk, 0, sizeof(struct chunk)); + cork_dllist_add(&connections, &server->entries); return server; @@ -1132,6 +1132,13 @@ static void free_server(struct server *server) { cork_dllist_remove(&server->entries); + if (server->chunk != NULL) { + if (server->chunk->buf != NULL) { + free(server->chunk->buf); + } + free(server->chunk); + server->chunk = NULL; + } if (server->remote != NULL) { server->remote->server = NULL; } diff --git a/src/server.h b/src/server.h index 2114605d..a2fcd64c 100644 --- a/src/server.h +++ b/src/server.h @@ -57,8 +57,7 @@ struct server { char *buf; // server send from, remote recv into int auth; - ssize_t hash_idx; - char hash_buf[HASH_BUF_LEN + HASH_BYTES]; // 2 bytes for HASH16 + struct chunk *chunk; struct enc_ctx *e_ctx; struct enc_ctx *d_ctx; diff --git a/src/tunnel.c b/src/tunnel.c index 00e26eda..b2fff704 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -197,7 +197,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (auth) { - remote->buf = ss_gen_hash(remote->buf, &r, remote->hash_buf, &remote->hash_idx, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); } remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); diff --git a/src/tunnel.h b/src/tunnel.h index 184be53c..3ac4317e 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -74,9 +74,6 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; - - ssize_t hash_idx; - char hash_buf[HASH_BUF_LEN]; }; #endif // _TUNNEL_H From 441ac4554701a6f108b7e430386325584ae26098 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Tue, 22 Sep 2015 17:35:31 +0800 Subject: [PATCH 2/2] Use one time key for BLAKE2b --- src/encrypt.c | 17 ++++++++++++++--- src/encrypt.h | 3 ++- src/local.c | 4 ++-- src/local.h | 1 + src/redir.c | 2 +- src/redir.h | 1 + src/server.c | 2 ++ src/tunnel.c | 2 +- src/tunnel.h | 1 + 9 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/encrypt.c b/src/encrypt.c index 6aab823d..301dfca4 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -1511,7 +1511,12 @@ int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf if (cidx == chunk->len + AUTH_BYTES) { // Compare hash uint8_t *hash = (uint8_t *)malloc(chunk->len); - crypto_generichash(hash, HASH_BYTES, (uint8_t *)chunk->buf + AUTH_BYTES, chunk->len, NULL, 0); + uint8_t key[MAX_KEY_LENGTH + sizeof(uint32_t)]; + + memcpy(key, enc_key, enc_key_len); + memcpy(key + enc_key_len, &chunk->counter, sizeof(uint32_t)); + crypto_generichash(hash, HASH_BYTES, (uint8_t *)chunk->buf + AUTH_BYTES, chunk->len, + key, enc_key_len + sizeof(uint32_t)); if (memcmp(hash, chunk->buf + CLEN_BYTES, HASH_BYTES) != 0) return 0; @@ -1523,6 +1528,7 @@ int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf j += chunk->len; k = j; cidx = 0; + chunk->counter++; } } @@ -1532,7 +1538,7 @@ int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf return 1; } -char *ss_gen_hash(char *buf, ssize_t *buf_len, int buf_size) +char *ss_gen_hash(char *buf, ssize_t *buf_len, uint32_t *counter, int buf_size) { ssize_t blen = *buf_len; int size = max(AUTH_BYTES + blen, buf_size); @@ -1543,12 +1549,17 @@ char *ss_gen_hash(char *buf, ssize_t *buf_len, int buf_size) uint16_t chunk_len = htons((uint16_t)blen); uint8_t hash[HASH_BYTES]; - crypto_generichash(hash, HASH_BYTES, (uint8_t *)buf, blen, NULL, 0); + uint8_t key[MAX_KEY_LENGTH + sizeof(uint32_t)]; + + memcpy(key, enc_key, enc_key_len); + memcpy(key + enc_key_len, counter, sizeof(uint32_t)); + crypto_generichash(hash, HASH_BYTES, (uint8_t *)buf, blen, key, enc_key_len + sizeof(uint32_t)); memmove(buf + AUTH_BYTES, buf, blen); memcpy(buf + CLEN_BYTES, hash, HASH_BYTES); memcpy(buf, &chunk_len, CLEN_BYTES); *buf_len = blen + AUTH_BYTES; + *counter = *counter + 1; return buf; } diff --git a/src/encrypt.h b/src/encrypt.h index 43bc6552..df148af5 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -156,6 +156,7 @@ typedef struct { struct chunk { uint32_t idx; uint32_t len; + uint32_t counter; char *buf; }; @@ -181,6 +182,6 @@ int ss_onetimeauth(char *auth, char *msg, int msg_len, struct enc_ctx *ctx); int ss_onetimeauth_verify(char *auth, char *msg, int msg_len, struct enc_ctx *ctx); int ss_check_hash(char **buf_ptr, ssize_t *buf_len, struct chunk *chunk, int buf_size); -char *ss_gen_hash(char *buf, ssize_t *buf_len, int buf_size); +char *ss_gen_hash(char *buf, ssize_t *buf_len, uint32_t *counter, int buf_size); #endif // _ENCRYPT_H diff --git a/src/local.c b/src/local.c index 4bc6ee34..016356b9 100644 --- a/src/local.c +++ b/src/local.c @@ -245,7 +245,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (!remote->direct && remote->send_ctx->connected && auth) { - remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, &remote->counter, BUF_SIZE); } // insert shadowsocks header @@ -485,7 +485,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) if (r > 0) { if (auth) { - buf = ss_gen_hash(buf, &r, BUF_SIZE); + buf = ss_gen_hash(buf, &r, &remote->counter, BUF_SIZE); } memcpy(remote->buf + addr_len, buf, r); } diff --git a/src/local.h b/src/local.h index 27588910..edbfa9c5 100644 --- a/src/local.h +++ b/src/local.h @@ -81,6 +81,7 @@ struct remote { struct server *server; struct sockaddr_storage addr; int addr_len; + uint32_t counter; }; #endif // _LOCAL_H diff --git a/src/redir.c b/src/redir.c index 38b2ab01..8c4427ae 100644 --- a/src/redir.c +++ b/src/redir.c @@ -185,7 +185,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (auth) { - remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, &remote->counter, BUF_SIZE); } remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); diff --git a/src/redir.h b/src/redir.h index 57a7f57c..398f5700 100644 --- a/src/redir.h +++ b/src/redir.h @@ -70,6 +70,7 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; + uint32_t counter; }; #endif // _LOCAL_H diff --git a/src/server.c b/src/server.c index 72940335..11de6f99 100644 --- a/src/server.c +++ b/src/server.c @@ -547,6 +547,8 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) * +------+---------+-------------+------+ * | 2 | 4 | Variable | ... * +------+---------+-------------+------+ + * + * The key of BLAKE2b is (KEY + CHUNK ID) */ int offset = 0; diff --git a/src/tunnel.c b/src/tunnel.c index b2fff704..ef575355 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -197,7 +197,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) } if (auth) { - remote->buf = ss_gen_hash(remote->buf, &r, BUF_SIZE); + remote->buf = ss_gen_hash(remote->buf, &r, &remote->counter, BUF_SIZE); } remote->buf = ss_encrypt(BUF_SIZE, remote->buf, &r, server->e_ctx); diff --git a/src/tunnel.h b/src/tunnel.h index 3ac4317e..16f2b0b3 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -74,6 +74,7 @@ struct remote { struct remote_ctx *recv_ctx; struct remote_ctx *send_ctx; struct server *server; + uint32_t counter; }; #endif // _TUNNEL_H