CRYPTODEV.TXT

Programs and patches

CryptoDev for Linux

A Linux port of the /dev/crypto interface.

Last updated · 27 November 2009

Device /dev/crypto (aka CryptoDev) is a way for userspace processes to use cryptographic algorithms provided by kernel CryptoAPI modules. For example a process that needs to AES-encrypt some data can either:

  1. Have the AES algorithm built in, or
  2. Use an external library like OpenSSL that will do the encryption, or
  3. Ask the kernel to do the encryption

Although in most cases using the external library for cryptographic tasks would be the best option, sometimes it may come handy to have a kernel interface for it as well. Using it one may create a really lightweight programs (see cryptodev-demo.c) supporting cryptography without any additional libraries.

The CryptoDev was originally introduced in OpenBSD (at least as far as I know :-), later ported to FreeBSD and on this page you can find my port for Linux 2.6. Because kernel internals differ a lot between BSD and Linux I only attempted to keep the API (i.e. the semantics used when talking with /dev/crypto) and wrote the driver itself from scratch.

Linux kernel patches

To enable /dev/crypto device patch your kernel with the following patch and configure with Cryptographic options -> Cryptodev (/dev/crypto) interface either built-in or as a module. If you are using udev the device /dev/crypto should be automatically created upon loading the module to the kernel.

Linux kernel 2.6.27+

cryptodev-20091126.tar.bz2
Standalone module, doesn't require kernel patching, simply unpack and run make && make install.
Known to work with 2.6.32 and probably later kernels as well.
Thanks Nikos Mavrogiannopoulos for the port to 2.6.27

Linux kernel 2.6.8

cryptodev-2.6.8.diff

The module takes two parameters:

verbosity=
This is 0 by default, i.e. no messages about CryptoDev usage are printed. Use 1 if you are developing or using a program that accesses /dev/crypto and is having problems with it. This way some detailed error messages will be print if things go wrong. If you really want to have your log full of messages about CryptoDev usage, set verbosity to 2.
enable_stats=
The driver can collect statistics about CryptoDev usage. For now it can tell you how many bytes was encrypted or decrypted during each session, what size had the biggest request and what was the average size per request. By default this is 0, i.e. no statistics are collected. Set enable_stats=1 and verbosity=2 if you want to be getting the statistics described above.

The parameters can be changed even when the module is already loaded by writing to /sys/module/cryptodev/{enable_stats,verbosity}.

Userspace demo

The basic structure of a program using CryptoDev is following:

	fd = open("/dev/crypto");
	ioctl(fd, CIOCGSESSION);
	ioctl(fd, CIOCRYPT);
	[... repeat CIOCRYPT ioctls ...]
	ioctl(fd, CIOCFSESSION);
	close(fd);

On OpenBSD it is needed to clone the filedescriptor before actually using it. This is also supported on Linux, but not required:

	fd = open("/dev/crypto");
	ioctl(fd, CRIOGET, &fd_new);
	ioctl(fd_new, CIOCGSESSION);
	ioctl(fd_new, CIOCRYPT);
	[... repeat CIOCRYPT ioctls ...]
	ioctl(fd_new, CIOCFSESSION);
	close(fd_new);
	close(fd);

Indeed, there are some structures floating around, but the for the overview this is enough. See the following demo for details:

CryptoDev usage demo
Read cryptodev-demo1.c below · Download raw cryptodev-demo1.c
CryptoDev with OpenSSL
Read cryptodev-demo2.c below · Download raw cryptodev-demo2.c

CryptoDev support for OpenSSL

If you want to combine the (dis:-)advantages of using both OpenSSL and CryptoDev you may want to give the following patch a try:

openssl-0.9.8k-cryptodev.diff
Apply this patch to the original openssl-0.9.8k.tar.gz and recompile.
openssl-0.9.7d-cryptodev.diff
Apply this patch to the original openssl-0.9.7d.tar.gz and recompile.

Using CryptoAPI in the Linux kernel

(This is a bit off-topic here :-)
CryptoAPI is a Linux in-kernel infrastructure that offers cryptography to all other subsystems. It provides a single API for accessing different ciphers and digests.

Simple Linux kernel module that shows how the encryption and decryption can be used:
Read cryptoapi-demo.c below · Download raw cryptoapi-demo.c

Useful CryptoAPI-related links:

CryptoAPI mailing list
Archive and subscription page
VIA PadLock support for Linux
Patches for Kernel and OpenSSL

cryptodev-demo1.c source


/*
 * Demo on how to use OpenBSD /dev/crypto device.
 *
 * Author: Michal Ludvig <michal@logix.cz>
 *         /michal
 *
 * Note: by default OpenBSD doesn't allow using
 *       /dev/crypto if there is no hardware accelerator
 *       for a given algorithm. To change this you'll have to
 *       set cryptodevallowsoft=1 in 
 *       /usr/src/sys/crypto/cryptodev.c and rebuild your kernel.
 */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <crypto/cryptodev.h>

#define DATA_SIZE       4096
#define BLOCK_SIZE      16
#define KEY_SIZE        24

static int
test_crypto(int cfd)
{
        struct {
                char    in[DATA_SIZE],
                        encrypted[DATA_SIZE],
                        decrypted[DATA_SIZE],
                        iv[BLOCK_SIZE],
                        key[KEY_SIZE];
        } data;
        struct session_op sess;
        struct crypt_op cryp;

        memset(&sess, 0, sizeof(sess));
        memset(&cryp, 0, sizeof(cryp));

        /* Use the garbage that is on the stack :-) */
        /* memset(&data, 0, sizeof(data)); */

        /* Get crypto session for AES128 */
        sess.cipher = CRYPTO_AES_CBC;
        sess.keylen = KEY_SIZE;
        sess.key = data.key;
        if (ioctl(cfd, CIOCGSESSION, &sess)) {
                perror("ioctl(CIOCGSESSION)");
                return 1;
        }

        /* Encrypt data.in to data.encrypted */
        cryp.ses = sess.ses;
        cryp.len = sizeof(data.in);
        cryp.src = data.in;
        cryp.dst = data.encrypted;
        cryp.iv = data.iv;
        cryp.op = COP_ENCRYPT;
        if (ioctl(cfd, CIOCCRYPT, &cryp)) {
                perror("ioctl(CIOCCRYPT)");
                return 1;
        }

        /* Decrypt data.encrypted to data.decrypted */
        cryp.src = data.encrypted;
        cryp.dst = data.decrypted;
        cryp.op = COP_DECRYPT;
        if (ioctl(cfd, CIOCCRYPT, &cryp)) {
                perror("ioctl(CIOCCRYPT)");
                return 1;
        }

        /* Verify the result */
        if (memcmp(data.in, data.decrypted, sizeof(data.in)) != 0) {
                fprintf(stderr,
                        "FAIL: Decrypted data are different from the input data.\n");
                return 1;
        } else
                printf("Test passed\n");

        /* Finish crypto session */
        if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
                perror("ioctl(CIOCFSESSION)");
                return 1;
        }

        return 0;
}

int
main()
{
        int fd = -1, cfd = -1;

        /* Open the crypto device */
        fd = open("/dev/crypto", O_RDWR, 0);
        if (fd < 0) {
                perror("open(/dev/crypto)");
                return 1;
        }

        /* Clone file descriptor */
        if (ioctl(fd, CRIOGET, &cfd)) {
                perror("ioctl(CRIOGET)");
                return 1;
        }

        /* Set close-on-exec (not really neede here */
        if (fcntl(cfd, F_SETFD, 1) == -1) {
                perror("fcntl(F_SETFD)");
                return 1;
        }

        /* Run the test itself */
        if (test_crypto(cfd))
                return 1;

        /* Close cloned descriptor */
        if (close(cfd)) {
                perror("close(cfd)");
                return 1;
        }

        /* Close the original descriptor */
        if (close(fd)) {
                perror("close(fd)");
                return 1;
        }

        return 0;
}

Download raw cryptodev-demo1.c ↓

cryptodev-demo2.c source


/*
 * This demo combines CryptoDev and OpenSSL encryption
 * to verify eachother.
 *
 * Author: Michal Ludvig <michal@logix.cz>
 *         /michal
 *
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <linux/cryptodev.h>

#include <openssl/evp.h>

#define DATA_SIZE       14096
#define BLOCK_SIZE      16
#define KEY_SIZE        16
#define ALG_NAME        "aes"
#define MODE            CRYPTO_FLAG_CBC

void
dump_mem(const char *msg, const unsigned char *buf, size_t len)
{
        int i;

        printf("%s\n", msg);
        for (i = 0; i < len; i++)
                printf("%02x%s", buf[i], (i%8==7) ? "\n" : "");
}

int
compare_buffers(const char *msg,
                const char *buf1_label, const unsigned char *buf1,
                const char *buf2_label, const unsigned char *buf2,
                size_t len)
{
        if (memcmp(buf1, buf2, len) != 0) {
                size_t i, dump;
                for (i = 0; i < len; i++)
                        if (buf1[i] != buf2[i])
                                break;
                fprintf(stderr, "FAIL: %s differ at offset %zu\n", msg, i);
                dump = len - i > 64 ? 64 : len - i;
                dump_mem(buf1_label, &buf1[i], dump);
                dump_mem(buf2_label, &buf2[i], dump);

                return 1;
        }
        return 0;
}

static int
test_crypto(int cfd)
{
        struct {
                char    in[DATA_SIZE],
                        encrypted[DATA_SIZE],
                        decrypted[DATA_SIZE],
                        iv[BLOCK_SIZE],
                        key[KEY_SIZE];
        } data, data_openssl;

        struct session_op sess;
        struct crypt_op cryp;
        char algbuf[100];
        EVP_CIPHER_CTX ctx;
        const EVP_CIPHER *c;

        /* ==== Prepare the input data ==== */

        /* Use the garbage that is on the stack :-) */
        memset(&data, 0, sizeof(data));

        memcpy (&data_openssl, &data, sizeof(data));

        /* ==== First the CryptoDev pass ==== */
        memset(&sess, 0, sizeof(sess));
        memset(&cryp, 0, sizeof(cryp));

        /* Get crypto session for AES128 */
        sess.cipher = CRYPTO_CIPHER_NAME | MODE;
        sess.alg_name = ALG_NAME;
        sess.alg_namelen = strlen(sess.alg_name);
        sess.keylen = KEY_SIZE;
        sess.key = data.key;
        if (ioctl(cfd, CIOCGSESSION, &sess)) {
                perror("ioctl(CIOCGSESSION)");
                return 1;
        }

        /* Encrypt data.in to data.encrypted */
        cryp.ses = sess.ses;
        cryp.len = sizeof(data.in);
        cryp.src = data.in;
        cryp.dst = data.encrypted;
        cryp.iv = data.iv;
        cryp.op = COP_ENCRYPT;
        if (ioctl(cfd, CIOCCRYPT, &cryp)) {
                perror("ioctl(CIOCCRYPT)");
                return 1;
        }

        /* Decrypt data.encrypted to data.decrypted */
        cryp.src = data.encrypted;
        cryp.dst = data.decrypted;
        cryp.op = COP_DECRYPT;
        if (ioctl(cfd, CIOCCRYPT, &cryp)) {
                perror("ioctl(CIOCCRYPT)");
                return 1;
        }

        /* Finish crypto session */
        if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
                perror("ioctl(CIOCFSESSION)");
                return 1;
        }

        /* ==== Now the OpenSSL pass ==== */

        OpenSSL_add_all_algorithms();
        EVP_CIPHER_CTX_init(&ctx);

        snprintf(algbuf, sizeof(algbuf), "%s-%d-%s", ALG_NAME, KEY_SIZE * 8,
                 MODE & CRYPTO_FLAG_CBC ? "cbc" : "ecb");

        c = EVP_get_cipherbyname(algbuf);
        if (!c) {
                perror("EVP_get_cipherbyname()");
                return 1;
        }

        if (!EVP_CipherInit(&ctx, c, data_openssl.key, data_openssl.iv, 1)) {
                perror("EVP_CipherInit()");
                return 1;
        }

        if (!EVP_Cipher(&ctx, data_openssl.encrypted, data_openssl.in, sizeof(data_openssl.in))) {
                perror("EVP_Cipher()");
                return 1;
        }

        EVP_CIPHER_CTX_cleanup(&ctx);
        EVP_CIPHER_CTX_init(&ctx);

        if (!EVP_CipherInit(&ctx, c, data_openssl.key, data_openssl.iv, 0)) {
                perror("EVP_CipherInit()");
                return 1;
        }

        if (!EVP_Cipher(&ctx, data_openssl.decrypted, data_openssl.encrypted, sizeof(data_openssl.in))) {
                perror("EVP_Cipher()");
                return 1;
        }

        /* ==== Verify the results ==== */

        if (compare_buffers("Encrypted data", "CryptoDev:", data.encrypted,
                            "OpenSSL:", data_openssl.encrypted, sizeof(data.encrypted)))
                return 1;

        if (compare_buffers("Decrypted data", "CryptoDev:", data.decrypted,
                            "OpenSSL:", data_openssl.decrypted, sizeof(data.decrypted)))
                return 1;

        printf("Test passed\n");

        return 0;
}

int
main()
{
        int fd = -1, cfd = -1;

        /* Open the crypto device */
        fd = open("/dev/crypto", O_RDWR, 0);
        if (fd < 0) {
                perror("open(/dev/crypto)");
                return 1;
        }

        /* Clone file descriptor */
        if (ioctl(fd, CRIOGET, &cfd)) {
                perror("ioctl(CRIOGET)");
                return 1;
        }

        /* Set close-on-exec (not really neede here) */
        if (fcntl(cfd, F_SETFD, 1) == -1) {
                perror("fcntl(F_SETFD)");
                return 1;
        }

        /* Run the test itself */
        if (test_crypto(cfd))
                return 1;

        /* Close cloned descriptor */
        if (close(cfd)) {
                perror("close(cfd)");
                return 1;
        }

        /* Close the original descriptor */
        if (close(fd)) {
                perror("close(fd)");
                return 1;
        }

        return 0;
}

Download raw cryptodev-demo2.c ↓

cryptoapi-demo.c source


/* 
 * Simple demo explaining usage of the Linux kernel CryptoAPI.
 * By Michal Ludvig <michal@logix.cz>
 *    /
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/mm.h>
#include <asm/scatterlist.h>

#define PFX "cryptoapi-demo: "

MODULE_AUTHOR("Michal Ludvig <michal@logix.cz>");
MODULE_DESCRIPTION("Simple CryptoAPI demo");
MODULE_LICENSE("GPL");

/* ====== CryptoAPI ====== */

#define DATA_SIZE       16

#define FILL_SG(sg,ptr,len)     do { (sg)->page = virt_to_page(ptr); (sg)->offset = offset_in_page(ptr); (sg)->length = len; } while (0)

static void
hexdump(unsigned char *buf, unsigned int len)
{
        while (len--)
                printk("%02x", *buf++);

        printk("\n");
}

static void
cryptoapi_demo(void)
{
        /* config options */
        char *algo = "aes";
        int mode = CRYPTO_TFM_MODE_CBC;
        char key[16], iv[16];

        /* local variables */
        struct crypto_tfm *tfm;
        struct scatterlist sg[8];
        int ret;
        char *input, *encrypted, *decrypted;

        memset(key, 0, sizeof(key));
        memset(iv, 0, sizeof(iv));

        tfm = crypto_alloc_tfm (algo, mode);

        if (tfm == NULL) {
                printk("failed to load transform for %s %s\n", algo, mode == CRYPTO_TFM_MODE_CBC ? "CBC" : "");
                return;
        }

        ret = crypto_cipher_setkey(tfm, key, sizeof(key));

        if (ret) {
                printk(KERN_ERR PFX "setkey() failed flags=%x\n", tfm->crt_flags);
                goto out;
        }

        input = kmalloc(GFP_KERNEL, DATA_SIZE);
        if (!input) {
                printk(KERN_ERR PFX "kmalloc(input) failed\n");
                goto out;
        }

        encrypted = kmalloc(GFP_KERNEL, DATA_SIZE);
        if (!encrypted) {
                printk(KERN_ERR PFX "kmalloc(encrypted) failed\n");
                kfree(input);
                goto out;
        }

        decrypted = kmalloc(GFP_KERNEL, DATA_SIZE);
        if (!decrypted) {
                printk(KERN_ERR PFX "kmalloc(decrypted) failed\n");
                kfree(encrypted);
                kfree(input);
                goto out;
        }

        memset(input, 0, DATA_SIZE);

        FILL_SG(&sg[0], input, DATA_SIZE);
        FILL_SG(&sg[1], encrypted, DATA_SIZE);
        FILL_SG(&sg[2], decrypted, DATA_SIZE);

        crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize (tfm));
        ret = crypto_cipher_encrypt(tfm, &sg[1], &sg[0], DATA_SIZE);
        if (ret) {
                printk(KERN_ERR PFX "encryption failed, flags=0x%x\n", tfm->crt_flags);
                goto out_kfree;
        }

        crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize (tfm));
        ret = crypto_cipher_decrypt(tfm, &sg[2], &sg[1], DATA_SIZE);
        if (ret) {
                printk(KERN_ERR PFX "decryption failed, flags=0x%x\n", tfm->crt_flags);
                goto out_kfree;
        }

        printk(KERN_ERR PFX "IN: "); hexdump(input, DATA_SIZE);
        printk(KERN_ERR PFX "EN: "); hexdump(encrypted, DATA_SIZE);
        printk(KERN_ERR PFX "DE: "); hexdump(decrypted, DATA_SIZE);

        if (memcmp(input, decrypted, DATA_SIZE) != 0)
                printk(KERN_ERR PFX "FAIL: input buffer != decrypted buffer\n");
        else
                printk(KERN_ERR PFX "PASS: encryption/decryption verified\n");

out_kfree:
        kfree(decrypted);
        kfree(encrypted);
        kfree(input);

out:
        crypto_free_tfm(tfm);
}

/* ====== Module init/exit ====== */

static int __init
init_cryptoapi_demo(void)
{
        cryptoapi_demo();

        return 0;
}

static void __exit
exit_cryptoapi_demo(void)
{
}

module_init(init_cryptoapi_demo);
module_exit(exit_cryptoapi_demo);

Download raw cryptoapi-demo.c ↓

CryptoDev for LinuxUTF-8 · LOGIX.CZ

Find in Logix

Search page titles and descriptions. Esc closes.

A few handy keys

A little Borland, a little early desktop, a corner of the web to call home.

Pick a directory from the menu or tree. Use Find when you know what you’re after.

Tab / Shift+Tab
Move between controls
↑ / ↓ in menus
Choose an entry
← / → in menus
Switch directories
/
Find a page
Esc
Close menu or dialog
F1
Open this help

Prefer a different atmosphere? View has blue, grey, and green palettes.

No commands to learn. Just follow your curiosity.