kat_main.h (11978B)
1 /* 2 Copyright 2010-2011, D. E. Shaw Research. 3 All rights reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are 7 met: 8 9 * Redistributions of source code must retain the above copyright 10 notice, this list of conditions, and the following disclaimer. 11 12 * Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions, and the following disclaimer in the 14 documentation and/or other materials provided with the distribution. 15 16 * Neither the name of D. E. Shaw Research nor the names of its 17 contributors may be used to endorse or promote products derived from 18 this software without specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 /* Known Answer Test */ 33 34 /* We use the same source files to implement the Known Answer Test 35 (KAT) in C, C++, OpenCL and CUDA. Supporting all four environments 36 with a single source file, and getting it all to work with 'make' 37 is a bit involved: 38 39 There are five "top-level" files: 40 kat_c.c 41 kat_cpp.cpp 42 kat_cuda.c 43 kat_opencl.c 44 kat_metal.m 45 46 These correspond to make targets: kat_c, kat_cpp, kat_cuda, 47 kat_opencl and kat_metal. 48 49 Those files are relatively simple. First, they #include this file, 50 which contains all the machinery for reading test vectors, 51 complaining about errors, etc.. Then they implement the function 52 host_execute_tests() in the appropriate environment. host_execute_tests 53 looks very different in C/C++/CUDA/OpenCL/Metal. 54 55 host_execute_tests contrives to call/launch "dev_execute_tests" 56 on the device. Except for a few environment-specific keywords, 57 (e.g., __global, __kernel), which are #defined in kat_XXX.c, 58 dev_execute_tests is obtained by including a common source file: 59 #include <kat_dev_execute.h> 60 61 One final complication: in order to fully "bake" the source code 62 into the binary at compile-time, dev_execute_tests for opencl is implemented in 63 kat_opencl_kernel.ocl, which is processed by gencl.sh into 64 kat_opencl_kernel.i, which is thein #include-ed by kat_opencl.c. 65 66 */ 67 #include "util.h" 68 #include "kat.h" 69 70 #define LINESIZE 1024 71 72 int have_aesni = 0; 73 int verbose = 0; 74 int debug = 0; 75 unsigned nfailed = 0; 76 const char *progname; 77 78 extern void host_execute_tests(kat_instance *tests, unsigned ntests); 79 80 /* A little hack to keep track of the test vectors that we don't know how to deal with: */ 81 int nunknowns = 0; 82 #define MAXUNKNOWNS 20 83 const char *unknown_names[MAXUNKNOWNS]; 84 int unknown_counts[MAXUNKNOWNS]; 85 86 void register_unknown(const char *name){ 87 int i; 88 for(i=0; i<nunknowns; ++i){ 89 if( strcmp(name, unknown_names[i]) == 0 ){ 90 unknown_counts[i]++; 91 return; 92 } 93 } 94 if( i >= MAXUNKNOWNS ){ 95 fprintf(stderr, "Too many unknown rng types. Bye.\n"); 96 exit(1); 97 } 98 nunknowns++; 99 unknown_names[i] = ntcsdup(name); 100 unknown_counts[i] = 1; 101 } 102 103 void report_unknowns(){ 104 int i; 105 for(i=0; i<nunknowns; ++i){ 106 printf("%d test vectors of type %s skipped\n", unknown_counts[i], unknown_names[i]); 107 } 108 } 109 110 /* read_<GEN>NxW */ 111 #define RNGNxW_TPL(base, N, W) \ 112 int read_##base##N##x##W(const char *line, kat_instance* tinst){ \ 113 size_t i; \ 114 int nchar; \ 115 const char *p = line; \ 116 char *newp; \ 117 size_t nkey = sizeof(tinst->u.base##N##x##W##_data.ukey.v)/sizeof(tinst->u.base##N##x##W##_data.ukey.v[0]); \ 118 tinst->method = base##N##x##W##_e; \ 119 sscanf(p, "%u%n", &tinst->nrounds, &nchar); \ 120 p += nchar; \ 121 for(i=0; i<N; ++i){ \ 122 tinst->u.base##N##x##W##_data.ctr.v[i] = strtou##W(p, &newp, 16); \ 123 p = newp; \ 124 } \ 125 for(i=0; i<nkey; ++i){ \ 126 tinst->u.base##N##x##W##_data.ukey.v[i] = strtou##W(p, &newp, 16); \ 127 p = newp; \ 128 } \ 129 for(i=0; i<N; ++i){ \ 130 tinst->u.base##N##x##W##_data.expected.v[i] = strtou##W(p, &newp, 16); \ 131 p = newp; \ 132 } \ 133 /* set the computed to 0xca. If the test fails to set computed, we'll see cacacaca in the FAILURE notices */ \ 134 memset(tinst->u.base##N##x##W##_data.computed.v, 0xca, sizeof(tinst->u.base##N##x##W##_data.computed.v)); \ 135 return 1; \ 136 } 137 #include "rngNxW.h" 138 #undef RNGNxW_TPL 139 140 /* readtest: dispatch to one of the read_<GEN>NxW functions */ 141 static int readtest(const char *line, kat_instance* tinst){ 142 int nchar; 143 char name[LINESIZE]; 144 if( line[0] == '#') return 0; 145 sscanf(line, "%s%n", name, &nchar); 146 if(!have_aesni){ 147 /* skip any tests that require AESNI */ 148 if(strncmp(name, "aes", 3)==0 || 149 strncmp(name, "ars", 3)==0){ 150 register_unknown(name); 151 return 0; 152 } 153 } 154 #define RNGNxW_TPL(base, N, W) if(strcmp(name, #base #N "x" #W) == 0) return read_##base##N##x##W(line+nchar, tinst); 155 #include "rngNxW.h" 156 #undef RNGNxW_TPL 157 158 register_unknown(name); 159 return 0; 160 } 161 162 #define RNGNxW_TPL(base, N, W) \ 163 void report_##base##N##x##W##error(const kat_instance *ti){ \ 164 size_t i; \ 165 size_t nkey = sizeof(ti->u.base##N##x##W##_data.ukey.v)/sizeof(ti->u.base##N##x##W##_data.ukey.v[0]); \ 166 fprintf(stderr, "FAIL: expected: "); \ 167 fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ 168 for(i=0; i<N; ++i){ \ 169 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.ctr.v[i]); \ 170 } \ 171 for(i=0; i<nkey; ++i){ \ 172 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.ukey.v[i]); \ 173 } \ 174 for(i=0; i<N; ++i){ \ 175 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.expected.v[i]); \ 176 } \ 177 fprintf(stderr, "\n"); \ 178 \ 179 fprintf(stderr, "FAIL: computed: "); \ 180 fprintf(stderr, #base #N "x" #W " %d", ti->nrounds); \ 181 for(i=0; i<N; ++i){ \ 182 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.ctr.v[i]); \ 183 } \ 184 for(i=0; i<nkey; ++i){ \ 185 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.ukey.v[i]); \ 186 } \ 187 for(i=0; i<N; ++i){ \ 188 fprintf(stderr, " "); prtu##W(ti->u.base##N##x##W##_data.computed.v[i]); \ 189 } \ 190 fprintf(stderr, "\n"); \ 191 nfailed++; \ 192 } 193 #include "rngNxW.h" 194 #undef RNGNxW_TPL 195 196 // dispatch to one of the report_<GEN>NxW() functions 197 void analyze_tests(const kat_instance *tests, unsigned ntests){ 198 unsigned i; 199 char zeros[512] = {0}; 200 for(i=0; i<ntests; ++i){ 201 const kat_instance *ti = &tests[i]; 202 switch(tests[i].method){ 203 #define RNGNxW_TPL(base, N, W) case base##N##x##W##_e: \ 204 if (memcmp(zeros, ti->u.base##N##x##W##_data.expected.v, N*W/8)==0){ \ 205 fprintf(stderr, "kat expected all zeros? Something is wrong with the test harness!\n"); \ 206 nfailed++; \ 207 } \ 208 if (memcmp(ti->u.base##N##x##W##_data.computed.v, ti->u.base##N##x##W##_data.expected.v, N*W/8)) \ 209 report_##base##N##x##W##error(ti); \ 210 break; 211 #include "rngNxW.h" 212 #undef RNGNxW_TPL 213 case last: ; 214 } 215 } 216 } 217 218 #define NTESTS 1000 219 220 int main(int argc, char **argv){ 221 kat_instance *tests; 222 unsigned t, ntests = NTESTS; 223 char linebuf[LINESIZE]; 224 FILE *inpfile; 225 const char *p; 226 const char *inname; 227 char filename[LINESIZE]; 228 229 progname = argv[0]; 230 231 /* If there's an argument, open that file. 232 else if getenv("srcdir") is non-empty open getenv("srcdir")/kat_vectors 233 else open "./kat_vectors" */ 234 if( argc > 1 ) 235 inname = argv[1]; 236 else{ 237 const char *e = getenv("srcdir"); 238 if(!e) 239 e = "."; 240 sprintf(filename, "%s/kat_vectors", e); 241 inname = filename; 242 } 243 244 if (strcmp(inname, "-") == 0) { 245 inpfile = stdin; 246 } else { 247 inpfile = fopen(inname, "r"); 248 if (inpfile == NULL) { 249 fprintf(stderr, "%s: error opening input file %s for reading: %s\n", 250 progname, inname, strerror(errno)); 251 exit(1); 252 } 253 } 254 if ((p = getenv("KATC_VERBOSE")) != NULL) { 255 verbose = atoi(p); 256 } 257 if ((p = getenv("KATC_DEBUG")) != NULL) { 258 debug = atoi(p); 259 } 260 261 #if R123_USE_AES_NI 262 have_aesni = haveAESNI(); 263 #else 264 have_aesni = 0; 265 #endif 266 267 tests = (kat_instance *) malloc(sizeof(tests[0])*ntests); 268 if (tests == NULL) { 269 fprintf(stderr, "Could not allocate %lu bytes for tests\n", 270 (unsigned long) ntests); 271 exit(1); 272 } 273 t = 0; 274 while (fgets(linebuf, sizeof linebuf, inpfile) != NULL) { 275 if( t==ntests ){ 276 ntests *= 2; 277 tests = (kat_instance *)realloc(tests, sizeof(tests[0])*ntests); 278 if (tests == NULL) { 279 fprintf(stderr, "Could not grow tests to %lu bytes\n", 280 (unsigned long) ntests); 281 exit(1); 282 } 283 } 284 if( readtest(linebuf, &tests[t]) ) 285 ++t; 286 } 287 if(t==ntests){ 288 fprintf(stderr, "No more space for tests? Recompile with a larger NTESTS\n"); 289 exit(1); 290 } 291 tests[t].method = last; // N.B *not* t++ - the 'ntests' value passed to host_execute_tests does not count the 'last' one. 292 293 report_unknowns(); 294 printf("Perform %lu tests.\n", (unsigned long)t); 295 host_execute_tests(tests, t); 296 297 analyze_tests(tests, t); 298 free(tests); 299 if(nfailed != 0){ 300 printf("FAILED %u out of %u\n", nfailed, t); 301 return 1; 302 }else{ 303 printf("PASSED %u known answer tests\n", t); 304 return 0; 305 } 306 }