mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 14:23:30 +01:00
Add helpful hybrid encryption functions
svn:r1423
This commit is contained in:
parent
36ff23209b
commit
35f531b94f
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user