crypto crypto-openssl: support hashing operations
[vpp.git] / src / plugins / unittest / crypto_test.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vlib/vlib.h>
16 #include <vppinfra/time.h>
17 #include <vppinfra/cache.h>
18 #include <vppinfra/error.h>
19 #include <vnet/crypto/crypto.h>
20 #include <unittest/crypto/crypto.h>
21
22 crypto_test_main_t crypto_test_main;
23
24 static int
25 sort_registrations (void *a0, void *a1)
26 {
27   unittest_crypto_test_registration_t **r0 = a0;
28   unittest_crypto_test_registration_t **r1 = a1;
29
30   return (strncmp (r0[0]->name, r1[0]->name, 256));
31 }
32
33 static void
34 print_results (vlib_main_t * vm, unittest_crypto_test_registration_t ** rv,
35                vnet_crypto_op_t * ops, vnet_crypto_op_chunk_t * chunks,
36                u32 n_ops, crypto_test_main_t * tm)
37 {
38   int i;
39   unittest_crypto_test_registration_t *r;
40   vnet_crypto_op_chunk_t *chp;
41   u8 *s = 0, *err = 0;
42   vnet_crypto_op_t *op;
43
44   vec_foreach (op, ops)
45   {
46     int fail = 0;
47     r = rv[op->user_data];
48     unittest_crypto_test_data_t *exp_pt = 0, *exp_ct = 0, exp_pt_data;
49     unittest_crypto_test_data_t *exp_digest = 0, *exp_tag = 0;
50     unittest_crypto_test_data_t *exp_pt_chunks = 0, *exp_ct_chunks = 0;
51
52     switch (vnet_crypto_get_op_type (op->op))
53       {
54       case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
55         exp_tag = &r->tag;
56         /* fall through */
57       case VNET_CRYPTO_OP_TYPE_ENCRYPT:
58         exp_ct = &r->ciphertext;
59         exp_ct_chunks = r->ct_chunks;
60         break;
61       case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
62       case VNET_CRYPTO_OP_TYPE_DECRYPT:
63         if (r->plaintext_incremental)
64           {
65             exp_pt_data.length = r->plaintext_incremental;
66             exp_pt_data.data = tm->inc_data;
67             exp_pt = &exp_pt_data;
68           }
69         else
70           {
71             exp_pt = &r->plaintext;
72             exp_pt_chunks = r->pt_chunks;
73           }
74         break;
75       case VNET_CRYPTO_OP_TYPE_HMAC:
76         exp_digest = &r->digest;
77         break;
78       case VNET_CRYPTO_OP_TYPE_HASH:
79         exp_digest = &r->digest;
80         break;
81       default:
82         ASSERT (0);
83       }
84
85     vec_reset_length (err);
86
87     if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
88       err = format (err, "%sengine error: %U", vec_len (err) ? ", " : "",
89                     format_vnet_crypto_op_status, op->status);
90
91     if (op->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)
92       {
93         if (exp_ct_chunks)
94           {
95             chp = vec_elt_at_index (chunks, op->chunk_index);
96             for (i = 0; i < op->n_chunks; i++)
97               {
98                 if (memcmp (chp->dst, exp_ct_chunks[i].data, chp->len))
99                   err = format (err, "%sciphertext mismatch [chunk %d]",
100                                 vec_len (err) ? ", " : "", i);
101                 chp += 1;
102               }
103           }
104
105         if (exp_pt_chunks)
106           {
107             chp = vec_elt_at_index (chunks, op->chunk_index);
108             for (i = 0; i < op->n_chunks; i++)
109               {
110                 if (memcmp (chp->dst, exp_pt_chunks[i].data, chp->len))
111                   err = format (err, "%splaintext mismatch [chunk %d]",
112                                 vec_len (err) ? ", " : "", i);
113                 chp += 1;
114               }
115           }
116       }
117     else
118       {
119         if (exp_ct && memcmp (op->dst, exp_ct->data, exp_ct->length) != 0)
120           err = format (err, "%sciphertext mismatch",
121                         vec_len (err) ? ", " : "");
122
123         if (exp_pt && memcmp (op->dst, exp_pt->data, exp_pt->length) != 0)
124           err = format (err, "%splaintext mismatch",
125                         vec_len (err) ? ", " : "");
126       }
127
128     if (exp_tag && memcmp (op->tag, exp_tag->data, exp_tag->length) != 0)
129       err = format (err, "%stag mismatch", vec_len (err) ? ", " : "");
130
131     if (exp_digest &&
132         memcmp (op->digest, exp_digest->data, exp_digest->length) != 0)
133       err = format (err, "%sdigest mismatch", vec_len (err) ? ", " : "");
134
135     vec_reset_length (s);
136     s = format (s, "%s (%U)", r->name, format_vnet_crypto_op, op->op,
137                 r->is_chained);
138
139     if (vec_len (err))
140       fail = 1;
141
142     vlib_cli_output (vm, "%-60v%s%v", s, vec_len (err) ? "FAIL: " : "OK",
143                      err);
144     if (tm->verbose)
145       {
146         if (tm->verbose == 2)
147           fail = 1;
148
149         if (exp_ct && fail)
150           vlib_cli_output (vm, "Expected ciphertext:\n%U"
151                            "\nCalculated ciphertext:\n%U",
152                            format_hexdump, exp_ct->data, exp_ct->length,
153                            format_hexdump, op->dst, exp_ct->length);
154         if (exp_pt && fail)
155           vlib_cli_output (vm, "Expected plaintext:\n%U"
156                            "\nCalculated plaintext:\n%U",
157                            format_hexdump, exp_pt->data, exp_pt->length,
158                            format_hexdump, op->dst, exp_pt->length);
159         if (r->tag.length && fail)
160           vlib_cli_output (vm, "Expected tag:\n%U"
161                            "\nCalculated tag:\n%U",
162                            format_hexdump, r->tag.data, r->tag.length,
163                            format_hexdump, op->tag, op->tag_len);
164         if (exp_digest && fail)
165           vlib_cli_output (vm, "Expected digest:\n%U"
166                            "\nCalculated Digest:\n%U",
167                            format_hexdump, exp_digest->data,
168                            exp_digest->length, format_hexdump, op->digest,
169                            op->digest_len);
170       }
171   }
172   vec_free (err);
173   vec_free (s);
174 }
175
176 static void
177 validate_data (u8 ** data, u32 len)
178 {
179   u32 i, diff, old_len;
180   if (vec_len (data[0]) >= len)
181     return;
182
183   old_len = vec_len (data[0]);
184   diff = len - vec_len (data[0]);
185   vec_validate (data[0], old_len + diff - 1);
186   for (i = old_len; i < len; i++)
187     data[0][i] = (u8) i;
188 }
189
190 static void
191 generate_digest (vlib_main_t * vm,
192                  unittest_crypto_test_registration_t * r,
193                  vnet_crypto_op_id_t id)
194 {
195   crypto_test_main_t *cm = &crypto_test_main;
196   vnet_crypto_op_t op[1];
197   vnet_crypto_op_init (op, id);
198   vec_validate (r->digest.data, r->digest.length - 1);
199   op->src = cm->inc_data;
200   op->len = r->plaintext_incremental;
201   op->digest = r->digest.data;
202   op->digest_len = r->digest.length;
203   op->key_index = vnet_crypto_key_add (vm, r->alg,
204                                        cm->inc_data, r->key.length);
205
206   /* at this point openssl is set for each algo */
207   vnet_crypto_process_ops (vm, op, 1);
208 }
209
210 static int
211 restore_engines (u32 * engs)
212 {
213   vnet_crypto_main_t *cm = &crypto_main;
214   u32 i;
215   vnet_crypto_engine_t *ce;
216
217   for (i = 1; i < VNET_CRYPTO_N_OP_IDS; i++)
218     {
219       vnet_crypto_op_data_t *od = &cm->opt_data[i];
220
221       if (engs[i] != ~0)
222         {
223           ce = vec_elt_at_index (cm->engines, engs[i]);
224           od->active_engine_index_simple = engs[i];
225           cm->ops_handlers[i] = ce->ops_handlers[i];
226         }
227     }
228
229   return 0;
230 }
231
232 static int
233 save_current_engines (u32 * engs)
234 {
235   vnet_crypto_main_t *cm = &crypto_main;
236   uword *p;
237   u32 i;
238   vnet_crypto_engine_t *ce;
239
240   p = hash_get_mem (cm->engine_index_by_name, "openssl");
241   if (!p)
242     return -1;
243
244   ce = vec_elt_at_index (cm->engines, p[0]);
245
246   /* set openssl for all crypto algs to generate expected data */
247   for (i = 1; i < VNET_CRYPTO_N_OP_IDS; i++)
248     {
249       vnet_crypto_op_data_t *od = &cm->opt_data[i];
250       if (od->active_engine_index_simple != ~0)
251         {
252           /* save engine index */
253           engs[i] = od->active_engine_index_simple;
254           od->active_engine_index_simple = ce - cm->engines;
255           cm->ops_handlers[i] = ce->ops_handlers[i];
256         }
257     }
258
259   return 0;
260 }
261
262 static clib_error_t *
263 test_crypto_incremental (vlib_main_t * vm, crypto_test_main_t * tm,
264                          unittest_crypto_test_registration_t ** rv, u32 n_ops,
265                          u32 computed_data_total_len)
266 {
267   vnet_crypto_main_t *cm = &crypto_main;
268   vnet_crypto_alg_data_t *ad;
269   vnet_crypto_key_index_t *key_indices = 0;
270   u32 i;
271   unittest_crypto_test_registration_t *r;
272   vnet_crypto_op_t *ops = 0, *op;
273   u8 *encrypted_data = 0, *decrypted_data = 0, *s = 0, *err = 0;
274
275   if (n_ops == 0)
276     return 0;
277
278   vec_validate_aligned (encrypted_data, computed_data_total_len - 1,
279                         CLIB_CACHE_LINE_BYTES);
280   vec_validate_aligned (decrypted_data, computed_data_total_len - 1,
281                         CLIB_CACHE_LINE_BYTES);
282   vec_validate_aligned (ops, n_ops - 1, CLIB_CACHE_LINE_BYTES);
283   computed_data_total_len = 0;
284
285   op = ops;
286   /* first stage: encrypt only */
287
288   vec_foreach_index (i, rv)
289   {
290     r = rv[i];
291     int t;
292     ad = vec_elt_at_index (cm->algs, r->alg);
293     for (t = 0; t < VNET_CRYPTO_OP_N_TYPES; t++)
294       {
295         vnet_crypto_op_id_t id = ad->op_by_type[t];
296
297         if (id == 0)
298           continue;
299
300         switch (t)
301           {
302           case VNET_CRYPTO_OP_TYPE_ENCRYPT:
303             vnet_crypto_op_init (op, id);
304             op->iv = tm->inc_data;
305             op->key_index = vnet_crypto_key_add (vm, r->alg,
306                                                  tm->inc_data, r->key.length);
307             vec_add1 (key_indices, op->key_index);
308             op->len = r->plaintext_incremental;
309             op->src = tm->inc_data;
310             op->dst = encrypted_data + computed_data_total_len;
311             computed_data_total_len += r->plaintext_incremental;
312             op->user_data = i;
313             op++;
314             break;
315           case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
316             vnet_crypto_op_init (op, id);
317             op->iv = tm->inc_data;
318             op->key_index = vnet_crypto_key_add (vm, r->alg,
319                                                  tm->inc_data, r->key.length);
320             vec_add1 (key_indices, op->key_index);
321             op->aad = tm->inc_data;
322             op->aad_len = r->aad.length;
323             op->len = r->plaintext_incremental;
324             op->dst = encrypted_data + computed_data_total_len;
325             computed_data_total_len += r->plaintext_incremental;
326             op->src = tm->inc_data;
327             op->tag = encrypted_data + computed_data_total_len;
328             computed_data_total_len += r->tag.length;
329             op->tag_len = r->tag.length;
330             op->user_data = i;
331             op++;
332             break;
333           case VNET_CRYPTO_OP_TYPE_HMAC:
334             /* compute hmac in the next stage */
335             op->op = VNET_CRYPTO_OP_NONE;
336             computed_data_total_len += r->digest.length;
337             op->user_data = i;
338             op++;
339             break;
340           default:
341             break;
342           };
343       }
344   }
345
346   vnet_crypto_process_ops (vm, ops, n_ops);
347   computed_data_total_len = 0;
348
349   /* second stage: hash/decrypt previously encrypted data */
350   op = ops;
351
352   vec_foreach_index (i, rv)
353   {
354     r = rv[i];
355     int t;
356     ad = vec_elt_at_index (cm->algs, r->alg);
357     for (t = 0; t < VNET_CRYPTO_OP_N_TYPES; t++)
358       {
359         vnet_crypto_op_id_t id = ad->op_by_type[t];
360
361         if (id == 0)
362           continue;
363
364         switch (t)
365           {
366           case VNET_CRYPTO_OP_TYPE_DECRYPT:
367             vnet_crypto_op_init (op, id);
368             op->iv = tm->inc_data;
369             op->key_index = vnet_crypto_key_add (vm, r->alg,
370                                                  tm->inc_data, r->key.length);
371             vec_add1 (key_indices, op->key_index);
372             op->len = r->plaintext_incremental;
373             op->src = encrypted_data + computed_data_total_len;
374             op->dst = decrypted_data + computed_data_total_len;
375             computed_data_total_len += r->plaintext_incremental;
376             op->user_data = i;
377             op++;
378             break;
379           case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
380             vnet_crypto_op_init (op, id);
381             op->iv = tm->inc_data;
382             op->key_index = vnet_crypto_key_add (vm, r->alg,
383                                                  tm->inc_data, r->key.length);
384             vec_add1 (key_indices, op->key_index);
385             op->aad = tm->inc_data;
386             op->aad_len = r->aad.length;
387             op->len = r->plaintext_incremental;
388             op->dst = decrypted_data + computed_data_total_len;
389             op->src = encrypted_data + computed_data_total_len;
390             computed_data_total_len += r->plaintext_incremental;
391
392             op->tag = encrypted_data + computed_data_total_len;
393             computed_data_total_len += r->tag.length;
394             op->tag_len = r->tag.length;
395             op->user_data = i;
396             op++;
397             break;
398           case VNET_CRYPTO_OP_TYPE_HMAC:
399             vnet_crypto_op_init (op, id);
400             op->key_index = vnet_crypto_key_add (vm, r->alg,
401                                                  tm->inc_data, r->key.length);
402             vec_add1 (key_indices, op->key_index);
403             op->src = tm->inc_data;
404             op->len = r->plaintext_incremental;
405             op->digest_len = r->digest.length;
406             op->digest = encrypted_data + computed_data_total_len;
407             computed_data_total_len += r->digest.length;
408             op->user_data = i;
409             op++;
410             break;
411           default:
412             break;
413           };
414
415       }
416   }
417
418   vnet_crypto_process_ops (vm, ops, n_ops);
419   print_results (vm, rv, ops, 0, n_ops, tm);
420
421   vec_foreach_index (i, key_indices) vnet_crypto_key_del (vm, key_indices[i]);
422   vec_free (tm->inc_data);
423   vec_free (ops);
424   vec_free (encrypted_data);
425   vec_free (decrypted_data);
426   vec_free (err);
427   vec_free (s);
428   return 0;
429 }
430
431 static clib_error_t *
432 test_crypto_static (vlib_main_t * vm, crypto_test_main_t * tm,
433                     unittest_crypto_test_registration_t ** rv, u32 n_ops,
434                     u32 n_chained_ops, u32 computed_data_total_len)
435 {
436   unittest_crypto_test_data_t *pt, *ct;
437   vnet_crypto_op_chunk_t *chunks = 0, ch;
438   unittest_crypto_test_registration_t *r;
439   vnet_crypto_op_t *ops = 0, *op, *chained_ops = 0;
440   vnet_crypto_op_t *current_chained_op = 0, *current_op = 0;
441   vnet_crypto_main_t *cm = &crypto_main;
442   vnet_crypto_alg_data_t *ad;
443   vnet_crypto_key_index_t *key_indices = 0;
444   u8 *computed_data = 0;
445   u32 i;
446
447   vec_sort_with_function (rv, sort_registrations);
448
449   vec_validate_aligned (computed_data, computed_data_total_len - 1,
450                         CLIB_CACHE_LINE_BYTES);
451   vec_validate_aligned (ops, n_ops - 1, CLIB_CACHE_LINE_BYTES);
452   vec_validate_aligned (chained_ops, n_chained_ops - 1,
453                         CLIB_CACHE_LINE_BYTES);
454   computed_data_total_len = 0;
455
456   current_op = ops;
457   current_chained_op = chained_ops;
458   /* *INDENT-OFF* */
459   vec_foreach_index (i, rv)
460     {
461       r = rv[i];
462       int t;
463       ad = vec_elt_at_index (cm->algs, r->alg);
464       for (t = 0; t < VNET_CRYPTO_OP_N_TYPES; t++)
465         {
466           vnet_crypto_op_id_t id = ad->op_by_type[t];
467
468           if (id == 0)
469             continue;
470
471           if (r->is_chained)
472           {
473             op = current_chained_op;
474             current_chained_op += 1;
475           }
476           else
477           {
478             op = current_op;
479             current_op += 1;
480           }
481
482           vnet_crypto_op_init (op, id);
483
484           switch (t)
485             {
486             case VNET_CRYPTO_OP_TYPE_ENCRYPT:
487             case VNET_CRYPTO_OP_TYPE_DECRYPT:
488               op->iv = r->iv.data;
489               op->key_index = vnet_crypto_key_add (vm, r->alg,
490                                                    r->key.data,
491                                                    r->key.length);
492               vec_add1 (key_indices, op->key_index);
493
494               if (r->is_chained)
495               {
496               pt = r->pt_chunks;
497               ct = r->ct_chunks;
498               op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
499               op->chunk_index = vec_len (chunks);
500               while (pt->data)
501                 {
502                   ch.src = t == VNET_CRYPTO_OP_TYPE_ENCRYPT ?
503                     pt->data : ct->data;
504                   ch.len = pt->length;
505                   ch.dst = computed_data + computed_data_total_len;
506                   computed_data_total_len += pt->length;
507                   vec_add1 (chunks, ch);
508                   op->n_chunks++;
509                   pt++;
510                   ct++;
511                 }
512               }
513               else
514               {
515               op->len = r->plaintext.length;
516               op->src = t == VNET_CRYPTO_OP_TYPE_ENCRYPT ?
517                 r->plaintext.data : r->ciphertext.data;
518               op->dst = computed_data + computed_data_total_len;
519               computed_data_total_len += r->ciphertext.length;
520               }
521               break;
522             case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
523             case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
524               if (r->is_chained)
525               {
526               op->iv = r->iv.data;
527               op->key_index = vnet_crypto_key_add (vm, r->alg,
528                                                    r->key.data,
529                                                    r->key.length);
530               vec_add1 (key_indices, op->key_index);
531               op->aad = r->aad.data;
532               op->aad_len = r->aad.length;
533               if (t == VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT)
534                 {
535                   pt = r->pt_chunks;
536                   op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
537                   op->chunk_index = vec_len (chunks);
538                   while (pt->data)
539                     {
540                       clib_memset (&ch, 0, sizeof (ch));
541                       ch.src = pt->data;
542                       ch.len = pt->length;
543                       ch.dst = computed_data + computed_data_total_len;
544                       computed_data_total_len += pt->length;
545                       vec_add1 (chunks, ch);
546                       op->n_chunks++;
547                       pt++;
548                     }
549                   op->tag = computed_data + computed_data_total_len;
550                   computed_data_total_len += r->tag.length;
551                 }
552               else
553                 {
554                   ct = r->ct_chunks;
555                   op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
556                   op->chunk_index = vec_len (chunks);
557                   while (ct->data)
558                     {
559                       clib_memset (&ch, 0, sizeof (ch));
560                       ch.src = ct->data;
561                       ch.len = ct->length;
562                       ch.dst = computed_data + computed_data_total_len;
563                       computed_data_total_len += ct->length;
564                       vec_add1 (chunks, ch);
565                       op->n_chunks++;
566                       ct++;
567                     }
568                   op->tag = r->tag.data;
569                 }
570               op->tag_len = r->tag.length;
571               }
572               else
573               {
574               op->iv = r->iv.data;
575               op->key_index = vnet_crypto_key_add (vm, r->alg,
576                                                    r->key.data,
577                                                    r->key.length);
578               vec_add1 (key_indices, op->key_index);
579               op->aad = r->aad.data;
580               op->aad_len = r->aad.length;
581               op->len = r->plaintext.length;
582               op->dst = computed_data + computed_data_total_len;
583               computed_data_total_len += r->ciphertext.length;
584
585               if (t == VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT)
586                 {
587                   op->src = r->plaintext.data;
588                   op->tag = computed_data + computed_data_total_len;
589                   computed_data_total_len += r->tag.length;
590                 }
591               else
592                 {
593                   op->tag = r->tag.data;
594                   op->src = r->ciphertext.data;
595                 }
596               op->tag_len = r->tag.length;
597               }
598               break;
599             case VNET_CRYPTO_OP_TYPE_HMAC:
600               if (r->is_chained)
601               {
602               op->key_index = vnet_crypto_key_add (vm, r->alg,
603                                                    r->key.data,
604                                                    r->key.length);
605               vec_add1 (key_indices, op->key_index);
606               op->digest_len = r->digest.length;
607               op->digest = computed_data + computed_data_total_len;
608               computed_data_total_len += r->digest.length;
609               pt = r->pt_chunks;
610               op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
611               op->chunk_index = vec_len (chunks);
612               while (pt->data)
613                 {
614                   clib_memset (&ch, 0, sizeof (ch));
615                   ch.src = pt->data;
616                   ch.len = pt->length;
617                   vec_add1 (chunks, ch);
618                   op->n_chunks++;
619                   pt++;
620                 }
621               }
622               else
623               {
624               op->key_index = vnet_crypto_key_add (vm, r->alg,
625                                                    r->key.data,
626                                                    r->key.length);
627               vec_add1 (key_indices, op->key_index);
628               op->digest_len = r->digest.length;
629               op->digest = computed_data + computed_data_total_len;
630               computed_data_total_len += r->digest.length;
631               op->src = r->plaintext.data;
632               op->len = r->plaintext.length;
633               }
634               break;
635             case VNET_CRYPTO_OP_TYPE_HASH:
636               op->digest = computed_data + computed_data_total_len;
637               computed_data_total_len += r->digest.length;
638               op->src = r->plaintext.data;
639               op->len = r->plaintext.length;
640               break;
641             default:
642               break;
643             };
644
645           op->user_data = i;
646         }
647     }
648   /* *INDENT-ON* */
649
650   vnet_crypto_process_ops (vm, ops, vec_len (ops));
651   vnet_crypto_process_chained_ops (vm, chained_ops, chunks,
652                                    vec_len (chained_ops));
653
654   print_results (vm, rv, ops, chunks, vec_len (ops), tm);
655   print_results (vm, rv, chained_ops, chunks, vec_len (chained_ops), tm);
656
657   vec_foreach_index (i, key_indices) vnet_crypto_key_del (vm, key_indices[i]);
658
659   vec_free (computed_data);
660   vec_free (ops);
661   vec_free (chained_ops);
662   vec_free (chunks);
663   return 0;
664 }
665
666 static u32
667 test_crypto_get_key_sz (vnet_crypto_alg_t alg)
668 {
669   switch (alg)
670     {
671 #define _(n, s, l) \
672   case VNET_CRYPTO_ALG_##n: \
673     return l;
674   /* *INDENT-OFF* */
675   foreach_crypto_cipher_alg
676   foreach_crypto_aead_alg
677   /* *INDENT-ON* */
678 #undef _
679     case VNET_CRYPTO_ALG_HMAC_MD5:
680     case VNET_CRYPTO_ALG_HMAC_SHA1:
681       return 20;
682     case VNET_CRYPTO_ALG_HMAC_SHA224:
683       return 28;
684     case VNET_CRYPTO_ALG_HMAC_SHA256:
685       return 32;
686     case VNET_CRYPTO_ALG_HMAC_SHA384:
687       return 48;
688     case VNET_CRYPTO_ALG_HMAC_SHA512:
689       return 64;
690     default:
691       return 0;
692     }
693   return 0;
694 }
695
696 static clib_error_t *
697 test_crypto (vlib_main_t * vm, crypto_test_main_t * tm)
698 {
699   clib_error_t *err = 0;
700   vnet_crypto_main_t *cm = &crypto_main;
701   unittest_crypto_test_registration_t *r = tm->test_registrations;
702   unittest_crypto_test_registration_t **static_tests = 0, **inc_tests = 0;
703   u32 i, j, n_ops_static = 0, n_ops_incr = 0, n_chained_ops = 0;
704   vnet_crypto_alg_data_t *ad;
705   u32 computed_data_total_len = 0;
706   u32 computed_data_total_incr_len = 0;
707   u32 saved_engs[VNET_CRYPTO_N_OP_IDS] = { ~0, };
708   unittest_crypto_test_data_t *ct;
709
710   /* pre-allocate plaintext data with reasonable length */
711   validate_data (&tm->inc_data, 2048);
712
713   int rc = save_current_engines (saved_engs);
714   if (rc)
715     return clib_error_return (0, "failed to set default crypto engine!");
716
717   /* construct registration vector */
718   while (r)
719     {
720       if (r->plaintext_incremental)
721         vec_add1 (inc_tests, r);
722       else
723         vec_add1 (static_tests, r);
724
725       ad = vec_elt_at_index (cm->algs, r->alg);
726
727       for (i = 0; i < VNET_CRYPTO_OP_N_TYPES; i++)
728         {
729           vnet_crypto_op_id_t id = ad->op_by_type[i];
730
731           if (id == 0)
732             continue;
733
734           switch (i)
735             {
736             case VNET_CRYPTO_OP_TYPE_ENCRYPT:
737               if (r->plaintext_incremental)
738                 {
739                   computed_data_total_incr_len += r->plaintext_incremental;
740                   n_ops_incr += 1;
741                 }
742               /* fall though */
743             case VNET_CRYPTO_OP_TYPE_DECRYPT:
744             case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
745               if (r->is_chained)
746                 {
747                   ct = r->ct_chunks;
748                   j = 0;
749                   while (ct->data)
750                     {
751                       if (j > CRYPTO_TEST_MAX_OP_CHUNKS)
752                         return clib_error_return (0,
753                                                   "test case '%s' exceeds extra data!",
754                                                   r->name);
755                       computed_data_total_len += ct->length;
756                       ct++;
757                       j++;
758                     }
759                   n_chained_ops += 1;
760                 }
761               else if (!r->plaintext_incremental)
762                 {
763                   computed_data_total_len += r->ciphertext.length;
764                   n_ops_static += 1;
765                 }
766               break;
767             case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
768               if (r->plaintext_incremental)
769                 {
770                   computed_data_total_incr_len += r->plaintext_incremental;
771                   computed_data_total_incr_len += r->tag.length;
772                   n_ops_incr += 1;
773                 }
774               else
775                 {
776                   computed_data_total_len += r->ciphertext.length;
777                   computed_data_total_len += r->tag.length;
778                   if (r->is_chained)
779                     {
780                       ct = r->ct_chunks;
781                       j = 0;
782                       while (ct->data)
783                         {
784                           if (j > CRYPTO_TEST_MAX_OP_CHUNKS)
785                             return clib_error_return (0,
786                                                       "test case '%s' exceeds extra data!",
787                                                       r->name);
788                           computed_data_total_len += ct->length;
789                           ct++;
790                           j++;
791                         }
792                       n_chained_ops += 1;
793                     }
794                   else
795                     n_ops_static += 1;
796                 }
797               break;
798             case VNET_CRYPTO_OP_TYPE_HMAC:
799               if (r->plaintext_incremental)
800                 {
801                   computed_data_total_incr_len += r->digest.length;
802                   n_ops_incr += 1;
803                   generate_digest (vm, r, id);
804                 }
805               else
806                 {
807                   computed_data_total_len += r->digest.length;
808                   if (r->is_chained)
809                     n_chained_ops += 1;
810                   else
811                     n_ops_static += 1;
812                 }
813               break;
814             case VNET_CRYPTO_OP_TYPE_HASH:
815               computed_data_total_len += r->digest.length;
816               n_ops_static += 1;
817               break;
818             default:
819               break;
820             };
821         }
822
823       /* next: */
824       r = r->next;
825     }
826   restore_engines (saved_engs);
827
828   err = test_crypto_static (vm, tm, static_tests, n_ops_static, n_chained_ops,
829                             computed_data_total_len);
830   if (err)
831     goto done;
832
833   err = test_crypto_incremental (vm, tm, inc_tests, n_ops_incr,
834                                  computed_data_total_incr_len);
835
836   r = tm->test_registrations;
837   while (r)
838     {
839       if (r->plaintext_incremental)
840         vec_free (r->digest.data);
841       r = r->next;
842     }
843
844 done:
845   vec_free (inc_tests);
846   vec_free (static_tests);
847   return err;
848 }
849
850 static clib_error_t *
851 test_crypto_perf (vlib_main_t * vm, crypto_test_main_t * tm)
852 {
853   vnet_crypto_main_t *cm = &crypto_main;
854   clib_error_t *err = 0;
855   u32 n_buffers, n_alloc = 0, warmup_rounds, rounds;
856   u32 *buffer_indices = 0;
857   vnet_crypto_op_t *ops1 = 0, *ops2 = 0, *op1, *op2;
858   vnet_crypto_alg_data_t *ad = vec_elt_at_index (cm->algs, tm->alg);
859   vnet_crypto_key_index_t key_index = ~0;
860   u8 key[32];
861   int buffer_size = vlib_buffer_get_default_data_size (vm);
862   u64 seed = clib_cpu_time_now ();
863   u64 t0[5], t1[5], t2[5], n_bytes = 0;
864   int i, j;
865
866   if (tm->buffer_size > buffer_size)
867     return clib_error_return (0, "buffer size must be <= %u", buffer_size);
868
869   rounds = tm->rounds ? tm->rounds : 100;
870   n_buffers = tm->n_buffers ? tm->n_buffers : 256;
871   buffer_size = tm->buffer_size ? tm->buffer_size : 2048;
872   warmup_rounds = tm->warmup_rounds ? tm->warmup_rounds : 100;
873
874   if (buffer_size > vlib_buffer_get_default_data_size (vm))
875     return clib_error_return (0, "buffer size too big");
876
877   vec_validate_aligned (buffer_indices, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
878   vec_validate_aligned (ops1, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
879   vec_validate_aligned (ops2, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
880
881   n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_buffers);
882   if (n_alloc != n_buffers)
883     {
884       if (n_alloc)
885         vlib_buffer_free (vm, buffer_indices, n_alloc);
886       err = clib_error_return (0, "buffer alloc failure");
887       goto done;
888     }
889
890   vlib_cli_output (vm, "%U: n_buffers %u buffer-size %u rounds %u "
891                    "warmup-rounds %u",
892                    format_vnet_crypto_alg, tm->alg, n_buffers, buffer_size,
893                    rounds, warmup_rounds);
894   vlib_cli_output (vm, "   cpu-freq %.2f GHz",
895                    (f64) vm->clib_time.clocks_per_second * 1e-9);
896
897   vnet_crypto_op_type_t ot = 0;
898
899   for (i = 0; i < sizeof (key); i++)
900     key[i] = i;
901
902   key_index = vnet_crypto_key_add (vm, tm->alg, key,
903                                    test_crypto_get_key_sz (tm->alg));
904
905   for (i = 0; i < VNET_CRYPTO_OP_N_TYPES; i++)
906     {
907       vnet_crypto_op_id_t id = ad->op_by_type[i];
908       if (id == 0)
909         continue;
910       ot = i;
911       break;
912     }
913
914   for (i = 0; i < n_buffers; i++)
915     {
916       vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
917       op1 = ops1 + i;
918       op2 = ops2 + i;
919
920       switch (ot)
921         {
922         case VNET_CRYPTO_OP_TYPE_ENCRYPT:
923         case VNET_CRYPTO_OP_TYPE_DECRYPT:
924           vnet_crypto_op_init (op1,
925                                ad->op_by_type[VNET_CRYPTO_OP_TYPE_ENCRYPT]);
926           vnet_crypto_op_init (op2,
927                                ad->op_by_type[VNET_CRYPTO_OP_TYPE_DECRYPT]);
928           op1->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
929           op1->src = op2->src = op1->dst = op2->dst = b->data;
930           op1->key_index = op2->key_index = key_index;
931           op1->iv = op2->iv = b->data - 64;
932           n_bytes += op1->len = op2->len = buffer_size;
933           break;
934         case VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT:
935         case VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT:
936           vnet_crypto_op_init (op1,
937                                ad->op_by_type
938                                [VNET_CRYPTO_OP_TYPE_AEAD_ENCRYPT]);
939           vnet_crypto_op_init (op2,
940                                ad->op_by_type
941                                [VNET_CRYPTO_OP_TYPE_AEAD_DECRYPT]);
942           op1->src = op2->src = op1->dst = op2->dst = b->data;
943           op1->key_index = op2->key_index = key_index;
944           op1->tag = op2->tag = b->data - 32;
945           op1->iv = op2->iv = b->data - 64;
946           op1->aad = op2->aad = b->data - VLIB_BUFFER_PRE_DATA_SIZE;
947           op1->aad_len = op2->aad_len = 64;
948           op1->tag_len = op2->tag_len = 16;
949           n_bytes += op1->len = op2->len = buffer_size;
950           break;
951         case VNET_CRYPTO_OP_TYPE_HMAC:
952           vnet_crypto_op_init (op1, ad->op_by_type[VNET_CRYPTO_OP_TYPE_HMAC]);
953           op1->src = b->data;
954           op1->key_index = key_index;
955           op1->iv = 0;
956           op1->digest = b->data - VLIB_BUFFER_PRE_DATA_SIZE;
957           op1->digest_len = 0;
958           n_bytes += op1->len = buffer_size;
959           break;
960         default:
961           return 0;
962         }
963
964       for (j = -VLIB_BUFFER_PRE_DATA_SIZE; j < buffer_size; j += 8)
965         *(u64 *) (b->data + j) = 1 + random_u64 (&seed);
966     }
967
968   for (i = 0; i < 5; i++)
969     {
970       for (j = 0; j < warmup_rounds; j++)
971         {
972           vnet_crypto_process_ops (vm, ops1, n_buffers);
973           if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
974             vnet_crypto_process_ops (vm, ops2, n_buffers);
975         }
976
977       t0[i] = clib_cpu_time_now ();
978       for (j = 0; j < rounds; j++)
979         vnet_crypto_process_ops (vm, ops1, n_buffers);
980       t1[i] = clib_cpu_time_now ();
981
982       if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
983         {
984           for (j = 0; j < rounds; j++)
985             vnet_crypto_process_ops (vm, ops2, n_buffers);
986           t2[i] = clib_cpu_time_now ();
987         }
988     }
989
990   for (i = 0; i < 5; i++)
991     {
992       f64 tpb1 = (f64) (t1[i] - t0[i]) / (n_bytes * rounds);
993       f64 gbps1 = vm->clib_time.clocks_per_second * 1e-9 * 8 / tpb1;
994       f64 tpb2, gbps2;
995
996       if (ot != VNET_CRYPTO_OP_TYPE_HMAC)
997         {
998           tpb2 = (f64) (t2[i] - t1[i]) / (n_bytes * rounds);
999           gbps2 = vm->clib_time.clocks_per_second * 1e-9 * 8 / tpb2;
1000           vlib_cli_output (vm, "%-2u: encrypt %.03f ticks/byte, %.02f Gbps; "
1001                            "decrypt %.03f ticks/byte, %.02f Gbps",
1002                            i + 1, tpb1, gbps1, tpb2, gbps2);
1003         }
1004       else
1005         {
1006           vlib_cli_output (vm, "%-2u: hash %.03f ticks/byte, %.02f Gbps\n",
1007                            i + 1, tpb1, gbps1);
1008         }
1009     }
1010
1011 done:
1012   if (n_alloc)
1013     vlib_buffer_free (vm, buffer_indices, n_alloc);
1014
1015   if (key_index != ~0)
1016     vnet_crypto_key_del (vm, key_index);
1017
1018   vec_free (buffer_indices);
1019   vec_free (ops1);
1020   vec_free (ops2);
1021   return err;
1022 }
1023
1024 static clib_error_t *
1025 test_crypto_command_fn (vlib_main_t * vm,
1026                         unformat_input_t * input, vlib_cli_command_t * cmd)
1027 {
1028   crypto_test_main_t *tm = &crypto_test_main;
1029   unittest_crypto_test_registration_t *tr;
1030   int is_perf = 0;
1031
1032   tr = tm->test_registrations;
1033   memset (tm, 0, sizeof (crypto_test_main_t));
1034   tm->test_registrations = tr;
1035   tm->alg = ~0;
1036
1037   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1038     {
1039       if (unformat (input, "verbose"))
1040         tm->verbose = 1;
1041       else if (unformat (input, "detail"))
1042         tm->verbose = 2;
1043       else
1044         if (unformat (input, "perf %U", unformat_vnet_crypto_alg, &tm->alg))
1045         is_perf = 1;
1046       else if (unformat (input, "buffers %u", &tm->n_buffers))
1047         ;
1048       else if (unformat (input, "rounds %u", &tm->rounds))
1049         ;
1050       else if (unformat (input, "warmup-rounds %u", &tm->warmup_rounds))
1051         ;
1052       else if (unformat (input, "buffer-size %u", &tm->buffer_size))
1053         ;
1054       else
1055         return clib_error_return (0, "unknown input '%U'",
1056                                   format_unformat_error, input);
1057     }
1058
1059   if (is_perf)
1060     return test_crypto_perf (vm, tm);
1061   else
1062     return test_crypto (vm, tm);
1063 }
1064
1065 /* *INDENT-OFF* */
1066 VLIB_CLI_COMMAND (test_crypto_command, static) =
1067 {
1068   .path = "test crypto",
1069   .short_help = "test crypto",
1070   .function = test_crypto_command_fn,
1071 };
1072 /* *INDENT-ON* */
1073
1074 static clib_error_t *
1075 crypto_test_init (vlib_main_t * vm)
1076 {
1077   return (0);
1078 }
1079
1080 VLIB_INIT_FUNCTION (crypto_test_init);
1081
1082 /*
1083  * fd.io coding-style-patch-verification: ON
1084  *
1085  * Local Variables:
1086  * eval: (c-set-style "gnu")
1087  * End:
1088  */