diff --git a/sys/arm64/qoriq/caam/caam_crypto.c b/sys/arm64/qoriq/caam/caam_crypto.c --- a/sys/arm64/qoriq/caam/caam_crypto.c +++ b/sys/arm64/qoriq/caam/caam_crypto.c @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -63,8 +65,20 @@ device_t jr_dev; }; +struct caam_blob_operation { + struct job_descriptor jobdesc; + bool encapsulate; + + caam_jr_dma_map_t key_mod; + caam_jr_dma_map_t in; + caam_jr_dma_map_t out; +}; + MALLOC_DEFINE(M_CAAM, "caam", "caam cryptography buffers"); +/* For use with character device interface */ +static device_t g_caam_dev; + static void dma_data_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) { @@ -369,10 +383,367 @@ return (0); } +static d_open_t caam_hwencap_cdev_open; +static d_close_t caam_hwencap_cdev_close; +static d_ioctl_t caam_hwencap_cdev_ioctl; + +static struct cdevsw caam_hwencap_cdevsw = { + .d_version = D_VERSION, + .d_open = caam_hwencap_cdev_open, + .d_close = caam_hwencap_cdev_close, + .d_ioctl = caam_hwencap_cdev_ioctl, + .d_name = "caam_hwencap_cdev", +}; + +static int +caam_hwencap_cdev_open(struct cdev *dev __unused, int oflags __unused, + int devtype __unused, struct thread *td __unused) +{ + /* Nothing to do */ + return (0); +} + +static int +caam_hwencap_cdev_close(struct cdev *dev __unused, int fflag __unused, + int devtype __unused, struct thread *td __unused) +{ + /* Nothing to do */ + return (0); +} + +static void +caam_blob_op_build_desc(struct caam_blob_operation *op) +{ + uint32_t *desc = op->jobdesc.desc; + + caam_jobdesc_init(desc); + caam_jobdesc_add_word(desc, DESC_HEADER(0)); + + caam_jobdesc_add_word(desc, CMD_CLASS(CLASS_2) | op->key_mod.buflen); + caam_jobdesc_add_ptr(desc, op->key_mod.bus_addr); + + caam_jobdesc_add_word(desc, SEQ_IN_PTR_EXT); + caam_jobdesc_add_ptr(desc, op->in.bus_addr); + caam_jobdesc_add_word(desc, op->in.buflen); + + caam_jobdesc_add_word(desc, SEQ_OUT_PTR_EXT); + caam_jobdesc_add_ptr(desc, op->out.bus_addr); + caam_jobdesc_add_word(desc, op->out.buflen); + + if (op->encapsulate) + caam_jobdesc_add_word(desc, BLOB_ENCAPS | PROT_BLOB_TYPE_RED); + else + caam_jobdesc_add_word(desc, BLOB_DECAPS | PROT_BLOB_TYPE_RED); +} + +static int +caam_crypto_red_blob_op(device_t dev, bool encapsulate, void *in, + uint32_t in_sz, void *out, uint32_t out_sz) +{ + int rv; + device_t jr_dev; + struct caam_softc *sc; + struct caam_blob_operation *op; + uint8_t key_blob_modifier[BLOB_KEY_MODIFIER_SIZE] = "CAAM_RED_BLOB"; + + if (in_sz > BLOB_MAX_BUFFER_LEN || out_sz > BLOB_MAX_BUFFER_LEN) { + CAAM_LOG_DEV_WARN(dev, "Buffer len too large for a register\n"); + rv = EINVAL; + goto exit; + } + + op = malloc_aligned(sizeof(*op), PAGE_SIZE, M_CAAM_CRYPTO, M_NOWAIT); + if (op == NULL) { + CAAM_LOG_DEV_WARN(dev, + "Failed to allocate blob operation structure\n"); + rv = ENOMEM; + goto exit; + } + op->encapsulate = encapsulate; + op->jobdesc.arg = NULL; + op->jobdesc.callback = NULL; + + sc = device_get_softc(dev); + jr_dev = jr_pool_acquire(&sc->jr_pool); + if (jr_dev == 0) { + CAAM_LOG_DEV_WARN(dev, "Failed to acquire JR\n"); + rv = EAGAIN; + goto op_cleanup; + } + + /* Map and sync the key modifier */ + rv = caam_jr_dma_map(jr_dev, &op->key_mod, key_blob_modifier, + sizeof(key_blob_modifier), BUS_DMA_COHERENT, BUS_DMA_WAITOK, + JR_MAP_SYNC); + if (rv != 0) { + CAAM_LOG_DEV_WARN(jr_dev, + "Failed to create key modifier map\n"); + goto release_jr; + } + + /* Map and sync the in buffer */ + rv = caam_jr_dma_map(jr_dev, &op->in, in, in_sz, BUS_DMA_COHERENT, + BUS_DMA_WAITOK, JR_MAP_SYNC); + if (rv != 0) { + CAAM_LOG_DEV_WARN(jr_dev, "Failed to create in data map\n"); + goto mod_map_cleanup; + } + + /* Map the out buffer */ + rv = caam_jr_dma_map(jr_dev, &op->out, out, out_sz, BUS_DMA_COHERENT, + BUS_DMA_WAITOK, JR_MAP_NOSYNC); + if (rv != 0) { + CAAM_LOG_DEV_WARN(jr_dev, "Failed to create out data map\n"); + goto in_map_cleanup; + } + + caam_blob_op_build_desc(op); + + rv = caam_run_descriptor_jr_blocking(dev, &op->jobdesc, jr_dev); + if (rv != 0) { + CAAM_LOG_DEV_WARN(dev, "Failed to run descriptor rv=(%d)\n", + rv); + goto out_map_cleanup; + } + + caam_jr_dma_sync_from_dma(jr_dev, &op->out); + +out_map_cleanup: + caam_jr_dma_unmap(jr_dev, &op->out, JR_MAP_NOSYNC); +in_map_cleanup: + caam_jr_dma_unmap(jr_dev, &op->in, JR_MAP_NOSYNC); +mod_map_cleanup: + caam_jr_dma_unmap(jr_dev, &op->key_mod, JR_MAP_NOSYNC); +release_jr: + jr_pool_release(&sc->jr_pool, jr_dev); +op_cleanup: + free(op, M_CAAM_CRYPTO); +exit: + return (rv); +} + +/* + * @brief Encapsulate data within a red blob. + * + * @param [in] dev caam device handle + * @param [in] in contiguous data buffer + * @param [in] in_sz data buffer length + * @param [out] out contiguous blob buffer + * @param [out] out_sz blob buffer length + * + * @retval :: 0 is returned on success + * @retval :: errno is returned on internal error + */ +static int +caam_crypto_red_blob_encap(device_t dev, void *in, uint32_t in_sz, void *out, + uint32_t out_sz) +{ + return caam_crypto_red_blob_op(dev, true, in, in_sz, out, out_sz); +} + +/* + * @brief Decapsulate a red blob. + * + * @param [in] dev caam device handle + * @param [in] in contiguous blob buffer + * @param [in] in_sz blob buffer length + * @param [out] out contiguous data buffer + * @param [out] out_sz data buffer length + * + * @retval :: 0 is returned on success + * @retval :: errno is returned on internal error + */ +static int +caam_crypto_red_blob_decap(device_t dev, void *in, uint32_t in_sz, void *out, + uint32_t out_sz) +{ + return caam_crypto_red_blob_op(dev, false, in, in_sz, out, out_sz); +} + +static int +caam_crypto_encap(struct hwencap_encap_arg *arg) +{ + int rv; + uint8_t *plain, *blob; + + if (arg->in_buf_sz < 1) { + CAAM_LOG_ERROR("Empty input buffer\n"); + rv = EINVAL; + goto exit; + } + + if (arg->out_buf_sz < arg->in_buf_sz + CAAM_CRYPTO_BLOB_OVERHEAD) { + CAAM_LOG_ERROR("out_buf_sz must be at least in_buf_sz + (%d)\n", + CAAM_CRYPTO_BLOB_OVERHEAD); + rv = EINVAL; + goto exit; + } + + plain = contigmalloc(arg->in_buf_sz, M_CAAM_CRYPTO, M_NOWAIT | M_ZERO, + 0, ~(vm_paddr_t)0, PAGE_SIZE, 0); + if (!plain) { + rv = ENOMEM; + CAAM_LOG_ERROR("Failed to allocate contig plain\n"); + goto exit; + } + + blob = contigmalloc(arg->out_buf_sz, M_CAAM_CRYPTO, M_NOWAIT | M_ZERO, + 0, ~(vm_paddr_t)0, PAGE_SIZE, 0); + if (!blob) { + rv = ENOMEM; + CAAM_LOG_ERROR("Failed to allocate contig blob\n"); + goto free_in; + } + + rv = copyin((void *)arg->in_buf, plain, arg->in_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Copyin operation failed rv=(%d)\n", rv); + goto free_out; + } + rv = caam_crypto_red_blob_encap(g_caam_dev, plain, arg->in_buf_sz, blob, + arg->out_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Blob operation failed rv=(%d)\n", rv); + goto free_out; + } + rv = copyout(blob, (void *)arg->out_buf, arg->out_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Copyout operation failed rv=(%d)\n", rv); + goto free_out; + } + +free_out: + contigfree(blob, arg->out_buf_sz, M_CAAM_CRYPTO); +free_in: + contigfree(plain, arg->in_buf_sz, M_CAAM_CRYPTO); +exit: + + return (rv); +} + +static int +caam_crypto_decap(struct hwencap_encap_arg *arg) +{ + int rv; + uint8_t *blob, *plain; + + if (arg->in_buf_sz <= CAAM_CRYPTO_BLOB_OVERHEAD) { + CAAM_LOG_ERROR("Malformed blob\n"); + rv = EINVAL; + goto exit; + } + + if (arg->out_buf_sz < arg->in_buf_sz - CAAM_CRYPTO_BLOB_OVERHEAD) { + CAAM_LOG_ERROR("out_buf_sz must be at least in_buf_sz - (%d)\n", + CAAM_CRYPTO_BLOB_OVERHEAD); + rv = EINVAL; + goto exit; + } + + blob = contigmalloc(arg->in_buf_sz, M_CAAM_CRYPTO, M_NOWAIT | M_ZERO, 0, + ~(vm_paddr_t)0, PAGE_SIZE, 0); + if (!blob) { + rv = ENOMEM; + CAAM_LOG_ERROR("Failed to allocate contig in_buf\n"); + goto exit; + } + + plain = contigmalloc(arg->out_buf_sz, M_CAAM_CRYPTO, M_NOWAIT | M_ZERO, + 0, ~(vm_paddr_t)0, PAGE_SIZE, 0); + if (!plain) { + rv = ENOMEM; + CAAM_LOG_ERROR("Failed to allocate contig out_buf\n"); + goto free_in; + } + + rv = copyin((void *)arg->in_buf, blob, arg->in_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Copyin operation failed rv=(%d)\n", rv); + goto free_out; + } + rv = caam_crypto_red_blob_decap(g_caam_dev, blob, arg->in_buf_sz, plain, + arg->out_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Blob operation failed rv=(%d)\n", rv); + goto free_out; + } + rv = copyout(plain, (void *)arg->out_buf, arg->out_buf_sz); + if (rv != 0) { + CAAM_LOG_ERROR("Copyout operation failed rv=(%d)\n", rv); + goto free_out; + } + +free_out: + contigfree(plain, arg->out_buf_sz, M_CAAM_CRYPTO); +free_in: + contigfree(blob, arg->in_buf_sz, M_CAAM_CRYPTO); +exit: + + return (rv); +} + +static enum hwencap_mode +caam_crypto_encap_mode(device_t caam_dev) +{ + uint32_t sec_status, mode; + + sec_status = caam_reg_read(caam_dev, SEC_REG_SSTA_OFFSET); + mode = (sec_status & SSTA_MOO_MASK) >> SSTA_MOO_SHIFT; + + switch (mode) { + case SSTA_MOO_SECURE: + case SSTA_MOO_TRUSTED: + return HWENCAP_MODE_HW_UNIQUE; + default: + return HWENCAP_MODE_HW_SHARED; + } +} + +static int +caam_hwencap_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, + struct thread *td) +{ + struct hwencap_blob_sz_arg *blob_arg; + struct hwencap_encap_arg *arg; + enum hwencap_mode *mode_arg; + int error = 0; + + switch (cmd) { + case HWENCAP_ENCAP: + arg = (struct hwencap_encap_arg *)addr; + error = caam_crypto_encap(arg); + break; + case HWENCAP_DECAP: + arg = (struct hwencap_encap_arg *)addr; + error = caam_crypto_decap(arg); + break; + case HWENCAP_ENCAP_BLOB_SZ: + blob_arg = (struct hwencap_blob_sz_arg *)addr; + blob_arg->out_sz = blob_arg->in_sz + CAAM_CRYPTO_BLOB_OVERHEAD; + break; + case HWENCAP_DECAP_PT_SZ: + blob_arg = (struct hwencap_blob_sz_arg *)addr; + if (blob_arg->in_sz <= CAAM_CRYPTO_BLOB_OVERHEAD) + return (EINVAL); + blob_arg->out_sz = blob_arg->in_sz - CAAM_CRYPTO_BLOB_OVERHEAD; + break; + case HWENCAP_MODE: + mode_arg = (enum hwencap_mode *)addr; + *mode_arg = caam_crypto_encap_mode(g_caam_dev); + break; + default: + error = ENOTTY; + break; + } + + return (error); +} + int caam_crypto_attach(device_t dev) { struct caam_softc *sc; + struct make_dev_args args; int error = 0; sc = device_get_softc(dev); @@ -385,6 +756,28 @@ return (ENXIO); } + if (g_caam_dev == NULL) { + g_caam_dev = dev; + + make_dev_args_init(&args); + args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; + args.mda_devsw = &caam_hwencap_cdevsw; + args.mda_cr = 0; + args.mda_uid = UID_ROOT; + args.mda_gid = GID_WHEEL; + args.mda_mode = 0600; + + CAAM_LOG_DEV_WARN(dev, + "Succesfully initialized caam hwencap backend\n"); + + error = make_dev_s(&args, &sc->crypto_cdev, "caam_hwencap"); + if (error != 0) { + CAAM_LOG_DEV_WARN(dev, + "Failed to register caam hwencap device"); + crypto_unregister_all(sc->crypto_id); + } + } + return (error); } @@ -395,4 +788,9 @@ sc = device_get_softc(dev); crypto_unregister_all(sc->crypto_id); + + if (g_caam_dev == dev) { + destroy_dev(sc->crypto_cdev); + g_caam_dev = NULL; + } } diff --git a/sys/arm64/qoriq/caam/caam_internal.h b/sys/arm64/qoriq/caam/caam_internal.h --- a/sys/arm64/qoriq/caam/caam_internal.h +++ b/sys/arm64/qoriq/caam/caam_internal.h @@ -32,6 +32,7 @@ struct jr_pool jr_pool; int32_t crypto_id; + struct cdev *crypto_cdev; }; #endif /* CAAM_INTERNAL_H */ diff --git a/sys/dev/hwencap/hwencap_ioctl.h b/sys/dev/hwencap/hwencap_ioctl.h new file mode 100644 --- /dev/null +++ b/sys/dev/hwencap/hwencap_ioctl.h @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Alstom Group + * Copyright 2024 Sii Poland + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef HWENCAP_IOCTL_H +#define HWENCAP_IOCTL_H + +#include +#include + +struct hwencap_encap_arg { + size_t in_buf_sz; + uintptr_t in_buf; + size_t out_buf_sz; + uintptr_t out_buf; +}; + +struct hwencap_blob_sz_arg { + size_t in_sz; + size_t out_sz; +}; + +enum hwencap_mode { + HWENCAP_MODE_HW_UNIQUE = 0, /* Blob bound to a specific device */ + HWENCAP_MODE_HW_SHARED = 1, /* Blob portable between similar devices */ + HWENCAP_MODE_SOFT = 2 /* Blob not HW bound */ +}; + +#define HWENCAP_ENCAP _IOWR('a', 1, struct hwencap_encap_arg) +#define HWENCAP_DECAP _IOWR('a', 2, struct hwencap_encap_arg) +#define HWENCAP_ENCAP_BLOB_SZ _IOWR('a', 3, struct hwencap_blob_sz_arg) +#define HWENCAP_DECAP_PT_SZ _IOWR('a', 4, struct hwencap_blob_sz_arg) +#define HWENCAP_MODE _IOR('a', 5, int) + +#endif /* HWENCAP_IOCTL_H */ diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -31,6 +31,7 @@ getfmac \ getpmac \ gstat \ + hwencap \ i2c \ ifmcstat \ iostat \ diff --git a/usr.sbin/hwencap/Makefile b/usr.sbin/hwencap/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/hwencap/Makefile @@ -0,0 +1,8 @@ + +PROG= hwencap +MAN= hwencap.8 +SRCS= hwencap.c + +CFLAGS+= -I${SRCTOP}/sys + +.include diff --git a/usr.sbin/hwencap/hwencap.8 b/usr.sbin/hwencap/hwencap.8 new file mode 100644 --- /dev/null +++ b/usr.sbin/hwencap/hwencap.8 @@ -0,0 +1,69 @@ +.Dd May 31, 2024 +.Dt HWENCAP 8 +.Os +.Sh NAME +.Nm hwencap +.Nd hardware encapsulation tool +.Sh SYNOPSIS +.Nm +.Fl -device Ns | Ns Fl f Ar device +.Fl -encapsulate Ns | Ns Fl e +.Op Fl -output Ns | Ns Fl o Ar output +.Op Ar file +.Nm +.Fl -device Ns | Ns Fl f Ar device +.Fl -decapsulate Ns | Ns Fl d +.Op Fl -output Ns | Ns Fl o Ar output +.Op Ar file +.Nm +.Fl -device Ns | Ns Fl f Ar device +.Fl -getmode Ns | Ns Fl m +.Sh DESCRIPTION +.Nm +uses a hardware device to encapsulate or decapsulate data. +The device should use it's own key for encapsulation and +the resulting output should only be able to be decoded on +a device it was created. +The data is by default read from standard input, but can be +read from +.Ar file. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl -device Ar device , Fl f Ar device +Device file which implements the hwencap interface. +This option is always needed. +.It Fl -encapsulate , Fl e +Encapsulate the input. +.It Fl -decapsulate , Fl d +Decapsulate the input. +.It Fl -output Ar output , Fl o Ar output +Output file. By default standard output is used. +.It Fl -getmode , Fl m +Get the mode of operation of hardware device. +Some devices may not use unique key for encapsulation. +This option allows checking if the key is unique and +if the encapsulation occurs in hardware or software. +.Sh EXAMPLES +.Bl -tag -width 0n +.It Sy Example 1\&: Encapsulate a key +.Pp +The following command encapsulates some key and +stores it in output.key file. +.Bd -literal -offset 2n +.Li # Ic hwencap -f /dev/caam_hwencap -e -o output.key ./mykey +.Ed +.It Sy Example 2\&: Get mode of operation +.Pp +The following command reads the mode of operation of /dev/caam_hwencap +.Bd -literal -offset 2n +.Li # Ic hwencap -f /dev/caam_hwencap -m +.Ed +.El +.Sh AUTHORS +The +.Nm +tool was written by Sii Poland and sponsored by Alstom Group. +.Pp +This manual page was written by +.An Dawid Gorecki Aq Mt dgorecki@sii.pl diff --git a/usr.sbin/hwencap/hwencap.c b/usr.sbin/hwencap/hwencap.c new file mode 100644 --- /dev/null +++ b/usr.sbin/hwencap/hwencap.c @@ -0,0 +1,312 @@ +/* + * Copyright 2024 Alstom Group + * Copyright 2024 Sii Poland + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define INITIAL_READ_SIZE 4096 +#define REALLOC_FACTOR 2 + +enum blob_operation { + op_encap, + op_decap, + op_getmode, +}; + +enum optval { + opt_end = -1, + /* to not collide with shortops */ + opt_dummy = CHAR_MAX, + opt_help, + opt_device, + opt_encap, + opt_decap, + opt_getmode, + opt_output, +}; + +static const struct option longopts[] = { + {"help", no_argument, 0, opt_help}, + {"device", required_argument, 0, opt_device}, + {"encapsulate", no_argument, 0, opt_encap}, + {"decapsulate", no_argument, 0, opt_decap}, + {"getmode", no_argument, 0, opt_getmode}, + {"output", required_argument, 0, opt_output}, + { } +}; + +static size_t +read_input(FILE *f, void **buf_addr) +{ + size_t buf_len, new_buf_len, data_len, read_len; + uint8_t *buf, *new_buf; + + /* Allocate initial buffer - will be realloced later if needed */ + buf_len = INITIAL_READ_SIZE; + buf = malloc(buf_len); + if (buf == NULL) + err(EX_SOFTWARE, "Cannot allocate input memory"); + data_len = 0; + + while ((read_len = fread(&buf[data_len], 1, buf_len - data_len, f)) > 0) { + data_len += read_len; + + if (buf_len - data_len == 0) { + /* + * Reallocate memory - realloc cannot be used, as + * we are dealing with something that needs to be + * cleared with explicit_bzero + */ + new_buf_len = buf_len * REALLOC_FACTOR; + new_buf = malloc(new_buf_len); + if (new_buf == NULL) { + explicit_bzero(buf, data_len); + err(EX_SOFTWARE, "Cannot reallocate memory"); + } + + memcpy(new_buf, buf, data_len); + explicit_bzero(buf, data_len); + free(buf); + + buf_len = new_buf_len; + buf = new_buf; + } + } + + *buf_addr = buf; + return (data_len); +} + +static void +blob_operation(int dev_fd, const char *in_path, const char *out_path, + enum blob_operation op) +{ + struct hwencap_blob_sz_arg sz_arg = { 0 }; + struct hwencap_encap_arg arg = { 0 }; + FILE *inf, *outf; + size_t in_len; + void *in_buf; + int rv; + size_t written; + + if (strcmp(in_path, "-") == 0) + inf = stdin; + else + inf = fopen(in_path, "r"); + if (inf == NULL) + err(EX_IOERR, "Could not open input file %s", in_path); + + in_len = read_input(inf, &in_buf); + if (inf != stdin) + fclose(inf); + arg.in_buf_sz = in_len; + arg.in_buf = (uintptr_t)in_buf; + + sz_arg.in_sz = in_len; + rv = ioctl(dev_fd, (op == op_encap) ? HWENCAP_ENCAP_BLOB_SZ : + HWENCAP_DECAP_PT_SZ, &sz_arg); + if (rv != 0) { + explicit_bzero((void *)arg.in_buf, arg.in_buf_sz); + errx(EX_IOERR, "ioctl error"); + } + arg.out_buf_sz = sz_arg.out_sz; + + arg.out_buf = (uintptr_t)malloc(arg.out_buf_sz); + if ((void *)arg.out_buf == NULL) { + explicit_bzero((void *)arg.in_buf, arg.in_buf_sz); + errx(EX_IOERR, "Failed to allocate output buffer"); + } + + rv = ioctl(dev_fd, (op == op_encap) ? HWENCAP_ENCAP : + HWENCAP_DECAP, &arg); + if (rv != 0) { + explicit_bzero((void *)arg.in_buf, arg.in_buf_sz); + errx(EX_IOERR, "Failed to %s data", (op == op_encap) ? + "encapsulate" : "decapsulate"); + } + + explicit_bzero((void *)arg.in_buf, arg.in_buf_sz); + free((void *)arg.in_buf); + + if (strcmp(out_path, "-") == 0) + outf = stdout; + else + outf = fopen(out_path, "w"); + if (outf == NULL) + err(EX_IOERR, "Could not open output file %s", out_path); + + written = fwrite((void *)arg.out_buf, 1, arg.out_buf_sz, outf); + if (written < arg.out_buf_sz) { + fprintf(stderr, "Failed to write entire output buffer\n"); + } + + explicit_bzero((void *)arg.out_buf, arg.out_buf_sz); + free((void *)arg.out_buf); + + close(dev_fd); + if (outf != stdout) + fclose(outf); +} + +static void +getmode_operation(int dev_fd) +{ + int rv; + enum hwencap_mode mode; + + rv = ioctl(dev_fd, HWENCAP_MODE, &mode); + if (rv != 0) + errx(EX_IOERR, "Failed to get encapsulation mode"); + + switch (mode) { + case HWENCAP_MODE_HW_UNIQUE: + printf("Mode: Hardware encapsulation, Device unique\n"); + break; + case HWENCAP_MODE_HW_SHARED: + printf("Mode: Hardware encapsulation, Device shared\n"); + break; + case HWENCAP_MODE_SOFT: + printf("Mode: Software encapsulation\n"); + break; + default: + printf("Mode: Unknown (%d)\n", mode); + break; + } +} + +static void +perform_operation(const char *device, const char *in_path, const char *out_path, + enum blob_operation op) +{ + int dev_fd; + + dev_fd = open(device, O_RDONLY); + if (dev_fd < 0) + errx(EX_IOERR, "Could not open device file %s", device); + + switch (op) { + case op_encap: + case op_decap: + blob_operation(dev_fd, in_path, out_path, op); + break; + case op_getmode: + getmode_operation(dev_fd); + break; + default: + close(dev_fd); + errx(EX_USAGE, "Unsupported operation %d", op); + } + + close(dev_fd); +} + +static void +usage(char *name) +{ + printf("Data encapsulation.\n\n"); + + printf("usage: %s [options] [input_file]\n", name); + printf("\tinput_file input file path, stdin if not specified\n"); + printf("\t-f | --device path to a device implementing hwencap\n"); + printf("\t-m | --getmode show encapsulation mode used by device\n"); + printf("\t-e | --encapsulate encapsulate input data\n"); + printf("\t-d | --decapsulate decapsulate input data\n"); + printf("\t-o | --output output file path, stdout if not specified\n"); + printf("\t-h | --help\n"); +} + +int +main(int argc, char *argv[]) +{ + const char *dev_path = NULL, *infile = NULL, *outfile = NULL; + enum blob_operation op; + int eflag = 0, dflag = 0, mflag = 0; + int opt; + + if (argc < 2) { + usage(argv[0]); + return (0); + } + + while ((opt = getopt_long(argc, argv, "hdemf:o:", longopts, NULL)) != opt_end) { + switch (opt) { + case 'h': + case opt_help: + usage(argv[0]); + return (0); + case 'd': + case opt_decap: + dflag = 1; + break; + case 'e': + case opt_encap: + eflag = 1; + break; + case 'm': + case opt_getmode: + mflag = 1; + break; + case 'f': + case opt_device: + dev_path = optarg; + break; + case 'o': + case opt_output: + outfile = optarg; + break; + default: + printf("Not implemented!\n"); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (!eflag && !dflag && !mflag) + errx(EX_USAGE, "No supported mode option given"); + if (eflag && dflag) + errx(EX_USAGE, "Cannot set both encapsulate and decapsulate"); + if (mflag && (eflag || dflag)) + errx(EX_USAGE, "Cannot set both getmode and en/decapsulate"); + if (dev_path == NULL) + errx(EX_USAGE, "Device path needs to be specified"); + if (argc > 1) + errx(EX_USAGE, "Only one input file supported\n"); + + /* Default to stdout */ + if (outfile == NULL) + outfile = "-"; + if (argc == 1) + infile = argv[0]; + else + infile = "-"; + + if (eflag) + op = op_encap; + if (dflag) + op = op_decap; + if (mflag) + op = op_getmode; + + perform_operation(dev_path, infile, outfile, op); + + return (0); +}