From ffb3e747f8b50ca18701530d1729a4ee3aa4ae69 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Sat, 24 Jul 2010 13:43:43 +0200 Subject: Added a support for reading session data directly from userspace. --- .gitignore | 1 + cryptodev_cipher.c | 8 +- cryptodev_int.h | 16 +- cryptodev_main.c | 11 +- examples/Makefile | 8 +- examples/ncr-direct.c | 443 +++++++++++++++++++++++++++++++++ examples/ncr.c | 15 +- examples/pk.c | 30 ++- examples/speed.c | 5 +- ncr-data.c | 2 + ncr-pk.c | 210 ++++++++++++---- ncr-pk.h | 22 +- ncr-sessions.c | 667 ++++++++++++++++++++++++++++++++++++++++---------- ncr.c | 1 + ncr.h | 26 +- ncr_int.h | 9 + 16 files changed, 1246 insertions(+), 228 deletions(-) create mode 100644 examples/ncr-direct.c diff --git a/.gitignore b/.gitignore index 4379d92..e3f0923 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ modules.order examples/cipher examples/hmac examples/ncr +examples/ncr-direct examples/pk examples/speed releases diff --git a/cryptodev_cipher.c b/cryptodev_cipher.c index fa5aab2..0dd0db7 100644 --- a/cryptodev_cipher.c +++ b/cryptodev_cipher.c @@ -188,24 +188,24 @@ struct scatterlist sg, sg2; } -ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len) +ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len) { int ret; INIT_COMPLETION(cdata->async.result->completion); - ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2, + ablkcipher_request_set_crypt(cdata->async.request, (struct scatterlist*)sg1, sg2, len, cdata->async.iv); ret = crypto_ablkcipher_encrypt(cdata->async.request); return waitfor(cdata->async.result,ret); } -ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len) +ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len) { int ret; INIT_COMPLETION(cdata->async.result->completion); - ablkcipher_request_set_crypt(cdata->async.request, sg1, sg2, + ablkcipher_request_set_crypt(cdata->async.request, (struct scatterlist*)sg1, sg2, len, cdata->async.iv); ret = crypto_ablkcipher_decrypt(cdata->async.request); diff --git a/cryptodev_int.h b/cryptodev_int.h index a0d3073..3c6eca4 100644 --- a/cryptodev_int.h +++ b/cryptodev_int.h @@ -24,6 +24,18 @@ extern int cryptodev_verbosity; +/* For zero copy */ +int __get_userbuf(uint8_t *addr, uint32_t len, int write, + int pgcount, struct page **pg, struct scatterlist *sg); +void release_user_pages(struct page **pg, int pagecount); + +/* last page - first page + 1 */ +#define PAGECOUNT(buf, buflen) \ + ((((unsigned long)(buf + buflen - 1) & PAGE_MASK) >> PAGE_SHIFT) - \ + (((unsigned long) buf & PAGE_MASK) >> PAGE_SHIFT) + 1) + +#define DEFAULT_PREALLOC_PAGES 32 + struct cipher_data { int init; /* 0 uninitialized */ @@ -39,8 +51,8 @@ struct cipher_data int cryptodev_cipher_init(struct cipher_data* out, const char* alg_name, uint8_t * key, size_t keylen); void cryptodev_cipher_deinit(struct cipher_data* cdata); -ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len); -ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, struct scatterlist *sg1, struct scatterlist *sg2, size_t len); +ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len); +ssize_t cryptodev_cipher_encrypt( struct cipher_data* cdata, const struct scatterlist *sg1, struct scatterlist *sg2, size_t len); void cryptodev_cipher_set_iv(struct cipher_data* cdata, void* iv, size_t iv_size); int _cryptodev_cipher_decrypt(struct cipher_data* cdata, const void* ciphertext, diff --git a/cryptodev_main.c b/cryptodev_main.c index 0316a74..89b9a02 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -65,8 +65,6 @@ module_param(enable_stats, int, 0644); MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage"); #endif -#define DEFAULT_PREALLOC_PAGES 16 - /* ====== CryptoAPI ====== */ struct fcrypt { struct list_head list; @@ -495,7 +493,7 @@ __crypto_run_std(struct csession *ses_ptr, struct crypt_op *cop) #ifndef DISABLE_ZCOPY -static void release_user_pages(struct page **pg, int pagecount) +void release_user_pages(struct page **pg, int pagecount) { while (pagecount--) { if (!PageReserved(pg[pagecount])) @@ -504,16 +502,11 @@ static void release_user_pages(struct page **pg, int pagecount) } } -/* last page - first page + 1 */ -#define PAGECOUNT(buf, buflen) \ - ((((unsigned long)(buf + buflen - 1) & PAGE_MASK) >> PAGE_SHIFT) - \ - (((unsigned long) buf & PAGE_MASK) >> PAGE_SHIFT) + 1) - /* offset of buf in it's first page */ #define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK) /* fetch the pages addr resides in into pg and initialise sg with them */ -static int __get_userbuf(uint8_t *addr, uint32_t len, int write, +int __get_userbuf(uint8_t *addr, uint32_t len, int write, int pgcount, struct page **pg, struct scatterlist *sg) { int ret, pglen, i = 0; diff --git a/examples/Makefile b/examples/Makefile index c65297f..f482649 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,7 +1,7 @@ CC = gcc CFLAGS = -Wall -g -O2 -progs := cipher hmac ncr pk speed +progs := cipher hmac ncr pk speed ncr-direct all: $(progs) @@ -17,15 +17,19 @@ hmac: hmac.c ncr: ncr.c $(CC) $(CFLAGS) $< -o $@ +ncr-direct: ncr-direct.c + $(CC) $(CFLAGS) $< -o $@ + pk: pk.c $(CC) $(CFLAGS) $< -o $@ -L/usr/local/lib -lgnutls check: $(progs) ./ncr + ./ncr-direct ./pk ./cipher ./hmac ./speed clean: - rm -f *.o *~ hmac cipher ncr pk speed + rm -f *.o *~ hmac cipher ncr pk speed ncr-direct diff --git a/examples/ncr-direct.c b/examples/ncr-direct.c new file mode 100644 index 0000000..0908e2c --- /dev/null +++ b/examples/ncr-direct.c @@ -0,0 +1,443 @@ +/* + * Demo on how to use /dev/crypto device for HMAC. + * + * Placed under public domain. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ncr.h" +#include + +#define DATA_SIZE 4096 +#define KEY_DATA_SIZE 16 + + +struct aes_vectors_st { + const uint8_t* key; + const uint8_t* plaintext; + const uint8_t* ciphertext; +} aes_vectors[] = { + { + .key = (uint8_t*)"\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .plaintext = (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .ciphertext = (uint8_t*)"\x4b\xc3\xf8\x83\x45\x0c\x11\x3c\x64\xca\x42\xe1\x11\x2a\x9e\x87", + }, + { + .key = (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .plaintext = (uint8_t*)"\xf3\x44\x81\xec\x3c\xc6\x27\xba\xcd\x5d\xc3\xfb\x08\xf2\x73\xe6", + .ciphertext = (uint8_t*)"\x03\x36\x76\x3e\x96\x6d\x92\x59\x5a\x56\x7c\xc9\xce\x53\x7f\x5e", + }, + { + .key = (uint8_t*)"\x10\xa5\x88\x69\xd7\x4b\xe5\xa3\x74\xcf\x86\x7c\xfb\x47\x38\x59", + .plaintext = (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .ciphertext = (uint8_t*)"\x6d\x25\x1e\x69\x44\xb0\x51\xe0\x4e\xaa\x6f\xb4\xdb\xf7\x84\x65", + }, + { + .key = (uint8_t*)"\xca\xea\x65\xcd\xbb\x75\xe9\x16\x9e\xcd\x22\xeb\xe6\xe5\x46\x75", + .plaintext = (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .ciphertext = (uint8_t*)"\x6e\x29\x20\x11\x90\x15\x2d\xf4\xee\x05\x81\x39\xde\xf6\x10\xbb", + }, + { + .key = (uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", + .plaintext = (uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + .ciphertext = (uint8_t*)"\x9b\xa4\xa9\x14\x3f\x4e\x5d\x40\x48\x52\x1c\x4f\x88\x77\xd8\x8e", + }, +}; + +/* AES cipher */ +static int +test_ncr_aes(int cfd) +{ + struct ncr_data_init_st dinit; + ncr_key_t key; + struct ncr_key_data_st keydata; + struct ncr_data_st kdata; + ncr_data_t dd, dd2; + uint8_t data[KEY_DATA_SIZE]; + int i, j; + struct ncr_session_once_op_st nop; + + dinit.max_object_size = KEY_DATA_SIZE; + dinit.flags = NCR_DATA_FLAG_EXPORTABLE; + dinit.initial_data = NULL; + dinit.initial_data_size = 0; + + if (ioctl(cfd, NCRIO_DATA_INIT, &dinit)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_DATA_INIT)"); + return 1; + } + + dd = dinit.desc; + + if (ioctl(cfd, NCRIO_DATA_INIT, &dinit)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_DATA_INIT)"); + return 1; + } + + dd2 = dinit.desc; + + /* convert it to key */ + if (ioctl(cfd, NCRIO_KEY_INIT, &key)) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + keydata.key_id[0] = 'a'; + keydata.key_id[2] = 'b'; + keydata.key_id_size = 2; + keydata.type = NCR_KEY_TYPE_SECRET; + keydata.algorithm = NCR_ALG_AES_CBC; + keydata.flags = NCR_KEY_FLAG_EXPORTABLE; + + + fprintf(stdout, "Tests on AES Encryption\n"); + for (i=0;imax_data_size = init.max_object_size; + + sg_init_one(&data->sg, data->data, data->max_data_size); if (init.initial_data != NULL) { if (unlikely(copy_from_user(data->data, init.initial_data, diff --git a/ncr-pk.c b/ncr-pk.c index bfe575d..8d85a92 100644 --- a/ncr-pk.c +++ b/ncr-pk.c @@ -377,129 +377,230 @@ int ncr_pk_cipher_init(const struct algo_properties_st *algo, } int ncr_pk_cipher_encrypt(const struct ncr_pk_ctx* ctx, - const void* input, size_t input_size, - void* output, size_t *output_size) + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size) { -int cret; -unsigned long osize = *output_size; +int cret, ret; +unsigned long osize = *osg_size; +uint8_t* tmp; +void * input, *output; + + tmp = kmalloc(isg_size + *osg_size, GFP_KERNEL); + if (tmp == NULL) { + err(); + return -ENOMEM; + } + + ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, tmp, isg_size); + if (ret != isg_size) { + err(); + ret = -EINVAL; + goto fail; + } + + input = tmp; + output = &tmp[isg_size]; + switch(ctx->algorithm->algo) { case NCR_ALG_RSA: - cret = rsa_encrypt_key_ex( input, input_size, output, &osize, + cret = rsa_encrypt_key_ex( input, isg_size, output, &osize, NULL, 0, ctx->oaep_hash, ctx->type, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { - printk("cret: %d type: %d\n", cret, ctx->type); err(); - return tomerr(cret); + ret = tomerr(cret); + goto fail; } - *output_size = osize; + *osg_size = osize; + break; case NCR_ALG_DSA: - return -EINVAL; - break; + ret = -EINVAL; + goto fail; default: err(); - return -EINVAL; + ret = -EINVAL; + goto fail; } - - return 0; + + ret = sg_copy_from_buffer(osg, osg_cnt, output, *osg_size); + if (ret != *osg_size) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + kfree(tmp); + return ret; } -int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, const void* input, size_t input_size, - void* output, size_t *output_size) +int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size) { -int cret; -unsigned long osize = *output_size; +int cret, ret; int stat; +unsigned long osize = *osg_size; +uint8_t* tmp; +void * input, *output; + + tmp = kmalloc(isg_size + *osg_size, GFP_KERNEL); + if (tmp == NULL) { + err(); + return -ENOMEM; + } + + input = tmp; + output = &tmp[isg_size]; + + ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, input, isg_size); + if (ret != isg_size) { + err(); + ret = -EINVAL; + goto fail; + } switch(ctx->algorithm->algo) { case NCR_ALG_RSA: - cret = rsa_decrypt_key_ex( input, input_size, output, &osize, + cret = rsa_decrypt_key_ex( input, isg_size, output, &osize, NULL, 0, ctx->oaep_hash, ctx->type, &stat, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); - return tomerr(cret); + ret = tomerr(cret); + goto fail; } if (stat==0) { err(); - return -EINVAL; + ret = -EINVAL; + goto fail; } - *output_size = osize; + *osg_size = osize; break; case NCR_ALG_DSA: - return -EINVAL; - break; + ret = -EINVAL; + goto fail; default: err(); - return -EINVAL; + ret = -EINVAL; + goto fail; } + + ret = sg_copy_from_buffer(osg, osg_cnt, output, *osg_size); + if (ret != *osg_size) { + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; +fail: + kfree(tmp); - return 0; + return ret; } int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, - const void* input, size_t input_size, - void* output, size_t *output_size) + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size) { -int cret; -unsigned long osize = *output_size; +int cret, ret; +unsigned long osize = *osg_size; +uint8_t* tmp; +void * input, *output; + + tmp = kmalloc(isg_size + *osg_size, GFP_KERNEL); + if (tmp == NULL) { + err(); + return -ENOMEM; + } + + input = tmp; + output = &tmp[isg_size]; + + ret = sg_copy_to_buffer((struct scatterlist*)isg, isg_cnt, input, isg_size); + if (ret != isg_size) { + err(); + ret = -EINVAL; + goto fail; + } switch(ctx->algorithm->algo) { case NCR_ALG_RSA: - if (ctx->sign_hash == NULL) { - err(); - return -EINVAL; - } - cret = rsa_sign_hash_ex( input, input_size, output, &osize, + cret = rsa_sign_hash_ex( input, isg_size, output, &osize, ctx->type, ctx->sign_hash, ctx->salt_len, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); return tomerr(cret); } - *output_size = osize; + *osg_size = osize; break; case NCR_ALG_DSA: - cret = dsa_sign_hash( input, input_size, output, &osize, + cret = dsa_sign_hash( input, isg_size, output, &osize, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); return tomerr(cret); } - *output_size = osize; + *osg_size = osize; break; default: err(); - return -EINVAL; + ret = -EINVAL; + goto fail; + } + + ret = sg_copy_from_buffer(osg, osg_cnt, output, *osg_size); + if (ret != *osg_size) { + err(); + ret = -EINVAL; + goto fail; } + ret = 0; +fail: + kfree(tmp); - return 0; + return ret; } int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const void* signature, size_t signature_size, + const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, const void* hash, size_t hash_size, ncr_error_t* err) { -int cret; +int cret, ret; int stat; +uint8_t* sig; + + sig = kmalloc(sign_sg_size, GFP_KERNEL); + if (sig == NULL) { + err(); + return -ENOMEM; + } + + ret = sg_copy_to_buffer((struct scatterlist*)sign_sg, sign_sg_cnt, sig, sign_sg_size); + if (ret != sign_sg_size) { + err(); + ret = -EINVAL; + goto fail; + } switch(ctx->algorithm->algo) { case NCR_ALG_RSA: - if (ctx->sign_hash == NULL) { - err(); - return -EINVAL; - } - cret = rsa_verify_hash_ex( signature, signature_size, + cret = rsa_verify_hash_ex( sig, sign_sg_size, hash, hash_size, ctx->type, ctx->sign_hash, ctx->salt_len, &stat, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { err(); - return tomerr(cret); + ret = tomerr(cret); + goto fail; } if (stat == 1) @@ -509,11 +610,12 @@ int stat; break; case NCR_ALG_DSA: - cret = dsa_verify_hash( signature, signature_size, + cret = dsa_verify_hash( sig, sign_sg_size, hash, hash_size, &stat, &ctx->key->key.pk.dsa); if (cret != CRYPT_OK) { err(); - return tomerr(cret); + ret = tomerr(cret); + goto fail; } if (stat == 1) @@ -524,8 +626,12 @@ int stat; break; default: err(); - return -EINVAL; + ret = -EINVAL; + goto fail; } - - return 0; + + ret = 0; +fail: + kfree(sig); + return ret; } diff --git a/ncr-pk.h b/ncr-pk.h index 873ee83..fdc5e1c 100644 --- a/ncr-pk.h +++ b/ncr-pk.h @@ -34,15 +34,21 @@ int ncr_pk_cipher_init(const struct algo_properties_st *algo, struct ncr_pk_ctx* ctx, struct ncr_key_params_st* params, struct key_item_st *key, const struct algo_properties_st *sign_hash); void ncr_pk_cipher_deinit(struct ncr_pk_ctx* ctx); -int ncr_pk_cipher_encrypt(const struct ncr_pk_ctx* ctx, const void* input, - size_t input_size, void* output, size_t *output_size); -int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, const void* input, - size_t input_size, void* output, size_t *output_size); -int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, const void* input, - size_t input_size, void* output, size_t *output_size); +int ncr_pk_cipher_encrypt(const struct ncr_pk_ctx* ctx, + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); + +int ncr_pk_cipher_decrypt(const struct ncr_pk_ctx* ctx, + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); + +int ncr_pk_cipher_sign(const struct ncr_pk_ctx* ctx, + const struct scatterlist* isg, unsigned int isg_cnt, size_t isg_size, + struct scatterlist *osg, unsigned int osg_cnt, size_t* osg_size); + int ncr_pk_cipher_verify(const struct ncr_pk_ctx* ctx, - const void* signature, size_t signature_size, - const void* hash, size_t hash_size, ncr_error_t*); + const struct scatterlist* sign_sg, unsigned int sign_sg_cnt, size_t sign_sg_size, + const void* hash, size_t hash_size, ncr_error_t* err); #endif diff --git a/ncr-sessions.c b/ncr-sessions.c index b8eabdd..fb9fe4f 100644 --- a/ncr-sessions.c +++ b/ncr-sessions.c @@ -23,6 +23,8 @@ #include "cryptodev.h" #include "ncr.h" #include "ncr_int.h" +#include +#include static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc); @@ -84,6 +86,8 @@ void _ncr_sessions_item_put( struct session_item_st* item) cryptodev_hash_deinit(&item->hash); if (item->key) _ncr_key_item_put(item->key); + kfree(item->sg); + kfree(item->pages); kfree(item); } } @@ -92,13 +96,25 @@ struct session_item_st* ncr_session_new(struct list_sem_st* lst) { struct session_item_st* sess; - sess = kmalloc(sizeof(*sess), GFP_KERNEL); + sess = kzalloc(sizeof(*sess), GFP_KERNEL); if (sess == NULL) { err(); return NULL; } - memset(sess, 0, sizeof(*sess)); + sess->array_size = DEFAULT_PREALLOC_PAGES; + sess->pages = kzalloc(sess->array_size * + sizeof(struct page *), GFP_KERNEL); + sess->sg = kzalloc(sess->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + if (sess->sg == NULL || sess->pages == NULL) { + err(); + kfree(sess->sg); + kfree(sess->pages); + kfree(sess); + return NULL; + } + init_MUTEX(&sess->mem_mutex); atomic_set(&sess->refcnt, 1); @@ -216,6 +232,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses ret = -EINVAL; goto fail; } + switch(session->op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: @@ -395,6 +412,63 @@ int ncr_session_init(struct ncr_lists* lists, void __user* arg) return ret; } +int _ncr_session_encrypt(struct session_item_st* sess, const struct scatterlist* input, unsigned input_cnt, + size_t input_size, void *output, unsigned output_cnt, size_t *output_size) +{ +int ret; + + if (sess->algorithm->is_symmetric) { + /* read key */ + ret = cryptodev_cipher_encrypt(&sess->cipher, input, + output, input_size); + if (ret < 0) { + err(); + return ret; + } + /* FIXME: handle ciphers that do not require that */ + + } else { /* public key */ + ret = ncr_pk_cipher_encrypt(&sess->pk, input, input_cnt, input_size, + output, output_cnt, output_size); + + if (ret < 0) { + err(); + return ret; + } + } + + return 0; +} + +int _ncr_session_decrypt(struct session_item_st* sess, const struct scatterlist* input, + unsigned input_cnt, size_t input_size, + struct scatterlist *output, unsigned output_cnt, size_t *output_size) +{ +int ret; + + if (sess->algorithm->is_symmetric) { + /* read key */ + ret = cryptodev_cipher_decrypt(&sess->cipher, input, + output, input_size); + if (ret < 0) { + err(); + return ret; + } + /* FIXME: handle ciphers that do not require equality */ + + } else { /* public key */ + ret = ncr_pk_cipher_decrypt(&sess->pk, input, input_cnt, input_size, + output, output_cnt, output_size); + + if (ret < 0) { + err(); + return ret; + } + } + + return 0; +} + /* Main update function */ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) @@ -403,6 +477,7 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st struct session_item_st* sess; struct data_item_st* data = NULL; struct data_item_st* odata = NULL; + size_t new_size; sess = ncr_sessions_item_get( &lists->sessions, op->ses); if (sess == NULL) { @@ -410,17 +485,17 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st return -EINVAL; } + /* obtain data item */ + data = ncr_data_item_get( &lists->data, op->data.ndata.input); + if (data == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + switch(sess->op) { case NCR_OP_ENCRYPT: - /* obtain data item */ - data = ncr_data_item_get( &lists->data, op->input); - if (data == NULL) { - err(); - ret = -EINVAL; - goto fail; - } - - odata = ncr_data_item_get( &lists->data, op->output); + odata = ncr_data_item_get( &lists->data, op->data.ndata.output); if (odata == NULL) { err(); ret = -EINVAL; @@ -433,39 +508,16 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st goto fail; } - if (sess->algorithm->is_symmetric) { - /* read key */ - ret = _cryptodev_cipher_encrypt(&sess->cipher, data->data, - data->data_size, odata->data, data->data_size); - if (ret < 0) { - err(); - goto fail; - } - /* FIXME: handle ciphers that do not require that */ - odata->data_size = data->data_size; - } else { /* public key */ - size_t new_size = odata->max_data_size; - ret = ncr_pk_cipher_encrypt(&sess->pk, data->data, data->data_size, - odata->data, &new_size); - - odata->data_size = new_size; - - if (ret < 0) { - err(); - goto fail; - } - } - break; - case NCR_OP_DECRYPT: - /* obtain data item */ - data = ncr_data_item_get( &lists->data, op->input); - if (data == NULL) { + odata->data_size = odata->max_data_size; + ret = _ncr_session_encrypt(sess, &data->sg, 1, data->data_size, + &odata->sg, 1, &odata->data_size); + if (ret < 0) { err(); - ret = -EINVAL; goto fail; } - - odata = ncr_data_item_get( &lists->data, op->output); + break; + case NCR_OP_DECRYPT: + odata = ncr_data_item_get( &lists->data, op->data.ndata.output); if (odata == NULL) { err(); ret = -EINVAL; @@ -477,63 +529,26 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st ret = -EINVAL; goto fail; } - - /* read key */ - if (sess->algorithm->is_symmetric) { - ret = _cryptodev_cipher_decrypt(&sess->cipher, data->data, data->data_size, odata->data, data->data_size); - if (ret < 0) { - err(); - goto fail; - } - /* FIXME: handle ciphers that do not require that */ - odata->data_size = data->data_size; - } else { /* public key */ - size_t new_size = odata->max_data_size; - ret = ncr_pk_cipher_decrypt(&sess->pk, data->data, data->data_size, - odata->data, &new_size); - - odata->data_size = new_size; - - if (ret < 0) { - err(); - goto fail; - } - } - - break; - - case NCR_OP_SIGN: - /* obtain data item */ - data = ncr_data_item_get( &lists->data, op->input); - if (data == NULL) { - err(); - ret = -EINVAL; - goto fail; - } - ret = _cryptodev_hash_update(&sess->hash, data->data, data->data_size); + new_size = odata->max_data_size; + ret = _ncr_session_decrypt(sess, &data->sg, 1, data->data_size, + &odata->sg, 1, &new_size); if (ret < 0) { err(); goto fail; } + odata->data_size = new_size; + break; + case NCR_OP_SIGN: case NCR_OP_VERIFY: - /* obtain data item */ - data = ncr_data_item_get( &lists->data, op->input); - if (data == NULL) { - err(); - ret = -EINVAL; - goto fail; - } - - ret = _cryptodev_hash_update(&sess->hash, data->data, data->data_size); + ret = cryptodev_hash_update(&sess->hash, &data->sg, data->data_size); if (ret < 0) { err(); goto fail; } break; - default: err(); ret = -EINVAL; @@ -550,16 +565,117 @@ fail: return ret; } -int ncr_session_update(struct ncr_lists* lists, void __user* arg) +/* Only the output buffer is given as scatterlist */ +static int get_userbuf1(struct session_item_st* ses, + struct ncr_session_op_st* op, struct scatterlist **dst_sg, unsigned *dst_cnt) { - struct ncr_session_op_st op; + int pagecount = 0; - if (unlikely(copy_from_user( &op, arg, sizeof(op)))) { - err(); - return -EFAULT; + if (op->data.udata.output == NULL) { + return -EINVAL; } - - return _ncr_session_update(lists, &op); + + pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + + + ses->available_pages = pagecount; + + if (pagecount > ses->array_size) { + while (ses->array_size < pagecount) + ses->array_size *= 2; + + dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", + __func__, ses->array_size); + ses->pages = krealloc(ses->pages, ses->array_size * + sizeof(struct page *), GFP_KERNEL); + ses->sg = krealloc(ses->sg, ses->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + + if (ses->sg == NULL || ses->pages == NULL) { + return -ENOMEM; + } + } + + if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, + pagecount, ses->pages, ses->sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); + return -EINVAL; + } + (*dst_sg) = ses->sg; + *dst_cnt = pagecount; + + return 0; +} + +/* make op->data.udata.input and op->data.udata.output available in scatterlists */ +static int get_userbuf2(struct session_item_st* ses, + struct ncr_session_op_st* op, struct scatterlist **src_sg, + unsigned *src_cnt, struct scatterlist **dst_sg, unsigned *dst_cnt) +{ + int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1; + + if (op->data.udata.input == NULL) { + return -EINVAL; + } + + src_pagecount = PAGECOUNT(op->data.udata.input, op->data.udata.input_size); + + if (op->data.udata.input != op->data.udata.output) { /* non-in-situ transformation */ + if (op->data.udata.output != NULL) { + dst_pagecount = PAGECOUNT(op->data.udata.output, op->data.udata.output_size); + write_src = 0; + } else { + dst_pagecount = 0; + } + } + + ses->available_pages = pagecount = src_pagecount + dst_pagecount; + + if (pagecount > ses->array_size) { + while (ses->array_size < pagecount) + ses->array_size *= 2; + + dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", + __func__, ses->array_size); + ses->pages = krealloc(ses->pages, ses->array_size * + sizeof(struct page *), GFP_KERNEL); + ses->sg = krealloc(ses->sg, ses->array_size * + sizeof(struct scatterlist), GFP_KERNEL); + + if (ses->sg == NULL || ses->pages == NULL) { + return -ENOMEM; + } + } + + if (__get_userbuf(op->data.udata.input, op->data.udata.input_size, write_src, + src_pagecount, ses->pages, ses->sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data input\n"); + return -EINVAL; + } + (*src_sg) = ses->sg; + *src_cnt = src_pagecount; + + if (dst_pagecount) { + *dst_cnt = dst_pagecount; + (*dst_sg) = ses->sg + src_pagecount; + + if (__get_userbuf(op->data.udata.output, op->data.udata.output_size, 1, dst_pagecount, + ses->pages + src_pagecount, *dst_sg)) { + dprintk(1, KERN_ERR, "failed to get user pages for data output\n"); + release_user_pages(ses->pages, src_pagecount); + return -EINVAL; + } + } else { + if (op->data.udata.output != NULL) { + *dst_cnt = src_pagecount; + (*dst_sg) = (*src_sg); + } else { + *dst_cnt = 0; + *dst_sg = NULL; + } + } + + return 0; } static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc) @@ -581,6 +697,15 @@ static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc) return; } +static int try_session_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +{ + if (op->data.ndata.input != NCR_DATA_INVALID) { + return _ncr_session_update(lists, op); + } + + return 0; +} + static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* op) { int ret; @@ -595,33 +720,260 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* return -EINVAL; } + ret = try_session_update(lists, op); + if (ret < 0) { + err(); + goto fail; + } + switch(sess->op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: - /* obtain data item */ - if (op->input != NCR_DATA_INVALID && - op->output != NCR_DATA_INVALID) { - ret = _ncr_session_update(lists, op); - if (ret < 0) + break; + + case NCR_OP_VERIFY: + digest_size = sess->hash.digestsize; + if (digest_size == 0 || sizeof(digest) < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + ret = cryptodev_hash_final(&sess->hash, digest); + if (ret < 0) { + err(); + goto fail; + } + + odata = ncr_data_item_get( &lists->data, op->data.ndata.output); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + if (sess->algorithm->is_hmac) { + if (digest_size != odata->data_size || + memcmp(odata->data, digest, digest_size) != 0) { + + op->err = NCR_VERIFICATION_FAILED; + } else { + op->err = NCR_SUCCESS; + } + } else { + /* PK signature */ + ret = ncr_pk_cipher_verify(&sess->pk, &odata->sg, 1, odata->data_size, + digest, digest_size, &op->err); + if (ret < 0) { + err(); goto fail; + } } break; - case NCR_OP_VERIFY: - /* obtain data item */ - if (op->input != NCR_DATA_INVALID) { - ret = _ncr_session_update(lists, op); - if (ret < 0) + case NCR_OP_SIGN: + odata = ncr_data_item_get( &lists->data, op->data.ndata.output); + if (odata == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || odata->max_data_size < digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + ret = cryptodev_hash_final(&sess->hash, odata->data); + odata->data_size = digest_size; + + cryptodev_hash_deinit(&sess->hash); + + if (sess->algorithm->is_pk) { + /* PK signature */ + size_t new_size = odata->max_data_size; + ret = ncr_pk_cipher_sign(&sess->pk, &odata->sg, 1, odata->data_size, + &odata->sg, 1, &new_size); + if (ret < 0) { + err(); goto fail; + } + odata->data_size = new_size; + } + break; + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + if (odata) _ncr_data_item_put(odata); + cryptodev_hash_deinit(&sess->hash); + if (sess->algorithm->is_symmetric) { + cryptodev_cipher_deinit(&sess->cipher); + } else { + ncr_pk_cipher_deinit(&sess->pk); + } + + _ncr_sessions_item_put(sess); + _ncr_session_remove(&lists->sessions, op->ses); + + return ret; +} + +/* Called when userspace buffers are used */ +static int _ncr_session_direct_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +{ + int ret; + struct session_item_st* sess; + struct scatterlist *isg; + struct scatterlist *osg; + unsigned osg_cnt=0, isg_cnt=0; + size_t isg_size, osg_size; + + sess = ncr_sessions_item_get( &lists->sessions, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + if (down_interruptible(&sess->mem_mutex)) { + err(); + _ncr_sessions_item_put(sess); + return -ERESTARTSYS; + } + + ret = get_userbuf2(sess, op, &isg, &isg_cnt, &osg, &osg_cnt); + if (ret < 0) { + err(); + goto fail; + } + isg_size = op->data.udata.input_size; + osg_size = op->data.udata.output_size; + + switch(sess->op) { + case NCR_OP_ENCRYPT: + if (osg == NULL) { + err(); + ret = -EINVAL; + goto fail; } - odata = ncr_data_item_get( &lists->data, op->output); - if (odata == NULL) { + ret = _ncr_session_encrypt(sess, isg, isg_cnt, isg_size, + osg, osg_cnt, &osg_size); + if (ret < 0) { + err(); + goto fail; + } + op->data.udata.output_size = osg_size; + + break; + case NCR_OP_DECRYPT: + if (osg == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + + if (osg_size < isg_size) { err(); ret = -EINVAL; goto fail; } + ret = _ncr_session_decrypt(sess, isg, isg_cnt, isg_size, + osg, osg_cnt, &osg_size); + if (ret < 0) { + err(); + goto fail; + } + op->data.udata.output_size = osg_size; + + break; + + case NCR_OP_SIGN: + case NCR_OP_VERIFY: + ret = cryptodev_hash_update(&sess->hash, isg, isg_size); + if (ret < 0) { + err(); + goto fail; + } + break; + default: + err(); + ret = -EINVAL; + goto fail; + } + + ret = 0; + +fail: + if (sess->available_pages) { + release_user_pages(sess->pages, sess->available_pages); + sess->available_pages = 0; + } + up(&sess->mem_mutex); + _ncr_sessions_item_put(sess); + + return ret; +} + +static int try_session_direct_update(struct ncr_lists* lists, struct ncr_session_op_st* op) +{ + if (op->data.udata.input != NULL) { + return _ncr_session_direct_update(lists, op); + } + + return 0; +} + +static int _ncr_session_direct_final(struct ncr_lists* lists, struct ncr_session_op_st* op) +{ + int ret; + struct session_item_st* sess; + struct data_item_st* odata = NULL; + int digest_size; + uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE]; + uint8_t vdigest[NCR_HASH_MAX_OUTPUT_SIZE]; + struct scatterlist *osg; + unsigned osg_cnt=0; + size_t osg_size = 0; + size_t orig_osg_size; + + sess = ncr_sessions_item_get( &lists->sessions, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + ret = try_session_direct_update(lists, op); + if (ret < 0) { + err(); + _ncr_sessions_item_put(sess); + return ret; + } + + if (down_interruptible(&sess->mem_mutex)) { + err(); + _ncr_sessions_item_put(sess); + return -ERESTARTSYS; + } + + switch(sess->op) { + case NCR_OP_ENCRYPT: + case NCR_OP_DECRYPT: + break; + case NCR_OP_VERIFY: + ret = get_userbuf1(sess, op, &osg, &osg_cnt); + if (ret < 0) { + err(); + goto fail; + } + orig_osg_size = osg_size = op->data.udata.output_size; + digest_size = sess->hash.digestsize; if (digest_size == 0 || sizeof(digest) < digest_size) { err(); @@ -633,11 +985,17 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* err(); goto fail; } - if (sess->algorithm->is_hmac) { + ret = sg_copy_to_buffer(osg, osg_cnt, vdigest, digest_size); + if (ret != digest_size) { + err(); + ret = -EINVAL; + goto fail; + } + if (digest_size != odata->data_size || - memcmp(odata->data, digest, digest_size) != 0) { + memcmp(vdigest, digest, digest_size) != 0) { op->err = NCR_VERIFICATION_FAILED; } else { @@ -645,7 +1003,7 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* } } else { /* PK signature */ - ret = ncr_pk_cipher_verify(&sess->pk, odata->data, odata->data_size, + ret = ncr_pk_cipher_verify(&sess->pk, osg, osg_cnt, osg_size, digest, digest_size, &op->err); if (ret < 0) { err(); @@ -655,40 +1013,46 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* break; case NCR_OP_SIGN: - /* obtain data item */ - if (op->input != NCR_DATA_INVALID) { - ret = _ncr_session_update(lists, op); - if (ret < 0) - goto fail; + ret = get_userbuf1(sess, op, &osg, &osg_cnt); + if (ret < 0) { + err(); + goto fail; } - odata = ncr_data_item_get( &lists->data, op->output); - if (odata == NULL) { + orig_osg_size = osg_size = op->data.udata.output_size; + + digest_size = sess->hash.digestsize; + if (digest_size == 0 || osg_size < digest_size) { err(); ret = -EINVAL; goto fail; } - digest_size = sess->hash.digestsize; - if (digest_size == 0 || odata->max_data_size < digest_size) { + ret = cryptodev_hash_final(&sess->hash, digest); + if (ret < 0) { + err(); + goto fail; + } + + ret = sg_copy_from_buffer(osg, osg_cnt, digest, digest_size); + if (ret != digest_size) { err(); ret = -EINVAL; goto fail; } - ret = cryptodev_hash_final(&sess->hash, odata->data); - odata->data_size = digest_size; + osg_size = digest_size; cryptodev_hash_deinit(&sess->hash); if (sess->algorithm->is_pk) { /* PK signature */ - size_t new_size = odata->max_data_size; - ret = ncr_pk_cipher_sign(&sess->pk, odata->data, odata->data_size, - odata->data, &new_size); + + ret = ncr_pk_cipher_sign(&sess->pk, osg, osg_cnt, osg_size, + osg, osg_cnt, &orig_osg_size); if (ret < 0) { err(); goto fail; } - odata->data_size = new_size; + osg_size = orig_osg_size; } break; default: @@ -697,10 +1061,18 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* goto fail; } + if (osg_size > 0) + op->data.udata.output_size = osg_size; + ret = 0; fail: - if (odata) _ncr_data_item_put(odata); + if (sess->available_pages) { + release_user_pages(sess->pages, sess->available_pages); + sess->available_pages = 0; + } + up(&sess->mem_mutex); + cryptodev_hash_deinit(&sess->hash); if (sess->algorithm->is_symmetric) { cryptodev_cipher_deinit(&sess->cipher); @@ -714,6 +1086,37 @@ fail: return ret; } + +int ncr_session_update(struct ncr_lists* lists, void __user* arg) +{ + struct ncr_session_op_st op; + int ret; + + if (unlikely(copy_from_user( &op, arg, sizeof(op)))) { + err(); + return -EFAULT; + } + + if (op.type == NCR_DIRECT_DATA) + ret = _ncr_session_direct_update(lists, &op); + else if (op.type == NCR_DATA) + ret = _ncr_session_update(lists, &op); + else + ret = -EINVAL; + + if (unlikely(ret)) { + err(); + return ret; + } + + if (unlikely(copy_to_user(arg, &op, sizeof(op)))) { + err(); + return -EFAULT; + } + + return 0; +} + int ncr_session_final(struct ncr_lists* lists, void __user* arg) { struct ncr_session_op_st op; @@ -724,7 +1127,14 @@ int ncr_session_final(struct ncr_lists* lists, void __user* arg) return -EFAULT; } - ret = _ncr_session_final(lists, &op); + if (op.type == NCR_DATA) { + ret = _ncr_session_final(lists, &op); + } else if (op.type == NCR_DIRECT_DATA) { + ret = _ncr_session_direct_final(lists, &op); + } else { + ret = -EINVAL; + } + if (unlikely(ret)) { err(); return ret; @@ -754,7 +1164,13 @@ int ncr_session_once(struct ncr_lists* lists, void __user* arg) } kop.op.ses = kop.init.ses; - ret = _ncr_session_final(lists, &kop.op); + if (kop.op.type == NCR_DIRECT_DATA) + ret = _ncr_session_direct_final(lists, &kop.op); + else if (kop.op.type == NCR_DATA) + ret = _ncr_session_final(lists, &kop.op); + else + ret = -EINVAL; + if (ret < 0) { err(); return ret; @@ -764,4 +1180,3 @@ int ncr_session_once(struct ncr_lists* lists, void __user* arg) return -EFAULT; return 0; } - diff --git a/ncr.c b/ncr.c index 7014a30..4cc7ef3 100644 --- a/ncr.c +++ b/ncr.c @@ -161,6 +161,7 @@ ncr_ioctl(struct ncr_lists* lst, struct file *filp, return ncr_session_final(lst, arg); case NCRIO_SESSION_ONCE: return ncr_session_once(lst, arg); + case NCRIO_MASTER_KEY_SET: return ncr_master_key_set(arg); case NCRIO_KEY_GENERATE_PAIR: diff --git a/ncr.h b/ncr.h index d3f4813..d666628 100644 --- a/ncr.h +++ b/ncr.h @@ -276,14 +276,30 @@ typedef enum { NCR_VERIFICATION_FAILED = -2, } ncr_error_t; +typedef enum { + NCR_DATA, + NCR_DIRECT_DATA, +} ncr_data_type_t; + struct ncr_session_op_st { /* input */ ncr_session_t ses; - ncr_data_t input; - ncr_data_t output; /* when verifying signature this is - * the place of the signature. - */ + union { + struct { + ncr_data_t input; + ncr_data_t output; /* when verifying signature this is + * the place of the signature. + */ + } ndata; + struct { + void* input; + size_t input_size; + void* output; + size_t output_size; + } udata; + } data; + ncr_data_type_t type; /* output of verification */ ncr_error_t err; @@ -296,7 +312,7 @@ struct ncr_session_once_op_st { #define NCRIO_SESSION_INIT _IOR ('c', 300, struct ncr_session_st) #define NCRIO_SESSION_UPDATE _IOWR ('c', 301, struct ncr_session_op_st) -#define NCRIO_SESSION_FINAL _IOR ('c', 302, struct ncr_session_op_st) +#define NCRIO_SESSION_FINAL _IOWR ('c', 302, struct ncr_session_op_st) /* everything in one call */ #define NCRIO_SESSION_ONCE _IOWR ('c', 303, struct ncr_session_once_op_st) diff --git a/ncr_int.h b/ncr_int.h index fadb46e..cbfe58a 100644 --- a/ncr_int.h +++ b/ncr_int.h @@ -41,6 +41,14 @@ struct session_item_st { struct ncr_pk_ctx pk; struct hash_data hash; + struct scatterlist *sg; + struct page **pages; + unsigned array_size; + unsigned available_pages; + struct semaphore mem_mutex; /* down when the + * values above are changed. + */ + struct key_item_st* key; atomic_t refcnt; @@ -53,6 +61,7 @@ struct data_item_st { * I see no reason to allow concurrent writes (reads are * not an issue). */ + struct scatterlist sg; /* points to data */ uint8_t* data; size_t data_size; -- cgit