File Coverage

AES.xs
Criterion Covered Total %
statement 139 153 90.8
branch 90 114 78.9
condition n/a
subroutine n/a
pod n/a
total 229 267 85.7


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "openssl/opensslv.h"
6              
7             #include
8             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
9             #include
10             #endif
11              
12             #include "ppport.h"
13              
14             /*
15             * Copyright (C) 2006-2024 DelTel, Inc.
16             *
17             * This library is free software; you can redistribute it and/or modify
18             * it under the same terms as Perl itself, either Perl version 5.8.5 or,
19             * at your option, any later version of Perl 5 you may have available.
20             */
21              
22             typedef struct state {
23             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
24             EVP_CIPHER_CTX *enc_ctx;
25             EVP_CIPHER_CTX *dec_ctx;
26             int padding;
27             #else
28             AES_KEY enc_key;
29             AES_KEY dec_key;
30             int padding;
31             #endif
32             } *Crypt__OpenSSL__AES;
33              
34             #define THROW(p_result) if (!(p_result)) { error = 1; goto err; }
35              
36 80           int get_option_ivalue (pTHX_ HV * options, char * name) {
37             SV **svp;
38             IV value;
39              
40 80 100         if (hv_exists(options, name, strlen(name))) {
41 61           svp = hv_fetch(options, name, strlen(name), 0);
42 61 50         if (SvIOKp(*svp)) {
43 61           value = SvIV(*svp);
44 61           return PTR2IV(value);
45             }
46             }
47 19           return 0;
48             }
49              
50 176           char * get_option_svalue (pTHX_ HV * options, char * name) {
51             SV **svp;
52             SV * value;
53              
54 176 100         if (hv_exists(options, name, strlen(name))) {
55 135           svp = hv_fetch(options, name, strlen(name), 0);
56 135           value = *svp;
57 135           return SvPV_nolen(value);
58             }
59              
60 41           return NULL;
61             }
62              
63             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
64             #ifdef LIBRESSL_VERSION_NUMBER
65             const EVP_CIPHER * get_cipher(pTHX_ HV * options, STRLEN keysize) {
66             #else
67 80           EVP_CIPHER * get_cipher(pTHX_ HV * options, STRLEN keysize) {
68             #endif
69 80           char * name = get_option_svalue(aTHX_ options, "cipher");
70              
71 80 100         if (keysize == 16) {
72 23 100         if (name == NULL)
73 1           return (EVP_CIPHER * ) EVP_aes_128_ecb();
74 22 100         else if (strcmp(name, "AES-128-ECB") == 0)
75 7           return (EVP_CIPHER * ) EVP_aes_128_ecb();
76 15 100         else if (strcmp(name, "AES-128-CBC") == 0)
77 2           return (EVP_CIPHER * ) EVP_aes_128_cbc();
78 13 100         else if (strcmp(name, "AES-128-CFB") == 0)
79 1           return (EVP_CIPHER * ) EVP_aes_128_cfb();
80 12 100         else if (strcmp(name, "AES-128-CTR") == 0)
81             #if OPENSSL_VERSION_NUMBER >= 0x10001000L
82 1           return (EVP_CIPHER * ) EVP_aes_128_ctr();
83             #else
84             croak ("CTR ciphers not supported on this version of OpenSSL");
85             #endif
86 11 100         else if (strcmp(name, "AES-128-OFB") == 0)
87 1           return (EVP_CIPHER * ) EVP_aes_128_ofb();
88             else
89 10           croak ("You specified an unsupported cipher for this keysize: 16");
90 57 100         } else if (keysize == 24) {
91 23 100         if (name == NULL)
92 1           return (EVP_CIPHER * ) EVP_aes_192_ecb();
93 22 100         else if (strcmp(name, "AES-192-ECB") == 0)
94 7           return (EVP_CIPHER * ) EVP_aes_192_ecb();
95 15 100         else if (strcmp(name, "AES-192-CBC") == 0)
96 2           return (EVP_CIPHER * ) EVP_aes_192_cbc();
97 13 100         else if (strcmp(name, "AES-192-CFB") == 0)
98 1           return (EVP_CIPHER * ) EVP_aes_192_cfb();
99 12 100         else if (strcmp(name, "AES-192-CTR") == 0)
100             #if OPENSSL_VERSION_NUMBER >= 0x10001000L
101 1           return (EVP_CIPHER * ) EVP_aes_192_ctr();
102             #else
103             croak ("CTR ciphers not supported on this version of OpenSSL");
104             #endif
105 11 100         else if (strcmp(name, "AES-192-OFB") == 0)
106 1           return (EVP_CIPHER * ) EVP_aes_192_ofb();
107             else
108 10           croak ("You specified an unsupported cipher for this keysize: 24");
109 34 50         } else if (keysize == 32) {
110 34 100         if (name == NULL)
111 4           return (EVP_CIPHER * ) EVP_aes_256_ecb();
112 30 100         else if (strcmp(name, "AES-256-ECB") == 0)
113 10           return (EVP_CIPHER * ) EVP_aes_256_ecb();
114 20 100         else if (strcmp(name, "AES-256-CBC") == 0)
115 5           return (EVP_CIPHER * ) EVP_aes_256_cbc();
116 15 100         else if (strcmp(name, "AES-256-CFB") == 0)
117 1           return (EVP_CIPHER * ) EVP_aes_256_cfb();
118 14 100         else if (strcmp(name, "AES-256-CTR") == 0)
119             #if OPENSSL_VERSION_NUMBER >= 0x10001000L
120 1           return (EVP_CIPHER * ) EVP_aes_256_ctr();
121             #else
122             croak ("CTR ciphers not supported on this version of OpenSSL");
123             #endif
124 13 100         else if (strcmp(name, "AES-256-OFB") == 0)
125 1           return (EVP_CIPHER * ) EVP_aes_256_ofb();
126             else
127 12           croak ("You specified an unsupported cipher for this keysize: 32");
128             }
129             else
130 0           croak ("You specified an unsupported keysize (16, 24 or 32 bytes only)");
131             }
132             #endif
133              
134 48           char * get_cipher_name (pTHX_ HV * options, long long keysize) {
135 48           char * value = get_option_svalue(aTHX_ options, "cipher");
136 48 100         if (value == NULL) {
137 6 100         if (keysize == 16)
138 1           return "AES-128-ECB";
139 5 100         else if (keysize == 24)
140 1           return "AES-192-ECB";
141 4 50         else if (keysize == 32)
142 4           return "AES-256-ECB";
143             else
144 0           croak ("get_cipher_name - Unsupported Key Size");
145             }
146              
147 42           return value;
148             }
149              
150 48           unsigned char * get_iv(pTHX_ HV * options) {
151 48           return (unsigned char * ) get_option_svalue(aTHX_ options, "iv");
152             }
153              
154 80           int get_padding(pTHX_ HV * options) {
155 80           return get_option_ivalue(aTHX_ options, "padding");
156             }
157              
158             /* Taken from p5-Git-Raw */
159 76           STATIC HV *ensure_hv(pTHX_ SV *sv, const char *identifier) {
160 76 50         if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
    50          
161 0           croak("Invalid type for '%s', expected a hash", identifier);
162              
163 76           return (HV *) SvRV(sv);
164             }
165              
166             MODULE = Crypt::OpenSSL::AES PACKAGE = Crypt::OpenSSL::AES
167              
168             PROTOTYPES: ENABLE
169              
170             BOOT:
171             {
172 9           HV *stash = gv_stashpv("Crypt::OpenSSL::AES", 0);
173              
174 9           newCONSTSUB (stash, "keysize", newSViv (32));
175 9           newCONSTSUB (stash, "blocksize", newSViv (AES_BLOCK_SIZE));
176             }
177              
178             Crypt::OpenSSL::AES
179             new(class, key_sv, ...)
180             SV * class
181             SV * key_sv
182             CODE:
183             {
184             PERL_UNUSED_ARG(class);
185             STRLEN keysize;
186             unsigned char * key;
187 80           HV * options = newHV();
188             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
189             #ifdef LIBRESSL_VERSION_NUMBER
190             const EVP_CIPHER * cipher;
191             #else
192             EVP_CIPHER * cipher;
193             #endif
194 80           unsigned char * iv = NULL;
195 80           char * cipher_name = NULL;
196             #endif
197 80 100         if (items > 2)
198 76           options = ensure_hv(aTHX_ ST(2), "options");
199              
200 80 50         if (!SvPOK (key_sv))
201 0           croak("Key must be a scalar");
202              
203 80           key = (unsigned char *) SvPVbyte_nolen(key_sv);
204 80           keysize = SvCUR(key_sv);
205              
206 80 100         if (keysize != 16 && keysize != 24 && keysize != 32)
    100          
    50          
207 0           croak ("The key must be 128, 192 or 256 bits long");
208              
209 80           Newz(0, RETVAL, 1, struct state);
210 80           RETVAL->padding = get_padding(aTHX_ options);
211             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
212 80           cipher = get_cipher(aTHX_ options, keysize);
213 48           iv = get_iv(aTHX_ options);
214 48           cipher_name = get_cipher_name(aTHX_ options, keysize);
215 48 100         if ((strcmp(cipher_name, "AES-128-ECB") == 0 ||
216 40 100         strcmp(cipher_name, "AES-192-ECB") == 0 ||
217 32 100         strcmp(cipher_name, "AES-256-ECB") == 0)
218 30 100         && hv_exists(options, "iv", strlen("iv")))
219 1           croak ("%s does not use IV", cipher_name);
220              
221             /* Create and initialise the context */
222 47 50         if(!(RETVAL->enc_ctx = EVP_CIPHER_CTX_new()))
223 0           croak ("EVP_CIPHER_CTX_new failed for enc_ctx");
224              
225 47 50         if(!(RETVAL->dec_ctx = EVP_CIPHER_CTX_new()))
226 0           croak ("EVP_CIPHER_CTX_new failed for dec_ctx");
227              
228 47 50         if(1 != EVP_EncryptInit_ex(RETVAL->enc_ctx, cipher,
229             NULL, key, iv))
230 0           croak ("EVP_EncryptInit_ex failed");
231              
232 47 50         if(1 != EVP_DecryptInit_ex(RETVAL->dec_ctx, cipher,
233             NULL, key, iv))
234 0           croak ("EVP_DecryptInit_ex failed");
235             #if OPENSSL_VERSION_NUMBER >= 0x30000000L
236 47           EVP_CIPHER_free(cipher);
237             #endif
238             #else
239             AES_set_encrypt_key(key,keysize*8,&RETVAL->enc_key);
240             AES_set_decrypt_key(key,keysize*8,&RETVAL->dec_key);
241             #endif
242             }
243             OUTPUT:
244             RETVAL
245              
246             SV *
247             encrypt(self, data)
248             Crypt::OpenSSL::AES self
249             SV *data
250             CODE:
251             {
252             int error;
253             STRLEN size;
254 38           unsigned char * plaintext = (unsigned char *) SvPVbyte(data,size);
255             const char * ciphertext;
256             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
257 38           int out_len = 0;
258 38           int ciphertext_len = 0;
259 38           int block_size = EVP_CIPHER_CTX_block_size(self->enc_ctx);
260             #else
261             int block_size = AES_BLOCK_SIZE;
262             #endif
263 38 50         if (size)
264             {
265 38           error = 0;
266 38 100         if((size % block_size != 0) && self->padding != 1) {
    100          
267 2           croak("AES: Data size must be multiple of blocksize (%d bytes)", block_size);
268             }
269 36           Newxc(ciphertext, size + block_size, unsigned char, const char);
270             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
271 36           EVP_CIPHER_CTX_set_padding(self->enc_ctx, self->padding);
272              
273 36 50         THROW(EVP_EncryptUpdate(self->enc_ctx, (unsigned char *) ciphertext , &out_len, plaintext, size));
274              
275 36           ciphertext_len += out_len;
276              
277 36 50         THROW(EVP_EncryptFinal_ex(self->enc_ctx, (unsigned char *) ciphertext + ciphertext_len, &out_len));
278              
279 36           ciphertext_len += out_len;
280              
281 36           RETVAL = newSVpvn(ciphertext, ciphertext_len);
282              
283 36           err:
284 36 50         if (ciphertext != NULL) Safefree(ciphertext);
285 36 50         if(error)
286 0           croak("Unable to Encrypt");
287             #else
288             AES_encrypt(plaintext, ciphertext, &self->enc_key);
289             RETVAL = newSVpvn((const unsigned char *) ciphertext, size);
290             #endif
291             }
292             else
293             {
294 0           RETVAL = newSVpv ("", 0);
295             }
296             }
297             OUTPUT:
298             RETVAL
299              
300             SV *
301             decrypt(self, data)
302             Crypt::OpenSSL::AES self
303             SV *data
304             CODE:
305             {
306             int error;
307             STRLEN size;
308 35           unsigned char * ciphertext = (unsigned char *) SvPVbyte(data,size);
309             const char * plaintext;
310             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
311 35           int out_len = 0;
312 35           int plaintext_len = 0;
313 35           int block_size = EVP_CIPHER_CTX_block_size(self->dec_ctx);
314             #else
315             int block_size = AES_BLOCK_SIZE;
316             #endif
317 35 50         if (size)
318             {
319 35           error = 0;
320 35 50         if ((size % block_size != 0) && self->padding != 1) {
    0          
321 0           croak("AES: Data size must be multiple of blocksize (%d bytes)", block_size);
322             }
323 35           Newxc(plaintext, size, const unsigned char, const char);
324             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
325 35           EVP_CIPHER_CTX_set_padding(self->dec_ctx, self->padding);
326 35 50         THROW(EVP_DecryptUpdate(self->dec_ctx, (unsigned char *) plaintext, &out_len, ciphertext, size));
327              
328 35           plaintext_len += out_len;
329              
330 35 50         THROW(EVP_DecryptFinal_ex(self->dec_ctx, (unsigned char *) plaintext + out_len, &out_len));
331              
332 35           plaintext_len += out_len;
333              
334 35           RETVAL = newSVpvn(plaintext, plaintext_len);
335             #else
336             AES_decrypt(ciphertext, plaintext, &self->dec_key);
337             RETVAL = newSVpvn((const unsigned char) plaintext, size);
338             #endif
339 35           err:
340 35 50         if(plaintext != NULL) Safefree(plaintext);
341 35 50         if(error)
342 0           croak("Unable to Decrypt");
343             }
344             else
345             {
346 0           RETVAL = newSVpv ("", 0);
347             }
348             }
349             OUTPUT:
350             RETVAL
351              
352             void
353             DESTROY(self)
354             Crypt::OpenSSL::AES self
355             CODE:
356             #if OPENSSL_VERSION_NUMBER >= 0x00908000L
357 47           EVP_CIPHER_CTX_free(self->enc_ctx);
358 47           EVP_CIPHER_CTX_free(self->dec_ctx);
359             #endif
360 47           Safefree(self);