You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
697 lines
21 KiB
697 lines
21 KiB
/*
|
|
* stream.c - Manage stream ciphers
|
|
*
|
|
* Copyright (C) 2013 - 2018, Max Lv <max.c.lv@gmail.com>
|
|
*
|
|
* This file is part of the shadowsocks-libev.
|
|
*
|
|
* shadowsocks-libev is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* shadowsocks-libev is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with shadowsocks-libev; see the file COPYING. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <mbedtls/entropy.h>
|
|
#include <mbedtls/ctr_drbg.h>
|
|
#include <mbedtls/version.h>
|
|
#define CIPHER_UNSUPPORTED "unsupported"
|
|
|
|
#include <sodium.h>
|
|
|
|
#include "ppbloom.h"
|
|
#include "stream.h"
|
|
#include "utils.h"
|
|
|
|
#define SODIUM_BLOCK_SIZE 64
|
|
|
|
/*
|
|
* Spec: http://shadowsocks.org/en/spec/Stream-Ciphers.html
|
|
*
|
|
* Stream ciphers provide only confidentiality. Data integrity and authenticity is not guaranteed. Users should use AEAD
|
|
* ciphers whenever possible.
|
|
*
|
|
* Stream Encryption/Decryption
|
|
*
|
|
* Stream_encrypt is a function that takes a secret key, an initialization vector, a message, and produces a ciphertext
|
|
* with the same length as the message.
|
|
*
|
|
* Stream_encrypt(key, IV, message) => ciphertext
|
|
*
|
|
* Stream_decrypt is a function that takes a secret key, an initializaiton vector, a ciphertext, and produces the
|
|
* original message.
|
|
*
|
|
* Stream_decrypt(key, IV, ciphertext) => message
|
|
*
|
|
* TCP
|
|
*
|
|
* A stream cipher encrypted TCP stream starts with a randomly generated initializaiton vector, followed by encrypted
|
|
* payload data.
|
|
*
|
|
* [IV][encrypted payload]
|
|
*
|
|
* UDP
|
|
*
|
|
* A stream cipher encrypted UDP packet has the following structure:
|
|
*
|
|
* [IV][encrypted payload]
|
|
*
|
|
* Each UDP packet is encrypted/decrypted independently with a randomly generated initialization vector.
|
|
*
|
|
*/
|
|
|
|
#define NONE -1
|
|
#define TABLE 0
|
|
#define RC4 1
|
|
#define RC4_MD5 2
|
|
#define AES_128_CFB 3
|
|
#define AES_192_CFB 4
|
|
#define AES_256_CFB 5
|
|
#define AES_128_CTR 6
|
|
#define AES_192_CTR 7
|
|
#define AES_256_CTR 8
|
|
#define BF_CFB 9
|
|
#define CAMELLIA_128_CFB 10
|
|
#define CAMELLIA_192_CFB 11
|
|
#define CAMELLIA_256_CFB 12
|
|
#define CAST5_CFB 13
|
|
#define DES_CFB 14
|
|
#define IDEA_CFB 15
|
|
#define RC2_CFB 16
|
|
#define SEED_CFB 17
|
|
#define SALSA20 18
|
|
#define CHACHA20 19
|
|
#define CHACHA20IETF 20
|
|
|
|
const char *supported_stream_ciphers[STREAM_CIPHER_NUM] = {
|
|
"table",
|
|
"rc4",
|
|
"rc4-md5",
|
|
"aes-128-cfb",
|
|
"aes-192-cfb",
|
|
"aes-256-cfb",
|
|
"aes-128-ctr",
|
|
"aes-192-ctr",
|
|
"aes-256-ctr",
|
|
"bf-cfb",
|
|
"camellia-128-cfb",
|
|
"camellia-192-cfb",
|
|
"camellia-256-cfb",
|
|
"cast5-cfb",
|
|
"des-cfb",
|
|
"idea-cfb",
|
|
"rc2-cfb",
|
|
"seed-cfb",
|
|
"salsa20",
|
|
"chacha20",
|
|
"chacha20-ietf"
|
|
};
|
|
|
|
static const char *supported_stream_ciphers_mbedtls[STREAM_CIPHER_NUM] = {
|
|
"table",
|
|
"ARC4-128",
|
|
"ARC4-128",
|
|
"AES-128-CFB128",
|
|
"AES-192-CFB128",
|
|
"AES-256-CFB128",
|
|
"AES-128-CTR",
|
|
"AES-192-CTR",
|
|
"AES-256-CTR",
|
|
"BLOWFISH-CFB64",
|
|
"CAMELLIA-128-CFB128",
|
|
"CAMELLIA-192-CFB128",
|
|
"CAMELLIA-256-CFB128",
|
|
CIPHER_UNSUPPORTED,
|
|
CIPHER_UNSUPPORTED,
|
|
CIPHER_UNSUPPORTED,
|
|
CIPHER_UNSUPPORTED,
|
|
CIPHER_UNSUPPORTED,
|
|
"salsa20",
|
|
"chacha20",
|
|
"chacha20-ietf"
|
|
};
|
|
|
|
static const int supported_stream_ciphers_nonce_size[STREAM_CIPHER_NUM] = {
|
|
0, 0, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8, 12
|
|
};
|
|
|
|
static const int supported_stream_ciphers_key_size[STREAM_CIPHER_NUM] = {
|
|
0, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32, 32
|
|
};
|
|
|
|
static int
|
|
crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen,
|
|
const uint8_t *n, uint64_t ic, const uint8_t *k,
|
|
int method)
|
|
{
|
|
switch (method) {
|
|
case SALSA20:
|
|
return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k);
|
|
case CHACHA20:
|
|
return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k);
|
|
case CHACHA20IETF:
|
|
return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k);
|
|
}
|
|
// always return 0
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cipher_nonce_size(const cipher_t *cipher)
|
|
{
|
|
if (cipher == NULL) {
|
|
return 0;
|
|
}
|
|
return cipher->info->iv_size;
|
|
}
|
|
|
|
int
|
|
cipher_key_size(const cipher_t *cipher)
|
|
{
|
|
/*
|
|
* Semi-API changes (technically public, morally prnonceate)
|
|
* Renamed a few headers to include _internal in the name. Those headers are
|
|
* not supposed to be included by users.
|
|
* Changed md_info_t into an opaque structure (use md_get_xxx() accessors).
|
|
* Changed pk_info_t into an opaque structure.
|
|
* Changed cipher_base_t into an opaque structure.
|
|
*/
|
|
if (cipher == NULL) {
|
|
return 0;
|
|
}
|
|
/* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */
|
|
return cipher->info->key_bitlen / 8;
|
|
}
|
|
|
|
const cipher_kt_t *
|
|
stream_get_cipher_type(int method)
|
|
{
|
|
if (method <= TABLE || method >= STREAM_CIPHER_NUM) {
|
|
LOGE("stream_get_cipher_type(): Illegal method");
|
|
return NULL;
|
|
}
|
|
|
|
if (method == RC4_MD5) {
|
|
method = RC4;
|
|
}
|
|
|
|
if (method >= SALSA20) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *ciphername = supported_stream_ciphers[method];
|
|
const char *mbedtlsname = supported_stream_ciphers_mbedtls[method];
|
|
if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) {
|
|
LOGE("Cipher %s currently is not supported by mbed TLS library",
|
|
ciphername);
|
|
return NULL;
|
|
}
|
|
return mbedtls_cipher_info_from_string(mbedtlsname);
|
|
}
|
|
|
|
void
|
|
stream_cipher_ctx_init(cipher_ctx_t *ctx, int method, int enc)
|
|
{
|
|
if (method <= TABLE || method >= STREAM_CIPHER_NUM) {
|
|
LOGE("stream_ctx_init(): Illegal method");
|
|
return;
|
|
}
|
|
|
|
if (method >= SALSA20) {
|
|
return;
|
|
}
|
|
|
|
const char *ciphername = supported_stream_ciphers[method];
|
|
const cipher_kt_t *cipher = stream_get_cipher_type(method);
|
|
|
|
ctx->evp = ss_malloc(sizeof(cipher_evp_t));
|
|
memset(ctx->evp, 0, sizeof(cipher_evp_t));
|
|
cipher_evp_t *evp = ctx->evp;
|
|
|
|
if (cipher == NULL) {
|
|
LOGE("Cipher %s not found in mbed TLS library", ciphername);
|
|
FATAL("Cannot initialize mbed TLS cipher");
|
|
}
|
|
mbedtls_cipher_init(evp);
|
|
if (mbedtls_cipher_setup(evp, cipher) != 0) {
|
|
FATAL("Cannot initialize mbed TLS cipher context");
|
|
}
|
|
}
|
|
|
|
void
|
|
stream_ctx_release(cipher_ctx_t *cipher_ctx)
|
|
{
|
|
if (cipher_ctx->chunk != NULL) {
|
|
bfree(cipher_ctx->chunk);
|
|
ss_free(cipher_ctx->chunk);
|
|
cipher_ctx->chunk = NULL;
|
|
}
|
|
|
|
if (cipher_ctx->cipher->method >= SALSA20) {
|
|
return;
|
|
}
|
|
|
|
mbedtls_cipher_free(cipher_ctx->evp);
|
|
ss_free(cipher_ctx->evp);
|
|
}
|
|
|
|
void
|
|
cipher_ctx_set_nonce(cipher_ctx_t *cipher_ctx, uint8_t *nonce, size_t nonce_len,
|
|
int enc)
|
|
{
|
|
const unsigned char *true_key;
|
|
|
|
cipher_t *cipher = cipher_ctx->cipher;
|
|
|
|
if (nonce == NULL) {
|
|
LOGE("cipher_ctx_set_nonce(): NONCE is null");
|
|
return;
|
|
}
|
|
|
|
if (cipher->method >= SALSA20) {
|
|
return;
|
|
}
|
|
|
|
if (cipher->method == RC4_MD5) {
|
|
unsigned char key_nonce[32];
|
|
memcpy(key_nonce, cipher->key, 16);
|
|
memcpy(key_nonce + 16, nonce, 16);
|
|
true_key = crypto_md5(key_nonce, 32, NULL);
|
|
nonce_len = 0;
|
|
} else {
|
|
true_key = cipher->key;
|
|
}
|
|
|
|
cipher_evp_t *evp = cipher_ctx->evp;
|
|
if (evp == NULL) {
|
|
LOGE("cipher_ctx_set_nonce(): Cipher context is null");
|
|
return;
|
|
}
|
|
if (mbedtls_cipher_setkey(evp, true_key, cipher->key_len * 8, enc) != 0) {
|
|
mbedtls_cipher_free(evp);
|
|
FATAL("Cannot set mbed TLS cipher key");
|
|
}
|
|
if (mbedtls_cipher_set_iv(evp, nonce, nonce_len) != 0) {
|
|
mbedtls_cipher_free(evp);
|
|
FATAL("Cannot set mbed TLS cipher NONCE");
|
|
}
|
|
if (mbedtls_cipher_reset(evp) != 0) {
|
|
mbedtls_cipher_free(evp);
|
|
FATAL("Cannot finalize mbed TLS cipher context");
|
|
}
|
|
|
|
#ifdef SS_DEBUG
|
|
dump("NONCE", (char *)nonce, nonce_len);
|
|
dump("KEY", (char *)true_key, 32);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
cipher_ctx_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen,
|
|
const uint8_t *input, size_t ilen)
|
|
{
|
|
cipher_evp_t *evp = ctx->evp;
|
|
return mbedtls_cipher_update(evp, (const uint8_t *)input, ilen,
|
|
(uint8_t *)output, olen);
|
|
}
|
|
|
|
int
|
|
stream_encrypt_all(buffer_t *plaintext, cipher_t *cipher, size_t capacity)
|
|
{
|
|
cipher_ctx_t cipher_ctx;
|
|
stream_ctx_init(cipher, &cipher_ctx, 1);
|
|
|
|
size_t nonce_len = cipher->nonce_len;
|
|
int err = CRYPTO_OK;
|
|
|
|
static buffer_t tmp = { 0, 0, 0, NULL };
|
|
brealloc(&tmp, nonce_len + plaintext->len, capacity);
|
|
buffer_t *ciphertext = &tmp;
|
|
ciphertext->len = plaintext->len;
|
|
|
|
uint8_t *nonce = cipher_ctx.nonce;
|
|
cipher_ctx_set_nonce(&cipher_ctx, nonce, nonce_len, 1);
|
|
memcpy(ciphertext->data, nonce, nonce_len);
|
|
|
|
#ifdef MODULE_REMOTE
|
|
ppbloom_add((void *)nonce, nonce_len);
|
|
#endif
|
|
|
|
if (cipher->method >= SALSA20) {
|
|
crypto_stream_xor_ic((uint8_t *)(ciphertext->data + nonce_len),
|
|
(const uint8_t *)plaintext->data, (uint64_t)(plaintext->len),
|
|
(const uint8_t *)nonce,
|
|
0, cipher->key, cipher->method);
|
|
} else {
|
|
err = cipher_ctx_update(&cipher_ctx, (uint8_t *)(ciphertext->data + nonce_len),
|
|
&ciphertext->len, (const uint8_t *)plaintext->data,
|
|
plaintext->len);
|
|
}
|
|
|
|
stream_ctx_release(&cipher_ctx);
|
|
|
|
if (err)
|
|
return CRYPTO_ERROR;
|
|
|
|
#ifdef SS_DEBUG
|
|
dump("PLAIN", plaintext->data, plaintext->len);
|
|
dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len);
|
|
dump("NONCE", ciphertext->data, nonce_len);
|
|
#endif
|
|
|
|
brealloc(plaintext, nonce_len + ciphertext->len, capacity);
|
|
memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len);
|
|
plaintext->len = nonce_len + ciphertext->len;
|
|
|
|
return CRYPTO_OK;
|
|
}
|
|
|
|
int
|
|
stream_encrypt(buffer_t *plaintext, cipher_ctx_t *cipher_ctx, size_t capacity)
|
|
{
|
|
if (cipher_ctx == NULL)
|
|
return CRYPTO_ERROR;
|
|
|
|
cipher_t *cipher = cipher_ctx->cipher;
|
|
|
|
static buffer_t tmp = { 0, 0, 0, NULL };
|
|
|
|
int err = CRYPTO_OK;
|
|
size_t nonce_len = 0;
|
|
if (!cipher_ctx->init) {
|
|
nonce_len = cipher_ctx->cipher->nonce_len;
|
|
}
|
|
|
|
brealloc(&tmp, nonce_len + plaintext->len, capacity);
|
|
buffer_t *ciphertext = &tmp;
|
|
ciphertext->len = plaintext->len;
|
|
|
|
if (!cipher_ctx->init) {
|
|
cipher_ctx_set_nonce(cipher_ctx, cipher_ctx->nonce, nonce_len, 1);
|
|
memcpy(ciphertext->data, cipher_ctx->nonce, nonce_len);
|
|
cipher_ctx->counter = 0;
|
|
cipher_ctx->init = 1;
|
|
|
|
#ifdef MODULE_REMOTE
|
|
ppbloom_add((void *)cipher_ctx->nonce, nonce_len);
|
|
#endif
|
|
}
|
|
|
|
if (cipher->method >= SALSA20) {
|
|
int padding = cipher_ctx->counter % SODIUM_BLOCK_SIZE;
|
|
brealloc(ciphertext, nonce_len + (padding + ciphertext->len) * 2, capacity);
|
|
if (padding) {
|
|
brealloc(plaintext, plaintext->len + padding, capacity);
|
|
memmove(plaintext->data + padding, plaintext->data, plaintext->len);
|
|
sodium_memzero(plaintext->data, padding);
|
|
}
|
|
crypto_stream_xor_ic((uint8_t *)(ciphertext->data + nonce_len),
|
|
(const uint8_t *)plaintext->data,
|
|
(uint64_t)(plaintext->len + padding),
|
|
(const uint8_t *)cipher_ctx->nonce,
|
|
cipher_ctx->counter / SODIUM_BLOCK_SIZE, cipher->key,
|
|
cipher->method);
|
|
cipher_ctx->counter += plaintext->len;
|
|
if (padding) {
|
|
memmove(ciphertext->data + nonce_len,
|
|
ciphertext->data + nonce_len + padding, ciphertext->len);
|
|
}
|
|
} else {
|
|
err = cipher_ctx_update(cipher_ctx,
|
|
(uint8_t *)(ciphertext->data + nonce_len),
|
|
&ciphertext->len, (const uint8_t *)plaintext->data,
|
|
plaintext->len);
|
|
if (err) {
|
|
return CRYPTO_ERROR;
|
|
}
|
|
}
|
|
|
|
#ifdef SS_DEBUG
|
|
dump("PLAIN", plaintext->data, plaintext->len);
|
|
dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len);
|
|
#endif
|
|
|
|
brealloc(plaintext, nonce_len + ciphertext->len, capacity);
|
|
memcpy(plaintext->data, ciphertext->data, nonce_len + ciphertext->len);
|
|
plaintext->len = nonce_len + ciphertext->len;
|
|
|
|
return CRYPTO_OK;
|
|
}
|
|
|
|
int
|
|
stream_decrypt_all(buffer_t *ciphertext, cipher_t *cipher, size_t capacity)
|
|
{
|
|
size_t nonce_len = cipher->nonce_len;
|
|
int err = CRYPTO_OK;
|
|
|
|
if (ciphertext->len <= nonce_len) {
|
|
return CRYPTO_ERROR;
|
|
}
|
|
|
|
cipher_ctx_t cipher_ctx;
|
|
stream_ctx_init(cipher, &cipher_ctx, 0);
|
|
|
|
static buffer_t tmp = { 0, 0, 0, NULL };
|
|
brealloc(&tmp, ciphertext->len, capacity);
|
|
buffer_t *plaintext = &tmp;
|
|
plaintext->len = ciphertext->len - nonce_len;
|
|
|
|
uint8_t *nonce = cipher_ctx.nonce;
|
|
memcpy(nonce, ciphertext->data, nonce_len);
|
|
|
|
if (ppbloom_check((void *)nonce, nonce_len) == 1) {
|
|
LOGE("crypto: stream: repeat IV detected");
|
|
return CRYPTO_ERROR;
|
|
}
|
|
|
|
cipher_ctx_set_nonce(&cipher_ctx, nonce, nonce_len, 0);
|
|
|
|
if (cipher->method >= SALSA20) {
|
|
crypto_stream_xor_ic((uint8_t *)plaintext->data,
|
|
(const uint8_t *)(ciphertext->data + nonce_len),
|
|
(uint64_t)(ciphertext->len - nonce_len),
|
|
(const uint8_t *)nonce, 0, cipher->key, cipher->method);
|
|
} else {
|
|
err = cipher_ctx_update(&cipher_ctx, (uint8_t *)plaintext->data, &plaintext->len,
|
|
(const uint8_t *)(ciphertext->data + nonce_len),
|
|
ciphertext->len - nonce_len);
|
|
}
|
|
|
|
stream_ctx_release(&cipher_ctx);
|
|
|
|
if (err)
|
|
return CRYPTO_ERROR;
|
|
|
|
#ifdef SS_DEBUG
|
|
dump("PLAIN", plaintext->data, plaintext->len);
|
|
dump("CIPHER", ciphertext->data + nonce_len, ciphertext->len - nonce_len);
|
|
dump("NONCE", ciphertext->data, nonce_len);
|
|
#endif
|
|
|
|
ppbloom_add((void *)nonce, nonce_len);
|
|
|
|
brealloc(ciphertext, plaintext->len, capacity);
|
|
memcpy(ciphertext->data, plaintext->data, plaintext->len);
|
|
ciphertext->len = plaintext->len;
|
|
|
|
return CRYPTO_OK;
|
|
}
|
|
|
|
int
|
|
stream_decrypt(buffer_t *ciphertext, cipher_ctx_t *cipher_ctx, size_t capacity)
|
|
{
|
|
if (cipher_ctx == NULL)
|
|
return CRYPTO_ERROR;
|
|
|
|
cipher_t *cipher = cipher_ctx->cipher;
|
|
|
|
static buffer_t tmp = { 0, 0, 0, NULL };
|
|
|
|
int err = CRYPTO_OK;
|
|
|
|
brealloc(&tmp, ciphertext->len, capacity);
|
|
buffer_t *plaintext = &tmp;
|
|
plaintext->len = ciphertext->len;
|
|
|
|
if (!cipher_ctx->init) {
|
|
if (cipher_ctx->chunk == NULL) {
|
|
cipher_ctx->chunk = (buffer_t *)ss_malloc(sizeof(buffer_t));
|
|
memset(cipher_ctx->chunk, 0, sizeof(buffer_t));
|
|
balloc(cipher_ctx->chunk, cipher->nonce_len);
|
|
}
|
|
|
|
size_t left_len = min(cipher->nonce_len - cipher_ctx->chunk->len,
|
|
ciphertext->len);
|
|
|
|
if (left_len > 0) {
|
|
memcpy(cipher_ctx->chunk->data + cipher_ctx->chunk->len, ciphertext->data, left_len);
|
|
memmove(ciphertext->data, ciphertext->data + left_len,
|
|
ciphertext->len - left_len);
|
|
cipher_ctx->chunk->len += left_len;
|
|
ciphertext->len -= left_len;
|
|
}
|
|
|
|
if (cipher_ctx->chunk->len < cipher->nonce_len)
|
|
return CRYPTO_NEED_MORE;
|
|
|
|
uint8_t *nonce = cipher_ctx->nonce;
|
|
size_t nonce_len = cipher->nonce_len;
|
|
plaintext->len -= left_len;
|
|
|
|
memcpy(nonce, cipher_ctx->chunk->data, nonce_len);
|
|
cipher_ctx_set_nonce(cipher_ctx, nonce, nonce_len, 0);
|
|
cipher_ctx->counter = 0;
|
|
cipher_ctx->init = 1;
|
|
|
|
if (cipher->method >= RC4_MD5) {
|
|
if (ppbloom_check((void *)nonce, nonce_len) == 1) {
|
|
LOGE("crypto: stream: repeat IV detected");
|
|
return CRYPTO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ciphertext->len <= 0)
|
|
return CRYPTO_NEED_MORE;
|
|
|
|
if (cipher->method >= SALSA20) {
|
|
int padding = cipher_ctx->counter % SODIUM_BLOCK_SIZE;
|
|
brealloc(plaintext, (plaintext->len + padding) * 2, capacity);
|
|
|
|
if (padding) {
|
|
brealloc(ciphertext, ciphertext->len + padding, capacity);
|
|
memmove(ciphertext->data + padding, ciphertext->data,
|
|
ciphertext->len);
|
|
sodium_memzero(ciphertext->data, padding);
|
|
}
|
|
crypto_stream_xor_ic((uint8_t *)plaintext->data,
|
|
(const uint8_t *)(ciphertext->data),
|
|
(uint64_t)(ciphertext->len + padding),
|
|
(const uint8_t *)cipher_ctx->nonce,
|
|
cipher_ctx->counter / SODIUM_BLOCK_SIZE, cipher->key,
|
|
cipher->method);
|
|
cipher_ctx->counter += ciphertext->len;
|
|
if (padding) {
|
|
memmove(plaintext->data, plaintext->data + padding, plaintext->len);
|
|
}
|
|
} else {
|
|
err = cipher_ctx_update(cipher_ctx, (uint8_t *)plaintext->data, &plaintext->len,
|
|
(const uint8_t *)(ciphertext->data),
|
|
ciphertext->len);
|
|
}
|
|
|
|
if (err)
|
|
return CRYPTO_ERROR;
|
|
|
|
#ifdef SS_DEBUG
|
|
dump("PLAIN", plaintext->data, plaintext->len);
|
|
dump("CIPHER", ciphertext->data, ciphertext->len);
|
|
#endif
|
|
|
|
// Add to bloom filter
|
|
if (cipher_ctx->init == 1) {
|
|
if (cipher->method >= RC4_MD5) {
|
|
if (ppbloom_check((void *)cipher_ctx->nonce, cipher->nonce_len) == 1) {
|
|
LOGE("crypto: stream: repeat IV detected");
|
|
return CRYPTO_ERROR;
|
|
}
|
|
ppbloom_add((void *)cipher_ctx->nonce, cipher->nonce_len);
|
|
cipher_ctx->init = 2;
|
|
}
|
|
}
|
|
|
|
brealloc(ciphertext, plaintext->len, capacity);
|
|
memcpy(ciphertext->data, plaintext->data, plaintext->len);
|
|
ciphertext->len = plaintext->len;
|
|
|
|
return CRYPTO_OK;
|
|
}
|
|
|
|
void
|
|
stream_ctx_init(cipher_t *cipher, cipher_ctx_t *cipher_ctx, int enc)
|
|
{
|
|
sodium_memzero(cipher_ctx, sizeof(cipher_ctx_t));
|
|
stream_cipher_ctx_init(cipher_ctx, cipher->method, enc);
|
|
cipher_ctx->cipher = cipher;
|
|
|
|
if (enc) {
|
|
rand_bytes(cipher_ctx->nonce, cipher->nonce_len);
|
|
}
|
|
}
|
|
|
|
cipher_t *
|
|
stream_key_init(int method, const char *pass, const char *key)
|
|
{
|
|
if (method <= TABLE || method >= STREAM_CIPHER_NUM) {
|
|
LOGE("cipher->key_init(): Illegal method");
|
|
return NULL;
|
|
}
|
|
|
|
cipher_t *cipher = (cipher_t *)ss_malloc(sizeof(cipher_t));
|
|
memset(cipher, 0, sizeof(cipher_t));
|
|
|
|
if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) {
|
|
cipher_kt_t *cipher_info = (cipher_kt_t *)ss_malloc(sizeof(cipher_kt_t));
|
|
cipher->info = cipher_info;
|
|
cipher->info->base = NULL;
|
|
cipher->info->key_bitlen = supported_stream_ciphers_key_size[method] * 8;
|
|
cipher->info->iv_size = supported_stream_ciphers_nonce_size[method];
|
|
} else {
|
|
cipher->info = (cipher_kt_t *)stream_get_cipher_type(method);
|
|
}
|
|
|
|
if (cipher->info == NULL && cipher->key_len == 0) {
|
|
LOGE("Cipher %s not found in crypto library", supported_stream_ciphers[method]);
|
|
FATAL("Cannot initialize cipher");
|
|
}
|
|
|
|
if (key != NULL)
|
|
cipher->key_len = crypto_parse_key(key, cipher->key, cipher_key_size(cipher));
|
|
else
|
|
cipher->key_len = crypto_derive_key(pass, cipher->key, cipher_key_size(cipher));
|
|
|
|
if (cipher->key_len == 0) {
|
|
FATAL("Cannot generate key and NONCE");
|
|
}
|
|
if (method == RC4_MD5) {
|
|
cipher->nonce_len = 16;
|
|
} else {
|
|
cipher->nonce_len = cipher_nonce_size(cipher);
|
|
}
|
|
cipher->method = method;
|
|
|
|
return cipher;
|
|
}
|
|
|
|
cipher_t *
|
|
stream_init(const char *pass, const char *key, const char *method)
|
|
{
|
|
int m = TABLE;
|
|
if (method != NULL) {
|
|
for (m = TABLE; m < STREAM_CIPHER_NUM; m++)
|
|
if (strcmp(method, supported_stream_ciphers[m]) == 0) {
|
|
break;
|
|
}
|
|
if (m >= STREAM_CIPHER_NUM) {
|
|
LOGE("Invalid cipher name: %s, use chacha20-ietf instead", method);
|
|
m = CHACHA20IETF;
|
|
}
|
|
}
|
|
if (m == TABLE) {
|
|
LOGE("Table is deprecated");
|
|
return NULL;
|
|
}
|
|
return stream_key_init(m, pass, key);
|
|
}
|