caam-blob-example: add example demostrating CAAM blob usage

CAAM blobs can be used to secure data with the OTPMK. More information
is available in the online documentation.

Signed-off-by: Jose Diaz de Grenu <Jose.DiazdeGrenu@digi.com>
This commit is contained in:
Jose Diaz de Grenu 2019-05-15 15:38:40 +02:00
parent 41289be92e
commit ab38a1e6c9
6 changed files with 572 additions and 0 deletions

View File

@ -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)

View File

@ -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:
```
$> . <DEY-toolchain-path>/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.

View File

@ -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 <linux/ioctl.h>
#include <linux/types.h>
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 */

View File

@ -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 <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#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;
}

View File

@ -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 <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <unistd.h>
#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 */

272
caam-blob-example/main.c Normal file
View File

@ -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 <getopt.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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=<KM> Set key modifier\n"
" -h --help Print help and exit\n"
"\n"
" [<KM>] 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;
}