1 #include <Rinternals.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <openssl/crypto.h>
5 #include <openssl/evp.h>
6 #include "utils.h"
7 
8 /*
9  * Adapted from example at: https://www.openssl.org/docs/crypto/EVP_EncryptInit.html
10  */
11 
R_aes_any(SEXP x,SEXP key,SEXP iv,SEXP encrypt,SEXP cipher)12 SEXP R_aes_any(SEXP x, SEXP key, SEXP iv, SEXP encrypt, SEXP cipher) {
13   int strength = LENGTH(key);
14   if(strength != 16 && strength != 24 && strength != 32)
15     error("key must be of length 16 (aes-128), 24 (aes-192) or 32 (aes-256)");
16 
17   EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
18   const EVP_CIPHER *cph = EVP_get_cipherbyname(CHAR(STRING_ELT(cipher, 0)));
19   if(!cph)
20     Rf_error("Invalid cipher: %s", CHAR(STRING_ELT(cipher, 0)));
21 
22 #ifdef EVP_CIPH_GCM_MODE //openssl 1.0.0 does not have GCM
23   if(EVP_CIPHER_mode(cph) == EVP_CIPH_GCM_MODE){
24     if(LENGTH(iv) != 12){
25       Rf_error("aes-gcm requires an iv of length 12");
26     }
27     //GCM mode has shorter IV from the others
28     bail(EVP_CipherInit_ex(ctx, cph, NULL, NULL, NULL, asLogical(encrypt)));
29     bail(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, LENGTH(iv), NULL));
30   } else
31 #endif //EVP_CIPH_GCM_MODE
32   if(LENGTH(iv) != 16){
33     Rf_error("aes requires an iv of length 16");
34   }
35   bail(EVP_CipherInit_ex(ctx, cph, NULL, RAW(key), RAW(iv), asLogical(encrypt)));
36 
37   int blocksize = EVP_CIPHER_CTX_block_size(ctx);
38   int remainder = LENGTH(x) % blocksize;
39   int outlen = LENGTH(x) + blocksize - remainder;
40   unsigned char *buf = OPENSSL_malloc(outlen);
41   unsigned char *cur = buf;
42 
43   int tmp;
44   bail(EVP_CipherUpdate(ctx, cur, &tmp, RAW(x), LENGTH(x)));
45   cur += tmp;
46 
47 
48 #ifdef EVP_CIPH_GCM_MODE //openssl 1.0.0
49   //in GCM mode, res indicates if the security tag was verified successfully.
50   int res = EVP_CipherFinal_ex(ctx, cur, &tmp);
51   if(EVP_CIPHER_mode(cph) != EVP_CIPH_GCM_MODE)
52     bail(res);
53 #else
54   EVP_CipherFinal_ex(ctx, cur, &tmp);
55 #endif //EVP_CIPH_GCM_MODE
56   cur += tmp;
57 
58   int total = cur - buf;
59   EVP_CIPHER_CTX_cleanup(ctx);
60   EVP_CIPHER_CTX_free(ctx);
61   SEXP out = allocVector(RAWSXP, total);
62   memcpy(RAW(out), buf, total);
63   OPENSSL_free(buf);
64   return out;
65 }
66