blob: 164327f6942c472209b6d747ab483fad9eb8a724 [file] [log] [blame]
/*
* Demo on how to use /dev/crypto device for ciphering.
*
* Placed under public domain.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <crypto/cryptodev.h>
#define DATA_SIZE (8*1024)
#define AUTH_SIZE 31
#define BLOCK_SIZE 16
#define KEY_SIZE 16
#define MAC_SIZE 20 /* SHA1 */
static int debug = 0;
static int
get_sha1_hmac(int cfd, void* key, int key_size, void* data1, int data1_size, void* data2, int data2_size, void* mac)
{
struct session_op sess;
struct crypt_op cryp;
memset(&sess, 0, sizeof(sess));
memset(&cryp, 0, sizeof(cryp));
sess.cipher = 0;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = key_size;
sess.mackey = key;
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
/* Encrypt data.in to data.encrypted */
cryp.ses = sess.ses;
cryp.len = data1_size;
cryp.src = data1;
cryp.dst = NULL;
cryp.iv = NULL;
cryp.mac = mac;
cryp.op = COP_ENCRYPT;
cryp.flags = COP_FLAG_UPDATE;
if (ioctl(cfd, CIOCCRYPT, &cryp)) {
perror("ioctl(CIOCCRYPT)");
return 1;
}
cryp.ses = sess.ses;
cryp.len = data2_size;
cryp.src = data2;
cryp.dst = NULL;
cryp.iv = NULL;
cryp.mac = mac;
cryp.op = COP_ENCRYPT;
cryp.flags = COP_FLAG_FINAL;
if (ioctl(cfd, CIOCCRYPT, &cryp)) {
perror("ioctl(CIOCCRYPT)");
return 1;
}
/* Finish crypto session */
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
return 0;
}
static void print_buf(char* desc, unsigned char* buf, int size)
{
int i;
fputs(desc, stdout);
for (i=0;i<size;i++) {
printf("%.2x", (uint8_t)buf[i]);
}
fputs("\n", stdout);
}
static int
test_crypto(int cfd)
{
char plaintext_raw[DATA_SIZE + 63], *plaintext;
char ciphertext_raw[DATA_SIZE + 63], *ciphertext;
char iv[BLOCK_SIZE];
char key[KEY_SIZE];
char auth[AUTH_SIZE];
unsigned char sha1mac[20];
int pad, i;
struct session_op sess;
struct crypt_op co;
struct crypt_auth_op cao;
struct session_info_op siop;
memset(&sess, 0, sizeof(sess));
memset(&cao, 0, sizeof(cao));
memset(&co, 0, sizeof(co));
memset(key,0x33, sizeof(key));
memset(iv, 0x03, sizeof(iv));
memset(auth, 0xf1, sizeof(auth));
/* Get crypto session for AES128 */
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = (void*)key;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = 16;
sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
siop.ses = sess.ses;
if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
perror("ioctl(CIOCGSESSINFO)");
return 1;
}
if (debug)
printf("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
plaintext = (char *)(((unsigned long)plaintext_raw + siop.alignmask) & ~siop.alignmask);
ciphertext = (char *)(((unsigned long)ciphertext_raw + siop.alignmask) & ~siop.alignmask);
memset(plaintext, 0x15, DATA_SIZE);
if (get_sha1_hmac(cfd, sess.mackey, sess.mackeylen, auth, sizeof(auth), plaintext, DATA_SIZE, sha1mac) != 0) {
fprintf(stderr, "SHA1 MAC failed\n");
return 1;
}
memcpy(ciphertext, plaintext, DATA_SIZE);
/* Encrypt data.in to data.encrypted */
cao.ses = sess.ses;
cao.auth_src = auth;
cao.auth_len = sizeof(auth);
cao.len = DATA_SIZE;
cao.src = ciphertext;
cao.dst = ciphertext;
cao.iv = iv;
cao.op = COP_ENCRYPT;
cao.flags = COP_FLAG_AEAD_TLS_TYPE;
if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
perror("ioctl(CIOCAUTHCRYPT)");
return 1;
}
//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, cao.len);
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
/* Get crypto session for AES128 */
memset(&sess, 0, sizeof(sess));
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = key;
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
/* Decrypt data.encrypted to data.decrypted */
co.ses = sess.ses;
co.len = cao.len;
co.src = ciphertext;
co.dst = ciphertext;
co.iv = iv;
co.op = COP_DECRYPT;
if (ioctl(cfd, CIOCCRYPT, &co)) {
perror("ioctl(CIOCCRYPT)");
return 1;
}
/* Verify the result */
if (memcmp(plaintext, ciphertext, DATA_SIZE) != 0) {
int i;
fprintf(stderr,
"FAIL: Decrypted data are different from the input data.\n");
printf("plaintext:");
for (i = 0; i < DATA_SIZE; i++) {
if ((i % 30) == 0)
printf("\n");
printf("%02x ", plaintext[i]);
}
printf("ciphertext:");
for (i = 0; i < DATA_SIZE; i++) {
if ((i % 30) == 0)
printf("\n");
printf("%02x ", ciphertext[i]);
}
printf("\n");
return 1;
}
pad = ciphertext[cao.len-1];
if (memcmp(&ciphertext[cao.len-MAC_SIZE-pad-1], sha1mac, 20) != 0) {
fprintf(stderr, "AEAD SHA1 MAC does not match plain MAC\n");
print_buf("SHA1: ", sha1mac, 20);
print_buf("SHA1-TLS: ", &ciphertext[cao.len-MAC_SIZE-pad-1], 20);
return 1;
}
for (i=0;i<pad;i++)
if (ciphertext[cao.len-1-i] != pad) {
fprintf(stderr, "Pad does not match (expected %d)\n", pad);
print_buf("PAD: ", &ciphertext[cao.len-1-pad], pad);
return 1;
}
if (debug) printf("Test passed\n");
/* Finish crypto session */
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
return 0;
}
static int
test_encrypt_decrypt(int cfd)
{
char plaintext_raw[DATA_SIZE + 63], *plaintext;
char ciphertext_raw[DATA_SIZE + 63], *ciphertext;
char iv[BLOCK_SIZE];
char key[KEY_SIZE];
char auth[AUTH_SIZE];
unsigned char sha1mac[20];
int enc_len;
struct session_op sess;
struct crypt_op co;
struct crypt_auth_op cao;
struct session_info_op siop;
memset(&sess, 0, sizeof(sess));
memset(&cao, 0, sizeof(cao));
memset(&co, 0, sizeof(co));
memset(key,0x33, sizeof(key));
memset(iv, 0x03, sizeof(iv));
memset(auth, 0xf1, sizeof(auth));
/* Get crypto session for AES128 */
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = key;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = 16;
sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
siop.ses = sess.ses;
if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
perror("ioctl(CIOCGSESSINFO)");
return 1;
}
// printf("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
// siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
plaintext = (char *)(((unsigned long)plaintext_raw + siop.alignmask) & ~siop.alignmask);
ciphertext = (char *)(((unsigned long)ciphertext_raw + siop.alignmask) & ~siop.alignmask);
memset(plaintext, 0x15, DATA_SIZE);
if (get_sha1_hmac(cfd, sess.mackey, sess.mackeylen, auth, sizeof(auth), plaintext, DATA_SIZE, sha1mac) != 0) {
fprintf(stderr, "SHA1 MAC failed\n");
return 1;
}
memcpy(ciphertext, plaintext, DATA_SIZE);
/* Encrypt data.in to data.encrypted */
cao.ses = sess.ses;
cao.auth_src = (void*)auth;
cao.auth_len = sizeof(auth);
cao.len = DATA_SIZE;
cao.src = (void*)ciphertext;
cao.dst = (void*)ciphertext;
cao.iv = iv;
cao.op = COP_ENCRYPT;
cao.flags = COP_FLAG_AEAD_TLS_TYPE;
if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
perror("ioctl(CIOCAUTHCRYPT)");
return 1;
}
enc_len = cao.len;
//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
/* Get crypto session for AES128 */
memset(&sess, 0, sizeof(sess));
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = key;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = 16;
sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
/* Decrypt data.encrypted to data.decrypted */
cao.ses = sess.ses;
cao.auth_src = auth;
cao.auth_len = sizeof(auth);
cao.len = enc_len;
cao.src = ciphertext;
cao.dst = ciphertext;
cao.iv = iv;
cao.op = COP_DECRYPT;
cao.flags = COP_FLAG_AEAD_TLS_TYPE;
if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
perror("ioctl(CIOCAUTHCRYPT)");
return 1;
}
if (cao.len != DATA_SIZE) {
fprintf(stderr, "decrypted data size incorrect!\n");
return 1;
}
/* Verify the result */
if (memcmp(plaintext, ciphertext, DATA_SIZE) != 0) {
int i;
fprintf(stderr,
"FAIL: Decrypted data are different from the input data.\n");
printf("plaintext:");
for (i = 0; i < DATA_SIZE; i++) {
if ((i % 30) == 0)
printf("\n");
printf("%02x ", plaintext[i]);
}
printf("ciphertext:");
for (i = 0; i < DATA_SIZE; i++) {
if ((i % 30) == 0)
printf("\n");
printf("%02x ", ciphertext[i]);
}
printf("\n");
return 1;
}
if (debug) printf("Test passed\n");
/* Finish crypto session */
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
return 0;
}
static int
test_encrypt_decrypt_error(int cfd, int err)
{
char plaintext_raw[DATA_SIZE + 63], *plaintext;
char ciphertext_raw[DATA_SIZE + 63], *ciphertext;
char iv[BLOCK_SIZE];
char key[KEY_SIZE];
char auth[AUTH_SIZE];
unsigned char sha1mac[20];
int enc_len;
struct session_op sess;
struct crypt_op co;
struct crypt_auth_op cao;
struct session_info_op siop;
memset(&sess, 0, sizeof(sess));
memset(&cao, 0, sizeof(cao));
memset(&co, 0, sizeof(co));
memset(key,0x33, sizeof(key));
memset(iv, 0x03, sizeof(iv));
memset(auth, 0xf1, sizeof(auth));
/* Get crypto session for AES128 */
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = key;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = 16;
sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
siop.ses = sess.ses;
if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
perror("ioctl(CIOCGSESSINFO)");
return 1;
}
// printf("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
// siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
plaintext = (char *)(((unsigned long)plaintext_raw + siop.alignmask) & ~siop.alignmask);
ciphertext = (char *)(((unsigned long)ciphertext_raw + siop.alignmask) & ~siop.alignmask);
memset(plaintext, 0x15, DATA_SIZE);
if (get_sha1_hmac(cfd, sess.mackey, sess.mackeylen, auth, sizeof(auth), plaintext, DATA_SIZE, sha1mac) != 0) {
fprintf(stderr, "SHA1 MAC failed\n");
return 1;
}
memcpy(ciphertext, plaintext, DATA_SIZE);
/* Encrypt data.in to data.encrypted */
cao.ses = sess.ses;
cao.auth_src = auth;
cao.auth_len = sizeof(auth);
cao.len = DATA_SIZE;
cao.src = ciphertext;
cao.dst = ciphertext;
cao.iv = iv;
cao.op = COP_ENCRYPT;
cao.flags = COP_FLAG_AEAD_TLS_TYPE;
if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
perror("ioctl(CIOCAUTHCRYPT)");
return 1;
}
enc_len = cao.len;
//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
/* Get crypto session for AES128 */
memset(&sess, 0, sizeof(sess));
sess.cipher = CRYPTO_AES_CBC;
sess.keylen = KEY_SIZE;
sess.key = key;
sess.mac = CRYPTO_SHA1_HMAC;
sess.mackeylen = 16;
sess.mackey = (uint8_t*)"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
if (ioctl(cfd, CIOCGSESSION, &sess)) {
perror("ioctl(CIOCGSESSION)");
return 1;
}
if (err == 0)
auth[2]++;
else
ciphertext[4]++;
/* Decrypt data.encrypted to data.decrypted */
cao.ses = sess.ses;
cao.auth_src = auth;
cao.auth_len = sizeof(auth);
cao.len = enc_len;
cao.src = ciphertext;
cao.dst = ciphertext;
cao.iv = iv;
cao.op = COP_DECRYPT;
cao.flags = COP_FLAG_AEAD_TLS_TYPE;
if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
if (debug) printf("Test passed\n");
return 0;
}
/* Finish crypto session */
if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
perror("ioctl(CIOCFSESSION)");
return 1;
}
fprintf(stderr, "Modification to ciphertext was not detected\n");
return 1;
}
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;
if (test_encrypt_decrypt(cfd))
return 1;
if (test_encrypt_decrypt_error(cfd, 0))
return 1;
if (test_encrypt_decrypt_error(cfd, 1))
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;
}