Talk: Crypto Kernel subsystem
(1) Using cryptography from userspace¶
The same crypto subsystem we presented being used in kernel space, can also be used in user
space. However, the API used doesn't follow the same pattern, since it makes use of the socket()
systemcall API,
with its other companions systemcalls, i.e. bind()
, accept()
and setsockopt()
.
The idea to enable access from userspace was first created to allow users to have some level of cryptography without any external library dependency and also to ease the use of separate hardwares such as crypto accelerators that are not commonly or easily available in userspace traditional libraries.
The Socket API is well known among a lot of applications and developers, since this is also the interface used to connect a program to the internet. As usual for the Socket API, the headers we need are:
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
And differently from the Crypto API inside the kernel, we don't need to be aware of how the data will be stored in the memory (like using scatterlists).
(2) Checking for the existence of the required algo¶
In this example we show how simple it is to perform a hash operation over a common user input data.
The first thing we need to do is to allocate a struct sockaddr_alg
structure, which contains the basic
information about the algorithm we want to use: the address family of the socket, the type of the algorithm and also
the precise algorithm name:
struct sockaddr_alg sa_alg = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "sha256"
};
Note
When handling a socket connection we use the term address family to specify to which family of protocols
our socket belongs to. In TCP/IP case, for instance, the family is known as AF_INET
, while for crypto we use
the AF_ALG
family.
Now, we can make sure we have this algorithm available to us during a direct call to socket()
:
int sock_fd;
int err;
sock_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (sock_fd < 0) {
perror("failed to allocate socket\n");
return -1;
}
err = bind(sock_fd, (struct sockaddr *)&sa_alg, sizeof(sa_alg));
if (err) {
perror("failed to bind socket, alg may not be supported\n");
return -EAFNOSUPPORT;
}
Note
Another interesting note is that for crypto we only use SOCK_SEQPACKET
, since we don't allow data fragmentation
and we always limit the size of the data buffer being transmitted to the maximum value.
It's important to check because some algorithms are not built-in in the kernel image, but rather are loadable modules that the user must first load prior to using the above code.
(3) Setting the parameters for the algo¶
In the case of a hash algorithm we don't really have much to add about additional parameters, but if we were
presenting a cipher algorithm we would need to set, for instance, the cryptographic key via the setsockopt()
systemcall:
char key[16] = {0};
setsockopt(sock_fd, SOL_ALG, ALG_SET_KEY, key, AES_KEY_LEN);
But considering that's not our case here, let's move forward.
(4) Performing the hash¶
First we make the socket ready for accepting data:
int fd;
fd = accept(sock_fd, NULL, 0);
In the case of a hash algorithm we only need to perform two simple calls now: one to write()
our plaintext to
the file descriptor we just got from accept()
and then read()
from the exact same file descriptor to retrieve the
final sha256 digest:
write(fd, plaintext, text_len);
read(fd, digest, SHA256_DIG_LEN);
Complete code¶
That's an example of a userspace program code that you can try:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
/* Some old versions of glibc doesn't have it set yet */
#ifndef AF_ALG
#define AF_ALG 38
#endif
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
#define SHA256_DIG_LEN 32
int main(int argc, char *argv[])
{
char *plaintext;
int sock_fd, fd, text_len;
unsigned char digest[SHA256_DIG_LEN];
int err, i;
/* Different from what we use in normal TCP/IP socket programming,
* that fills a sockaddr_in structure, here we work over a
* sockaddr_alg one */
struct sockaddr_alg sa_alg = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "sha256"
};
/* Get input from user */
if (argc > 1) {
plaintext = argv[1];
} else {
plaintext = strndup("Hello World", 11);
if (!plaintext) {
fprintf(stderr, "not enough memory\n");
return -ENOMEM;
}
}
/* AF_ALG is the address family we use to interact with Kernel
* Crypto API. SOCK_SEQPACKET is used because we always know the
* maximum size of our data (no fragmentation) and we care about
* getting things in order in case there are consecutive calls */
sock_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (sock_fd < 0) {
perror("failed to allocate socket\n");
return -1;
}
err = bind(sock_fd, (struct sockaddr *)&sa_alg, sizeof(sa_alg));
if (err) {
perror("failed to bind socket, alg may not be supported\n");
return -EAFNOSUPPORT;
}
/* Once it's "configured", we tell the kernel to get ready for
* receiving some requests */
fd = accept(sock_fd, NULL, 0);
if (fd < 0) {
perror("failed to open connection for the socket\n");
return -EBADF;
}
/* In hash cases, we don't really need to inform anything else, we
* can start sending data to the fd and read back from it to get our
* digest. OTOH, when working with ciphers, we need to perform some
* operations via setsockopt() interface, using the specifics
* options, like ALG_SET_KEY */
text_len = strlen(plaintext);
err = write(fd, plaintext, text_len);
if (err != text_len) {
perror("something went wrong while writing data to fd\n");
return -1;
}
read(fd, digest, SHA256_DIG_LEN);
close(fd);
close(sock_fd);
/* Print digest to output */
for (i = 0; i < SHA256_DIG_LEN; i++)
printf("%02x", digest[i]);
printf("\n");
return 0;
}
Finally, to check that it works (consider the above program was named hash
):
$ echo -n "aloha" | sha256sum
0206a97843b1ba4fbb147d472550ec3b5ee8aacadf3707522157240940d1bebd -
$ ./hash aloha
0206a97843b1ba4fbb147d472550ec3b5ee8aacadf3707522157240940d1bebd
Further material¶
You can find more documentation directly in the kernel's documentation folder, in the file
Documentation/crypto/userspace-if.rst
.
Another document is the slide deck presented in one of the LKCAMP meetings, which should be available in the presentations bin repository.