Linux AES Driver
Linux AES Driver for Zynq Ultrascale+ MPSoC & Versal
Table of Contents
Introduction
The Zynq UltraScale+ MPSoC includes an AES-GCM engine for symmetric key encryption and decryption. It is an symmetric algorithm.
This block uses AES-GCM algorithm to encrypt or decrypt the provided data. It requires a key of size 256 bits and initialization vector(IV) of size 96 bits.
This driver is supported for ZynqMP and Versal.
HW IP Features
Supports Symmetric key algorithm.
Features supported in driver
Driver provides below interface to select key.
Scenario 1: Default case: No Explicit key type selected. Driver will use ZYNQMP_AES_KUP_KEY for zynqmp and VERSAL_AES_USER_KEY_0 for versal.
setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, keylen); encrypt();Scenario 2: Key already programmed in SoC, Select key src e.g VERSAL_AES_PUF_KEY
hwkey.magic = 0x3EA0; hwkey.type = VERSAL_AES_PUF_KEY; setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, &hwkey, sizeof(struct xilinx_hwkey_info));Scenario 3: Select key source and program key in corresponding register e.g VERSAL_AES_USER_KEY_1
hwkey.magic = 0x3EA0; hwkey.type = VERSAL_AES_USER_KEY_0; setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, &hwkey, sizeof(struct xilinx_hwkey_info)); setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, &hwkey, sizeof(struct xilinx_hwkey_info));
Kernel Configuration
For ZynqMP, the ZynqMP AES driver and the userspace interface for AEAD algorithms are enabled by default. So there is no need for kernel configuration.
For Versal, the driver and the userspace interface is not enabled by default. Enabling the driver shall enable the userspace interface as well.
To enable the driver follow this path in menuconfig:
Cryptographic API -> Hardware crypto devices → <*> Support for Xilinx ZynqMP AES hardware accelerator
Userspace examples for ZynqMP
AF ALG AES example with KUP key
The below example demonstrate how to use user provided key or KUP key for AES encryption :
/*
* AES-GCM example for Zynq UltraScale+ (AF_ALG / Linux crypto API).
*/
#include <stdio.h>
#include <errno.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
#define IV_SIZE 12
#define GCM_TAG_SIZE 16
#define DATA_SIZE 68
#define XILINX_KEY_MAGIC 0x3EA0
enum zynqmp_aead_keysrc {
ZYNQMP_AES_KUP_KEY = 0, /* DEFAULT_KEY_LOCATION; SELECT_AND_SAVE_KEY_HW */
ZYNQMP_AES_DEV_KEY, /* SELECT_ALREADY_SAVED_KEY_HW */
ZYNQMP_AES_PUF_KEY, /* SELECT_ALREADY_SAVED_KEY_HW */
};
struct xilinx_hwkey_info {
__u16 magic;
__u16 type;
};
enum xlnx_key_flow {
SELECT_ALREADY_SAVED_KEY_HW,
SELECT_AND_SAVE_KEY_HW,
DEFAULT_KEY_LOCATION,
};
static const __u8 sw_aes_key[] = {
0xF8, 0x78, 0xB8, 0x38, 0xD8, 0x58, 0x98, 0x18, 0xE8, 0x68, 0xA8, 0x28, 0xC8, 0x48,
0x88, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20,
0xC0, 0x40, 0x80, 0x00
};
static int key_validate(enum xlnx_key_flow key_flow,
enum zynqmp_aead_keysrc key_type)
{
switch (key_flow) {
case SELECT_ALREADY_SAVED_KEY_HW:
switch (key_type) {
case ZYNQMP_AES_DEV_KEY:
case ZYNQMP_AES_PUF_KEY:
return 0;
default:
break;
}
errno = EINVAL;
perror("key_validate: key_type invalid for SELECT_ALREADY_SAVED_KEY_HW");
return -1;
case SELECT_AND_SAVE_KEY_HW:
if (key_type == ZYNQMP_AES_KUP_KEY)
return 0;
errno = EINVAL;
perror("key_validate: key_type must be ZYNQMP_AES_KUP_KEY for SELECT_AND_SAVE_KEY_HW");
return -1;
case DEFAULT_KEY_LOCATION:
if (key_type == ZYNQMP_AES_KUP_KEY)
return 0;
errno = EINVAL;
perror("key_validate: key_type must be ZYNQMP_AES_KUP_KEY for DEFAULT_KEY_LOCATION");
return -1;
default:
errno = EINVAL;
perror("key_validate: invalid key_flow");
return -1;
}
}
static int xlnx_alg_program_key(int tfmfd, enum xlnx_key_flow key_flow,
enum zynqmp_aead_keysrc key_type)
{
struct xilinx_hwkey_info keyinfo = {
.magic = XILINX_KEY_MAGIC,
.type = (__u16)key_type,
};
if (key_validate(key_flow, key_type) < 0)
return -1;
switch (key_flow) {
case SELECT_ALREADY_SAVED_KEY_HW:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)&keyinfo, sizeof(keyinfo)) < 0) {
perror("setsockopt ALG_SET_KEY (key already in HW)");
return -1;
}
return 0;
case SELECT_AND_SAVE_KEY_HW:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)&keyinfo, sizeof(keyinfo)) < 0) {
perror("setsockopt ALG_SET_KEY (HW key slot)");
return -1;
}
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)sw_aes_key, sizeof(sw_aes_key)) < 0) {
perror("setsockopt ALG_SET_KEY (software key)");
return -1;
}
return 0;
case DEFAULT_KEY_LOCATION:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)sw_aes_key, sizeof(sw_aes_key)) < 0) {
perror("setsockopt ALG_SET_KEY (default software key)");
return -1;
}
return 0;
default:
errno = EINVAL;
perror("xlnx_alg_program_key: invalid key_flow");
return -1;
}
}
static const __u8 test_iv[] = {
0xD2, 0x45, 0x0E, 0x07, 0xEA, 0x5D, 0xE0, 0x42, 0x6C, 0x0F, 0xA1, 0x33
};
static void xlnx_close_fd(int fd, const char *what)
{
if (fd < 0)
return;
if (close(fd) < 0)
perror(what);
}
static int xlnx_aead_gcm(const char *alg_name, int alg_op,
unsigned char *ibuf, int ilen,
unsigned char *obuf, int read_len,
enum xlnx_key_flow key_flow,
enum zynqmp_aead_keysrc key_type)
{
int tfmfd = -1;
int opfd = -1;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4) + CMSG_SPACE(1024)] = {0};
struct af_alg_iv *iv;
struct iovec iov;
int err;
strncpy((char *)sa.salg_name, alg_name, sizeof(sa.salg_name) - 1);
sa.salg_name[sizeof(sa.salg_name) - 1] = '\0';
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfmfd < 0) {
perror("socket(AF_ALG)");
return -1;
}
if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind(AF_ALG)");
goto out_tfm;
}
if (xlnx_alg_program_key(tfmfd, key_flow, key_type) < 0)
goto out_tfm;
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 16) < 0) {
perror("setsockopt ALG_SET_AEAD_AUTHSIZE");
goto out_tfm;
}
opfd = accept(tfmfd, NULL, 0);
if (opfd < 0) {
perror("accept(AF_ALG)");
goto out_tfm;
}
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;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(1024);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = IV_SIZE;
memcpy(iv->iv, test_iv, IV_SIZE);
iov.iov_base = ibuf;
iov.iov_len = ilen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
err = sendmsg(opfd, &msg, 0);
if (err < 0) {
perror("sendmsg");
goto out_both;
}
err = read(opfd, obuf, read_len);
if (err < 0) {
perror("read");
goto out_both;
}
xlnx_close_fd(opfd, "close(opfd)");
xlnx_close_fd(tfmfd, "close(tfmfd)");
return err;
out_both:
xlnx_close_fd(opfd, "close(opfd)");
out_tfm:
xlnx_close_fd(tfmfd, "close(tfmfd)");
return -1;
}
int enc(unsigned char *ibuf, int ilen, unsigned char *obuf, int olen,
enum xlnx_key_flow key_flow, enum zynqmp_aead_keysrc key_type)
{
int err;
int i;
printf("Plain text\n");
for (i = 0; i < ilen; i++)
printf("%02x", ibuf[i]);
printf("\n");
err = xlnx_aead_gcm("gcm(aes)", ALG_OP_ENCRYPT, ibuf, ilen, obuf, olen,
key_flow, key_type);
if (err < 0)
return -1;
return 0;
}
int dec(unsigned char *dbuf, int dlen, enum xlnx_key_flow key_flow,
enum zynqmp_aead_keysrc key_type)
{
int len = DATA_SIZE + GCM_TAG_SIZE;
unsigned char buf[DATA_SIZE];
int err;
int i;
printf("Decrypting data \n");
printf("Data in plain text + tag 0x%x\r\n", dlen);
for (i = 0; i < dlen; i++)
printf("%02x", dbuf[i]);
printf("\n");
err = xlnx_aead_gcm("gcm(aes)", ALG_OP_DECRYPT, dbuf, dlen,
buf, sizeof(buf), key_flow, key_type);
if (err < 0)
return -1;
printf("GCM TAG:\r\n");
for (i = len - GCM_TAG_SIZE; i < len; i++)
printf("%02x", dbuf[i]);
printf("\n");
printf("Decrypted Plain text:\r\n");
for (i = 0; i < (int)sizeof(buf); i++)
printf("%02x", buf[i]);
printf("\n");
return 0;
}
int main(void)
{
enum xlnx_key_flow key_flow = DEFAULT_KEY_LOCATION;
enum zynqmp_aead_keysrc key_type = ZYNQMP_AES_KUP_KEY;
int len = DATA_SIZE + GCM_TAG_SIZE;
unsigned char buf[len];
__u8 input[DATA_SIZE] = {
0x12, 0x34, 0x56, 0x78, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0,
0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00, 0xA5, 0xDE, 0x08, 0xD8, 0x58, 0x98, 0xA5,
0xA5, 0xFE, 0xDC, 0xA1, 0x01, 0x34, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90,
0x09, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x87, 0x65, 0x41, 0x24, 0x45, 0x66, 0x79,
0x87, 0x43, 0x09, 0x71, 0x36, 0x27, 0x46, 0x38, 0x01, 0xAD, 0x10, 0x56
};
if (enc(input, DATA_SIZE, buf, len, key_flow, key_type) < 0)
return 1;
if (dec(buf, len, key_flow, key_type) < 0)
return 1;
return 0;
}
With Device/PUF key :
To use Device key or PUF key for data blob encryption/decryption , Create a primary boot image with PMU_FW (built with SECURE_ENVIRONMENT flag set).
To use device/puf key encryption/decryption, above applications remain same but with minor changes. Those changes should be as follows
enum xlnx_key_flow key_flow = SELECT_ALREADY_SAVED_KEY_HW;
enum zynqmp_aead_keysrc key_type = AES_PUF_KEY; // or ZYNQMP_AES_DEV_KEY
for puf key and device key respectively.
Expected Output
root@xilinx-zcu102-2019_1:/run/media/mmcblk0# ./Enc
Plain text
1234567808f070b030d0509010e060a020c0408000a5de08d85898a5a5fedca10134abcdef12345678900987654321123487654124456679874309713627463801ad1056
Decrypting data
Data in plain text + tag 0x54
edaae826b6b416bbd2815879692058f16df1133c7222d549141dcfef0faf01afd6433f9dc6bb7d3207d4a531cdf5e6ebf596bd4a0c3d430313ce88e2b9e16c086e633d24b52b80cd5117afb47b116d4a8f89cf75
GCM TAG:
b52b80cd5117afb47b116d4a8f89cf75
Decrypted Plain text:
1234567808f070b030d0509010e060a020c0408000a5de08d85898a5a5fedca10134abcdef12345678900987654321123487654124456679874309713627463801ad1056
Userspace examples for Versal
The examples provided in Userspace examples for ZynqMP shall work for Versal as well.
However Versal also supports Additional Authenticated Data (AAD). Below examples demonstrate the support for AAD in Versal.
AF ALG AES Encrypt example with AAD using KUP key
#include <stdio.h>
#include <errno.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
#define IV_SIZE 12
#define AAD_SIZE 16
#define GCM_TAG_SIZE 16
#define DATA_SIZE 68
#define XILINX_KEY_MAGIC 0x3EA0
enum versal_aead_keysrc {
VERSAL_AES_EFUSE_USER_KEY_0 = 6, //SELECT_ALREADY_SAVED_KEY_HW
VERSAL_AES_EFUSE_USER_KEY_1, //SELECT_ALREADY_SAVED_KEY_HW
VERSAL_AES_EFUSE_USER_RED_KEY_0, //SELECT_ALREADY_SAVED_KEY_HW
VERSAL_AES_EFUSE_USER_RED_KEY_1, //SELECT_ALREADY_SAVED_KEY_HW
VERSAL_AES_PUF_KEY = 11, //SELECT_ALREADY_SAVED_KEY_HW
VERSAL_AES_USER_KEY_0, // DEFAULT_KEY_LOCATION. Can be used as SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_1, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_2, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_3, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_4, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_5, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_6, //SELECT_AND_SAVE_KEY_HW
VERSAL_AES_USER_KEY_7, //SELECT_AND_SAVE_KEY_HW
};
struct xilinx_hwkey_info {
__u16 magic;
__u16 type;
};
enum xlnx_key_flow {
SELECT_ALREADY_SAVED_KEY_HW,
SELECT_AND_SAVE_KEY_HW,
DEFAULT_KEY_LOCATION,
};
static const __u8 sw_aes_key[] = {
0xF8, 0x78, 0xB8, 0x38, 0xD8, 0x58, 0x98, 0x18, 0xE8, 0x68, 0xA8, 0x28, 0xC8, 0x48,
0x88, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20,
0xC0, 0x40, 0x80, 0x00
};
static int key_validate(enum xlnx_key_flow key_flow,
enum versal_aead_keysrc key_type)
{
switch (key_flow) {
case SELECT_ALREADY_SAVED_KEY_HW:
switch (key_type) {
case VERSAL_AES_EFUSE_USER_KEY_0:
case VERSAL_AES_EFUSE_USER_KEY_1:
case VERSAL_AES_EFUSE_USER_RED_KEY_0:
case VERSAL_AES_EFUSE_USER_RED_KEY_1:
case VERSAL_AES_PUF_KEY:
return 0;
default:
break;
}
errno = EINVAL;
perror("key_validate: key_type invalid for SELECT_ALREADY_SAVED_KEY_HW");
return -1;
case SELECT_AND_SAVE_KEY_HW:
if (key_type >= VERSAL_AES_USER_KEY_0 &&
key_type <= VERSAL_AES_USER_KEY_7)
return 0;
errno = EINVAL;
perror("key_validate: key_type invalid for SELECT_AND_SAVE_KEY_HW");
return -1;
case DEFAULT_KEY_LOCATION:
if (key_type == VERSAL_AES_USER_KEY_0)
return 0;
errno = EINVAL;
perror("key_validate: key_type must be VERSAL_AES_USER_KEY_0 for DEFAULT_KEY_LOCATION");
return -1;
default:
errno = EINVAL;
perror("key_validate: invalid key_flow");
return -1;
}
}
static int xlnx_alg_program_key(int tfmfd, enum xlnx_key_flow key_flow,
enum versal_aead_keysrc key_type)
{
struct xilinx_hwkey_info keyinfo = {
.magic = XILINX_KEY_MAGIC,
.type = (__u16)key_type,
};
if (key_validate(key_flow, key_type) < 0)
return -1;
switch (key_flow) {
case SELECT_ALREADY_SAVED_KEY_HW:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)&keyinfo, sizeof(keyinfo)) < 0) {
perror("setsockopt ALG_SET_KEY (key already in HW)");
return -1;
}
return 0;
case SELECT_AND_SAVE_KEY_HW:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)&keyinfo, sizeof(keyinfo)) < 0) {
perror("setsockopt ALG_SET_KEY (HW key slot)");
return -1;
}
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)sw_aes_key, sizeof(sw_aes_key)) < 0) {
perror("setsockopt ALG_SET_KEY (software key)");
return -1;
}
return 0;
case DEFAULT_KEY_LOCATION:
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY,
(__u8 *)sw_aes_key, sizeof(sw_aes_key)) < 0) {
perror("setsockopt ALG_SET_KEY (default software key)");
return -1;
}
return 0;
default:
errno = EINVAL;
perror("xlnx_alg_program_key: invalid key_flow");
return -1;
}
}
static const __u8 test_iv[] = {
0xD2, 0x45, 0x0E, 0x07, 0xEA, 0x5D, 0xE0, 0x42, 0x6C, 0x0F, 0xA1, 0x33
};
static void xlnx_close_fd(int fd, const char *what)
{
if (fd < 0)
return;
if (close(fd) < 0)
perror(what);
}
static int xlnx_aead_gcm(const char *alg_name, int alg_op,
unsigned char *ibuf, int ilen,
unsigned char *obuf, int read_len,
enum xlnx_key_flow key_flow,
enum versal_aead_keysrc key_type)
{
int tfmfd = -1;
int opfd = -1;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4) + CMSG_SPACE(1024) + CMSG_SPACE(1024)] = {0};
struct af_alg_iv *iv;
struct iovec iov;
int err;
strncpy((char *)sa.salg_name, alg_name, sizeof(sa.salg_name) - 1);
sa.salg_name[sizeof(sa.salg_name) - 1] = '\0';
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfmfd < 0) {
perror("socket(AF_ALG)");
return -1;
}
if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind(AF_ALG)");
goto out_tfm;
}
if (xlnx_alg_program_key(tfmfd, key_flow, key_type) < 0)
goto out_tfm;
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 16) < 0) {
perror("setsockopt ALG_SET_AEAD_AUTHSIZE");
goto out_tfm;
}
opfd = accept(tfmfd, NULL, 0);
if (opfd < 0) {
perror("accept(AF_ALG)");
goto out_tfm;
}
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;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(1024);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = IV_SIZE;
memcpy(iv->iv, test_iv, IV_SIZE);
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
cmsg->cmsg_len = CMSG_LEN(1024);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = AAD_SIZE;
iov.iov_base = ibuf;
iov.iov_len = ilen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
err = sendmsg(opfd, &msg, 0);
if (err < 0) {
perror("sendmsg");
goto out_both;
}
err = read(opfd, obuf, read_len);
if (err < 0) {
perror("read");
goto out_both;
}
xlnx_close_fd(opfd, "close(opfd)");
xlnx_close_fd(tfmfd, "close(tfmfd)");
return err;
out_both:
xlnx_close_fd(opfd, "close(opfd)");
out_tfm:
xlnx_close_fd(tfmfd, "close(tfmfd)");
return -1;
}
int enc(unsigned char *ibuf, int ilen, unsigned char *obuf, int olen,
enum xlnx_key_flow key_flow, enum versal_aead_keysrc key_type)
{
int err;
int i;
printf("AAD + Plain data\n");
for (i = 0; i < ilen; i++)
printf("%02x", ibuf[i]);
printf("\n");
err = xlnx_aead_gcm("gcm(aes)", ALG_OP_ENCRYPT, ibuf, ilen, obuf, olen,
key_flow, key_type);
if (err < 0)
return -1;
return 0;
}
int dec(unsigned char *dbuf, int dlen, enum xlnx_key_flow key_flow,
enum versal_aead_keysrc key_type)
{
int len = AAD_SIZE + DATA_SIZE + GCM_TAG_SIZE;
unsigned char buf[len - GCM_TAG_SIZE];
int err;
int i;
printf("Decrypting data \n");
printf("Data in aad + plain text + tag 0x%x\r\n", dlen);
for (i = 0; i < dlen; i++)
printf("%02x", dbuf[i]);
printf("\n");
err = xlnx_aead_gcm("gcm(aes)", ALG_OP_DECRYPT, dbuf, dlen,
buf, sizeof(buf), key_flow, key_type);
if (err < 0)
return -1;
printf("GCM TAG:\r\n");
for (i = len - GCM_TAG_SIZE; i < len; i++)
printf("%02x", dbuf[i]);
printf("\n");
printf("Decrypted Plain text:\r\n");
for (i = AAD_SIZE; i < (int)sizeof(buf); i++)
printf("%02x", buf[i]);
printf("\n");
return 0;
}
int main(void)
{
enum xlnx_key_flow key_flow = SELECT_ALREADY_SAVED_KEY_HW;
enum versal_aead_keysrc key_type = VERSAL_AES_PUF_KEY;
int len = AAD_SIZE + DATA_SIZE + GCM_TAG_SIZE;
unsigned char buf[len];
__u8 input[DATA_SIZE + AAD_SIZE] = {
/* First 16Bytes are AAD data */
0x67, 0xe2, 0x1c, 0xf3, 0xcb, 0x29, 0xe0, 0xdc,
0xbc, 0x4d, 0x8b, 0x1d, 0x0c, 0xc5, 0x33, 0x4b,
/* Plain text */
0x12, 0x34, 0x56, 0x78, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0,
0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00, 0xA5, 0xDE, 0x08, 0xD8, 0x58, 0x98, 0xA5,
0xA5, 0xFE, 0xDC, 0xA1, 0x01, 0x34, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90,
0x09, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x87, 0x65, 0x41, 0x24, 0x45, 0x66, 0x79,
0x87, 0x43, 0x09, 0x71, 0x36, 0x27, 0x46, 0x38, 0x01, 0xAD, 0x10, 0x56
};
if (enc(input, (len - GCM_TAG_SIZE), buf, len, key_flow, key_type) < 0)
return 1;
if (dec(buf, len, key_flow, key_type) < 0)
return 1;
return 0;
}
Mainline status
The driver is available in mainline kernel (https://github.com/torvalds/linux/blob/master/drivers/crypto/xilinx/zynqmp-aes-gcm.c)
Change Log
2018.3
Summary
crypto: zynqmp-aes: Adds zynqmp-aes driver
Commits
Initial commit
c7e7089 crypto: synqmp-aes: Adds zynqmp-aes driver
Bug fixes
6c6033a crypto: zynqmp-aes: Fix for segfault seen with large sets of data
c1a602d crypto: zynqmp-aes: Adds an error code for zynqmp-aes driver
2023.1
Userspace interface for AEAD algorithms(CONFIG_CRYPTO_USER_API_AEAD) and the driver(CONFIG_CRYPTO_DEV_ZYNQMP_AES) are enabled by default in the xilinx_defconfig and xilinx_zynqmp_defconfig
Existing driver is replaced with new driver written using aead framework
Added support for Versal
Related Links
Driver Code : https://github.com/Xilinx/linux-xlnx/blob/master/drivers/crypto/xilinx/zynqmp-aes-gcm.c