add ecc encrypt secure message exchange, hide ecEncCtx

This commit is contained in:
toddouska 2013-11-13 17:53:11 -08:00
parent 2e9e372875
commit cf4230b073
5 changed files with 361 additions and 69 deletions

View File

@ -3510,33 +3510,189 @@ void ecc_fp_free(void)
#ifdef HAVE_ECC_ENCRYPT
/* init and set defaults, just holders */
void ecc_encrypt_init_options(ecEncOptions* options)
{
if (options) {
XMEMSET(options, 0, sizeof(ecEncOptions));
options->encAlgo = ecAES_128_CBC;
options->kdfAlgo = ecHKDF_SHA256;
options->macAlgo = ecHMAC_SHA256;
enum ecCliState {
ecCLI_INIT = 1,
ecCLI_SALT_GET = 2,
ecCLI_SALT_SET = 3,
ecCLI_SENT_REQ = 4,
ecCLI_RECV_RESP = 5,
ecCLI_BAD_STATE = 99
};
enum ecSrvState {
ecSRV_INIT = 1,
ecSRV_SALT_GET = 2,
ecSRV_SALT_SET = 3,
ecSRV_RECV_REQ = 4,
ecSRV_SENT_RESP = 5,
ecSRV_BAD_STATE = 99
};
struct ecEncCtx {
byte* kdfSalt; /* optional salt for kdf */
byte* kdfInfo; /* optional info for kdf */
byte* macSalt; /* optional salt for mac */
word32 kdfSaltSz; /* size of kdfSalt */
word32 kdfInfoSz; /* size of kdfInfo */
word32 macSaltSz; /* size of macSalt */
byte clientSalt[EXCHANGE_SALT_SZ]; /* for msg exchange */
byte serverSalt[EXCHANGE_SALT_SZ]; /* for msg exchange */
byte encAlgo; /* which encryption type */
byte kdfAlgo; /* which key derivation function type */
byte macAlgo; /* which mac function type */
byte protocol; /* are we REQ_RESP client or server ? */
byte cliSt; /* protocol state, for sanity checks */
byte srvSt; /* protocol state, for sanity checks */
};
const byte* ecc_ctx_get_own_salt(ecEncCtx* ctx)
{
if (ctx == NULL || ctx->protocol == 0)
return NULL;
if (ctx->protocol == REQ_RESP_CLIENT) {
if (ctx->cliSt == ecCLI_INIT) {
ctx->cliSt = ecCLI_SALT_GET;
return ctx->clientSalt;
}
else {
ctx->cliSt = ecCLI_BAD_STATE;
return NULL;
}
}
else if (ctx->protocol == REQ_RESP_SERVER) {
if (ctx->srvSt == ecSRV_INIT) {
ctx->srvSt = ecSRV_SALT_GET;
return ctx->serverSalt;
}
else {
ctx->srvSt = ecSRV_BAD_STATE;
return NULL;
}
}
return NULL;
}
static const char* exchange_info = "Secure Message Exchange";
int ecc_ctx_set_peer_salt(ecEncCtx* ctx, const byte* salt)
{
byte tmp[EXCHANGE_SALT_SZ/2];
int halfSz = EXCHANGE_SALT_SZ/2;
if (ctx == NULL || ctx->protocol == 0 || salt == NULL)
return BAD_FUNC_ARG;
if (ctx->protocol == REQ_RESP_CLIENT) {
XMEMCPY(ctx->serverSalt, salt, EXCHANGE_SALT_SZ);
if (ctx->cliSt == ecCLI_SALT_GET)
ctx->cliSt = ecCLI_SALT_SET;
else {
ctx->cliSt = ecCLI_BAD_STATE;
return BAD_ENC_STATE_E;
}
}
else {
XMEMCPY(ctx->clientSalt, salt, EXCHANGE_SALT_SZ);
if (ctx->srvSt == ecSRV_SALT_GET)
ctx->srvSt = ecSRV_SALT_SET;
else {
ctx->srvSt = ecSRV_BAD_STATE;
return BAD_ENC_STATE_E;
}
}
/* mix half and half */
/* tmp stores 2nd half of client before overwrite */
XMEMCPY(tmp, ctx->clientSalt + halfSz, halfSz);
XMEMCPY(ctx->clientSalt + halfSz, ctx->serverSalt, halfSz);
XMEMCPY(ctx->serverSalt, tmp, halfSz);
ctx->kdfSalt = ctx->clientSalt;
ctx->kdfSaltSz = EXCHANGE_SALT_SZ;
ctx->macSalt = ctx->serverSalt;
ctx->macSaltSz = EXCHANGE_SALT_SZ;
ctx->kdfInfo = (byte*)exchange_info;
ctx->kdfInfoSz = EXCHANGE_INFO_SZ;
return 0;
}
static int ecc_ctx_set_salt(ecEncCtx* ctx, int flags, RNG* rng)
{
byte* saltBuffer = NULL;
if (ctx == NULL || rng == NULL || flags == 0)
return BAD_FUNC_ARG;
saltBuffer = (flags == REQ_RESP_CLIENT) ? ctx->clientSalt : ctx->serverSalt;
RNG_GenerateBlock(rng, saltBuffer, EXCHANGE_SALT_SZ);
return 0;
}
static void ecc_ctx_init(ecEncCtx* ctx, int flags)
{
if (ctx) {
XMEMSET(ctx, 0, sizeof(ecEncCtx));
ctx->encAlgo = ecAES_128_CBC;
ctx->kdfAlgo = ecHKDF_SHA256;
ctx->macAlgo = ecHMAC_SHA256;
ctx->protocol = (byte)flags;
if (flags == REQ_RESP_CLIENT)
ctx->cliSt = ecCLI_INIT;
if (flags == REQ_RESP_SERVER)
ctx->srvSt = ecSRV_INIT;
}
}
/* alloc/init and set defaults, return new Context */
ecEncCtx* ecc_ctx_new(int flags, RNG* rng)
{
int ret = 0;
ecEncCtx* ctx = (ecEncCtx*)XMALLOC(sizeof(ecEncCtx), 0, DYNAMIC_TYPE_ECC);
ecc_ctx_init(ctx, flags);
if (ctx && flags)
ret = ecc_ctx_set_salt(ctx, flags, rng);
if (ret != 0) {
ecc_ctx_free(ctx);
ctx = NULL;
}
return ctx;
}
/* free any resources, clear any keys */
void ecc_encrypt_free_options(ecEncOptions* options)
void ecc_ctx_free(ecEncCtx* ctx)
{
if (options) {
XMEMSET(options, 0, sizeof(ecEncOptions));
if (ctx) {
XMEMSET(ctx, 0, sizeof(ecEncCtx));
XFREE(ctx, 0, DYNAMIC_TYPE_ECC);
}
}
static int ecc_get_key_sizes(ecEncOptions* options, int* encKeySz, int* ivSz,
static int ecc_get_key_sizes(ecEncCtx* ctx, int* encKeySz, int* ivSz,
int* keysLen, word32* digestSz, word32* blockSz)
{
if (options) {
switch (options->encAlgo) {
if (ctx) {
switch (ctx->encAlgo) {
case ecAES_128_CBC:
*encKeySz = KEY_SIZE_128;
*ivSz = IV_SIZE_64;
@ -3546,7 +3702,7 @@ static int ecc_get_key_sizes(ecEncOptions* options, int* encKeySz, int* ivSz,
return BAD_FUNC_ARG;
}
switch (options->macAlgo) {
switch (ctx->macAlgo) {
case ecHMAC_SHA256:
*digestSz = SHA256_DIGEST_SIZE;
break;
@ -3563,22 +3719,23 @@ static int ecc_get_key_sizes(ecEncOptions* options, int* encKeySz, int* ivSz,
/* ecc encrypt with shared secret run through kdf
options holds non default algos and inputs
ctx holds non default algos and inputs
msgSz should be the right size for encAlgo, i.e., already padded
return 0 on success */
int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
word32 msgSz, byte* out, word32* outSz, ecEncOptions* opts)
word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx)
{
int ret;
word32 blockSz;
word32 digestSz;
ecEncOptions options;
ecEncCtx localCtx;
byte sharedSecret[ECC_MAXSIZE]; /* 521 max size */
byte keys[ECC_BUFSIZE]; /* max size */
word32 sharedSz = sizeof(sharedSecret);
int keysLen;
int encKeySz;
int ivSz;
int offset; /* keys offset if doing msg exchange */
byte* encKey;
byte* encIv;
byte* macKey;
@ -3587,19 +3744,37 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
outSz == NULL)
return BAD_FUNC_ARG;
if (opts)
options = *opts;
else {
ecc_encrypt_init_options(&options); /* defaults */
if (ctx == NULL) { /* use defaults */
ecc_ctx_init(&localCtx, 0);
ctx = &localCtx;
}
ret = ecc_get_key_sizes(&options, &encKeySz, &ivSz, &keysLen, &digestSz,
ret = ecc_get_key_sizes(ctx, &encKeySz, &ivSz, &keysLen, &digestSz,
&blockSz);
if (ret != 0)
return ret;
if (ctx->protocol == REQ_RESP_SERVER) {
offset = keysLen;
keysLen *= 2;
if (ctx->srvSt != ecSRV_RECV_REQ)
return BAD_ENC_STATE_E;
ctx->srvSt = ecSRV_BAD_STATE; /* we're done no more ops allowed */
}
else if (ctx->protocol == REQ_RESP_CLIENT) {
if (ctx->cliSt != ecCLI_SALT_SET)
return BAD_ENC_STATE_E;
ctx->cliSt = ecCLI_SENT_REQ; /* only do this once */
}
if (keysLen > (int)sizeof(keys))
return BUFFER_E;
if ( (msgSz%blockSz) != 0)
return BAD_FUNC_ARG;
return BAD_PADDING_E;
if (*outSz < (msgSz + digestSz))
return BUFFER_E;
@ -3608,11 +3783,11 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
if (ret != 0)
return ret;
switch (options.kdfAlgo) {
switch (ctx->kdfAlgo) {
case ecHKDF_SHA256 :
ret = HKDF(SHA256, sharedSecret, sharedSz, options.kdfSalt,
options.kdfSaltSz, options.kdfInfo,
options.kdfInfoSz, keys, keysLen);
ret = HKDF(SHA256, sharedSecret, sharedSz, ctx->kdfSalt,
ctx->kdfSaltSz, ctx->kdfInfo,
ctx->kdfInfoSz, keys, keysLen);
if (ret != 0)
return ret;
break;
@ -3621,11 +3796,11 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
return BAD_FUNC_ARG;
}
encKey = keys;
encKey = keys + offset;
encIv = encKey + encKeySz;
macKey = encKey + encKeySz + ivSz;
switch (options.encAlgo) {
switch (ctx->encAlgo) {
case ecAES_128_CBC:
{
Aes aes;
@ -3642,7 +3817,7 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
return BAD_FUNC_ARG;
}
switch (options.macAlgo) {
switch (ctx->macAlgo) {
case ecHMAC_SHA256:
{
Hmac hmac;
@ -3650,7 +3825,7 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
if (ret != 0)
return ret;
HmacUpdate(&hmac, out, msgSz);
HmacUpdate(&hmac, options.macSalt, options.macSaltSz);
HmacUpdate(&hmac, ctx->macSalt, ctx->macSaltSz);
HmacFinal(&hmac, out+msgSz);
}
break;
@ -3665,19 +3840,23 @@ int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
}
/* ecc decrypt with shared secret run through kdf
ctx holds non default algos and inputs
return 0 on success */
int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
word32 msgSz, byte* out, word32* outSz, ecEncOptions* opts)
word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx)
{
int ret;
word32 blockSz;
word32 digestSz;
ecEncOptions options;
ecEncCtx localCtx;
byte sharedSecret[ECC_MAXSIZE]; /* 521 max size */
byte keys[ECC_BUFSIZE]; /* max size */
word32 sharedSz = sizeof(sharedSecret);
int keysLen;
int encKeySz;
int ivSz;
int offset; /* in case using msg exchange */
byte* encKey;
byte* encIv;
byte* macKey;
@ -3686,19 +3865,37 @@ int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
outSz == NULL)
return BAD_FUNC_ARG;
if (opts)
options = *opts;
else {
ecc_encrypt_init_options(&options); /* defaults */
if (ctx == NULL) { /* use defaults */
ecc_ctx_init(&localCtx, 0);
ctx = &localCtx;
}
ret = ecc_get_key_sizes(&options, &encKeySz, &ivSz, &keysLen, &digestSz,
ret = ecc_get_key_sizes(ctx, &encKeySz, &ivSz, &keysLen, &digestSz,
&blockSz);
if (ret != 0)
return ret;
if (ctx->protocol == REQ_RESP_CLIENT) {
offset = keysLen;
keysLen *= 2;
if (ctx->cliSt != ecCLI_SENT_REQ)
return BAD_ENC_STATE_E;
ctx->cliSt = ecSRV_BAD_STATE; /* we're done no more ops allowed */
}
else if (ctx->protocol == REQ_RESP_SERVER) {
if (ctx->srvSt != ecSRV_SALT_SET)
return BAD_ENC_STATE_E;
ctx->srvSt = ecSRV_RECV_REQ; /* only do this once */
}
if (keysLen > (int)sizeof(keys))
return BUFFER_E;
if ( ((msgSz-digestSz) % blockSz) != 0)
return BAD_FUNC_ARG;
return BAD_PADDING_E;
if (*outSz < (msgSz - digestSz))
return BUFFER_E;
@ -3707,11 +3904,11 @@ int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
if (ret != 0)
return ret;
switch (options.kdfAlgo) {
switch (ctx->kdfAlgo) {
case ecHKDF_SHA256 :
ret = HKDF(SHA256, sharedSecret, sharedSz, options.kdfSalt,
options.kdfSaltSz, options.kdfInfo,
options.kdfInfoSz, keys, keysLen);
ret = HKDF(SHA256, sharedSecret, sharedSz, ctx->kdfSalt,
ctx->kdfSaltSz, ctx->kdfInfo,
ctx->kdfInfoSz, keys, keysLen);
if (ret != 0)
return ret;
break;
@ -3720,11 +3917,11 @@ int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
return BAD_FUNC_ARG;
}
encKey = keys;
encKey = keys + offset;
encIv = encKey + encKeySz;
macKey = encKey + encKeySz + ivSz;
switch (options.macAlgo) {
switch (ctx->macAlgo) {
case ecHMAC_SHA256:
{
byte verify[SHA256_DIGEST_SIZE];
@ -3733,7 +3930,7 @@ int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
if (ret != 0)
return ret;
HmacUpdate(&hmac, msg, msgSz-digestSz);
HmacUpdate(&hmac, options.macSalt, options.macSaltSz);
HmacUpdate(&hmac, ctx->macSalt, ctx->macSaltSz);
HmacFinal(&hmac, verify);
if (memcmp(verify, msg + msgSz - digestSz, digestSz) != 0) {
@ -3746,7 +3943,7 @@ int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
return BAD_FUNC_ARG;
}
switch (options.encAlgo) {
switch (ctx->encAlgo) {
case ecAES_128_CBC:
{
Aes aes;

View File

@ -323,6 +323,14 @@ void CTaoCryptErrorString(int error, char* buffer)
XSTRNCPY(buffer, "ASN OCSP sig error, confirm failure", max);
break;
case BAD_ENC_STATE_E:
XSTRNCPY(buffer, "Bad ecc encrypt state operation", max);
break;
case BAD_PADDING_E:
XSTRNCPY(buffer, "Bad padding, message wrong length", max);
break;
default:
XSTRNCPY(buffer, "unknown error number", max);

View File

@ -3628,12 +3628,12 @@ int ecc_encrypt_test(void)
for (i = 0; i < 48; i++)
msg[i] = i;
/* send encrypted msg to B */
/* encrypt msg to B */
ret = ecc_encrypt(&userA, &userB, msg, sizeof(msg), out, &outSz, NULL);
if (ret != 0)
return -3003;
/* decrypted msg to B */
/* decrypt msg from A */
ret = ecc_decrypt(&userB, &userA, out, outSz, plain, &plainSz, NULL);
if (ret != 0)
return -3004;
@ -3641,6 +3641,84 @@ int ecc_encrypt_test(void)
if (memcmp(plain, msg, sizeof(msg)) != 0)
return -3005;
{ /* let's verify message exchange works, A is client, B is server */
ecEncCtx* cliCtx = ecc_ctx_new(REQ_RESP_CLIENT, &rng);
ecEncCtx* srvCtx = ecc_ctx_new(REQ_RESP_SERVER, &rng);
byte cliSalt[EXCHANGE_SALT_SZ];
byte srvSalt[EXCHANGE_SALT_SZ];
const byte* tmpSalt;
if (cliCtx == NULL || srvCtx == NULL)
return -3006;
/* get salt to send to peer */
tmpSalt = ecc_ctx_get_own_salt(cliCtx);
if (tmpSalt == NULL)
return -3007;
memcpy(cliSalt, tmpSalt, EXCHANGE_SALT_SZ);
tmpSalt = ecc_ctx_get_own_salt(srvCtx);
if (tmpSalt == NULL)
return -3007;
memcpy(srvSalt, tmpSalt, EXCHANGE_SALT_SZ);
/* in actual use, we'd get the peer's salt over the transport */
ret = ecc_ctx_set_peer_salt(cliCtx, srvSalt);
ret += ecc_ctx_set_peer_salt(srvCtx, cliSalt);
if (ret != 0)
return -3008;
/* get encrypted msg (request) to send to B */
outSz = sizeof(out);
ret = ecc_encrypt(&userA, &userB, msg, sizeof(msg), out, &outSz,cliCtx);
if (ret != 0)
return -3009;
/* B decrypts msg (request) from A */
plainSz = sizeof(plain);
ret = ecc_decrypt(&userB, &userA, out, outSz, plain, &plainSz, srvCtx);
if (ret != 0)
return -3010;
if (memcmp(plain, msg, sizeof(msg)) != 0)
return -3011;
{
/* msg2 (response) from B to A */
byte msg2[48];
byte plain2[48];
byte out2[80];
word32 outSz2 = sizeof(out2);
word32 plainSz2 = sizeof(plain2);
for (i = 0; i < 48; i++)
msg2[i] = i+48;
/* get encrypted msg (response) to send to B */
ret = ecc_encrypt(&userB, &userA, msg2, sizeof(msg2), out2,
&outSz2, srvCtx);
if (ret != 0)
return -3012;
/* A decrypts msg (response) from B */
ret = ecc_decrypt(&userA, &userB, out2, outSz2, plain2, &plainSz2,
cliCtx);
if (ret != 0)
return -3013;
if (memcmp(plain2, msg2, sizeof(msg2)) != 0)
return -3014;
}
/* cleanup */
ecc_ctx_free(srvCtx);
ecc_ctx_free(cliCtx);
}
/* cleanup */
ecc_free(&userB);
ecc_free(&userA);

View File

@ -119,6 +119,7 @@ CYASSL_API
int ecc_sig_size(ecc_key* key);
#ifdef HAVE_ECC_ENCRYPT
/* ecc encrypt */
enum ecEncAlgo {
@ -137,34 +138,39 @@ enum ecMacAlgo {
};
enum {
KEY_SIZE_128 = 16,
KEY_SIZE_256 = 32,
IV_SIZE_64 = 8
KEY_SIZE_128 = 16,
KEY_SIZE_256 = 32,
IV_SIZE_64 = 8,
EXCHANGE_SALT_SZ = 16,
EXCHANGE_INFO_SZ = 23
};
typedef struct ecEncOptions {
byte encAlgo; /* which encryption type */
byte kdfAlgo; /* which key derivation function type */
byte macAlgo; /* which mac function type */
byte* kdfSalt; /* optional salt for kdf */
byte* kdfInfo; /* optional info for kdf */
byte* macSalt; /* optional salt for mac */
word32 kdfSaltSz; /* size of kdfSalt */
word32 kdfInfoSz; /* size of kdfInfo */
word32 macSaltSz; /* size of macSalt */
} ecEncOptions;
enum ecFlags {
REQ_RESP_CLIENT = 1,
REQ_RESP_SERVER = 2
};
typedef struct ecEncCtx ecEncCtx;
CYASSL_API
void ecc_encrypt_init_options(ecEncOptions*); /* init and set to defaults */
ecEncCtx* ecc_ctx_new(int flags, RNG* rng);
CYASSL_API
void ecc_encrypt_free_options(ecEncOptions*); /* release/clear options */
void ecc_ctx_free(ecEncCtx*);
CYASSL_API
const byte* ecc_ctx_get_own_salt(ecEncCtx*);
CYASSL_API
int ecc_ctx_set_peer_salt(ecEncCtx*, const byte* salt);
CYASSL_API
int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
word32 msgSz, byte* out, word32* outSz, ecEncOptions* options);
word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx);
CYASSL_API
int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
word32 msgSz, byte* out, word32* outSz, ecEncOptions* options);
word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx);
#endif /* HAVE_ECC_ENCRYPT */
#ifdef __cplusplus
} /* extern "C" */

View File

@ -114,6 +114,9 @@ enum {
ASN_CRL_NO_SIGNER_E = -190, /* ASN CRL no signer to confirm failure */
ASN_OCSP_CONFIRM_E = -191, /* ASN OCSP signature confirm failure */
BAD_ENC_STATE_E = -192, /* Bad ecc enc state operation */
BAD_PADDING_E = -193, /* Bad padding, msg not correct length */
MIN_CODE_E = -200 /* errors -101 - -199 */
};