/* * main.c * * Copyright (C) 2019-2021 by Digi International Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * Description: CAAM blob encryption/decryption example application * */ #include #include #include #include #include #include #include "caam_ops.h" enum caam_op { ENCRYPT = 1 << 0, DECRYPT = 1 << 1 }; static const char *program_name = ""; static enum caam_op op; static const char *input_file; static const char *output_file; static const char *key_modifier_string; static void usage_and_exit(int exitval) { fprintf(stdout, "Example application for using CAAM blobs.\n" "Copyright(c) Digi International Inc.\n" "\n" "Usage: %s (-e | -d) [options] input_file [output_file]\n" "\n" " -e --encrypt Read plain data and write a CAAM blob\n" " -d --decrypt Read a CAAM blob, validate it and write the data it contains\n" "\n" " -m --modifier= Set key modifier\n" " -h --help Print help and exit\n" "\n" " [] Key modifier encoded as 32 hexadecimal characters\n" "\n" "Notes: if the key modifier is not specified, a zero key modifier is used.\n" " if the output_file is not specified, the encryption/decryption will be done in-place.\n" "\n", program_name); exit(exitval); } static int bitcount(int n) { int count = 0; while (n != 0) { n &= (n - 1); count++; } return count; } static void parse_options(int argc, char *argv[]) { int opt_index, opt; const char *short_options = "edm:h"; const struct option long_options[] = { {"encrypt", no_argument, NULL, 'e'}, {"decrypt", no_argument, NULL, 'd'}, {"modifier", required_argument, NULL, 'm'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; if (argc == 1) usage_and_exit(EXIT_SUCCESS); while (1) { opt = getopt_long(argc, argv, short_options, long_options, &opt_index); if (opt == -1) break; switch (opt) { case 'e': op |= ENCRYPT; break; case 'd': op |= DECRYPT; break; case 'm': key_modifier_string = optarg; break; default: usage_and_exit(EXIT_FAILURE); break; } } /* * Parse positional arguments: * * - input file [required] * - output file (optional) */ if (argc == (optind + 1)) { input_file = argv[optind++]; } else if (argc == (optind + 2)) { input_file = argv[optind++]; output_file = argv[optind++]; } else { usage_and_exit(EXIT_FAILURE); } /* Validate one and only one operation is set */ if (bitcount(op) != 1) usage_and_exit(EXIT_FAILURE); } static int parse_hex_string(const char *hex, uint8_t *bytes, size_t length) { static const char *hex_chars = "0123456789abcdefABCDEF"; const size_t string_len = strlen(hex); size_t i; /* Ensure correct string size and hexadecimal characters */ if (string_len != length * 2 || strspn(hex, hex_chars) != string_len) return -1; for (i = 0; i < length; i++) { sscanf(hex, "%2hhx", &bytes[i]); hex += 2; } return 0; } static size_t filesize(const char *path) { struct stat file_stats; if (stat(path, &file_stats) < 0) return SIZE_MAX; return file_stats.st_size; } /* * Note: open files are explicitly closed only if needed. They will be closed * by the OS when the program finishes, so there is no need to ensure they are * closed before finishing on error conditions. * The same applies to allocated memory. */ int main(int argc, char *argv[]) { int input_fd; uint8_t *input_data; size_t input_len; int output_fd; uint8_t *output_data; size_t output_len; uint8_t key_modifier[KEY_MODIFIER_SIZE]; int ret = EXIT_SUCCESS; size_t max_input_len = BLOB_MAX_INPUT_SIZE; if (argc > 0) program_name = argv[0]; /* read and parse command line */ parse_options(argc, argv); /* read input data */ if ((input_fd = open(input_file, O_RDONLY, 0)) < 0) { perror("[ERROR] Could not open input file"); return EXIT_FAILURE; } input_len = filesize(input_file); if (input_len == SIZE_MAX) { perror("[ERROR] Could not stat input file"); return EXIT_FAILURE; } /* * if we're going to encrypt a file, subtract the blob overhead from * the maximum allowed size so that the output blob size can fit in the * CAAM encryption job descriptor */ max_input_len -= op == ENCRYPT ? BLOB_OVERHEAD : 0; /* this is a limitation of the current driver implementation */ if (input_len > max_input_len) { fprintf(stderr, "[ERROR] Input is too big, continuing may result in unexpected behavior.\n" "[ERROR] The maximum input size for %s is %zu bytes.\n", op == ENCRYPT ? "encryption" : "decryption", max_input_len); return EXIT_FAILURE; } input_data = mmap(NULL, input_len, PROT_READ, MAP_PRIVATE | MAP_POPULATE, input_fd, 0); if (input_data == MAP_FAILED) { perror("[ERROR] Could not map input file into memory"); return EXIT_FAILURE; } /* if specified, read and validate key modifier */ if (key_modifier_string) { if (parse_hex_string(key_modifier_string, key_modifier, KEY_MODIFIER_SIZE)) { fprintf(stderr, "Invalid key modifier. A 16 bytes value encoded as 32 hexadecimal characters is required.\n" "Example: --modifier=000102030405060708090a0b0c0d0e0f\n"); return EXIT_FAILURE; } } else { memset(key_modifier, 0, KEY_MODIFIER_SIZE); } /* alloc memory for output data */ output_len = input_len + (op == ENCRYPT ? BLOB_OVERHEAD : -BLOB_OVERHEAD); if (op == DECRYPT && input_len <= BLOB_OVERHEAD) { fprintf(stderr, "[ERROR] Input data is too small to be a CAAM blob\n"); return EXIT_FAILURE; } output_data = malloc(output_len); if (!output_data) { fprintf(stderr, "[ERROR] Not enough memory\n"); return EXIT_FAILURE; } /* perform encryption/decryption */ if (op == ENCRYPT) { if (caamblob_encrypt(input_data, input_len, key_modifier, output_data)) { fprintf(stderr, "[ERROR] Encryption failed\n"); return EXIT_FAILURE; } } else { if (caamblob_decrypt(input_data, input_len, key_modifier, output_data)) { fprintf(stderr, "[ERROR] Decryption failed\n"); return EXIT_FAILURE; } } /* if no output file given, close the input file and write to it */ if (!output_file) { if (munmap(input_data, input_len) < 0) perror("[WARNING] Error unmapping input file"); if (close(input_fd) < 0) perror("[WARNING] Error closing input file"); output_file = input_file; } output_fd = open(output_file, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR); if (output_fd < 0) { perror("[ERROR] Could not open output file"); return EXIT_FAILURE; } if (write(output_fd, output_data, output_len) != output_len) { perror("[WARNING] write to output file failed"); ret = EXIT_FAILURE; } if (close(output_fd) < 0) { perror("[WARNING] could not close output file"); ret = EXIT_FAILURE; } return ret; }