1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 
9 #include "pk11pub.h"
10 #include "secerr.h"
11 #include "nss.h"
12 
13 static SECStatus
hex_to_byteval(const char * c2,unsigned char * byteval)14 hex_to_byteval(const char *c2, unsigned char *byteval)
15 {
16     int i;
17     unsigned char offset;
18     *byteval = 0;
19     for (i = 0; i < 2; i++) {
20         if (c2[i] >= '0' && c2[i] <= '9') {
21             offset = c2[i] - '0';
22             *byteval |= offset << 4 * (1 - i);
23         } else if (c2[i] >= 'a' && c2[i] <= 'f') {
24             offset = c2[i] - 'a';
25             *byteval |= (offset + 10) << 4 * (1 - i);
26         } else if (c2[i] >= 'A' && c2[i] <= 'F') {
27             offset = c2[i] - 'A';
28             *byteval |= (offset + 10) << 4 * (1 - i);
29         } else {
30             return SECFailure;
31         }
32     }
33     return SECSuccess;
34 }
35 
36 static SECStatus
aes_encrypt_buf(const unsigned char * key,unsigned int keysize,const unsigned char * iv,unsigned int ivsize,unsigned char * output,unsigned int * outputlen,unsigned int maxoutputlen,const unsigned char * input,unsigned int inputlen,const unsigned char * aad,unsigned int aadlen,unsigned int tagsize)37 aes_encrypt_buf(
38     const unsigned char *key, unsigned int keysize,
39     const unsigned char *iv, unsigned int ivsize,
40     unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
41     const unsigned char *input, unsigned int inputlen,
42     const unsigned char *aad, unsigned int aadlen, unsigned int tagsize)
43 {
44     SECStatus rv = SECFailure;
45     SECItem key_item;
46     PK11SlotInfo *slot = NULL;
47     PK11SymKey *symKey = NULL;
48     CK_NSS_GCM_PARAMS gcm_params;
49     SECItem param;
50 
51     /* Import key into NSS. */
52     key_item.type = siBuffer;
53     key_item.data = (unsigned char *)key; /* const cast */
54     key_item.len = keysize;
55     slot = PK11_GetInternalSlot();
56     symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap,
57                                CKA_ENCRYPT, &key_item, NULL);
58     PK11_FreeSlot(slot);
59     slot = NULL;
60     if (!symKey) {
61         fprintf(stderr, "PK11_ImportSymKey failed\n");
62         goto loser;
63     }
64 
65     gcm_params.pIv = (unsigned char *)iv; /* const cast */
66     gcm_params.ulIvLen = ivsize;
67     gcm_params.pAAD = (unsigned char *)aad; /* const cast */
68     gcm_params.ulAADLen = aadlen;
69     gcm_params.ulTagBits = tagsize * 8;
70 
71     param.type = siBuffer;
72     param.data = (unsigned char *)&gcm_params;
73     param.len = sizeof(gcm_params);
74 
75     if (PK11_Encrypt(symKey, CKM_AES_GCM, &param,
76                      output, outputlen, maxoutputlen,
77                      input, inputlen) != SECSuccess) {
78         fprintf(stderr, "PK11_Encrypt failed\n");
79         goto loser;
80     }
81 
82     rv = SECSuccess;
83 
84 loser:
85     if (symKey != NULL) {
86         PK11_FreeSymKey(symKey);
87     }
88     return rv;
89 }
90 
91 static SECStatus
aes_decrypt_buf(const unsigned char * key,unsigned int keysize,const unsigned char * iv,unsigned int ivsize,unsigned char * output,unsigned int * outputlen,unsigned int maxoutputlen,const unsigned char * input,unsigned int inputlen,const unsigned char * aad,unsigned int aadlen,const unsigned char * tag,unsigned int tagsize)92 aes_decrypt_buf(
93     const unsigned char *key, unsigned int keysize,
94     const unsigned char *iv, unsigned int ivsize,
95     unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
96     const unsigned char *input, unsigned int inputlen,
97     const unsigned char *aad, unsigned int aadlen,
98     const unsigned char *tag, unsigned int tagsize)
99 {
100     SECStatus rv = SECFailure;
101     unsigned char concatenated[11 * 16]; /* 1 to 11 blocks */
102     SECItem key_item;
103     PK11SlotInfo *slot = NULL;
104     PK11SymKey *symKey = NULL;
105     CK_NSS_GCM_PARAMS gcm_params;
106     SECItem param;
107 
108     if (inputlen + tagsize > sizeof(concatenated)) {
109         fprintf(stderr, "aes_decrypt_buf: local buffer too small\n");
110         goto loser;
111     }
112     memcpy(concatenated, input, inputlen);
113     memcpy(concatenated + inputlen, tag, tagsize);
114 
115     /* Import key into NSS. */
116     key_item.type = siBuffer;
117     key_item.data = (unsigned char *)key; /* const cast */
118     key_item.len = keysize;
119     slot = PK11_GetInternalSlot();
120     symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap,
121                                CKA_DECRYPT, &key_item, NULL);
122     PK11_FreeSlot(slot);
123     slot = NULL;
124     if (!symKey) {
125         fprintf(stderr, "PK11_ImportSymKey failed\n");
126         goto loser;
127     }
128 
129     gcm_params.pIv = (unsigned char *)iv;
130     gcm_params.ulIvLen = ivsize;
131     gcm_params.pAAD = (unsigned char *)aad;
132     gcm_params.ulAADLen = aadlen;
133     gcm_params.ulTagBits = tagsize * 8;
134 
135     param.type = siBuffer;
136     param.data = (unsigned char *)&gcm_params;
137     param.len = sizeof(gcm_params);
138 
139     if (PK11_Decrypt(symKey, CKM_AES_GCM, &param,
140                      output, outputlen, maxoutputlen,
141                      concatenated, inputlen + tagsize) != SECSuccess) {
142         goto loser;
143     }
144 
145     rv = SECSuccess;
146 
147 loser:
148     if (symKey != NULL) {
149         PK11_FreeSymKey(symKey);
150     }
151     return rv;
152 }
153 
154 /*
155  * Perform the AES Known Answer Test (KAT) in Galois Counter Mode (GCM).
156  *
157  * respfn is the pathname of the RESPONSE file.
158  */
159 static void
aes_gcm_kat(const char * respfn)160 aes_gcm_kat(const char *respfn)
161 {
162     char buf[512]; /* holds one line from the input REQUEST file.
163                          * needs to be large enough to hold the longest
164                          * line "CIPHERTEXT = <320 hex digits>\n".
165                          */
166     FILE *aesresp; /* input stream from the RESPONSE file */
167     int i, j;
168     unsigned int test_group = 0;
169     unsigned int num_tests = 0;
170     PRBool is_encrypt;
171     unsigned char key[32]; /* 128, 192, or 256 bits */
172     unsigned int keysize = 16;
173     unsigned char iv[10 * 16]; /* 1 to 10 blocks */
174     unsigned int ivsize = 12;
175     unsigned char plaintext[10 * 16]; /* 1 to 10 blocks */
176     unsigned int plaintextlen = 0;
177     unsigned char aad[10 * 16]; /* 1 to 10 blocks */
178     unsigned int aadlen = 0;
179     unsigned char ciphertext[10 * 16]; /* 1 to 10 blocks */
180     unsigned int ciphertextlen = 0;
181     unsigned char tag[16];
182     unsigned int tagsize = 16;
183     unsigned char output[10 * 16]; /* 1 to 10 blocks */
184     unsigned int outputlen = 0;
185 
186     unsigned int expected_keylen = 0;
187     unsigned int expected_ivlen = 0;
188     unsigned int expected_ptlen = 0;
189     unsigned int expected_aadlen = 0;
190     unsigned int expected_taglen = 0;
191     SECStatus rv;
192 
193     if (strstr(respfn, "Encrypt") != NULL) {
194         is_encrypt = PR_TRUE;
195     } else if (strstr(respfn, "Decrypt") != NULL) {
196         is_encrypt = PR_FALSE;
197     } else {
198         fprintf(stderr, "Input file name must contain Encrypt or Decrypt\n");
199         exit(1);
200     }
201     aesresp = fopen(respfn, "r");
202     if (aesresp == NULL) {
203         fprintf(stderr, "Cannot open input file %s\n", respfn);
204         exit(1);
205     }
206     while (fgets(buf, sizeof buf, aesresp) != NULL) {
207         /* a comment or blank line */
208         if (buf[0] == '#' || buf[0] == '\n') {
209             continue;
210         }
211         /* [Keylen = ...], [IVlen = ...], etc. */
212         if (buf[0] == '[') {
213             if (strncmp(&buf[1], "Keylen = ", 9) == 0) {
214                 expected_keylen = atoi(&buf[10]);
215             } else if (strncmp(&buf[1], "IVlen = ", 8) == 0) {
216                 expected_ivlen = atoi(&buf[9]);
217             } else if (strncmp(&buf[1], "PTlen = ", 8) == 0) {
218                 expected_ptlen = atoi(&buf[9]);
219             } else if (strncmp(&buf[1], "AADlen = ", 9) == 0) {
220                 expected_aadlen = atoi(&buf[10]);
221             } else if (strncmp(&buf[1], "Taglen = ", 9) == 0) {
222                 expected_taglen = atoi(&buf[10]);
223 
224                 test_group++;
225                 if (test_group > 1) {
226                     /* Report num_tests for the previous test group. */
227                     printf("%u tests\n", num_tests);
228                 }
229                 num_tests = 0;
230                 printf("Keylen = %u, IVlen = %u, PTlen = %u, AADlen = %u, "
231                        "Taglen = %u: ",
232                        expected_keylen, expected_ivlen,
233                        expected_ptlen, expected_aadlen, expected_taglen);
234                 /* Convert lengths in bits to lengths in bytes. */
235                 PORT_Assert(expected_keylen % 8 == 0);
236                 expected_keylen /= 8;
237                 PORT_Assert(expected_ivlen % 8 == 0);
238                 expected_ivlen /= 8;
239                 PORT_Assert(expected_ptlen % 8 == 0);
240                 expected_ptlen /= 8;
241                 PORT_Assert(expected_aadlen % 8 == 0);
242                 expected_aadlen /= 8;
243                 PORT_Assert(expected_taglen % 8 == 0);
244                 expected_taglen /= 8;
245             } else {
246                 fprintf(stderr, "Unexpected input line: %s\n", buf);
247                 exit(1);
248             }
249             continue;
250         }
251         /* "Count = x" begins a new data set */
252         if (strncmp(buf, "Count", 5) == 0) {
253             /* zeroize the variables for the test with this data set */
254             memset(key, 0, sizeof key);
255             keysize = 0;
256             memset(iv, 0, sizeof iv);
257             ivsize = 0;
258             memset(plaintext, 0, sizeof plaintext);
259             plaintextlen = 0;
260             memset(aad, 0, sizeof aad);
261             aadlen = 0;
262             memset(ciphertext, 0, sizeof ciphertext);
263             ciphertextlen = 0;
264             memset(output, 0, sizeof output);
265             outputlen = 0;
266             num_tests++;
267             continue;
268         }
269         /* Key = ... */
270         if (strncmp(buf, "Key", 3) == 0) {
271             i = 3;
272             while (isspace(buf[i]) || buf[i] == '=') {
273                 i++;
274             }
275             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
276                 hex_to_byteval(&buf[i], &key[j]);
277             }
278             keysize = j;
279             if (keysize != expected_keylen) {
280                 fprintf(stderr, "Unexpected key length: %u vs. %u\n",
281                         keysize, expected_keylen);
282                 exit(1);
283             }
284             continue;
285         }
286         /* IV = ... */
287         if (strncmp(buf, "IV", 2) == 0) {
288             i = 2;
289             while (isspace(buf[i]) || buf[i] == '=') {
290                 i++;
291             }
292             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
293                 hex_to_byteval(&buf[i], &iv[j]);
294             }
295             ivsize = j;
296             if (ivsize != expected_ivlen) {
297                 fprintf(stderr, "Unexpected IV length: %u vs. %u\n",
298                         ivsize, expected_ivlen);
299                 exit(1);
300             }
301             continue;
302         }
303         /* PT = ... */
304         if (strncmp(buf, "PT", 2) == 0) {
305             i = 2;
306             while (isspace(buf[i]) || buf[i] == '=') {
307                 i++;
308             }
309             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
310                 hex_to_byteval(&buf[i], &plaintext[j]);
311             }
312             plaintextlen = j;
313             if (plaintextlen != expected_ptlen) {
314                 fprintf(stderr, "Unexpected PT length: %u vs. %u\n",
315                         plaintextlen, expected_ptlen);
316                 exit(1);
317             }
318 
319             if (!is_encrypt) {
320                 rv = aes_decrypt_buf(key, keysize, iv, ivsize,
321                                      output, &outputlen, sizeof output,
322                                      ciphertext, ciphertextlen, aad, aadlen, tag, tagsize);
323                 if (rv != SECSuccess) {
324                     fprintf(stderr, "aes_decrypt_buf failed\n");
325                     goto loser;
326                 }
327                 if (outputlen != plaintextlen) {
328                     fprintf(stderr, "aes_decrypt_buf: wrong output size\n");
329                     goto loser;
330                 }
331                 if (memcmp(output, plaintext, plaintextlen) != 0) {
332                     fprintf(stderr, "aes_decrypt_buf: wrong plaintext\n");
333                     goto loser;
334                 }
335             }
336             continue;
337         }
338         /* FAIL */
339         if (strncmp(buf, "FAIL", 4) == 0) {
340             plaintextlen = 0;
341 
342             PORT_Assert(!is_encrypt);
343             rv = aes_decrypt_buf(key, keysize, iv, ivsize,
344                                  output, &outputlen, sizeof output,
345                                  ciphertext, ciphertextlen, aad, aadlen, tag, tagsize);
346             if (rv != SECFailure) {
347                 fprintf(stderr, "aes_decrypt_buf succeeded unexpectedly\n");
348                 goto loser;
349             }
350             if (PORT_GetError() != SEC_ERROR_BAD_DATA) {
351                 fprintf(stderr, "aes_decrypt_buf failed with incorrect "
352                                 "error code\n");
353                 goto loser;
354             }
355             continue;
356         }
357         /* AAD = ... */
358         if (strncmp(buf, "AAD", 3) == 0) {
359             i = 3;
360             while (isspace(buf[i]) || buf[i] == '=') {
361                 i++;
362             }
363             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
364                 hex_to_byteval(&buf[i], &aad[j]);
365             }
366             aadlen = j;
367             if (aadlen != expected_aadlen) {
368                 fprintf(stderr, "Unexpected AAD length: %u vs. %u\n",
369                         aadlen, expected_aadlen);
370                 exit(1);
371             }
372             continue;
373         }
374         /* CT = ... */
375         if (strncmp(buf, "CT", 2) == 0) {
376             i = 2;
377             while (isspace(buf[i]) || buf[i] == '=') {
378                 i++;
379             }
380             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
381                 hex_to_byteval(&buf[i], &ciphertext[j]);
382             }
383             ciphertextlen = j;
384             if (ciphertextlen != expected_ptlen) {
385                 fprintf(stderr, "Unexpected CT length: %u vs. %u\n",
386                         ciphertextlen, expected_ptlen);
387                 exit(1);
388             }
389             continue;
390         }
391         /* Tag = ... */
392         if (strncmp(buf, "Tag", 3) == 0) {
393             i = 3;
394             while (isspace(buf[i]) || buf[i] == '=') {
395                 i++;
396             }
397             for (j = 0; isxdigit(buf[i]); i += 2, j++) {
398                 hex_to_byteval(&buf[i], &tag[j]);
399             }
400             tagsize = j;
401             if (tagsize != expected_taglen) {
402                 fprintf(stderr, "Unexpected tag length: %u vs. %u\n",
403                         tagsize, expected_taglen);
404                 exit(1);
405             }
406 
407             if (is_encrypt) {
408                 rv = aes_encrypt_buf(key, keysize, iv, ivsize,
409                                      output, &outputlen, sizeof output,
410                                      plaintext, plaintextlen, aad, aadlen, tagsize);
411                 if (rv != SECSuccess) {
412                     fprintf(stderr, "aes_encrypt_buf failed\n");
413                     goto loser;
414                 }
415                 if (outputlen != plaintextlen + tagsize) {
416                     fprintf(stderr, "aes_encrypt_buf: wrong output size\n");
417                     goto loser;
418                 }
419                 if (memcmp(output, ciphertext, plaintextlen) != 0) {
420                     fprintf(stderr, "aes_encrypt_buf: wrong ciphertext\n");
421                     goto loser;
422                 }
423                 if (memcmp(output + plaintextlen, tag, tagsize) != 0) {
424                     fprintf(stderr, "aes_encrypt_buf: wrong tag\n");
425                     goto loser;
426                 }
427             }
428             continue;
429         }
430     }
431     /* Report num_tests for the last test group. */
432     printf("%u tests\n", num_tests);
433     printf("%u test groups\n", test_group);
434     printf("PASS\n");
435 loser:
436     fclose(aesresp);
437 }
438 
439 int
main(int argc,char ** argv)440 main(int argc, char **argv)
441 {
442     if (argc < 2)
443         exit(1);
444 
445     NSS_NoDB_Init(NULL);
446 
447     /*************/
448     /*   AES     */
449     /*************/
450     if (strcmp(argv[1], "aes") == 0) {
451         /* argv[2]=kat argv[3]=gcm argv[4]=<test name>.rsp */
452         if (strcmp(argv[2], "kat") == 0) {
453             /* Known Answer Test (KAT) */
454             aes_gcm_kat(argv[4]);
455         }
456     }
457 
458     NSS_Shutdown();
459     return 0;
460 }
461