diff --git a/caam-blob-example/Makefile b/caam-blob-example/Makefile new file mode 100644 index 0000000..8bbc785 --- /dev/null +++ b/caam-blob-example/Makefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2019 Digi International Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# + +PROGRAM := caam-blob-example + +CFLAGS += -Wall + +all: $(PROGRAM) + +SRCS := $(wildcard *.c) +OBJS := $(SRCS:.c=.o) + +$(PROGRAM): $(OBJS) + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +.PHONY: install +install: $(PROGRAM) + install -d $(DESTDIR)/usr/bin + install -m 0755 $(PROGRAM) $(DESTDIR)/usr/bin/ + +.PHONY: clean +clean: + -rm -f *.o $(PROGRAM) diff --git a/caam-blob-example/README.md b/caam-blob-example/README.md new file mode 100644 index 0000000..a0d8167 --- /dev/null +++ b/caam-blob-example/README.md @@ -0,0 +1,79 @@ +CAAM blob Example Application +=================================== + +This example application shows how to encapsulate/decapsulate data to/from CAAM blobs. +CAAM blobs are a way to protect sensitive data by encrypting their contents. +You can think of CAAM blobs as data encrypted with an internal unreadable device-specific key which is protected by the hardware. +Data encapsulated in a CAAM blob can only be decapsulated by the device that created it. +When creating CAAM blobs, the input data size is limited to 1048527 bytes. +Note that CAAM blobs are slightly bigger than the input data (48 bytes bigger). + +A key modifier may be used to further differentiate the key used in a particular blob. + +WARNING: CAAM blobs are only secure if created in a secure boot enable device. + +For more information about CAAM blobs, see 'Secure Storage' in the [online documentation](https://www.digi.com/resources/documentation/digidocs/embedded/). + +Note: This application is only supported when using a platform and Digi Embedded Yocto version that support Trustfence. + +Running the application +----------------------- + +The following example shows how to encrypt and decrypt and file in place: + +``` +# echo "Test file" > test.txt +# caam-blob-example -e test.txt +# hexdump -C test.txt +00000000 01 ee 30 e3 31 6b 72 94 94 06 62 e2 ef 17 4e 05 |..0.1kr...b...N.| +00000010 34 c3 7b 96 58 35 ad b7 a2 89 b6 bc eb eb 81 39 |4.{.X5.........9| +00000020 3c b2 e7 d7 2d 93 7d ff 8b a8 80 bd 55 e9 70 cf |<...-.}.....U.p.| +00000030 20 51 9f 15 9e c5 aa 68 b3 90 | Q.....h..| +0000003a +# caam-blob-example -d test.txt +# hexdump -C test.txt +00000000 54 65 73 74 20 66 69 6c 65 0a |Test file.| +0000000a +``` + +You can also use the "-m" parameter to specify a key modifier. In that case, you also need the same key modifier to decrypt the data. +The key modifier is a 16 byte value encoded as 32 hexadecimal characters. +If you want to write the CAAM blob to a different file, add another positional argument. + +``` +# caam-blob-example -e -m ff0102030405060708090a0b0c0d0e0f test.txt encrypted.bin +# caam-blob-example -d encrypted.bin decrypted.txt +[ERROR] could not decrypt data. +[ERROR] Decryption failed +# caam-blob-example -d -m ff0102030405060708090a0b0c0d0e0f encrypted.bin decrypted.txt +# cat decrypted.txt +Test file +``` + +Compiling the application +------------------------- +This demo can be compiled using a Digi Embedded Yocto based toolchain. Make +sure to source the corresponding toolchain of the platform you are using, e.g: + +``` +$> . /environment-setup-cortexa7hf-vfp-neon-dey-linux-gnueabi +$> make +``` + +More information about [Digi Embedded Yocto](https://github.com/digi-embedded/meta-digi). + +License +------- +Copyright 2019, Digi International Inc. + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appears in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/caam-blob-example/caam_keyblob.h b/caam-blob-example/caam_keyblob.h new file mode 100644 index 0000000..e313e87 --- /dev/null +++ b/caam-blob-example/caam_keyblob.h @@ -0,0 +1,42 @@ +/* + * CAAM public-level include definitions for the key blob + * + * Copyright (C) 2015 Freescale Semiconductor, Inc. + */ + +#ifndef CAAM_KEYBLOB_H +#define CAAM_KEYBLOB_H + +#include +#include + +struct caam_kb_data { + char *rawkey; + size_t rawkey_len; + char *keyblob; + size_t keyblob_len; + char *keymod; + size_t keymod_len; +}; + +#define CAAM_KB_MAGIC 'I' + +/** + * DOC: CAAM_KB_ENCRYPT - generate a key blob from raw key + * + * Takes an caam_kb_data struct and returns it with the key blob + */ +#define CAAM_KB_ENCRYPT _IOWR(CAAM_KB_MAGIC, 0, struct caam_kb_data) + +/** + * DOC: CAAM_KB_DECRYPT - get keys from a key blob + * + * Takes an caam_kb_data struct and returns it with the raw key. + */ +#define CAAM_KB_DECRYPT _IOWR(CAAM_KB_MAGIC, 1, struct caam_kb_data) + +#ifndef GENMEM_KEYMOD_LEN +#define GENMEM_KEYMOD_LEN 16 +#endif + +#endif /* CAAM_KEYBLOB_H */ diff --git a/caam-blob-example/caam_ops.c b/caam-blob-example/caam_ops.c new file mode 100644 index 0000000..dd8ce54 --- /dev/null +++ b/caam-blob-example/caam_ops.c @@ -0,0 +1,91 @@ +/* + * caam_ops.c + * + * Copyright (C) 2019 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 functions + */ + +#include +#include +#include +#include +#include + +#include "caam_ops.h" + +int caamblob_encrypt(const uint8_t *data, + size_t size, + const uint8_t keymod[KEY_MODIFIER_SIZE], + uint8_t *encrypted_data) +{ + const struct caam_kb_data kb_data = { + .rawkey = (char *) data, + .rawkey_len = size, + .keyblob = (char *) encrypted_data, + .keyblob_len = size + BLOB_OVERHEAD, + .keymod = (char *) keymod, + .keymod_len = KEY_MODIFIER_SIZE + }; + int fd; + int ret = 0; + + if (size <= 0) + return -1; + + if ((fd = open(CAAM_KEY_DEV, O_RDWR)) < 0) { + perror("[ERROR] could not open CAAM keyblob device node:"); + return fd; + } + + if (ioctl(fd, CAAM_KB_ENCRYPT, &kb_data)) { + fprintf(stderr, "[ERROR] could not encrypt data.\n"); + ret = 1; + } + + close(fd); + + return ret; +} + +int caamblob_decrypt(const uint8_t *encrypted_data, + size_t encrypted_size, + const uint8_t keymod[KEY_MODIFIER_SIZE], + uint8_t *data) +{ + const struct caam_kb_data kb_data = { + .rawkey = (char *) data, + .rawkey_len = encrypted_size - BLOB_OVERHEAD, + .keyblob = (char *) encrypted_data, + .keyblob_len = encrypted_size, + .keymod = (char *) keymod, + .keymod_len = KEY_MODIFIER_SIZE + }; + int fd; + int ret = 0; + + if (encrypted_size <= BLOB_OVERHEAD) { + fprintf(stderr, "[ERROR] encrypted data is too small (%zu bytes < blob overhead (%d bytes)\n", + encrypted_size, BLOB_OVERHEAD); + return -1; + } + + if ((fd = open(CAAM_KEY_DEV, O_RDWR)) < 0) { + perror("[ERROR] could not open CAAM keyblob device node"); + return fd; + } + + if (ioctl(fd, CAAM_KB_DECRYPT, &kb_data)) { + fprintf(stderr, "[ERROR] could not decrypt data.\n"); + ret = 1; + } + + close(fd); + + return ret; +} diff --git a/caam-blob-example/caam_ops.h b/caam-blob-example/caam_ops.h new file mode 100644 index 0000000..639c531 --- /dev/null +++ b/caam-blob-example/caam_ops.h @@ -0,0 +1,51 @@ +/* + * caam_ops.h + * + * Copyright (C) 2019 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 functions + * + */ + +#ifndef CAAM_OPS_H +#define CAAM_OPS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "caam_keyblob.h" + +#define CAAM_KEY_DEV "/dev/caam_kb" + +/* Define space required for BKEK (32 bytes) + MAC tag (16 bytes) storage in any blob */ +#define BLOB_OVERHEAD (32 + 16) + +/* Key modifier: 16 bytes for a general memory blob (see SRM 5.8.4.7.1) */ +#define KEY_MODIFIER_SIZE 16 + +/* + * Testing shows that input sizes bigger than this value usually fail. + */ +#define BLOB_MAX_INPUT_SIZE (1024 * 1024 - BLOB_OVERHEAD) + +int caamblob_encrypt(const uint8_t *data, + size_t size, + const uint8_t keymod[KEY_MODIFIER_SIZE], + uint8_t *encrypted_data); + +int caamblob_decrypt(const uint8_t *encrypted_data, + size_t encrypted_size, + const uint8_t keymod[KEY_MODIFIER_SIZE], + uint8_t *data); + +#endif /* CAAM_OPS_H */ diff --git a/caam-blob-example/main.c b/caam-blob-example/main.c new file mode 100644 index 0000000..e1fe848 --- /dev/null +++ b/caam-blob-example/main.c @@ -0,0 +1,272 @@ +/* + * main.c + * + * Copyright (C) 2019 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; + + 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; + } + + /* this is a limitation of the current driver implementation */ + if (input_len >= BLOB_MAX_INPUT_SIZE) + fprintf(stderr, "[WARNING] Input is too big, %s may fail\n", + op == ENCRYPT ? "encryption" : "decryption"); + + 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; +}