Skip to content
Advertisement

linux kernel crypto API for ECB AES

I am trying to write a sample code for using linux kernel crypto userspace interface.

And I do found a example really helped me a lot, thanks to nibrunie:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
#include <string.h>

#ifndef SOL_ALG
#define SOL_ALG 279
#endif

int main(void)
{
  int opfd;
  int tfmfd;
  struct sockaddr_alg sa = {
    .salg_family = AF_ALG,
    .salg_type = "skcipher",
    .salg_name = "cbc(aes)"
  };
  struct msghdr msg = {};
  struct cmsghdr *cmsg;
  char cbuf[CMSG_SPACE(4) + CMSG_SPACE(20)] = {0};
  char buf[16];
  struct af_alg_iv *iv;
  struct iovec iov;
  int i;

  tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);

  bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));

  setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
       "x06xa9x21x40x36xb8xa1x5b"
       "x51x2ex03xd5x34x12x00x06", 16);

  opfd = accept(tfmfd, NULL, 0);

  msg.msg_control = cbuf;
  msg.msg_controllen = sizeof(cbuf);

  cmsg = CMSG_FIRSTHDR(&msg);
  cmsg->cmsg_level = SOL_ALG;
  cmsg->cmsg_type = ALG_SET_OP;
  cmsg->cmsg_len = CMSG_LEN(4);
  *(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;

  cmsg = CMSG_NXTHDR(&msg, cmsg);
  cmsg->cmsg_level = SOL_ALG;
  cmsg->cmsg_type = ALG_SET_IV;
  cmsg->cmsg_len = CMSG_LEN(20);
  iv = (void *)CMSG_DATA(cmsg);
  iv->ivlen = 16;
  memcpy(iv->iv, "x3dxafxbax42x9dx9exb4x30"
           "xb4x22xdax80x2cx9fxacx41", 16);

  iov.iov_base = "Single block msg";
  iov.iov_len = 16;

  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  sendmsg(opfd, &msg, 0);
  read(opfd, buf, 16);

  for (i = 0; i < 16; i++) {
    printf("%02x", (unsigned char)buf[i]);
  }
  printf("n");

  close(opfd);
  close(tfmfd);

  return 0;
}

This is quite useful for CBC AES, but not for ECB AES.

For What I understand, the variable iov is for Plaintext, iv for initialization Vector, and call ALG_SET_KEY for key.

To get an ECB AES result, I have to set iv to 16 bit 0, and do encryption only once a time; then I can get CBC AES result as same as ECB AES does.

I have tried changing .salg_name to “ecb(aes)” and commenting the iv part, but the program will stuck after the read(opfd, buf, 16) function executed.

Please tell me the correct way to do. I search the entire internet and can barely find a example for this simple task.

Edit: Thanks to gby, I got the sample code working by:

  1. changing .salg_name to “ecb(aes)”

  2. remove the initialization vector part

  3. do not forget to resize the cbuf (msg.msg_control) to only fit first header

Here is the working ECB AES sample code:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
#include <string.h>

#ifndef SOL_ALG
#define SOL_ALG 279
#endif

int main(void)
{
  int opfd;
  int tfmfd;
  struct sockaddr_alg sa = {
    .salg_family = AF_ALG,
    .salg_type = "skcipher",
    .salg_name = "ecb(aes)"
  };
  struct msghdr msg = {};
  struct cmsghdr *cmsg;
  char cbuf[CMSG_SPACE(4)] = {0};
  char buf[16];

  struct iovec iov;
  int i;

  tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);

  bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));

  setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
       "x06xa9x21x40x36xb8xa1x5b"
       "x51x2ex03xd5x34x12x00x06", 16);

  opfd = accept(tfmfd, NULL, 0);

  msg.msg_control = cbuf;
  msg.msg_controllen = sizeof(cbuf);

  cmsg = CMSG_FIRSTHDR(&msg);
  cmsg->cmsg_level = SOL_ALG;
  cmsg->cmsg_type = ALG_SET_OP;
  cmsg->cmsg_len = CMSG_LEN(4);
  *(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;

  iov.iov_base = "Single block msg";
  iov.iov_len = 16;

  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  sendmsg(opfd, &msg, 0);
  read(opfd, buf, 16);

  for (i = 0; i < 16; i++) {
    printf("%02x", (unsigned char)buf[i]);
  }
  printf("n");

  close(opfd);
  close(tfmfd);

  return 0;
}

Advertisement

Answer

The best advice is to not reinvent the wheel and instead of trying to write directly to the kernel Crypto API netlink socket use the userspace libkcapi libraryenter link description here to do that for you, it is much simpler (it’s double licensed under BSD so no license issues).

If you must, you can at least use it’s source code as a reference to how to do it.

As for your program itself, I am only guessing – but when commenting out the IV and changing the cipher alg name, did you also make sure to change cbuf variable definition and shrink it to only contain the first header?

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement