summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crypto.415
-rw-r--r--cryptodev_cipher.c46
-rw-r--r--cryptodev_int.h4
-rw-r--r--examples/Makefile2
-rw-r--r--examples/ncr.c232
-rw-r--r--examples/pk.c20
-rw-r--r--libtomcrypt/hashes/hash_memory.c2
-rw-r--r--libtomcrypt/hashes/hash_memory_multi.c2
-rw-r--r--ncr-int.h31
-rw-r--r--ncr-key-wrap.c225
-rw-r--r--ncr-key.c74
-rw-r--r--ncr-sessions.c434
-rw-r--r--ncr.h8
-rw-r--r--utils.c1
14 files changed, 810 insertions, 286 deletions
diff --git a/crypto.4 b/crypto.4
index c2d1102b538..a1339339841 100644
--- a/crypto.4
+++ b/crypto.4
@@ -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;
}
diff --git a/ncr.h b/ncr.h
index 47981d1ffd6..c248c6c87f1 100644
--- a/ncr.h
+++ b/ncr.h
@@ -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;
diff --git a/utils.c b/utils.c
index 514833cc65c..a427833fdad 100644
--- a/utils.c
+++ b/utils.c
@@ -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,