diff options
-rw-r--r-- | crypto.4 | 15 | ||||
-rw-r--r-- | cryptodev_cipher.c | 46 | ||||
-rw-r--r-- | cryptodev_int.h | 4 | ||||
-rw-r--r-- | examples/Makefile | 2 | ||||
-rw-r--r-- | examples/ncr.c | 232 | ||||
-rw-r--r-- | examples/pk.c | 20 | ||||
-rw-r--r-- | libtomcrypt/hashes/hash_memory.c | 2 | ||||
-rw-r--r-- | libtomcrypt/hashes/hash_memory_multi.c | 2 | ||||
-rw-r--r-- | ncr-int.h | 31 | ||||
-rw-r--r-- | ncr-key-wrap.c | 225 | ||||
-rw-r--r-- | ncr-key.c | 74 | ||||
-rw-r--r-- | ncr-sessions.c | 434 | ||||
-rw-r--r-- | ncr.h | 8 | ||||
-rw-r--r-- | utils.c | 1 |
14 files changed, 810 insertions, 286 deletions
@@ -431,14 +431,10 @@ members. The following input attributes are recognized: .RS -.IP \fBNCR_ATTR_ALGORITHM\fP -Mandatory. .IP \fBNCR_ATTR_IV\fP Optional, an empty IV is used if not present. .IP \fBNCR_ATTR_KEY_FLAGS\fP Optional, flags are unchanged if not present. -.IP \fBNCR_ATTR_KEY_TYPE\fP -Mandatory. .IP \fBNCR_ATTR_WRAPPING_ALGORITHM\fP Mandatory. .RE @@ -520,13 +516,18 @@ The following input attributes are recognized: .RS .IP \fBNCR_ATTR_ALGORITHM\fP -Mandatory. +Mandatory unless +.B NCR_ATTR_SESSION_CLONE_FROM +is provided. .IP \fBNCR_ATTR_IV\fP Mandatory for some operations and algorithms. .IP \fBNCR_ATTR_KEY\fP Mandatory for some operations and algorithms. An 32-bit unsigned integer in native byte order specifying the key to use for the operation. +If +.B NCR_ATTR_SESSION_CLONE_FROM +is provided, the key from the original session is used. .IP \fBNCR_ATTR_RSA_ENCODING_METHOD\fP Mandatory for RSA. An 32-bit unsigned integer in native byte order @@ -543,6 +544,10 @@ For RSA with \fBRSA_PKCS1_PSS\fP. An 32-bit unsigned integer in native byte order specifying the PSS salt length. Optional, defaults to 0. +.IP \fBNCR_ATTR_SESSION_CLONE_FROM\fP +Optional, a 32-bit unsigned integer in native byte order +specifying session state to clone. +Only supported for some operations and algorithms. .IP \fBNCR_ATTR_SIGNATURE_HASH_ALGORITHM\fP Mandatory for some operations and algorithms. A NUL-terminated string specifying a hash algorithm underlying a signature, diff --git a/cryptodev_cipher.c b/cryptodev_cipher.c index 4e74fcc39d0..1fb11473715 100644 --- a/cryptodev_cipher.c +++ b/cryptodev_cipher.c @@ -216,7 +216,7 @@ ssize_t cryptodev_cipher_decrypt( struct cipher_data* cdata, const struct scatte /* Hash functions */ -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void * mackey, size_t mackeylen) +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen) { int ret; @@ -228,7 +228,7 @@ int ret; } /* Copy the key from user and set to TFM. */ - if (hmac_mode != 0) { + if (mackey != NULL) { ret = crypto_ahash_setkey(hdata->async.s, mackey, mackeylen); if (unlikely(ret)) { @@ -278,6 +278,48 @@ error: return ret; } +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen) +{ + const char *algo; + void *state; + int ret; + + /* We want exactly the same driver. */ + algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(old_data->async.s)); + ret = cryptodev_hash_init(hdata, algo, mackey, mackeylen); + if (unlikely(ret != 0)) + return ret; + + state = kmalloc(crypto_ahash_statesize(hdata->async.s), GFP_KERNEL); + if (unlikely(state == NULL)) { + ret = -ENOMEM; + goto err; + } + + ret = crypto_ahash_export(old_data->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0, KERN_ERR, "error exporting hash state\n"); + goto err; + } + ret = crypto_ahash_import(hdata->async.request, state); + if (unlikely(ret != 0)) { + dprintk(0,KERN_ERR, + "error in crypto_hash_init()\n"); + goto err; + } + + kfree(state); + + hdata->init = 1; + return 0; + +err: + kfree(state); + cryptodev_hash_deinit(hdata); + return ret; +} + void cryptodev_hash_deinit(struct hash_data* hdata) { if (hdata->init) { diff --git a/cryptodev_int.h b/cryptodev_int.h index e38150545b0..4b140ba9820 100644 --- a/cryptodev_int.h +++ b/cryptodev_int.h @@ -77,6 +77,8 @@ ssize_t cryptodev_hash_update( struct hash_data* hdata, struct scatterlist *sg, ssize_t _cryptodev_hash_update( struct hash_data* hdata, const void* data, size_t len); int cryptodev_hash_reset( struct hash_data* hdata); void cryptodev_hash_deinit(struct hash_data* hdata); -int cryptodev_hash_init( struct hash_data* hdata, const char* alg_name, int hmac_mode, void* mackey, size_t mackeylen); +int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name, const void *mackey, size_t mackeylen); +int cryptodev_hash_clone(struct hash_data *hdata, struct hash_data *old_data, + const void *mackey, size_t mackeylen); #endif /* CRYPTODEV_INT_H */ diff --git a/examples/Makefile b/examples/Makefile index d14908866bb..9911100263e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -20,4 +20,4 @@ check: $(progs) ./speed clean: - rm -f *.o *~ ncr pk speed + rm -f *.o *~ $(progs) diff --git a/examples/ncr.c b/examples/ncr.c index a7d34f0e881..9691fea5dc1 100644 --- a/examples/ncr.c +++ b/examples/ncr.c @@ -375,10 +375,6 @@ test_ncr_wrap_key(int cfd) struct ncr_key_unwrap f; struct nlattr wrap_algo_head ALIGN_NL; char wrap_algo[sizeof(NCR_WALG_AES_RFC3394)] ALIGN_NL; - struct nlattr algo_head ALIGN_NL; - char algo[sizeof(ALG_AES_CBC)] ALIGN_NL; - struct nlattr type_head ALIGN_NL; - uint32_t type ALIGN_NL; struct nlattr flags_head ALIGN_NL; uint32_t flags ALIGN_NL; } kunwrap; @@ -417,7 +413,7 @@ test_ncr_wrap_key(int cfd) strcpy(kimport.algo, ALG_AES_CBC); kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; - kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; ret = ioctl(cfd, NCRIO_KEY_IMPORT, &kimport); if (geteuid() == 0 && ret) { @@ -518,12 +514,6 @@ test_ncr_wrap_key(int cfd) kunwrap.wrap_algo_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.wrap_algo); kunwrap.wrap_algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; strcpy(kunwrap.wrap_algo, NCR_WALG_AES_RFC3394); - kunwrap.algo_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.algo); - kunwrap.algo_head.nla_type = NCR_ATTR_ALGORITHM; - strcpy(kunwrap.algo, ALG_AES_CBC); - kunwrap.type_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.type); - kunwrap.type_head.nla_type = NCR_ATTR_KEY_TYPE; - kunwrap.type = NCR_KEY_TYPE_SECRET; kunwrap.flags_head.nla_len = NLA_HDRLEN + sizeof(kunwrap.flags); kunwrap.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; kunwrap.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE; @@ -629,7 +619,7 @@ test_ncr_wrap_key2(int cfd) strcpy(kimport.algo, ALG_AES_CBC); kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; - kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); @@ -1237,6 +1227,221 @@ test_ncr_hash(int cfd) } static int +test_ncr_hash_clone(int cfd) +{ + ncr_key_t key; + struct __attribute__((packed)) { + struct ncr_key_import f; + struct nlattr id_head ALIGN_NL; + uint8_t id[2] ALIGN_NL; + struct nlattr type_head ALIGN_NL; + uint32_t type ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kimport; + uint8_t data[HASH_DATA_SIZE]; + const struct hash_vectors_st *hv; + int j; + size_t data_size; + struct __attribute__((packed)) { + struct ncr_session_init f; + struct nlattr key_head ALIGN_NL; + uint32_t key ALIGN_NL; + struct nlattr algo_head ALIGN_NL; + char algo[128] ALIGN_NL; + } kinit; + struct __attribute__((packed)) { + struct ncr_session_update f; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + } kupdate; + struct __attribute__((packed)) { + struct ncr_session_final f; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } kfinal; + struct __attribute__((packed)) { + struct ncr_session_once f; + struct nlattr clone_head ALIGN_NL; + uint32_t clone ALIGN_NL; + struct nlattr input_head ALIGN_NL; + struct ncr_session_input_data input ALIGN_NL; + struct nlattr output_head ALIGN_NL; + struct ncr_session_output_buffer output ALIGN_NL; + } kclone; + ncr_session_t ses; + + /* convert it to key */ + key = ioctl(cfd, NCRIO_KEY_INIT); + if (key == -1) { + perror("ioctl(NCRIO_KEY_INIT)"); + return 1; + } + + fprintf(stdout, "Tests of hash cloning\n"); + for (hv = hash_vectors; + hv < hash_vectors + sizeof(hash_vectors) / sizeof(hash_vectors[0]); + hv++) { + size_t algo_size; + + algo_size = strlen(hv->algorithm) + 1; + fprintf(stdout, "\t%s:\n", hv->algorithm); + /* import key */ + if (hv->key != NULL) { + + memset(&kimport.f, 0, sizeof(kimport.f)); + kimport.f.key = key; + kimport.f.data = hv->key; + kimport.f.data_size = hv->key_size; + kimport.id_head.nla_len + = NLA_HDRLEN + sizeof(kimport.id); + kimport.id_head.nla_type = NCR_ATTR_KEY_ID; + kimport.id[0] = 'a'; + kimport.id[1] = 'b'; + kimport.type_head.nla_len + = NLA_HDRLEN + sizeof(kimport.type); + kimport.type_head.nla_type = NCR_ATTR_KEY_TYPE; + kimport.type = NCR_KEY_TYPE_SECRET; + kimport.flags_head.nla_len + = NLA_HDRLEN + sizeof(kimport.flags); + kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE; + kimport.algo_head.nla_len = NLA_HDRLEN + algo_size; + kimport.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kimport.algo, hv->algorithm, algo_size); + kimport.f.input_size + = kimport.algo + algo_size - (char *)&kimport; + if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_KEY_IMPORT)"); + return 1; + } + } + + /* Initialize a session */ + memset(&kinit.f, 0, sizeof(kinit.f)); + kinit.f.op = hv->op; + kinit.key_head.nla_len = NLA_HDRLEN + sizeof(kinit.key); + kinit.key_head.nla_type = NCR_ATTR_KEY; + kinit.key = hv->key != NULL ? key : NCR_KEY_INVALID; + kinit.algo_head.nla_len = NLA_HDRLEN + algo_size; + kinit.algo_head.nla_type = NCR_ATTR_ALGORITHM; + memcpy(kinit.algo, hv->algorithm, algo_size); + kinit.f.input_size = kinit.algo + algo_size - (char *)&kinit; + + ses = ioctl(cfd, NCRIO_SESSION_INIT, &kinit); + if (ses < 0) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_INIT)"); + return 1; + } + + /* Submit half of the data */ + memset(&kupdate.f, 0, sizeof(kupdate.f)); + kupdate.f.input_size = sizeof(kupdate); + kupdate.f.ses = ses; + kupdate.input_head.nla_len = NLA_HDRLEN + sizeof(kupdate.input); + kupdate.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kupdate.input.data = hv->plaintext; + kupdate.input.data_size = hv->plaintext_size / 2; + + if (ioctl(cfd, NCRIO_SESSION_UPDATE, &kupdate)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_UPDATE)"); + return 1; + } + + /* Clone a session, submit the other half, verify. */ + memset(&kclone.f, 0, sizeof(kclone.f)); + kclone.f.input_size = sizeof(kclone); + kclone.f.op = hv->op; + kclone.clone_head.nla_len = NLA_HDRLEN + sizeof(kclone.clone); + kclone.clone_head.nla_type = NCR_ATTR_SESSION_CLONE_FROM; + kclone.clone = ses; + kclone.input_head.nla_len = NLA_HDRLEN + sizeof(kclone.input); + kclone.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kclone.input.data = hv->plaintext + hv->plaintext_size / 2; + kclone.input.data_size + = hv->plaintext_size - hv->plaintext_size / 2; + kclone.output_head.nla_len = NLA_HDRLEN + sizeof(kclone.output); + kclone.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + kclone.output.buffer = data; + kclone.output.buffer_size = sizeof(data); + kclone.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_ONCE, &kclone)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_ONCE)"); + return 1; + } + + if (data_size != hv->output_size + || memcmp(data, hv->output, hv->output_size) != 0) { + fprintf(stderr, "HASH test vector %td failed!\n", + hv - hash_vectors); + + fprintf(stderr, "Output[%zu]: ", data_size); + for(j = 0; j < data_size; j++) + fprintf(stderr, "%.2x:", (int)data[j]); + fprintf(stderr, "\n"); + + fprintf(stderr, "Expected[%d]: ", hv->output_size); + for (j = 0; j < hv->output_size; j++) + fprintf(stderr, "%.2x:", (int)hv->output[j]); + fprintf(stderr, "\n"); + return 1; + } + + /* Submit the other half to the original session, verify. */ + memset(&kfinal.f, 0, sizeof(kfinal.f)); + kfinal.f.input_size = sizeof(kfinal); + kfinal.f.ses = ses; + kfinal.input_head.nla_len = NLA_HDRLEN + sizeof(kfinal.input); + kfinal.input_head.nla_type = NCR_ATTR_UPDATE_INPUT_DATA; + kfinal.input.data = hv->plaintext + hv->plaintext_size / 2; + kfinal.input.data_size + = hv->plaintext_size - hv->plaintext_size / 2; + kfinal.output_head.nla_len = NLA_HDRLEN + sizeof(kfinal.output); + kfinal.output_head.nla_type = NCR_ATTR_FINAL_OUTPUT_BUFFER; + kfinal.output.buffer = data; + kfinal.output.buffer_size = sizeof(data); + kfinal.output.result_size_ptr = &data_size; + + if (ioctl(cfd, NCRIO_SESSION_FINAL, &kfinal)) { + fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); + perror("ioctl(NCRIO_SESSION_FINAL)"); + return 1; + } + + if (data_size != hv->output_size + || memcmp(data, hv->output, hv->output_size) != 0) { + fprintf(stderr, "HASH test vector %td failed!\n", + hv - hash_vectors); + + fprintf(stderr, "Output[%zu]: ", data_size); + for(j = 0; j < data_size; j++) + fprintf(stderr, "%.2x:", (int)data[j]); + fprintf(stderr, "\n"); + + fprintf(stderr, "Expected[%d]: ", hv->output_size); + for (j = 0; j < hv->output_size; j++) + fprintf(stderr, "%.2x:", (int)hv->output[j]); + fprintf(stderr, "\n"); + return 1; + } + } + + fprintf(stdout, "\n"); + + return 0; + +} + +static int test_ncr_hash_key(int cfd) { ncr_key_t key; @@ -1415,6 +1620,9 @@ main() if (test_ncr_hash(fd)) return 1; + if (test_ncr_hash_clone(fd)) + return 1; + if (test_ncr_hash_key(fd)) return 1; diff --git a/examples/pk.c b/examples/pk.c index 6acbadf12b0..81c5b49263d 100644 --- a/examples/pk.c +++ b/examples/pk.c @@ -624,10 +624,8 @@ test_ncr_wrap_key3(int cfd) struct ncr_key_unwrap f; struct nlattr wrap_algo_head ALIGN_NL; char wrap_algo[sizeof(NCR_WALG_AES_RFC5649)] ALIGN_NL; - struct nlattr algo_head ALIGN_NL; - char algo[sizeof(ALG_RSA)] ALIGN_NL; - struct nlattr type_head ALIGN_NL; - uint32_t type ALIGN_NL; + struct nlattr flags_head ALIGN_NL; + uint32_t flags ALIGN_NL; } kunwrap; struct __attribute__((packed)) { struct ncr_key_generate_pair f; @@ -694,7 +692,7 @@ test_ncr_wrap_key3(int cfd) strcpy(kimport.algo, ALG_AES_CBC); kimport.flags_head.nla_len = NLA_HDRLEN + sizeof(kimport.flags); kimport.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; - kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING; + kimport.flags = NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPING|NCR_KEY_FLAG_UNWRAPPING; if (ioctl(cfd, NCRIO_KEY_IMPORT, &kimport)) { fprintf(stderr, "Error: %s:%d\n", __func__, __LINE__); @@ -782,14 +780,10 @@ test_ncr_wrap_key3(int cfd) kunwrap.wrap_algo_head.nla_type = NCR_ATTR_WRAPPING_ALGORITHM; strcpy(kunwrap.wrap_algo, NCR_WALG_AES_RFC5649); - kunwrap.algo_head.nla_len - = NLA_HDRLEN + sizeof(kunwrap.algo); - kunwrap.algo_head.nla_type = NCR_ATTR_ALGORITHM; - strcpy(kunwrap.algo, ALG_RSA); - kunwrap.type_head.nla_len - = NLA_HDRLEN + sizeof(kunwrap.type); - kunwrap.type_head.nla_type = NCR_ATTR_KEY_TYPE; - kunwrap.type = NCR_KEY_TYPE_PRIVATE; + kunwrap.flags_head.nla_len + = NLA_HDRLEN + sizeof(kunwrap.flags); + kunwrap.flags_head.nla_type = NCR_ATTR_KEY_FLAGS; + kunwrap.flags = 0; ret = ioctl(cfd, NCRIO_KEY_UNWRAP, &kunwrap); if (ret) { diff --git a/libtomcrypt/hashes/hash_memory.c b/libtomcrypt/hashes/hash_memory.c index a416de9624e..c6f51881245 100644 --- a/libtomcrypt/hashes/hash_memory.c +++ b/libtomcrypt/hashes/hash_memory.c @@ -44,7 +44,7 @@ int hash_memory(const struct algo_properties_st *hash, const unsigned char *in, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/libtomcrypt/hashes/hash_memory_multi.c b/libtomcrypt/hashes/hash_memory_multi.c index a9149166700..74226767a72 100644 --- a/libtomcrypt/hashes/hash_memory_multi.c +++ b/libtomcrypt/hashes/hash_memory_multi.c @@ -50,7 +50,7 @@ int hash_memory_multi(const struct algo_properties_st *hash, unsigned char *out, return CRYPT_BUFFER_OVERFLOW; } - err = cryptodev_hash_init( &hdata, hash->kstr, 0, NULL, 0); + err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; diff --git a/ncr-int.h b/ncr-int.h index 5e86aff5641..a6b37397c5f 100644 --- a/ncr-int.h +++ b/ncr-int.h @@ -56,32 +56,6 @@ struct algo_properties_st { ncr_key_type_t key_type; }; -struct session_item_st { - const struct algo_properties_st *algorithm; - ncr_crypto_op_t op; - - /* contexts for various options. - * simpler to have them like that than - * in a union. - */ - struct cipher_data cipher; - struct ncr_pk_ctx pk; - struct hash_data hash; - - struct scatterlist *sg; - struct page **pages; - unsigned array_size; - unsigned available_pages; - struct mutex mem_mutex; /* down when the - * values above are changed. - */ - - struct key_item_st* key; - - atomic_t refcnt; - ncr_session_t desc; -}; - struct key_item_st { /* This object is also not protected from concurrent access. */ @@ -137,7 +111,7 @@ int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, struct nlattr *tb[]); void ncr_key_clear(struct key_item_st* item); -int ncr_key_assign_flags(struct key_item_st *item, unsigned int flags); +int ncr_key_update_flags(struct key_item_st *item, const struct nlattr *nla); /* key handling */ int ncr_key_init(struct ncr_lists *lst); @@ -186,9 +160,6 @@ int ncr_key_storage_unwrap(struct ncr_lists *lst, struct nlattr *tb[]); /* sessions */ -struct session_item_st* ncr_session_new(struct ncr_lists *lst); -void _ncr_sessions_item_put( struct session_item_st* item); -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc); void ncr_sessions_list_deinit(struct ncr_lists *lst); int ncr_session_init(struct ncr_lists *lists, diff --git a/ncr-key-wrap.c b/ncr-key-wrap.c index c409bb93fda..7ea70cfc847 100644 --- a/ncr-key-wrap.c +++ b/ncr-key-wrap.c @@ -34,6 +34,8 @@ #include "ncr-int.h" #include "cryptodev_int.h" +#define KEY_WRAP_VERSION 0 + typedef uint8_t val64_t[8]; static const val64_t initA = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6"; @@ -264,7 +266,7 @@ static int wrap_aes_rfc5649(struct key_item_st* tobewrapped, struct key_item_st void* output, size_t* output_size, const void* iv, size_t iv_size) { int ret; -uint8_t* sdata; +uint8_t* sdata = NULL; size_t sdata_size = 0; ret = key_to_packed_data(&sdata, &sdata_size, tobewrapped); @@ -329,7 +331,7 @@ fail: /* Wraps using the RFC3394 way. */ -static int wrap_aes(struct key_item_st* tobewrapped, struct key_item_st *kek, +static int wrap_aes_rfc3394(struct key_item_st* tobewrapped, struct key_item_st *kek, void* output, size_t *output_size, const void* iv, size_t iv_size) { size_t key_size, n; @@ -471,13 +473,10 @@ const uint8_t *iv; goto cleanup; } - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(output, nla_get_u32(nla)); - if (ret != 0) { - err(); - goto cleanup; - } + ret = ncr_key_update_flags(output, tb[NCR_ATTR_KEY_FLAGS]); + if (ret != 0) { + err(); + goto cleanup; } memset(&output->key, 0, sizeof(output->key)); @@ -499,7 +498,7 @@ cleanup: /* will check if the kek is of equal or higher security level than * wkey. To prevent encrypting a 256 bit key with an 128 bit one. */ -int check_key_level(struct key_item_st* kek, struct key_item_st* wkey) +static int check_key_level(struct key_item_st* kek, struct key_item_st* wkey) { int kek_level, wkey_level; @@ -597,7 +596,8 @@ int ret; goto fail; } if (nla_strcmp(nla, NCR_WALG_AES_RFC3394) == 0) - ret = wrap_aes(wkey, key, data, &data_size, iv, iv_size); + ret = wrap_aes_rfc3394(wkey, key, data, &data_size, iv, + iv_size); else if (nla_strcmp(nla, NCR_WALG_AES_RFC5649) == 0) ret = wrap_aes_rfc5649(wkey, key, data, &data_size, iv, iv_size); @@ -652,7 +652,7 @@ int ret; goto fail; } - if (!(key->flags & NCR_KEY_FLAG_WRAPPING)) { + if (!(key->flags & NCR_KEY_FLAG_UNWRAPPING)) { err(); ret = -EPERM; goto fail; @@ -826,11 +826,27 @@ fail: return ret; } +#define DER_KEY_MAX_SIZE (KEY_DATA_MAX_SIZE+16) + +/* Packed data are DER encoded: + * PackedData ::= SEQUENCE { + * version INTEGER { v1(0) } + * type INTEGER { secret_key(0), rsa_privkey(1), rsa_pubkey(2), dsa_privkey(3), dsa_pubkey(4), + * dh_privkey(5), dh_pubkey(6) }, + * data OCTET STRING + * } + * + * This allows distinguishing types of wrapped keys. + */ static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struct key_item_st *key) { - uint8_t * pkey; - uint32_t usize; - int ret; + uint8_t * pkey = NULL; + uint8_t * derkey = NULL; + uint32_t pkey_size; + int ret, err; + unsigned long version = KEY_WRAP_VERSION; + unsigned long type; + unsigned long derlen; *sdata_size = KEY_DATA_MAX_SIZE; pkey = kmalloc(*sdata_size, GFP_KERNEL); @@ -839,63 +855,185 @@ static int key_to_packed_data( uint8_t** sdata, size_t * sdata_size, const struc return -ENOMEM; } + derlen = DER_KEY_MAX_SIZE; + derkey = kmalloc(derlen, GFP_KERNEL); + if (derkey == NULL) { + err(); + goto fail; + } + if (key->type == NCR_KEY_TYPE_SECRET) { memcpy(pkey, key->key.secret.data, key->key.secret.size); - *sdata_size = key->key.secret.size; + pkey_size = key->key.secret.size; + + type = 0; } else if (key->type == NCR_KEY_TYPE_PRIVATE || key->type == NCR_KEY_TYPE_PUBLIC) { - usize = *sdata_size; - ret = ncr_pk_pack( key, pkey, &usize); + pkey_size = *sdata_size; + ret = ncr_pk_pack( key, pkey, &pkey_size); if (ret < 0) { err(); goto fail; } - *sdata_size = usize; + + switch (key->algorithm->algo) { + case NCR_ALG_RSA: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 2; + else type = 1; + break; + case NCR_ALG_DSA: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 4; + else type = 3; + break; + case NCR_ALG_DH: + if (key->type == NCR_KEY_TYPE_PUBLIC) + type = 6; + else type = 5; + break; + default: + /* unsupported yet */ + ret = -EINVAL; + err(); + goto fail; + } + } else { err(); ret = -EINVAL; goto fail; } - *sdata = (void*)pkey; + err = der_encode_sequence_multi(derkey, &derlen, + LTC_ASN1_SHORT_INTEGER, 1UL, &version, + LTC_ASN1_SHORT_INTEGER, 1UL, &type, + LTC_ASN1_OCTET_STRING, (unsigned long)pkey_size, pkey, + LTC_ASN1_EOL, 0UL, NULL); + + kfree(pkey); + + if (err != CRYPT_OK) { + err(); + ret = _ncr_tomerr(err); + goto fail; + } + + *sdata = (void*)derkey; + *sdata_size = derlen; return 0; fail: kfree(pkey); + kfree(derkey); return ret; } +inline static int packed_type_to_key_type(unsigned long type, struct key_item_st* key) +{ + switch(type) { + case 0: + key->type = NCR_KEY_TYPE_SECRET; + key->algorithm = _ncr_algo_to_properties("cbc(aes)"); + break; + case 1: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("rsa"); + break; + case 2: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("rsa"); + break; + case 3: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("dsa"); + break; + case 4: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("dsa"); + break; + case 5: + key->type = NCR_KEY_TYPE_PRIVATE; + key->algorithm = _ncr_algo_to_properties("dh"); + break; + case 6: + key->type = NCR_KEY_TYPE_PUBLIC; + key->algorithm = _ncr_algo_to_properties("dh"); + break; + default: + err(); + return -EINVAL; + } + + if (key->algorithm == NULL) { + err(); + return -EINVAL; + } + + return 0; +} + + + +/* Unpack, or better decode the DER data + */ static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key, const void *data, size_t data_size) { - const struct nlattr *nla; - int ret; + ltc_asn1_list list[6]; + int ret, i = 0, pkey_size, err; + unsigned long version, type; + uint8_t * pkey = NULL; - if (data_size > KEY_DATA_MAX_SIZE) { + if (data_size > DER_KEY_MAX_SIZE) { err(); return -EINVAL; } - key->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); - if (key->algorithm == NULL) { + pkey_size = KEY_DATA_MAX_SIZE; + pkey = kmalloc(pkey_size, GFP_KERNEL); + if (pkey == NULL) { err(); - return -EINVAL; + return -ENOMEM; } - nla = tb[NCR_ATTR_KEY_TYPE]; - if (tb == NULL) { + list[i].type = LTC_ASN1_SHORT_INTEGER; + list[i].size = 1; + list[i++].data = &version; + + list[i].type = LTC_ASN1_SHORT_INTEGER; + list[i].size = 1; + list[i++].data = &type; + + list[i].type = LTC_ASN1_OCTET_STRING; + list[i].size = pkey_size; + list[i++].data = pkey; + + err = der_decode_sequence(data, data_size, list, i); + if (err != CRYPT_OK) { err(); - return -EINVAL; + ret = _ncr_tomerr(err); + goto fail; } - key->type = nla_get_u32(nla); + + if (version != KEY_WRAP_VERSION) { + err(); + ret = -EINVAL; + goto fail; + } + + pkey_size = list[2].size; - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(key, nla_get_u32(nla)); - if (ret != 0) { - err(); - return ret; - } + ret = packed_type_to_key_type(type, key); + if (ret < 0) { + err(); + goto fail; + } + + ret = ncr_key_update_flags(key, tb[NCR_ATTR_KEY_FLAGS]); + if (ret != 0) { + err(); + return ret; } if (key->type == NCR_KEY_TYPE_SECRET) { @@ -903,12 +1041,12 @@ static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key, err(); return -EINVAL; } - key->key.secret.size = data_size; - memcpy(key->key.secret.data, data, data_size); + key->key.secret.size = pkey_size; + memcpy(key->key.secret.data, pkey, pkey_size); } else if (key->type == NCR_KEY_TYPE_PUBLIC || key->type == NCR_KEY_TYPE_PRIVATE) { - ret = ncr_pk_unpack( key, data, data_size); + ret = ncr_pk_unpack( key, pkey, pkey_size); if (ret < 0) { err(); return ret; @@ -918,5 +1056,10 @@ static int key_from_packed_data(struct nlattr *tb[], struct key_item_st *key, return -EINVAL; } - return 0; + ret = 0; + +fail: + kfree(pkey); + + return ret; } diff --git a/ncr-key.c b/ncr-key.c index 4942bc451d0..406e03299c3 100644 --- a/ncr-key.c +++ b/ncr-key.c @@ -287,9 +287,15 @@ fail: } -int ncr_key_assign_flags(struct key_item_st* item, unsigned int flags) +int ncr_key_update_flags(struct key_item_st* item, const struct nlattr *nla) { - if (!capable(CAP_SYS_ADMIN) && (flags & NCR_KEY_FLAG_WRAPPING) != 0) + uint32_t flags; + + if (nla == NULL) + return 0; + flags = nla_get_u32(nla); + if (!capable(CAP_SYS_ADMIN) + && (flags & (NCR_KEY_FLAG_WRAPPING | NCR_KEY_FLAG_UNWRAPPING)) != 0) return -EPERM; item->flags = flags; return 0; @@ -341,13 +347,10 @@ size_t tmp_size; goto fail; } - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(item, nla_get_u32(nla)); - if (ret < 0) { - err(); - goto fail; - } + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; } nla = tb[NCR_ATTR_KEY_ID]; @@ -434,13 +437,10 @@ size_t size; ncr_key_clear(item); /* we generate only secret keys */ - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(item, nla_get_u32(nla)); - if (ret < 0) { - err(); - goto fail; - } + ret = ncr_key_update_flags(item, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; } algo = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); @@ -495,7 +495,7 @@ fail: * Keysizes (2009-2010)". It maps the strength of public key algorithms to * symmetric ones. Should be kept up to date. */ -struct { +static const struct { unsigned int bits; /* sec level */ unsigned int rsa_bits; unsigned int dlog_bits; @@ -510,7 +510,7 @@ struct { {0,0,0} }; -unsigned int rsa_to_bits(unsigned int rsa_bits) +static unsigned int rsa_to_bits(unsigned int rsa_bits) { int i = 1; @@ -529,7 +529,7 @@ int i = 1; return ecrypt_vals[i-1].bits; } -unsigned int dlog_to_bits(unsigned int dlog_bits) +static unsigned int dlog_to_bits(unsigned int dlog_bits) { int i = 1; @@ -662,7 +662,6 @@ int ncr_key_generate_pair(struct ncr_lists *lst, const struct ncr_key_generate_pair *gen, struct nlattr *tb[]) { -const struct nlattr *nla; struct key_item_st* private = NULL; struct key_item_st* public = NULL; int ret; @@ -692,18 +691,15 @@ int ret; } public->type = public->algorithm->key_type; private->type = NCR_KEY_TYPE_PRIVATE; - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(private, nla_get_u32(nla)); - if (ret < 0) { - err(); - goto fail; - } - ret = ncr_key_assign_flags(public, nla_get_u32(nla)); - if (ret < 0) { - err(); - goto fail; - } + ret = ncr_key_update_flags(private, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; + } + ret = ncr_key_update_flags(public, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; } public->flags |= (NCR_KEY_FLAG_EXPORTABLE|NCR_KEY_FLAG_WRAPPABLE); @@ -736,7 +732,6 @@ fail: int ncr_key_derive(struct ncr_lists *lst, const struct ncr_key_derive *data, struct nlattr *tb[]) { -const struct nlattr *nla; int ret; struct key_item_st* key = NULL; struct key_item_st* newkey = NULL; @@ -749,7 +744,7 @@ struct key_item_st* newkey = NULL; /* wrapping keys cannot be used for anything except wrapping. */ - if (key->flags & NCR_KEY_FLAG_WRAPPING) { + if (key->flags & NCR_KEY_FLAG_WRAPPING || key->flags & NCR_KEY_FLAG_UNWRAPPING) { err(); ret = -EINVAL; goto fail; @@ -763,13 +758,10 @@ struct key_item_st* newkey = NULL; ncr_key_clear(newkey); - nla = tb[NCR_ATTR_KEY_FLAGS]; - if (nla != NULL) { - ret = ncr_key_assign_flags(newkey, nla_get_u32(nla)); - if (ret < 0) { - err(); - goto fail; - } + ret = ncr_key_update_flags(newkey, tb[NCR_ATTR_KEY_FLAGS]); + if (ret < 0) { + err(); + goto fail; } switch (key->type) { diff --git a/ncr-sessions.c b/ncr-sessions.c index 460df6f637c..3691127a757 100644 --- a/ncr-sessions.c +++ b/ncr-sessions.c @@ -31,9 +31,35 @@ #include <linux/scatterlist.h> #include <net/netlink.h> -static int _ncr_session_update_key(struct ncr_lists *lists, ncr_session_t ses, +struct session_item_st { + atomic_t refcnt; + /* Constant values throughout the life of this object follow. */ + ncr_session_t desc; + const struct algo_properties_st *algorithm; + ncr_crypto_op_t op; + struct key_item_st *key; + + /* Variable values, protected usually by mutex, follow. */ + struct mutex mutex; + + /* contexts for various options. + * simpler to have them like that than + * in a union. + */ + struct cipher_data cipher; + struct ncr_pk_ctx pk; + struct hash_data hash; + + struct scatterlist *sg; + struct page **pages; + unsigned array_size; + unsigned available_pages; +}; + +static void _ncr_sessions_item_put(struct session_item_st *item); +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[]); -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc); static int session_list_deinit_fn(int id, void *item, void *unused) { @@ -53,12 +79,57 @@ void ncr_sessions_list_deinit(struct ncr_lists *lst) mutex_unlock(&lst->session_idr_mutex); } +/* Allocate a descriptor without making a sesssion available to userspace. */ +static ncr_session_t session_alloc_desc(struct ncr_lists *lst) +{ + int ret, desc; + + mutex_lock(&lst->session_idr_mutex); + if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0) { + ret = -ENOMEM; + goto end; + } + /* idr_pre_get() should preallocate enough, and, due to + session_idr_mutex, nobody else can use the preallocated data. + Therefore the loop recommended in idr_get_new() documentation is not + necessary. */ + ret = idr_get_new(&lst->session_idr, NULL, &desc); + if (ret != 0) + goto end; + ret = desc; +end: + mutex_unlock(&lst->session_idr_mutex); + return ret; +} + +/* Drop a pre-allocated, unpublished session descriptor */ +static void session_drop_desc(struct ncr_lists *lst, ncr_session_t desc) +{ + mutex_lock(&lst->session_idr_mutex); + idr_remove(&lst->session_idr, desc); + mutex_unlock(&lst->session_idr_mutex); +} + +/* Make a session descriptor visible in user-space, stealing the reference */ +static void session_publish_ref(struct ncr_lists *lst, + struct session_item_st *sess) +{ + void *old; + + mutex_lock(&lst->session_idr_mutex); + old = idr_replace(&lst->session_idr, sess, sess->desc); + mutex_unlock(&lst->session_idr_mutex); + BUG_ON(old != NULL); +} + /* returns the data item corresponding to desc */ -struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc) +static struct session_item_st *session_get_ref(struct ncr_lists *lst, + ncr_session_t desc) { struct session_item_st* item; mutex_lock(&lst->session_idr_mutex); + /* item may be NULL for pre-allocated session IDs. */ item = idr_find(&lst->session_idr, desc); if (item != NULL) { atomic_inc(&item->refcnt); @@ -71,7 +142,24 @@ struct session_item_st* item; return NULL; } -void _ncr_sessions_item_put( struct session_item_st* item) +/* Find a session, stealing the reference, but keep the descriptor allocated. */ +static struct session_item_st *session_unpublish_ref(struct ncr_lists *lst, + ncr_session_t desc) +{ + struct session_item_st *sess; + + mutex_lock(&lst->session_idr_mutex); + /* sess may be NULL for pre-allocated session IDs. */ + sess = idr_replace(&lst->session_idr, NULL, desc); + mutex_unlock(&lst->session_idr_mutex); + if (sess != NULL && !IS_ERR(sess)) + return sess; + + err(); + return NULL; +} + +static void _ncr_sessions_item_put(struct session_item_st *item) { if (atomic_dec_and_test(&item->refcnt)) { cryptodev_cipher_deinit(&item->cipher); @@ -85,7 +173,7 @@ void _ncr_sessions_item_put( struct session_item_st* item) } } -struct session_item_st* ncr_session_new(struct ncr_lists *lst) +static struct session_item_st *ncr_session_new(ncr_session_t desc) { struct session_item_st* sess; @@ -104,21 +192,10 @@ struct session_item_st* ncr_session_new(struct ncr_lists *lst) err(); goto err_sess; } - mutex_init(&sess->mem_mutex); - - atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */ + mutex_init(&sess->mutex); - mutex_lock(&lst->session_idr_mutex); - /* idr_pre_get() should preallocate enough, and, due to - session_idr_mutex, nobody else can use the preallocated data. - Therefore the loop recommended in idr_get_new() documentation is not - necessary. */ - if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0 || - idr_get_new(&lst->session_idr, sess, &sess->desc) != 0) { - mutex_unlock(&lst->session_idr_mutex); - goto err_sess; - } - mutex_unlock(&lst->session_idr_mutex); + atomic_set(&sess->refcnt, 1); + sess->desc = desc; return sess; @@ -257,27 +334,75 @@ static int key_item_get_nla_read(struct key_item_st **st, return ret; } -static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, - struct nlattr *tb[]) +/* The caller is responsible for locking of "session". old_session must not be + locked on entry. */ +static int +init_or_clone_hash(struct session_item_st *session, + struct session_item_st *old_session, + const struct algo_properties_st *algo, + const void *mac_key, size_t mac_key_size) +{ + int ret; + + if (old_session == NULL) + return cryptodev_hash_init(&session->hash, algo->kstr, + mac_key, mac_key_size); + + if (mutex_lock_interruptible(&old_session->mutex)) { + err(); + return -ERESTARTSYS; + } + ret = cryptodev_hash_clone(&session->hash, &old_session->hash, mac_key, + mac_key_size); + mutex_unlock(&old_session->mutex); + + return ret; +} + +static struct session_item_st *_ncr_session_init(struct ncr_lists *lists, + ncr_session_t desc, + ncr_crypto_op_t op, + struct nlattr *tb[]) { const struct nlattr *nla; - struct session_item_st* ns = NULL; + struct session_item_st *ns, *old_session = NULL; int ret; const struct algo_properties_st *sign_hash; - ns = ncr_session_new(lists); + ns = ncr_session_new(desc); if (ns == NULL) { err(); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } + /* ns is the only reference throughout this function, so no locking + is necessary. */ ns->op = op; - ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); - if (ns->algorithm == NULL) { - err(); - ret = -EINVAL; - goto fail; + nla = tb[NCR_ATTR_SESSION_CLONE_FROM]; + if (nla != NULL) { + /* "ns" is not visible to userspace, so this is safe. */ + old_session = session_get_ref(lists, nla_get_u32(nla)); + if (old_session == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + if (ns->op != old_session->op) { + err(); + ret = -EINVAL; + goto fail; + } } + + if (old_session == NULL) { + ns->algorithm = _ncr_nla_to_properties(tb[NCR_ATTR_ALGORITHM]); + if (ns->algorithm == NULL) { + err(); + ret = -EINVAL; + goto fail; + } + } else + ns->algorithm = old_session->algorithm; switch(op) { case NCR_OP_ENCRYPT: @@ -288,6 +413,12 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, goto fail; } + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + /* read key */ ret = key_item_get_nla_read(&ns->key, lists, tb[NCR_ATTR_KEY]); @@ -298,7 +429,7 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, /* wrapping keys cannot be used for encryption or decryption */ - if (ns->key->flags & NCR_KEY_FLAG_WRAPPING) { + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { err(); ret = -EINVAL; goto fail; @@ -363,24 +494,32 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 0, NULL, 0); + ret = init_or_clone_hash(ns, old_session, + ns->algorithm, NULL, + 0); if (ret < 0) { err(); goto fail; } } else { - /* read key */ - ret = key_item_get_nla_read(&ns->key, lists, - tb[NCR_ATTR_KEY]); - if (ret < 0) { - err(); - goto fail; + /* Get key */ + if (old_session == NULL) { + ret = key_item_get_nla_read(&ns->key, + lists, + tb[NCR_ATTR_KEY]); + if (ret < 0) { + err(); + goto fail; + } + } else { + atomic_inc(&old_session->key->refcnt); + ns->key = old_session->key; } /* wrapping keys cannot be used for anything except wrapping. */ - if (ns->key->flags & NCR_KEY_FLAG_WRAPPING) { + if (ns->key->flags & NCR_KEY_FLAG_WRAPPING || ns->key->flags & NCR_KEY_FLAG_UNWRAPPING) { err(); ret = -EINVAL; goto fail; @@ -393,7 +532,7 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, goto fail; } - ret = cryptodev_hash_init(&ns->hash, ns->algorithm->kstr, 1, + ret = init_or_clone_hash(ns, old_session, ns->algorithm, ns->key->key.secret.data, ns->key->key.secret.size); if (ret < 0) { err(); @@ -401,6 +540,12 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, } } else if (ns->algorithm->is_pk && (ns->key->type == NCR_KEY_TYPE_PRIVATE || ns->key->type == NCR_KEY_TYPE_PUBLIC)) { + if (old_session != NULL) { + err(); + ret = -EOPNOTSUPP; + goto fail; + } + nla = tb[NCR_ATTR_SIGNATURE_HASH_ALGORITHM]; sign_hash = _ncr_nla_to_properties(nla); if (sign_hash == NULL) { @@ -428,7 +573,7 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, goto fail; } - ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, 0, NULL, 0); + ret = cryptodev_hash_init(&ns->hash, sign_hash->kstr, NULL, 0); if (ret < 0) { err(); goto fail; @@ -446,25 +591,46 @@ static int _ncr_session_init(struct ncr_lists *lists, ncr_crypto_op_t op, ret = -EINVAL; goto fail; } + + if (old_session != NULL) + _ncr_sessions_item_put(old_session); - ret = ns->desc; + return ns; fail: - if (ret < 0) { - _ncr_session_remove(lists, ns->desc); - } + if (old_session != NULL) + _ncr_sessions_item_put(old_session); _ncr_sessions_item_put(ns); - return ret; + return ERR_PTR(ret); } int ncr_session_init(struct ncr_lists *lists, const struct ncr_session_init *session, struct nlattr *tb[]) { - return _ncr_session_init(lists, session->op, tb); + ncr_session_t desc; + struct session_item_st *sess; + + desc = session_alloc_desc(lists); + if (desc < 0) { + err(); + return desc; + } + + sess = _ncr_session_init(lists, desc, session->op, tb); + if (IS_ERR(sess)) { + err(); + session_drop_desc(lists, desc); + return PTR_ERR(sess); + } + + session_publish_ref(lists, sess); + + return desc; } +/* The caller is responsible for locking of the session. */ static 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) { @@ -493,6 +659,7 @@ int ret; return 0; } +/* The caller is responsible for locking of the session. */ static 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) @@ -522,20 +689,7 @@ int ret; return 0; } -static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc) -{ - struct session_item_st * item; - - mutex_lock(&lst->session_idr_mutex); - item = idr_find(&lst->session_idr, desc); - if (item != NULL) - idr_remove(&lst->session_idr, desc); /* Steal the reference */ - mutex_unlock(&lst->session_idr_mutex); - - if (item != NULL) - _ncr_sessions_item_put(item); -} - +/* The caller is responsible for locking of the session. */ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) { struct scatterlist *sg; @@ -567,7 +721,8 @@ static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) } /* Make NCR_ATTR_UPDATE_INPUT_DATA and NCR_ATTR_UPDATE_OUTPUT_BUFFER available - in scatterlists */ + in scatterlists. + The caller is responsible for locking of the session. */ static int get_userbuf2(struct session_item_st *ses, struct nlattr *tb[], struct scatterlist **src_sg, unsigned *src_cnt, size_t *src_size, struct ncr_session_output_buffer *dst, @@ -657,36 +812,24 @@ static int get_userbuf2(struct session_item_st *ses, struct nlattr *tb[], return 0; } -/* Called when userspace buffers are used */ -static int _ncr_session_update(struct ncr_lists *lists, ncr_session_t ses, +/* Called when userspace buffers are used. + The caller is responsible for locking of the session. */ +static int _ncr_session_update(struct session_item_st *sess, struct nlattr *tb[], int compat) { const struct nlattr *nla; int ret; - struct session_item_st* sess; struct scatterlist *isg = NULL; struct scatterlist *osg = NULL; unsigned osg_cnt=0, isg_cnt=0; size_t isg_size = 0, osg_size; struct ncr_session_output_buffer out; - sess = ncr_sessions_item_get(lists, ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } - ret = get_userbuf2(sess, tb, &isg, &isg_cnt, &isg_size, &out, &osg, &osg_cnt, compat); if (ret < 0) { err(); - goto fail; + return ret; } switch(sess->op) { @@ -786,52 +929,42 @@ fail: release_user_pages(sess->pages, sess->available_pages); sess->available_pages = 0; } - mutex_unlock(&sess->mem_mutex); - _ncr_sessions_item_put(sess); return ret; } -static int try_session_update(struct ncr_lists *lists, ncr_session_t ses, - struct nlattr *tb[], int compat) +/* The caller is responsible for locking of the session. */ +static int try_session_update(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) - return _ncr_session_update_key(lists, ses, tb); + return _ncr_session_update_key(lists, sess, tb); else if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) - return _ncr_session_update(lists, ses, tb, compat); - - return 0; + return _ncr_session_update(sess, tb, compat); + else + return 0; } -static int _ncr_session_final(struct ncr_lists *lists, ncr_session_t ses, - struct nlattr *tb[], int compat) +/* The caller is responsible for locking of the session. + Note that one or more _ncr_session_update()s may still be blocked on + sess->mutex and will execute after this function! */ +static int _ncr_session_final(struct ncr_lists *lists, + struct session_item_st *sess, struct nlattr *tb[], + int compat) { const struct nlattr *nla; int ret; - struct session_item_st* sess; int digest_size; uint8_t digest[NCR_HASH_MAX_OUTPUT_SIZE]; void *buffer = NULL; - sess = ncr_sessions_item_get(lists, ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - - ret = try_session_update(lists, ses, tb, compat); + ret = try_session_update(lists, sess, tb, compat); if (ret < 0) { err(); - _ncr_sessions_item_put(sess); return ret; } - if (mutex_lock_interruptible(&sess->mem_mutex)) { - err(); - _ncr_sessions_item_put(sess); - return -ERESTARTSYS; - } - switch(sess->op) { case NCR_OP_ENCRYPT: case NCR_OP_DECRYPT: @@ -961,42 +1094,26 @@ static int _ncr_session_final(struct ncr_lists *lists, ncr_session_t ses, } fail: - mutex_unlock(&sess->mem_mutex); kfree(buffer); - 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, ses); - return ret; } -/* Direct with key: Allows to hash a key */ -static int _ncr_session_update_key(struct ncr_lists *lists, ncr_session_t ses, +/* Direct with key: Allows to hash a key. + The caller is responsible for locking of the session. */ +static int _ncr_session_update_key(struct ncr_lists *lists, + struct session_item_st* sess, struct nlattr *tb[]) { int ret; - struct session_item_st* sess; struct key_item_st* key = NULL; - sess = ncr_sessions_item_get(lists, ses); - if (sess == NULL) { - err(); - return -EINVAL; - } - /* read key */ ret = key_item_get_nla_read(&key, lists, tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA]); if (ret < 0) { err(); - goto fail; + return ret; } if (key->type != NCR_KEY_TYPE_SECRET) { @@ -1029,8 +1146,7 @@ static int _ncr_session_update_key(struct ncr_lists *lists, ncr_session_t ses, ret = 0; fail: - if (key) _ncr_key_item_put(key); - _ncr_sessions_item_put(sess); + _ncr_key_item_put(key); return ret; } @@ -1039,14 +1155,33 @@ int ncr_session_update(struct ncr_lists *lists, const struct ncr_session_update *op, struct nlattr *tb[], int compat) { + struct session_item_st *sess; int ret; + sess = session_get_ref(lists, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + /* Note that op->ses may be reallocated from now on, making the audit + information confusing. */ + + if (mutex_lock_interruptible(&sess->mutex)) { + err(); + ret = -ERESTARTSYS; + goto end; + } if (tb[NCR_ATTR_UPDATE_INPUT_DATA] != NULL) - ret = _ncr_session_update(lists, op->ses, tb, compat); + ret = _ncr_session_update(sess, tb, compat); else if (tb[NCR_ATTR_UPDATE_INPUT_KEY_AS_DATA] != NULL) - ret = _ncr_session_update_key(lists, op->ses, tb); + ret = _ncr_session_update_key(lists, sess, tb); else ret = -EINVAL; + mutex_unlock(&sess->mutex); + +end: + _ncr_sessions_item_put(sess); if (unlikely(ret)) { err(); @@ -1060,26 +1195,53 @@ int ncr_session_final(struct ncr_lists *lists, const struct ncr_session_final *op, struct nlattr *tb[], int compat) { - return _ncr_session_final(lists, op->ses, tb, compat); + struct session_item_st *sess; + int ret; + + /* Make the session inaccessible atomically to avoid concurrent + session_final() callers, but keep the ID allocated to keep audit + information unambiguous. */ + sess = session_unpublish_ref(lists, op->ses); + if (sess == NULL) { + err(); + return -EINVAL; + } + + if (mutex_lock_interruptible(&sess->mutex)) { + err(); + /* Other threads may observe the session descriptor + disappearing and reappearing - but then they should not be + accessing it anyway if it is being freed. + session_unpublish_ref keeps the ID allocated for us. */ + session_publish_ref(lists, sess); + return -ERESTARTSYS; + } + ret = _ncr_session_final(lists, sess, tb, compat); + mutex_unlock(&sess->mutex); + + _ncr_sessions_item_put(sess); + session_drop_desc(lists, op->ses); + + return ret; } int ncr_session_once(struct ncr_lists *lists, const struct ncr_session_once *once, struct nlattr *tb[], int compat) { + struct session_item_st *sess; int ret; - ret = _ncr_session_init(lists, once->op, tb); - if (ret < 0) { + sess = _ncr_session_init(lists, -1, once->op, tb); + if (IS_ERR(sess)) { err(); - return ret; + return PTR_ERR(sess); } - ret = _ncr_session_final(lists, ret, tb, compat); - if (ret < 0) { - err(); - return ret; - } + /* No locking of sess necessary, "sess" is the only reference. */ + ret = _ncr_session_final(lists, sess, tb, compat); + + _ncr_sessions_item_put(sess); return ret; } @@ -58,6 +58,7 @@ enum { NCR_ATTR_DH_BASE, /* NLA_BINARY */ NCR_ATTR_DH_PUBLIC, /* NLA_BINARY */ NCR_ATTR_WANTED_ATTRS, /* NLA_BINARY - array of u16 IDs */ + NCR_ATTR_SESSION_CLONE_FROM, /* NLA_U32 - ncr_session_t */ /* Add new attributes here */ @@ -94,10 +95,13 @@ typedef __s32 ncr_key_t; */ #define NCR_KEY_FLAG_DECRYPT (1<<2) #define NCR_KEY_FLAG_SIGN (1<<3) -/* This flag can only be set by administrator, to prevent +#define NCR_KEY_FLAG_ENCRYPT (1<<4) +#define NCR_KEY_FLAG_VERIFY (1<<5) +/* These flags can only be set by administrator, to prevent * adversaries exporting wrappable keys with random ones. */ -#define NCR_KEY_FLAG_WRAPPING (1<<4) +#define NCR_KEY_FLAG_WRAPPING (1<<6) +#define NCR_KEY_FLAG_UNWRAPPING (1<<7) struct ncr_key_generate { __u32 input_size, output_size; @@ -82,6 +82,7 @@ static const struct nla_policy ncr_attr_policy[NCR_ATTR_MAX + 1] = { [NCR_ATTR_DH_BASE] = { NLA_BINARY, 0 }, [NCR_ATTR_DH_PUBLIC] = { NLA_BINARY, 0 }, [NCR_ATTR_WANTED_ATTRS] = { NLA_BINARY, 0 }, + [NCR_ATTR_SESSION_CLONE_FROM] = { NLA_U32, 0 }, }; void *__ncr_get_input_args(void *fixed, struct nlattr *tb[], size_t fixed_size, |