File Coverage

AES.xs
Criterion Covered Total %
statement 124 138 89.8
branch 77 100 77.0
condition n/a
subroutine n/a
pod n/a
total 201 238 84.4


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