/** * Test encryption / decryption using cryptoapi. */ #include #include #include #include #include #include #include #include struct test_fixture; struct test_fixture { int valid; int lineno; char *raw_line; char *cipher_name; size_t key_len; size_t iv_len; size_t data_len; uint8_t *key; uint8_t *iv; uint8_t *pt; uint8_t *ct; struct test_fixture *next; }; static void usage(void); static const char *get_test_fixture_header(void); static void print_test_fixture_header(FILE *out); static void print_test_fixture(FILE *out, struct test_fixture *fixture, uint8_t *result); static int read_test_fixture(char *line, int lineno, struct test_fixture *fixture); static struct test_fixture *read_fixtures_from_file(const char *filename); static void free_test_fixture(struct test_fixture *fixture); static void print_hex(FILE *out, const uint8_t *bytes, size_t len); static void read_hex(const char *str, uint8_t *bytes, size_t len); static int parse_hex_digit(char ch); static char to_hex(int n); typedef int(crypt_impl_t)( const char *cipher_name, unsigned char *key, size_t klen, unsigned char *iv, unsigned char *in, unsigned char *out, size_t len, int do_encrypt, int repetitions); crypt_impl_t syscrypt_cryptoapi; static uint8_t *safe_syscrypt( const char *cipher_name, crypt_impl_t *impl, const uint8_t *key, size_t klen, const uint8_t *iv, size_t ivlen, const uint8_t *data, size_t datalen, int do_encrypt, int repetitions); const char *get_test_fixture_header(void) { return "cipher;key_len;iv_len;data_len;key;iv;pt;ct\n"; } void print_test_fixture_header(FILE *out) { fprintf(out, "%s", get_test_fixture_header()); } void print_test_fixture( FILE *out, struct test_fixture *fixture, uint8_t *result) { fprintf( out, "%s;%ld;%ld;%ld;", fixture->cipher_name, fixture->key_len, fixture->iv_len, fixture->data_len); print_hex(out, fixture->key, fixture->key_len); fprintf(out, ";"); print_hex(out, fixture->iv, fixture->iv_len); fprintf(out, ";"); print_hex(out, fixture->pt, fixture->data_len); fprintf(out, ";"); print_hex(out, result ? result : fixture->ct, fixture->data_len); fprintf(out, "\n"); } int read_test_fixture(char *line, int lineno, struct test_fixture *fixture) { char *token; bzero(fixture, sizeof(*fixture)); fixture->valid = 0; fixture->lineno = lineno; fixture->raw_line = strdup(line); if (line[0] == '\n' || line[0] == '#') return (0); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->cipher_name = strdup(token); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->key_len = atoi(token); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->iv_len = atoi(token); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->data_len = atoi(token); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->key = malloc(fixture->key_len); bzero(fixture->key, fixture->key_len); read_hex(token, fixture->key, fixture->key_len); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->iv = malloc(fixture->iv_len); bzero(fixture->iv, fixture->iv_len); read_hex(token, fixture->iv, fixture->iv_len); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->pt = malloc(fixture->data_len); bzero(fixture->pt, fixture->data_len); read_hex(token, fixture->pt, fixture->data_len); token = strsep(&line, ";"); if (token == NULL) return (1); fixture->ct = malloc(fixture->data_len); bzero(fixture->ct, fixture->data_len); read_hex(token, fixture->ct, fixture->data_len); fixture->valid = 1; return (0); } void free_test_fixture(struct test_fixture *fixture) { if (fixture->raw_line) free(fixture->raw_line); if (fixture->cipher_name) free(fixture->cipher_name); if (fixture->key) free(fixture->key); if (fixture->iv) free(fixture->iv); if (fixture->pt) free(fixture->pt); if (fixture->ct) free(fixture->ct); free(fixture); } void print_hex(FILE *out, const uint8_t *bytes, size_t len) { for (size_t i = 0; i < len; ++i) { fputc(to_hex(bytes[i] / 16), out); fputc(to_hex(bytes[i] % 16), out); } } int parse_hex_digit(char ch) { if (ch >= '0' && ch <= '9') return (ch - '0'); if (ch >= 'a' && ch <= 'f') return (10 + ch - 'a'); if (ch >= 'A' && ch <= 'F') return (10 + ch - 'A'); return (-1); } char to_hex(int n) { if (n >= 0 && n <= 9) return ('0' + n); if (n >= 10 && n <= 15) return ('a' + n - 10); return ('X'); } void read_hex(const char *str, uint8_t *bytes, size_t len) { for (size_t i = 0; i < len; i++) { int digit0 = parse_hex_digit(str[2 * i]); if (digit0 < 0) { fprintf( stderr, "ERROR: Failed to parse hex string %s (near: %s)\n", str, &str[2 * i]); exit(1); } int digit1 = parse_hex_digit(str[2 * i + 1]); if (digit1 < 0) { fprintf( stderr, "ERROR: Failed to parse hex string %s (near: %s)\n", str, &str[2 * i + 1]); exit(1); } *(bytes++) = digit0 * 16 + digit1; } } /** * On success, returns the encyrypted/decrypted data (allocated). */ static uint8_t *safe_syscrypt( const char *cipher_name, crypt_impl_t *impl, const uint8_t *key, size_t klen, const uint8_t *iv, size_t ivlen, const uint8_t *data, size_t datalen, int do_encrypt, int repetitions) { char keycopy[64]; char ivcopy[256]; char *inbuf, *outbuf; int error; inbuf = outbuf = NULL; if (klen > sizeof(keycopy)) { fprintf(stderr, "ERROR: Invalid klen\n"); exit(1); } if (ivlen > sizeof(ivcopy)) { fprintf(stderr, "ERROR: Invalid iv\n"); exit(1); } inbuf = malloc(datalen); if (inbuf == NULL) { fprintf(stderr, "ERROR: No memory\n"); exit(1); } outbuf = malloc(datalen); if (outbuf == NULL) { fprintf(stderr, "ERROR: No memory\n"); free(inbuf); exit(1); } bzero(inbuf, datalen); bzero(outbuf, datalen); memcpy(inbuf, data, datalen); bzero(keycopy, sizeof(keycopy)); memcpy(keycopy, key, klen); bzero(ivcopy, sizeof(ivcopy)); memcpy(ivcopy, iv, ivlen); error = impl( cipher_name, keycopy, klen, ivcopy, inbuf, outbuf, datalen, do_encrypt, repetitions); if (error) { fprintf( stderr, "ERROR: syscrypt impl failed with: %d\n", error); free(inbuf); free(outbuf); return (NULL); } free(inbuf); return outbuf; } void usage(void) { printf("Usage: cryptoapi_test [OPTIONS]\n\n"); printf("Options:\n"); printf( " -t CRYPTOIMPL Crypto implementation to test [default: " "cryptoapi]\n"); printf( " -f FIXTURE Test fixture [default: fixtures.csv]\n"); printf(" -o FILE Write results to FILE\n"); printf( " -b REPETITIONS Run benchmark with specified number of " "repetitions\n"); printf(" -h Print this help message\n"); } struct test_fixture *read_fixtures_from_file(const char *filename) { static char linebuf[4 * 4096]; FILE *f; struct test_fixture *head = NULL; struct test_fixture *prev = NULL; struct test_fixture *fixture = NULL; int error; f = fopen(filename, "r"); if (!f) { fprintf(stderr, "ERROR: Failed to open file %s\n", filename); exit(1); } fgets(linebuf, sizeof(linebuf), f); if (strcmp(linebuf, get_test_fixture_header()) != 0) { fprintf(stderr, "ERROR: invalid test fixture header\n"); exit(1); } for (int lineno = 1; fgets(linebuf, sizeof(linebuf), f); ++lineno) { fixture = malloc(sizeof(struct test_fixture)); error = read_test_fixture(linebuf, lineno, fixture); if (error) { fprintf( stderr, "Invalid test fixture in line %d. Line: %s", lineno, fixture->raw_line); exit(1); } fixture->next = NULL; if (head == NULL) head = fixture; if (prev) prev->next = fixture; prev = fixture; } fclose(f); return (head); } int main(int argc, char *argv[]) { struct timespec start, stop, tv; int ch; const char *crypt_impl_name = "cryptoapi"; const char *fixtures_filename = "fixtures.csv"; crypt_impl_t *crypt_impl = NULL; int repetitions = 1; bool benchmark = false; const char *result_filename = NULL; FILE *result_file = NULL; while ((ch = getopt(argc, argv, "t:f:b:o:h")) != -1) { switch (ch) { case 't': crypt_impl_name = optarg; break; case 'f': fixtures_filename = optarg; break; case 'b': benchmark = true; repetitions = atoi(optarg); break; case 'o': result_filename = optarg; break; case 'h': /* fall-through */ usage(); exit(0); break; default: usage(); exit(1); break; } } if (strcmp(crypt_impl_name, "cryptoapi") == 0) crypt_impl = &syscrypt_cryptoapi; else { usage(); exit(2); } struct test_fixture *fixtures = read_fixtures_from_file(fixtures_filename); int total = 0; int failures = 0; int ok = 0; if (result_filename) result_file = fopen(result_filename, "w"); if (result_file) print_test_fixture_header(result_file); if (benchmark) { fprintf( stderr, "--------------------------------------------\n"); fprintf( stderr, "%-22s %21s\n", "crypto implementation:", crypt_impl_name); fprintf(stderr, "%-22s %21s\n", "fixtures:", fixtures_filename); fprintf(stderr, "%-22s %21d\n", "repetitions:", repetitions); fprintf( stderr, "--------------------------------------------\n"); fprintf( stderr, "%15s %5s %9s %5s %6s\n", "Cipher", "Mode", "Blocklen", "Time", "MiB/s"); } for (struct test_fixture *fixture = fixtures; fixture; fixture = fixture->next) { if (!fixture->valid) { if (result_file) fprintf(result_file, "%s", fixture->raw_line); continue; } for (int do_encrypt = 0; do_encrypt <= 1; ++do_encrypt) { clock_gettime(CLOCK_REALTIME, &start); uint8_t *data = do_encrypt ? fixture->pt : fixture->ct; uint8_t *expected = do_encrypt ? fixture->ct : fixture->pt; uint8_t *result = safe_syscrypt( fixture->cipher_name, crypt_impl, fixture->key, fixture->key_len, fixture->iv, fixture->iv_len, data, fixture->data_len, do_encrypt, repetitions); clock_gettime(CLOCK_REALTIME, &stop); timespecsub(&stop, &start, &tv); double secs = (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0; double mib_per_second = ((double)fixture->data_len * repetitions * 2) / (1024.0 * 1024.0) / secs; if (benchmark) fprintf( stderr, "%15s %5s %9ld %5.2f %6.1f\n", fixture->cipher_name, (do_encrypt ? "ENC" : "DEC"), fixture->data_len, secs, mib_per_second); ++total; if (result && memcmp(result, expected, fixture->data_len) == 0) { if (!benchmark) { fprintf(stderr, "."); fflush(stderr); } ++ok; } else { ++failures; if (!benchmark) { fprintf( stderr, "\nFAILED %s line %d\n", do_encrypt ? "ENC" : "DEC", fixture->lineno); fprintf(stderr, "Expected: \n"); print_hex( stderr, expected, fixture->data_len); fprintf(stderr, "\nActual: \n"); print_hex( stderr, result, fixture->data_len); fprintf(stderr, "\n"); } } if (result_file && do_encrypt) print_test_fixture( result_file, fixture, result); if (result) free(result); } } if (result_file) fclose(result_file); while (fixtures) { struct test_fixture *next = fixtures->next; free_test_fixture(fixtures); fixtures = next; } fprintf(stderr, "\n"); fprintf(stderr, "--------------------------------------------\n"); fprintf( stderr, "Total: %d, OK: %d, Failures: %d\n", total, ok, failures); fprintf(stderr, "--------------------------------------------\n"); if (!benchmark) { if (failures > 0 || ok != total) exit(1); } return 0; }