Add helpful hybrid encryption functions

svn:r1423
This commit is contained in:
Nick Mathewson 2004-04-01 03:08:35 +00:00
parent 36ff23209b
commit 35f531b94f
3 changed files with 148 additions and 1 deletions

View File

@ -114,6 +114,17 @@ crypto_cipher_evp_cipher(int type, int enc) {
}
}
static INLINE int
crypto_get_rsa_padding_overhead(int padding) {
switch(padding)
{
case RSA_NO_PADDING: return 0;
case RSA_PKCS1_OAEP_PADDING: return 42;
case RSA_PKCS1_PADDING: return 11;
default: assert(0); return -1;
}
}
static int _crypto_global_initialized = 0;
int crypto_global_init()
@ -645,6 +656,118 @@ int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromle
}
}
/* Perform a hybrid (public/secret) encryption on 'fromlen' bytes of data
* from 'from', with padding type 'padding', storing the results on 'to'.
*
* If no padding is used, the public key must be at least as large as
* 'from'.
*
* Returns the number of bytes written on success, -1 on failure.
*
* The encrypted data consists of:
*
* The source data, padded and encrypted with the public key, if the
* padded source data is no longer than the public key.
* OR
* The beginning of the source data prefixed with a 16-symmetric key,
* padded and encrypted with the public key; followed by the rest of
* the source data encrypted in AES-CTR mode with the symmetric key.
*/
int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, unsigned char *from,
int fromlen, unsigned char *to,
int padding)
{
int overhead, pkeylen, outlen, r, symlen;
crypto_cipher_env_t *cipher = NULL;
char buf[1024];
assert(env && from && to);
overhead = crypto_get_rsa_padding_overhead(padding);
pkeylen = crypto_pk_keysize(env);
if (padding == RSA_NO_PADDING && fromlen < pkeylen)
return -1;
if (fromlen+overhead <= pkeylen) {
/* It all fits in a single encrypt. */
return crypto_pk_public_encrypt(env,from,fromlen,to,padding);
}
cipher = crypto_new_cipher_env(CRYPTO_CIPHER_AES_CTR);
if (!cipher) return -1;
if (crypto_cipher_generate_key(cipher)<0)
goto err;
if (padding == RSA_NO_PADDING)
cipher->key[0] &= 0x7f;
if (crypto_cipher_encrypt_init_cipher(cipher)<0)
goto err;
memcpy(buf, cipher->key, 16);
memcpy(buf+16, from, pkeylen-overhead-16);
/* Length of symmetrically encrypted data. */
symlen = fromlen-(pkeylen-overhead-16);
outlen = crypto_pk_public_encrypt(env,buf,pkeylen-overhead,to,padding);
if (outlen!=pkeylen) {
goto err;
}
r = crypto_cipher_encrypt(cipher,
from+pkeylen-overhead-16, symlen,
to+outlen);
if (r<0) goto err;
memset(buf, 0, 1024);
crypto_free_cipher_env(cipher);
return outlen + symlen;
err:
memset(buf, 0, 1024);
if (cipher) crypto_free_cipher_env(cipher);
return -1;
}
/* Invert crypto_pk_public_hybrid_encrypt. */
int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, unsigned char *from,
int fromlen, unsigned char *to,
int padding)
{
int overhead, pkeylen, outlen, r;
crypto_cipher_env_t *cipher = NULL;
char buf[1024];
overhead = crypto_get_rsa_padding_overhead(padding);
pkeylen = crypto_pk_keysize(env);
if (fromlen <= pkeylen) {
return crypto_pk_private_decrypt(env,from,fromlen,to,padding);
}
outlen = crypto_pk_private_decrypt(env,from,pkeylen,buf,padding);
if (outlen<0) {
log_fn(LOG_WARN, "Error decrypting public-key data");
return -1;
}
if (outlen < 16) {
log_fn(LOG_WARN, "No room for a symmetric key");
return -1;
}
cipher = crypto_create_init_cipher(CRYPTO_CIPHER_AES_CTR, buf, "", 0);
if (!cipher) {
return -1;
}
memcpy(to,buf+16,outlen-16);
outlen -= 16;
r = crypto_cipher_decrypt(cipher, from+pkeylen, fromlen-pkeylen,
to+outlen);
if (r<0)
goto err;
memset(buf,0,1024);
crypto_free_cipher_env(cipher);
return outlen + (fromlen-pkeylen);
err:
memset(buf, 0, 1024);
if (cipher) crypto_free_cipher_env(cipher);
return -1;
}
/* Encode the public portion of 'pk' into 'dest'. Return -1 on error,
* or the number of characters used on success.
*/

View File

@ -59,6 +59,13 @@ int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int from
int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, unsigned char *from,
int fromlen, unsigned char *to,
int padding);
int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, unsigned char *from,
int fromlen, unsigned char *to,
int padding);
#define FINGERPRINT_LEN 49
int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, int dest_len);
crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, int len);

View File

@ -242,7 +242,7 @@ test_crypto()
crypto_pk_env_t *pk1, *pk2;
char *data1, *data2, *data3, *cp;
FILE *f;
int i, j;
int i, j, p, len;
int str_ciphers[] = { CRYPTO_CIPHER_IDENTITY,
CRYPTO_CIPHER_DES,
CRYPTO_CIPHER_RC4,
@ -416,6 +416,23 @@ test_crypto()
pk2 = crypto_pk_asn1_decode(data1, i);
test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
/* Try with hybrid encryption wrappers. */
crypto_rand(1024, data1);
for (i = 0; i < 3; ++i) {
for (j = 85; j < 140; ++j) {
memset(data2,0,1024);
memset(data3,0,1024);
if (i == 0 && j < 129)
continue;
p = (i==0)?RSA_NO_PADDING:
(i==1)?RSA_PKCS1_PADDING:RSA_PKCS1_OAEP_PADDING;
len = crypto_pk_public_hybrid_encrypt(pk1,data1,j,data2,p);
test_assert(len>=0);
len = crypto_pk_private_hybrid_decrypt(pk1,data2,len,data3,p);
test_eq(len,j);
test_memeq(data1,data3,j);
}
}
crypto_free_pk_env(pk1);
crypto_free_pk_env(pk2);