linuxOS_D21X/source/artinchip/test-ce/kcapi/skciphers.c
2024-11-29 16:33:21 +08:00

501 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2020-2021 Artinchip Technology Co., Ltd.
* Authors: Wu Dehuang <dehuang.wu@artinchip.com>
*/
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <sys/user.h>
#include <time.h>
#include <sys/utsname.h>
#include <linux/random.h>
#include <sys/syscall.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <libgen.h>
#include <kcapi.h>
#include "util.h"
#include "app.h"
#define MAX_KEY_SIZE 64
#define MAX_IV_SIZE 16
#define MAX_FILE_NAME 512
#define MAX_CIPHER_NAME 32
/* Max 16 Pages is sent to kernel with libkcapi one shot api */
#define ONE_SHOT_SIZE (4096 * 16)
#define ONE_SHOT_SIZE2 (128)
struct sk_options {
char cipher_name[MAX_CIPHER_NAME];
int enc;
uint8_t key[MAX_KEY_SIZE];
int keylen;
uint8_t iv[MAX_IV_SIZE];
int ivlen;
char in_file[MAX_FILE_NAME];
char out_file[MAX_FILE_NAME];
int alignment;
int access;
int stream_api;
uint8_t key_flag;
uint8_t iv_flag;
uint8_t in_flag;
uint8_t out_flag;
};
static int skcipher_do_crypt_sync_oneshot(struct sk_options *opts)
{
struct kcapi_handle *handle = NULL;
uint8_t *inbuf = NULL, *outbuf = NULL, *pin, *pout;
FILE *fin = NULL, *fout = NULL;
struct stat st;
size_t bufsize, todo;
ssize_t processed;
int ret = 0, flags;
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
fin = fopen(opts->in_file, "rb");
if (!fin) {
printf("Failed to open %s\n", opts->in_file);
ret = -EINVAL;
goto err;
}
if (stat(opts->in_file, &st)) {
printf("Failed to get file information.\n");
ret = -EINVAL;
goto err;
}
bufsize = st.st_size + pagesize;
if ((st.st_size > ONE_SHOT_SIZE) && opts->iv_flag) {
printf("File too large to use one shot API for CTR/CBC\n");
ret = -EINVAL;
goto err;
}
fout = fopen(opts->out_file, "wb");
if (!fout) {
printf("Failed to open %s\n", opts->out_file);
ret = -EINVAL;
goto err;
}
if (posix_memalign((void**)&inbuf, pagesize, bufsize)) {
printf("Failed to allocate inbuf.\n");
ret = -ENOMEM;
goto err;
}
if (posix_memalign((void**)&outbuf, pagesize, bufsize)) {
printf("Failed to allocate outbuf.\n");
ret = -ENOMEM;
goto err;
}
/*
* Both input and output buffer page alignment is the best,
* here setup for test purpose
*/
if (opts->alignment == NO_ALIGNMENT) {
pin = inbuf + 7;
pout = outbuf + 1;
} else if (opts->alignment == INPUT_ALIGNMENT) {
pin = inbuf;
pout = outbuf + 1;
} else if (opts->alignment == OUTPUT_ALIGNMENT) {
pin = inbuf + 7;
pout = outbuf;
} else {
pin = inbuf;
pout = outbuf;
}
flags = opts->access;
if (kcapi_cipher_init(&handle, opts->cipher_name, flags)) {
printf("Allocation of %s cipher failed\n", opts->cipher_name);
ret = -1;
goto err;
}
kcapi_cipher_setkey(handle, opts->key, opts->keylen);
/* Do it in one shot */
todo = fread(pin, 1, st.st_size, fin);
if (opts->enc)
processed = kcapi_cipher_encrypt(handle, pin, todo, opts->iv,
pout, todo, flags);
else
processed = kcapi_cipher_decrypt(handle, pin, todo, opts->iv,
pout, todo, flags);
if (processed < 0) {
printf("process error, ret %ld\n", processed);
ret = -1;
goto err;
}
fwrite(pout, 1, processed, fout);
err:
if (handle)
kcapi_cipher_destroy(handle);
if (inbuf)
free(inbuf);
if (outbuf)
free(outbuf);
if (fin)
fclose(fin);
if (fout)
fclose(fout);
return ret;
}
/*
* 如果想使用 vmsplice 进行 zerocopy则每次发送的数据大小不要超过 1 page size
*
* 否则会自动改用 sendmsg 接口发送
*
* 如果设置的 iov 个数超过 16 个,也会使用 sendmsg 接口发送
*/
static int skcipher_do_crypt_stream(struct sk_options *opts)
{
struct kcapi_handle *handle = NULL;
uint8_t *inbuf = NULL, *outbuf = NULL, *pin, *pout;
FILE *fin = NULL, *fout = NULL;
size_t todo;
ssize_t processed;
int ret = 0, flags;
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
struct iovec iov;
fin = fopen(opts->in_file, "rb");
if (!fin) {
printf("Failed to open %s\n", opts->in_file);
ret = -EINVAL;
goto err;
}
fout = fopen(opts->out_file, "wb");
if (!fout) {
printf("Failed to open %s\n", opts->out_file);
ret = -EINVAL;
goto err;
}
if (posix_memalign((void **)&inbuf, pagesize, ONE_SHOT_SIZE)) {
printf("Failed to allocate inbuf.\n");
ret = -ENOMEM;
goto err;
}
if (posix_memalign((void **)&outbuf, pagesize, ONE_SHOT_SIZE)) {
printf("Failed to allocate outbuf.\n");
ret = -ENOMEM;
goto err;
}
/*
* Both input and output buffer page alignment is the best,
* here setup for test purpose
*/
if (opts->alignment == NO_ALIGNMENT) {
pin = inbuf + 7;
pout = outbuf + 1;
} else if (opts->alignment == INPUT_ALIGNMENT) {
pin = inbuf;
pout = outbuf + 1;
} else if (opts->alignment == OUTPUT_ALIGNMENT) {
pin = inbuf + 7;
pout = outbuf;
} else {
pin = inbuf;
pout = outbuf;
}
flags = opts->access;
if (kcapi_cipher_init(&handle, opts->cipher_name, flags)) {
printf("Allocation of %s cipher failed\n", opts->cipher_name);
ret = -1;
goto err;
}
kcapi_cipher_setkey(handle, opts->key, opts->keylen);
if (opts->enc)
ret = kcapi_cipher_stream_init_enc(handle, opts->iv, NULL, 0);
else
ret = kcapi_cipher_stream_init_dec(handle, opts->iv, NULL, 0);
while (1) {
todo = fread(pin, 1, ONE_SHOT_SIZE, fin);
if (todo <= 0)
break;
iov.iov_base = pin;
iov.iov_len = todo;
/*
* Stream API also support multi-thread operation:
* - kcapi_cipher_stream_update work in write data thread
* - kcapi_cipher_stream_op work in read data thread
*/
if (todo < ONE_SHOT_SIZE)
processed = kcapi_cipher_stream_update_last(handle, &iov, 1);
else
processed = kcapi_cipher_stream_update(handle, &iov, 1);
if (processed != todo) {
printf("Send data error: %ld.\n", processed);
ret = -1;
break;
}
iov.iov_base = pout;
iov.iov_len = processed;
processed = kcapi_cipher_stream_op(handle, &iov, 1);
if (processed < 0) {
printf("Read result error: %ld.\n", processed);
ret = -1;
break;
}
fwrite(pout, 1, processed, fout);
}
kcapi_cipher_destroy(handle);
err:
if (inbuf)
free(inbuf);
if (outbuf)
free(outbuf);
if (fin)
fclose(fin);
if (fout)
fclose(fout);
return ret;
}
static void usage_sk(struct subcmd_cfg *cmd)
{
printf("Usage:\n");
printf(" %s %s <options>\n", cmd->app_name, cmd->cmd_name);
printf(" --help: display this help.\n");
printf(" --enc: do encryption.\n");
printf(" --dec: do decryption.\n");
printf(" --keyhex: key hex string.\n");
printf(" --keyfile: key binary file.\n");
if (cmd->ivlen) {
printf(" --ivhex: iv hex string.\n");
printf(" --ivfile: iv binary file.\n");
}
printf(" --in: input data file.\n");
printf(" --out: output file.\n");
printf(" --align-in: input data buffer page alignment.\n");
printf(" --align-out: output data buffer page alignment.\n");
printf(" --align: input and output buffer page alignment.\n");
printf(" --access-vmsplice: app always use the sendmsg kernel interface.\n");
printf(" when --stream is enable, this setting will be ignored.\n");
printf(" --access-sendmsg: app always use the vmsplice zero copy kernel interface.\n");
printf(" when --stream is enable, this setting will be ignored.\n");
printf(" --access-heuristic: app to determine the optimal kernel access type.\n");
printf(" when --stream is enable, this setting will be ignored.\n");
printf(" --stream: app use stream api to perform crypto.\n");
}
static int check_options(struct sk_options *opts)
{
if (!opts->key_flag) {
printf("Key is required.\n");
return -EINVAL;
}
if (opts->ivlen && !opts->iv_flag) {
printf("IV is required.\n");
return -EINVAL;
}
if (!opts->in_flag) {
printf("input file is required.\n");
return -EINVAL;
}
if (!opts->out_flag) {
printf("output file is required.\n");
return -EINVAL;
}
return 0;
}
static int skcipher_cmd_arg_parse(int argc, char *argv[],
struct sk_options *opts)
{
int opt_index = 0;
int c = 0;
FILE *fp;
static struct option cmd_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "enc", no_argument, 0, 'e' },
{ "dec", no_argument, 0, 'd' },
{ "keyhex", required_argument, 0, 'k' },
{ "keyfile", required_argument, 0, 'K' },
{ "ivhex", required_argument, 0, 'v' },
{ "ivfile", required_argument, 0, 'I' },
{ "in", required_argument, 0, 'i' },
{ "out", required_argument, 0, 'o' },
{ "align-in", no_argument, 0, 'a' },
{ "align-out", no_argument, 0, 'L' },
{ "align", no_argument, 0, 'A' },
{ "access-heuristic", no_argument, 0, 'R' },
{ "access-vmsplice", no_argument, 0, 'V' },
{ "access-sendmsg", no_argument, 0, 'S' },
{ "stream", no_argument, 0, 's' },
{ "oneshot", no_argument, 0, 'O' },
{ 0, 0, 0, 0 }
};
/* Use stream API as default */
opts->stream_api = 1;
while (1)
{
c = getopt_long_only(argc, argv, "h", cmd_opts, &opt_index);
if (c < 0)
break;
switch (c) {
case 'e': /* enc */
opts->enc = 1;
break;
case 'd': /* dec */
opts->enc = 0;
break;
case 'k': /* keyhex */
if (strlen(optarg) > MAX_KEY_SIZE * 2) {
printf("HEX key string is too long.\n");
return -EINVAL;
}
hex2bin(optarg, strlen(optarg), opts->key,
MAX_KEY_SIZE);
opts->key_flag = 1;
break;
case 'K': /* keyfile */
fp = fopen(optarg, "rb");
if (!fp) {
printf("Failed to open %s\n", optarg);
return -EINVAL;
}
fread(opts->key, 1, MAX_KEY_SIZE, fp);
fclose(fp);
fp = NULL;
opts->key_flag = 1;
break;
case 'v': /* ivhex */
if (strlen(optarg) > MAX_IV_SIZE) {
printf("HEX iv string is too long.\n");
return -EINVAL;
}
hex2bin(optarg, strlen(optarg), opts->iv,
MAX_IV_SIZE);
opts->iv_flag = 1;
break;
case 'I': /* ivfile */
fp = fopen(optarg, "rb");
if (!fp) {
printf("Failed to open %s\n", optarg);
return -EINVAL;
}
fread(opts->iv, 1, MAX_IV_SIZE, fp);
fclose(fp);
fp = NULL;
opts->iv_flag = 1;
break;
case 'i': /* in */
if (strlen(optarg) >= MAX_FILE_NAME) {
printf("Input file name is too long.\n");
return -EINVAL;
}
strcpy(opts->in_file, optarg);
opts->in_flag = 1;
break;
case 'o': /* out */
if (strlen(optarg) >= MAX_FILE_NAME) {
printf("Output file name is too long.\n");
return -EINVAL;
}
strcpy(opts->out_file, optarg);
opts->out_flag = 1;
break;
case 'A': /* align */
opts->alignment = BIDIRECTION_ALIGNMENT;
break;
case 'a': /* align-in */
opts->alignment = INPUT_ALIGNMENT;
break;
case 'L': /* align-out */
opts->alignment = OUTPUT_ALIGNMENT;
break;
case 'R': /* access-heuristic */
opts->access = KCAPI_ACCESS_HEURISTIC;
break;
case 'V': /* access-vmsplice */
opts->access = KCAPI_ACCESS_VMSPLICE;
break;
case 'S': /* access-sendmsg */
opts->access = KCAPI_ACCESS_SENDMSG;
break;
case 's': /* stream */
opts->stream_api = 1;
break;
case 'O': /* Oneshot */
opts->stream_api = 0;
break;
case 'h':
return 1;
default:
return -EINVAL;
}
}
return 0;
}
int skcipher_command(struct subcmd_cfg *cmd, int argc, char *argv[])
{
struct sk_options sk_opts;
int ret = -1;
memset(&sk_opts, 0, sizeof(sk_opts));
ret = skcipher_cmd_arg_parse(argc, argv, &sk_opts);
if (ret > 0) {
usage_sk(cmd);
return 0;
}
if (ret < 0)
return ret;
if (check_options(&sk_opts))
return -EINVAL;
strcpy(sk_opts.cipher_name, cmd->cipher_name);
sk_opts.keylen = cmd->keylen;
sk_opts.ivlen = cmd->ivlen;
if (sk_opts.stream_api)
ret = skcipher_do_crypt_stream(&sk_opts);
else
ret = skcipher_do_crypt_sync_oneshot(&sk_opts);
return ret;
}