The userspace interface of the kernel crypto API is documented with * a general explanation * a discussion of the memory in-place operation * the description of the message digest API * the description of the symmetric cipher API In addition, a fully self contained example that can readily be used as a library is added as well. Signed-off-by: Stephan Mueller <smueller@xxxxxxxxxx> CC: Marek Vasut <marex@xxxxxxx> --- Documentation/crypto/crypto-API-userspace.txt | 662 ++++++++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 Documentation/crypto/crypto-API-userspace.txt diff --git a/Documentation/crypto/crypto-API-userspace.txt b/Documentation/crypto/crypto-API-userspace.txt new file mode 100644 index 0000000..30ca6a7 --- /dev/null +++ b/Documentation/crypto/crypto-API-userspace.txt @@ -0,0 +1,662 @@ +Introduction +============ + +The concepts of the kernel crypto API visible to kernel space is fully +applicable to the user space interface as well. Therefore, the kernel crypto API +high level discussion for the in-kernel use cases applies here as well. + +The major difference, however, is that user space can only act as a consumer +and never as a provider of a transformation or cipher algorithm. + +The following covers the user space interface exported by the kernel crypto +API. It provides a fully working sample code at the that can be used as a +library for user space applications that require cryptographic services from +the kernel. + +Some details of the in-kernel kernel crypto API aspects do not +apply to user space, however. This includes the difference between synchronous +and asynchronous invocations. The user space API call is fully synchronous. +In addition, only a subset of all cipher types are available as documented +below. + + +User space API general remarks +============================== + +The kernel crypto API is accessible from user space. Currently, the following +ciphers are accessible: + + * Message digest including keyed message digest (HMAC, CMAC) + + * Symmetric ciphers + +Note, AEAD ciphers are currently not supported via the symmetric cipher +interface. + +The interface is provided via Netlink using the type AF_ALG. In addition, the +setsockopt option type is SOL_ALG. In case the user space header files do not +export these flags yet, use the following macros: + +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +A cipher is accessed with the same name as done for the in-kernel API calls. +This includes the generic vs. unique naming schema for ciphers as well as the +enforcement of priorities for generic names. + +To interact with the kernel crypto API, a Netlink socket must be created by +the user space application. User space invokes the cipher operation with the +send/write system call family. The result of the cipher operation is obtained +with the read/recv system call family. + +The following API calls assume that the Netlink socket descriptor is already +opened by the user space application and discusses only the kernel crypto API +specific invocations. + +In-place cipher operation +========================= + +Just like the in-kernel operation of the kernel crypto API, the user space +interface allows the cipher operation in-place. That means that the input buffer +used for the send/write system call and the output buffer used by the read/recv +system call may be one and the same. This is of particular interest for +symmetric cipher operations where a copying of the output data to its final +destination can be avoided. + +Message digest API +================== + +The message digest type to be used for the cipher operation is selected when +invoking the bind syscall. bind requires the caller to provide a filled +struct sockaddr data structure. This data structure must be filled as follows: + +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", /* this selects the hash logic in the kernel */ + .salg_name = "sha1" /* this is the cipher name */ +}; + +The salg_type value "hash" applies to message digests and keyed message digests. +Though, a keyed message digest is referenced by the appropriate salg_name and +providing a key for the cipher operation. + +Using the send() system call, the application provides the data that should be +processed with the message digest. The send system call allows the following +flags to be specified: + + * MSG_MORE: If this flag is set, the send system call acts like a + message digest update function where the final hash is not + yet calculated. If the flag is not set, the send system call + calculates the final message digest immediately. + +With the recv() system call, the application can read the message digest from +the kernel crypto API. If the buffer is too small for the message digest, the +flag MSG_TRUNC is set by the kernel. + +In order to set a message digest key, the calling application must use the +setsockopt() option of ALG_SET_KEY. If the key is not set the HMAC operation is +performed without the initial HMAC state change caused by the key. + + +Symmetric cipher API +==================== + +The operation is very similar to the message digest discussion. During +initialization, the struct sockaddr data structure must be filled as follows: + +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", /* this selects the symmetric cipher */ + .salg_name = "cbc(aes)" /* this is the cipher name */ +}; + +Using the sendmsg() system call, the application provides the data that should +be processed for encryption or decryption. In addition, the IV is specified +with the data structure provided by the sendmsg() system call. + +The sendmsg system call parameter of struct msghdr is embedded into the +struct cmsghdr data structure. See recv(2) and cmsg(3) for more information +on how the cmsghdr data structure is used together with the send/recv system +call family. That cmsghdr data structure holds the following information +specified with a separate header instances: + + * specification of the cipher operation type with one of these flags: + ALG_OP_ENCRYPT - encryption of data + ALG_OP_DECRYPT - decryption of data + + * specification of the IV information marked with the flag ALG_SET_IV + +The send system call family allows the following flag to be specified: + + * MSG_MORE: If this flag is set, the send system call acts like a + cipher update function where more input data is expected + with a subsequent invocation of the send system call. + +Note: The kernel reports -EINVAL for any unexpected data. The caller must +make sure that all data matches the constraints given in /proc/crypto for the +selected cipher. + +With the recv() system call, the application can read the result of the +cipher operation from the kernel crypto API. The output buffer must be at least +as large as to hold all blocks of the encrypted or decrypted data. If the output +data size is smaller, only as many blocks are returned that fit into that +output buffer size. + +User space API example +====================== + +Compile the following code with the gcc flags of "-Wextra -Wall -pedantic". + +/* + * Generic kernel crypto API user space interface library + * + * Copyright (C) 2014, Stephan Mueller <smueller@xxxxxxxxxx> + * + * Derived from cryptsetup 1.6.4: + * + * Linux kernel user space API crypto backend implementation (skcipher) + * + * Copyright (C) 2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2012, Milan Broz + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Code from cryptsetup version 1.6.4 used as a basis. See files + * lib/crypto_backend/crypto_cipher_kernel.c and + * lib/crypto_backend/crypto_kernel.c + */ + +#include <stdio.h> + +#include <unistd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <linux/if_alg.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +/************************************************************ + * Application interfaces + ************************************************************/ + +/* Cipher handle */ +struct kcapi_handle { + int tfmfd; + int opfd; +}; + +/************************************************************ + * Internal logic + ************************************************************/ + +/* The in/out should be aligned to page boundary */ +static int _kcapi_cipher_crypt(struct kcapi_handle *handle, + const unsigned char *in, size_t inlen, + unsigned char *out, size_t outlen, + const unsigned char *iv, size_t ivlen, + uint32_t enc) +{ + int r = 0; + ssize_t ret; + struct af_alg_iv *alg_iv; + struct cmsghdr *header; + uint32_t *type; + struct iovec iov; + int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + ivlen) : 0; + char *buffer = NULL; + volatile void *_buffer = NULL; + unsigned int bufferlen = CMSG_SPACE(sizeof(*type)) + iv_msg_size; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + + if (!in || !out || !inlen || !outlen) + return -EINVAL; + + if ((!iv && ivlen) || (iv && !ivlen)) + return -EINVAL; + + buffer = calloc(1, bufferlen); + if (!buffer) + return -ENOMEM; + + iov.iov_base = (void*)(uintptr_t)in; + iov.iov_len = inlen; + msg.msg_control = buffer; + msg.msg_controllen = bufferlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* encrypt/decrypt operation */ + header = CMSG_FIRSTHDR(&msg); + header->cmsg_level = SOL_ALG; + header->cmsg_type = ALG_SET_OP; + header->cmsg_len = CMSG_LEN(sizeof(*type)); + type = (void*)CMSG_DATA(header); + *type = enc; + + /* set IV */ + if (iv) { + header = CMSG_NXTHDR(&msg, header); + header->cmsg_level = SOL_ALG; + header->cmsg_type = ALG_SET_IV; + header->cmsg_len = iv_msg_size; + alg_iv = (void*)CMSG_DATA(header); + alg_iv->ivlen = ivlen; + memcpy(alg_iv->iv, iv, ivlen); + } + + ret = sendmsg(handle->opfd, &msg, 0); + if (ret != (ssize_t)inlen) { + r = -EIO; + goto bad; + } + + ret = read(handle->opfd, out, outlen); + if (ret != (ssize_t)outlen) + r = -EIO; +bad: + memset(buffer, 0, bufferlen); + _buffer = memchr(buffer, 1, bufferlen); + if (_buffer) + _buffer = '\0'; + free(buffer); + return r; +} + +/************************************************************ + * API to application + ************************************************************/ + +/* + * Initialization of a cipher handle and establishing the connection to + * the kernel + * + * @handle cipher handle filled during the call - output + * @type cipher type, one of the following - input: + * "hash" for message digests (including keyed message digests) + * "skcipher" for symmetric ciphers + * @ciphername kernel crypto API cipher name as specified in + * /proc/crypto - input + * + * return: 0 upon success + * < 0 in case of error + * ENOENT - algorithm not available + * ENOTSUP - AF_ALG family not available + * EINVAL - accept syscall failed + */ +int kcapi_cipher_init(struct kcapi_handle *handle, + const char *type, const char *ciphername) +{ + struct sockaddr_alg sa; + + memset(&sa, 0, sizeof(sa)); + sa.salg_family = AF_ALG; + snprintf((char *)sa.salg_type, sizeof(sa.salg_type),"%s", type); + snprintf((char *)sa.salg_name, sizeof(sa.salg_name),"%s", ciphername); + + handle->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (handle->tfmfd == -1) + return -ENOTSUP; + + if (bind(handle->tfmfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + close(handle->tfmfd); + handle->tfmfd = -1; + return -ENOENT; + } + + handle->opfd = accept(handle->tfmfd, NULL, 0); + if (handle->opfd == -1) { + close(handle->tfmfd); + handle->tfmfd = -1; + return -EINVAL; + } + + return 0; +} + +/* + * Close the cipher handle and release resources + * + * @handle cipher handle to release - input + * + * return: 0 upon success + */ +int kcapi_cipher_destory(struct kcapi_handle *handle) +{ + if (handle->tfmfd != -1) + close(handle->tfmfd); + if (handle->opfd != -1) + close(handle->opfd); + return 0; +} + + +/* + * Set the key for the cipher handle + * + * This call is applicable for keyed message digests and symmetric ciphers. + * + * @handle cipher handle - input + * @key key buffer - input + * @keylen length of key buffer - input + * + * return: 0 upon success + * < 0 in case of error + */ +int kcapi_cipher_setkey(struct kcapi_handle *handle, + const unsigned char *key, size_t keylen) +{ + if (setsockopt(handle->tfmfd, SOL_ALG, ALG_SET_KEY, + key, keylen) == -1) + return -EINVAL; + + return 0; +} + +/* + * Message digest update function + * + * @handle cipher handle - input + * @buffer holding the data to add to the message digest - input + * @len buffer length - input + * + * return: 0 upon success + * < 0 in case of error + */ +int kcapi_md_update(struct kcapi_handle *handle, + const unsigned char *buffer, size_t len) +{ + ssize_t r; + + r = send(handle->opfd, buffer, len, MSG_MORE); + if (r < 0 || (size_t)r < len) + return -EIO; + + return 0; +} + +/* + * Message digest finalization function + * + * @handle cipher handle - input + * @buffer filled with the message digest - output + * @len buffer length - input + * + * return: 0 upon success + * < 0 in case of error + * EIO - data cannot be obtained + * ENOMEM - buffer is too small for the complete message digest, + * the buffer is filled with the truncated message digest + */ + +int kcapi_md_final(struct kcapi_handle *handle, + unsigned char *buffer, size_t len) +{ + ssize_t r; + struct iovec iov; + struct msghdr msg; + + iov.iov_base = (void*)(uintptr_t)buffer; + iov.iov_len = len; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + + r = recvmsg(handle->opfd, &msg, 0); + if (r < 0) + return -EIO; + if (msg.msg_flags & MSG_TRUNC) + return -ENOMEM; + + return 0; +} + +/* + * Encrypt data + * + * @handle cipher handle - input + * @in plaintext data buffer - input + * @inlen length of in buffer - input + * @out ciphertext data buffer - output + * @outlen length of out buffer - input + * @iv buffer holding the IV (may be NULL if IV is not needed) - input + * @ivlen length of iv (should be zero if iv is NULL) - input + * + * return: 0 upon success + * < 0 in case of error + */ +int kcapi_cipher_encrypt(struct kcapi_handle *handle, + const unsigned char *in, size_t inlen, + unsigned char *out, size_t outlen, + const unsigned char *iv, size_t ivlen) +{ + return _kcapi_cipher_crypt(handle, in, inlen, out, outlen, + iv, ivlen, ALG_OP_ENCRYPT); +} + +/* + * Decrypt data + * + * @handle cipher handle - input + * @in ciphertext data buffer - input + * @inlen length of in buffer - input + * @out plaintext data buffer - output + * @outlen length of out buffer - input + * @iv buffer holding the IV (may be NULL if IV is not needed) - input + * @ivlen length of iv (should be zero if iv is NULL) - input + * + * return: 0 upon success + * < 0 in case of error + */ +int kcapi_cipher_decrypt(struct kcapi_handle *handle, + const unsigned char *in, size_t inlen, + unsigned char *out, size_t outlen, + const unsigned char *iv, size_t ivlen) +{ + return _kcapi_cipher_crypt(handle, in, inlen, out, outlen, + iv, ivlen, ALG_OP_DECRYPT); +} + +/************************************************************ + * Application requiring cryptographic services + ************************************************************/ + +static char hex_char_map_l[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +static char hex_char_map_u[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; +static char hex_char(unsigned int bin, int u) +{ + if (bin < sizeof(hex_char_map_l)) + return (u) ? hex_char_map_u[bin] : hex_char_map_l[bin]; + return 'X'; +} + +/* + * Convert binary string into hex representation + * @bin input buffer with binary data + * @binlen length of bin + * @hex output buffer to store hex data + * @hexlen length of already allocated hex buffer (should be at least + * twice binlen -- if not, only a fraction of binlen is converted) + * @u case of hex characters (0=>lower case, 1=>upper case) + */ +static void bin2hex(const unsigned char *bin, size_t binlen, + char *hex, size_t hexlen, int u) +{ + size_t i = 0; + size_t chars = (binlen > (hexlen / 2)) ? (hexlen / 2) : binlen; + + for (i = 0; i < chars; i++) { + hex[(i*2)] = hex_char((bin[i] >> 4), u); + hex[((i*2)+1)] = hex_char((bin[i] & 0x0f), u); + } +} + + +int main(int argc, char *argv[]) +{ + struct kcapi_handle handle; +#define BUFLEN 32 + unsigned char inbuf[BUFLEN]; /* Plaintext */ +#define IVLEN 16 + unsigned char ivbuf[IVLEN]; /* IV */ + unsigned char outbuf[BUFLEN]; /* ciphertext for encryption */ + unsigned char outbuf2[BUFLEN]; /* plaintext for decryption */ + char hexbuf[BUFLEN * 2 + 1]; + + (void)argc; + (void)argv; + + /* + * Calculate a message digest + */ + if (kcapi_cipher_init(&handle, "hash", "sha256")) { + printf("Allocation of hash failed\n"); + return(1); + } + memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN); + if (kcapi_md_update(&handle, inbuf, BUFLEN)) { + printf("Hash update of buffer failed\n"); + return(1); + } + if (kcapi_md_final(&handle, outbuf, BUFLEN)) { + printf("Hash final failed\n"); + return(1); + } + kcapi_cipher_destory(&handle); + memset(hexbuf, 0, BUFLEN * 2 + 1); + bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0); + printf("Calculated hash %s\n", hexbuf); + + /* + * Calculate a keyed message digest + */ + if (kcapi_cipher_init(&handle, "hash", "hmac(sha256)")) { + printf("Allocation of HMAC failed\n"); + return(1); + } + memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN); + if (kcapi_cipher_setkey(&handle, inbuf, BUFLEN)) { + printf("HMAC setkey failed\n"); + return(1); + } + if (kcapi_md_update(&handle, inbuf, BUFLEN)) { + printf("HMAC update of buffer failed\n"); + return(1); + } + if (kcapi_md_final(&handle, outbuf, BUFLEN)) { + printf("HMAC final failed\n"); + return(1); + } + kcapi_cipher_destory(&handle); + memset(hexbuf, 0, BUFLEN * 2 + 1); + bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0); + printf("Calculated hmac %s\n", hexbuf); + + /* + * Encrypt data + */ + if (kcapi_cipher_init(&handle, "skcipher", "cbc(aes)")) { + printf("Allocation of cipher failed\n"); + return(1); + } + + /* Set key */ + memcpy(inbuf, "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", BUFLEN); + if (kcapi_cipher_setkey(&handle, inbuf, BUFLEN)) { + printf("AES setkey failed\n"); + return(1); + } + + /* Prepare IV */ + memcpy(ivbuf, "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", IVLEN); + + /* + * Encrypt inbuf -- key and plaintext are the same in this example + * + * It is perfectly legal to use inbuf for the plaintext and ciphertext + * pointers. That would mean that after the encryption operation, the + * plaintext is overwritten with the cipher text. + */ + if (kcapi_cipher_encrypt(&handle, inbuf, BUFLEN, + outbuf, BUFLEN, ivbuf, IVLEN)) { + printf("Encryption buffer failed\n"); + return(1); + } + + /* outbuf now contains the cipher text */ + memset(hexbuf, 0, BUFLEN * 2 + 1); + bin2hex(outbuf, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0); + printf("Encrypted data %s\n", hexbuf); + + /* + * Decrypt previously encrypted data + * + * Just like for the encryption operation, the ciphertext buffer pointer + * and the plaintext buffer pointer may point to the same memory + * location. After completion of this operation, the ciphertext is + * overwritten with the plaintext. + */ + if (kcapi_cipher_decrypt(&handle, outbuf, BUFLEN, + outbuf2, BUFLEN, ivbuf, IVLEN)) { + printf("Decryption buffer failed\n"); + return(1); + } + kcapi_cipher_destory(&handle); + memset(hexbuf, 0, BUFLEN * 2 + 1); + bin2hex(outbuf2, BUFLEN, hexbuf, BUFLEN * 2 + 1, 0); + printf("Decrypted data %s\n", hexbuf); + if (!memcmp(inbuf, outbuf2, BUFLEN)) + printf("Decrypted data match original plaintext as expected\n"); + else + printf("FAILURE: Decrypted data does not match original plaintext\n"); + + return 0; +} + +Author +====== + +Stephan Mueller <smueller@xxxxxxxxxx> -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html