Message ID | 146672255872.23101.10938182451423661314.stgit@tstruk-mobl1.ra.intel.com (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Herbert Xu |
Headers | show |
Hi, [auto build test ERROR on cryptodev/master] [also build test ERROR on v4.7-rc4 next-20160623] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Tadeusz-Struk/crypto-algif-add-akcipher/20160624-065803 base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master config: x86_64-allmodconfig (attached as .config) compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430 reproduce: # save the attached .config to linux build tree make ARCH=x86_64 All error/warnings (new ones prefixed by >>): crypto/algif_akcipher.c: In function 'asym_key_encrypt': >> crypto/algif_akcipher.c:326:9: error: variable 'params' has initializer but incomplete type struct kernel_pkey_params params = {0}; ^~~~~~~~~~~~~~~~~~ >> crypto/algif_akcipher.c:326:38: warning: excess elements in struct initializer struct kernel_pkey_params params = {0}; ^ crypto/algif_akcipher.c:326:38: note: (near initialization for 'params') >> crypto/algif_akcipher.c:326:28: error: storage size of 'params' isn't known struct kernel_pkey_params params = {0}; ^~~~~~ >> crypto/algif_akcipher.c:352:8: error: implicit declaration of function 'encrypt_blob' [-Werror=implicit-function-declaration] ret = encrypt_blob(¶ms, in, out); ^~~~~~~~~~~~ >> crypto/algif_akcipher.c:326:28: warning: unused variable 'params' [-Wunused-variable] struct kernel_pkey_params params = {0}; ^~~~~~ crypto/algif_akcipher.c: In function 'asym_key_decrypt': crypto/algif_akcipher.c:366:9: error: variable 'params' has initializer but incomplete type struct kernel_pkey_params params = {0}; ^~~~~~~~~~~~~~~~~~ crypto/algif_akcipher.c:366:38: warning: excess elements in struct initializer struct kernel_pkey_params params = {0}; ^ crypto/algif_akcipher.c:366:38: note: (near initialization for 'params') crypto/algif_akcipher.c:366:28: error: storage size of 'params' isn't known struct kernel_pkey_params params = {0}; ^~~~~~ >> crypto/algif_akcipher.c:392:8: error: implicit declaration of function 'decrypt_blob' [-Werror=implicit-function-declaration] ret = decrypt_blob(¶ms, in, out); ^~~~~~~~~~~~ crypto/algif_akcipher.c:366:28: warning: unused variable 'params' [-Wunused-variable] struct kernel_pkey_params params = {0}; ^~~~~~ crypto/algif_akcipher.c: In function 'asym_key_sign': crypto/algif_akcipher.c:406:9: error: variable 'params' has initializer but incomplete type struct kernel_pkey_params params = {0}; ^~~~~~~~~~~~~~~~~~ crypto/algif_akcipher.c:406:38: warning: excess elements in struct initializer struct kernel_pkey_params params = {0}; ^ crypto/algif_akcipher.c:406:38: note: (near initialization for 'params') crypto/algif_akcipher.c:406:28: error: storage size of 'params' isn't known struct kernel_pkey_params params = {0}; ^~~~~~ >> crypto/algif_akcipher.c:432:8: error: implicit declaration of function 'create_signature' [-Werror=implicit-function-declaration] ret = create_signature(¶ms, in, out); ^~~~~~~~~~~~~~~~ crypto/algif_akcipher.c:406:28: warning: unused variable 'params' [-Wunused-variable] struct kernel_pkey_params params = {0}; ^~~~~~ crypto/algif_akcipher.c: In function 'asym_key_verify': >> crypto/algif_akcipher.c:460:5: error: 'struct public_key_signature' has no member named 'encoding' sig.encoding = "pkcs1"; ^ cc1: some warnings being treated as errors vim +/params +326 crypto/algif_akcipher.c 320 321 return err ? err : size; 322 } 323 324 static int asym_key_encrypt(const struct key *key, struct akcipher_request *req) 325 { > 326 struct kernel_pkey_params params = {0}; 327 char *src = NULL, *dst = NULL, *in, *out; 328 int ret; 329 330 if (!sg_is_last(req->src)) { 331 src = kmalloc(req->src_len, GFP_KERNEL); 332 if (!src) 333 return -ENOMEM; 334 scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); 335 in = src; 336 } else { 337 in = sg_virt(req->src); 338 } 339 if (!sg_is_last(req->dst)) { 340 dst = kmalloc(req->dst_len, GFP_KERNEL); 341 if (!dst) { 342 kfree(src); 343 return -ENOMEM; 344 } 345 out = dst; 346 } else { 347 out = sg_virt(req->dst); 348 } 349 params.key = (struct key *)key; 350 params.in_len = req->src_len; 351 params.out_len = req->dst_len; > 352 ret = encrypt_blob(¶ms, in, out); 353 if (ret) 354 goto free; 355 356 if (dst) 357 scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); 358 free: 359 kfree(src); 360 kfree(dst); 361 return ret; 362 } 363 364 static int asym_key_decrypt(const struct key *key, struct akcipher_request *req) 365 { > 366 struct kernel_pkey_params params = {0}; 367 char *src = NULL, *dst = NULL, *in, *out; 368 int ret; 369 370 if (!sg_is_last(req->src)) { 371 src = kmalloc(req->src_len, GFP_KERNEL); 372 if (!src) 373 return -ENOMEM; 374 scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); 375 in = src; 376 } else { 377 in = sg_virt(req->src); 378 } 379 if (!sg_is_last(req->dst)) { 380 dst = kmalloc(req->dst_len, GFP_KERNEL); 381 if (!dst) { 382 kfree(src); 383 return -ENOMEM; 384 } 385 out = dst; 386 } else { 387 out = sg_virt(req->dst); 388 } 389 params.key = (struct key *)key; 390 params.in_len = req->src_len; 391 params.out_len = req->dst_len; > 392 ret = decrypt_blob(¶ms, in, out); 393 if (ret) 394 goto free; 395 396 if (dst) 397 scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); 398 free: 399 kfree(src); 400 kfree(dst); 401 return ret; 402 } 403 404 static int asym_key_sign(const struct key *key, struct akcipher_request *req) 405 { > 406 struct kernel_pkey_params params = {0}; 407 char *src = NULL, *dst = NULL, *in, *out; 408 int ret; 409 410 if (!sg_is_last(req->src)) { 411 src = kmalloc(req->src_len, GFP_KERNEL); 412 if (!src) 413 return -ENOMEM; 414 scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); 415 in = src; 416 } else { 417 in = sg_virt(req->src); 418 } 419 if (!sg_is_last(req->dst)) { 420 dst = kmalloc(req->dst_len, GFP_KERNEL); 421 if (!dst) { 422 kfree(src); 423 return -ENOMEM; 424 } 425 out = dst; 426 } else { 427 out = sg_virt(req->dst); 428 } 429 params.key = (struct key *)key; 430 params.in_len = req->src_len; 431 params.out_len = req->dst_len; > 432 ret = create_signature(¶ms, in, out); 433 if (ret) 434 goto free; 435 436 if (dst) 437 scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); 438 free: 439 kfree(src); 440 kfree(dst); 441 return ret; 442 } 443 444 static int asym_key_verify(const struct key *key, struct akcipher_request *req) 445 { 446 struct public_key_signature sig; 447 char *src = NULL, *in, digest[20]; 448 int ret; 449 450 if (!sg_is_last(req->src)) { 451 src = kmalloc(req->src_len, GFP_KERNEL); 452 if (!src) 453 return -ENOMEM; 454 scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); 455 in = src; 456 } else { 457 in = sg_virt(req->src); 458 } 459 sig.pkey_algo = "rsa"; > 460 sig.encoding = "pkcs1"; 461 /* Need to find a way to pass the hash param */ 462 sig.hash_algo = "sha1"; 463 sig.digest_size = sizeof(digest); --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Tadeusz, On Thu, 23 Jun 2016, Tadeusz Struk wrote: > This patch adds support for asymmetric key type to AF_ALG. > It will work as follows: A new PF_ALG socket options are > added on top of existing ALG_SET_KEY and ALG_SET_PUBKEY, namely > ALG_SET_KEY_ID and ALG_SET_PUBKEY_ID for setting public and > private keys respectively. When these new options will be used > the user, instead of providing the key material, will provide a > key id and the key itself will be obtained from kernel keyring > subsystem. The user will use the standard tools (keyctl tool > or the keyctl syscall) for key instantiation and to obtain the > key id. The key id can also be obtained by reading the > /proc/keys file. > > When a key corresponding to the given keyid is found, it is stored > in the socket context and subsequent crypto operation invoked by the > user will use the new asymmetric accessor functions instead of akcipher > api. The asymmetric subtype can internally use akcipher api or > invoke operations defined by a given subtype, depending on the > key type. > > Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com> > --- > crypto/af_alg.c | 10 ++ > crypto/algif_akcipher.c | 212 ++++++++++++++++++++++++++++++++++++++++++- > include/crypto/if_alg.h | 1 > include/uapi/linux/if_alg.h | 2 > 4 files changed, 220 insertions(+), 5 deletions(-) > > diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c > index 2b8d37e..106f715 100644 > --- a/crypto/algif_akcipher.c > +++ b/crypto/algif_akcipher.c > +static int asym_key_verify(const struct key *key, struct akcipher_request *req) > +{ > + struct public_key_signature sig; > + char *src = NULL, *in, digest[20]; > + int ret; > + > + if (!sg_is_last(req->src)) { > + src = kmalloc(req->src_len, GFP_KERNEL); > + if (!src) > + return -ENOMEM; > + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); > + in = src; > + } else { > + in = sg_virt(req->src); > + } > + sig.pkey_algo = "rsa"; > + sig.encoding = "pkcs1"; > + /* Need to find a way to pass the hash param */ Comment still needed? > + sig.hash_algo = "sha1"; > + sig.digest_size = sizeof(digest); > + sig.digest = digest; > + sig.s_size = req->src_len; > + sig.s = src; > + ret = verify_signature(key, &sig); > + if (!ret) { > + req->dst_len = sizeof(digest); I think you fixed the BUG_ON() problem but there's still an issue with the handling of the digest. Check the use of sig->digest in public_key_verify_signature(), it's an input not an output. Right now it looks like 20 uninitialized bytes are compared with the computed digest within verify_signature, and then the unintialized bytes are copied to req->dst here. With some modifications to public_key_verify_signature you could get the digest you need, but I'm not sure if verification with a hardware key (like a key in a TPM) can or can not provide the digest needed. Maybe this is why the verify_signature hook in struct asymmetric_key_subtype is optional. > + scatterwalk_map_and_copy(digest, req->dst, 0, req->dst_len, 1); > + } > + kfree(src); > + return ret; > +} > + -- Mat Martineau Intel OTC -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Mat, On 06/29/2016 11:43 AM, Mat Martineau wrote: >> + ret = verify_signature(key, &sig); >> + if (!ret) { >> + req->dst_len = sizeof(digest); > > I think you fixed the BUG_ON() problem but there's still an issue with > the handling of the digest. Check the use of sig->digest in > public_key_verify_signature(), it's an input not an output. Right now it > looks like 20 uninitialized bytes are compared with the computed digest > within verify_signature, and then the unintialized bytes are copied to > req->dst here. > > With some modifications to public_key_verify_signature you could get the > digest you need, but I'm not sure if verification with a hardware key > (like a key in a TPM) can or can not provide the digest needed. Maybe > this is why the verify_signature hook in struct asymmetric_key_subtype > is optional. > >> + scatterwalk_map_and_copy(digest, req->dst, 0, req->dst_len, 1); >> + } So it looks like the only thing that we need to return to the user in this case is the return code. Do you agree? Thanks,
On Tue, 5 Jul 2016, Tadeusz Struk wrote: > Hi Mat, > On 06/29/2016 11:43 AM, Mat Martineau wrote: >>> + ret = verify_signature(key, &sig); >>> + if (!ret) { >>> + req->dst_len = sizeof(digest); >> >> I think you fixed the BUG_ON() problem but there's still an issue with >> the handling of the digest. Check the use of sig->digest in >> public_key_verify_signature(), it's an input not an output. Right now it >> looks like 20 uninitialized bytes are compared with the computed digest >> within verify_signature, and then the unintialized bytes are copied to >> req->dst here. >> >> With some modifications to public_key_verify_signature you could get the >> digest you need, but I'm not sure if verification with a hardware key >> (like a key in a TPM) can or can not provide the digest needed. Maybe >> this is why the verify_signature hook in struct asymmetric_key_subtype >> is optional. >> >>> + scatterwalk_map_and_copy(digest, req->dst, 0, req->dst_len, 1); >>> + } > > So it looks like the only thing that we need to return to the user in > this case is the return code. Do you agree? The way verify_signature is implemented today, the only output is the return code. For verify, maybe no read is required (just sendmsg() and check the return code). But this isn't the extent of the problem: verify_signature needs both the signature to be verified and the expected hash as inputs. How is the expected hash provided? Would you include it as a cmsg header? ALG_OP_VERIFY should have consistent inputs and outputs whether the key was set with ALG_SET_KEY_ID or ALG_SET_KEY. -- Mat Martineau Intel OTC -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Mat, On 07/06/2016 12:38 PM, Mat Martineau wrote: >> So it looks like the only thing that we need to return to the user in >> this case is the return code. Do you agree? > > The way verify_signature is implemented today, the only output is the > return code. For verify, maybe no read is required (just sendmsg() and > check the return code). > > But this isn't the extent of the problem: verify_signature needs both > the signature to be verified and the expected hash as inputs. How is the > expected hash provided? Would you include it as a cmsg header? > ALG_OP_VERIFY should have consistent inputs and outputs whether the key > was set with ALG_SET_KEY_ID or ALG_SET_KEY. The signature of verify_signature() is quite different from the other new public key handlers, i.e. create_signature(), encrypt_blob(), and decrypt_blob(). For verify_signature() we need the following parameters: encrypted src, hash function to use, expected digest. The expected digest could be optional if we would modify the verify_signature() to return the decrypted buffer. I think the best solution for now would be to just return -ENOPROTOOPT for verify_signature in SET_KEY_ID mode. All the four operations will be supported in the SET_KEY mode and all but verify_signature() will be supported in the SET_KEY_ID mode. This can added later if we will find a way to pass all parameters in a consistent way. What do you think? If you are ok with that I will send a new version soon. Thanks,
On Fri, 8 Jul 2016, Tadeusz Struk wrote: > Hi Mat, > On 07/06/2016 12:38 PM, Mat Martineau wrote: >>> So it looks like the only thing that we need to return to the user in >>> this case is the return code. Do you agree? >> >> The way verify_signature is implemented today, the only output is the >> return code. For verify, maybe no read is required (just sendmsg() and >> check the return code). >> >> But this isn't the extent of the problem: verify_signature needs both >> the signature to be verified and the expected hash as inputs. How is the >> expected hash provided? Would you include it as a cmsg header? >> ALG_OP_VERIFY should have consistent inputs and outputs whether the key >> was set with ALG_SET_KEY_ID or ALG_SET_KEY. > > The signature of verify_signature() is quite different from the other > new public key handlers, i.e. create_signature(), encrypt_blob(), and > decrypt_blob(). For verify_signature() we need the following parameters: > encrypted src, hash function to use, expected digest. > The expected digest could be optional if we would modify the > verify_signature() to return the decrypted buffer. > I think the best solution for now would be to just return -ENOPROTOOPT > for verify_signature in SET_KEY_ID mode. > All the four operations will be supported in the SET_KEY mode and > all but verify_signature() will be supported in the SET_KEY_ID mode. > This can added later if we will find a way to pass all parameters in a > consistent way. What do you think? If you are ok with that I will send a > new version soon. Are the inputs and outputs defined for ALG_OP_VERIFY in SET_KEY mode going to work for hardware keys (like TPM) in SET_KEY_ID mode? That's needed if the verify SET_KEY_ID mode is to be added later. -- Mat Martineau Intel OTC -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 07/08/2016 09:38 AM, Mat Martineau wrote: > Are the inputs and outputs defined for ALG_OP_VERIFY in SET_KEY mode > going to work for hardware keys (like TPM) in SET_KEY_ID mode? That's > needed if the verify SET_KEY_ID mode is to be added later. Yes, we will just need to change the verify_signature() in public_key.c to be consistent with the rest of handlers. What we need really is the src (encrypted input), key (or key id), and an output buffer where we can copy the result to. Thanks,
diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 24dc082..59c8244 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -260,6 +260,16 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, err = alg_setkey(sk, optval, optlen, type->setpubkey); break; + + case ALG_SET_KEY_ID: + case ALG_SET_PUBKEY_ID: + /* ALG_SET_KEY_ID is only for akcipher */ + if (!strcmp(type->name, "akcipher") || + sock->state == SS_CONNECTED) + goto unlock; + + err = alg_setkey(sk, optval, optlen, type->setkeyid); + break; case ALG_SET_AEAD_AUTHSIZE: if (sock->state == SS_CONNECTED) goto unlock; diff --git a/crypto/algif_akcipher.c b/crypto/algif_akcipher.c index 2b8d37e..106f715 100644 --- a/crypto/algif_akcipher.c +++ b/crypto/algif_akcipher.c @@ -14,6 +14,8 @@ #include <crypto/akcipher.h> #include <crypto/scatterwalk.h> #include <crypto/if_alg.h> +#include <crypto/public_key.h> +#include <keys/asymmetric-type.h> #include <linux/init.h> #include <linux/list.h> #include <linux/kernel.h> @@ -29,6 +31,7 @@ struct akcipher_sg_list { struct akcipher_tfm { struct crypto_akcipher *akcipher; + char keyid[12]; bool has_key; }; @@ -37,6 +40,7 @@ struct akcipher_ctx { struct af_alg_sgl rsgl[ALG_MAX_PAGES]; struct af_alg_completion completion; + struct key *key; unsigned long used; @@ -317,6 +321,158 @@ unlock: return err ? err : size; } +static int asym_key_encrypt(const struct key *key, struct akcipher_request *req) +{ + struct kernel_pkey_params params = {0}; + char *src = NULL, *dst = NULL, *in, *out; + int ret; + + if (!sg_is_last(req->src)) { + src = kmalloc(req->src_len, GFP_KERNEL); + if (!src) + return -ENOMEM; + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); + in = src; + } else { + in = sg_virt(req->src); + } + if (!sg_is_last(req->dst)) { + dst = kmalloc(req->dst_len, GFP_KERNEL); + if (!dst) { + kfree(src); + return -ENOMEM; + } + out = dst; + } else { + out = sg_virt(req->dst); + } + params.key = (struct key *)key; + params.in_len = req->src_len; + params.out_len = req->dst_len; + ret = encrypt_blob(¶ms, in, out); + if (ret) + goto free; + + if (dst) + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); +free: + kfree(src); + kfree(dst); + return ret; +} + +static int asym_key_decrypt(const struct key *key, struct akcipher_request *req) +{ + struct kernel_pkey_params params = {0}; + char *src = NULL, *dst = NULL, *in, *out; + int ret; + + if (!sg_is_last(req->src)) { + src = kmalloc(req->src_len, GFP_KERNEL); + if (!src) + return -ENOMEM; + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); + in = src; + } else { + in = sg_virt(req->src); + } + if (!sg_is_last(req->dst)) { + dst = kmalloc(req->dst_len, GFP_KERNEL); + if (!dst) { + kfree(src); + return -ENOMEM; + } + out = dst; + } else { + out = sg_virt(req->dst); + } + params.key = (struct key *)key; + params.in_len = req->src_len; + params.out_len = req->dst_len; + ret = decrypt_blob(¶ms, in, out); + if (ret) + goto free; + + if (dst) + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); +free: + kfree(src); + kfree(dst); + return ret; +} + +static int asym_key_sign(const struct key *key, struct akcipher_request *req) +{ + struct kernel_pkey_params params = {0}; + char *src = NULL, *dst = NULL, *in, *out; + int ret; + + if (!sg_is_last(req->src)) { + src = kmalloc(req->src_len, GFP_KERNEL); + if (!src) + return -ENOMEM; + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); + in = src; + } else { + in = sg_virt(req->src); + } + if (!sg_is_last(req->dst)) { + dst = kmalloc(req->dst_len, GFP_KERNEL); + if (!dst) { + kfree(src); + return -ENOMEM; + } + out = dst; + } else { + out = sg_virt(req->dst); + } + params.key = (struct key *)key; + params.in_len = req->src_len; + params.out_len = req->dst_len; + ret = create_signature(¶ms, in, out); + if (ret) + goto free; + + if (dst) + scatterwalk_map_and_copy(dst, req->dst, 0, req->dst_len, 1); +free: + kfree(src); + kfree(dst); + return ret; +} + +static int asym_key_verify(const struct key *key, struct akcipher_request *req) +{ + struct public_key_signature sig; + char *src = NULL, *in, digest[20]; + int ret; + + if (!sg_is_last(req->src)) { + src = kmalloc(req->src_len, GFP_KERNEL); + if (!src) + return -ENOMEM; + scatterwalk_map_and_copy(src, req->src, 0, req->src_len, 0); + in = src; + } else { + in = sg_virt(req->src); + } + sig.pkey_algo = "rsa"; + sig.encoding = "pkcs1"; + /* Need to find a way to pass the hash param */ + sig.hash_algo = "sha1"; + sig.digest_size = sizeof(digest); + sig.digest = digest; + sig.s_size = req->src_len; + sig.s = src; + ret = verify_signature(key, &sig); + if (!ret) { + req->dst_len = sizeof(digest); + scatterwalk_map_and_copy(digest, req->dst, 0, req->dst_len, 1); + } + kfree(src); + return ret; +} + static int akcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) { @@ -366,16 +522,28 @@ static int akcipher_recvmsg(struct socket *sock, struct msghdr *msg, usedpages); switch (ctx->op) { case ALG_OP_VERIFY: - err = crypto_akcipher_verify(&ctx->req); + if (ctx->key) + err = asym_key_verify(ctx->key, &ctx->req); + else + err = crypto_akcipher_verify(&ctx->req); break; case ALG_OP_SIGN: - err = crypto_akcipher_sign(&ctx->req); + if (ctx->key) + err = asym_key_sign(ctx->key, &ctx->req); + else + err = crypto_akcipher_sign(&ctx->req); break; case ALG_OP_ENCRYPT: - err = crypto_akcipher_encrypt(&ctx->req); + if (ctx->key) + err = asym_key_encrypt(ctx->key, &ctx->req); + else + err = crypto_akcipher_encrypt(&ctx->req); break; case ALG_OP_DECRYPT: - err = crypto_akcipher_decrypt(&ctx->req); + if (ctx->key) + err = asym_key_decrypt(ctx->key, &ctx->req); + else + err = crypto_akcipher_decrypt(&ctx->req); break; default: err = -EFAULT; @@ -568,6 +736,27 @@ static void akcipher_release(void *private) kfree(tfm); } +static int akcipher_setkeyid(void *private, const u8 *key, unsigned int keylen) +{ + struct akcipher_tfm *tfm = private; + struct key *akey; + u32 keyid = *((u32 *)key); + int err = -ENOKEY; + + /* Store the key id and verify that a key with the given id is present. + * The actual key will be acquired in the accept_parent function + */ + sprintf(tfm->keyid, "id:%08x", keyid); + akey = request_key(&key_type_asymmetric, tfm->keyid, NULL); + if (IS_ERR(key)) + goto out; + + tfm->has_key = true; + key_put(akey); +out: + return err; +} + static int akcipher_setprivkey(void *private, const u8 *key, unsigned int keylen) { @@ -599,6 +788,8 @@ static void akcipher_sock_destruct(struct sock *sk) akcipher_put_sgl(sk); sock_kfree_s(sk, ctx, ctx->len); af_alg_release_parent(sk); + if (ctx->key) + key_put(ctx->key); } static int akcipher_accept_parent_nokey(void *private, struct sock *sk) @@ -607,6 +798,7 @@ static int akcipher_accept_parent_nokey(void *private, struct sock *sk) struct alg_sock *ask = alg_sk(sk); struct akcipher_tfm *tfm = private; struct crypto_akcipher *akcipher = tfm->akcipher; + struct key *key; unsigned int len = sizeof(*ctx) + crypto_akcipher_reqsize(akcipher); ctx = sock_kmalloc(sk, len, GFP_KERNEL); @@ -623,11 +815,20 @@ static int akcipher_accept_parent_nokey(void *private, struct sock *sk) af_alg_init_completion(&ctx->completion); sg_init_table(ctx->tsgl.sg, ALG_MAX_PAGES); - ask->private = ctx; + if (strlen(tfm->keyid)) { + key = request_key(&key_type_asymmetric, tfm->keyid, NULL); + if (IS_ERR(key)) { + sock_kfree_s(sk, ctx, len); + return -ENOKEY; + } + ctx->key = key; + memset(tfm->keyid, '\0', sizeof(tfm->keyid)); + } akcipher_request_set_tfm(&ctx->req, akcipher); akcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, af_alg_complete, &ctx->completion); + ask->private = ctx; sk->sk_destruct = akcipher_sock_destruct; @@ -649,6 +850,7 @@ static const struct af_alg_type algif_type_akcipher = { .release = akcipher_release, .setkey = akcipher_setprivkey, .setpubkey = akcipher_setpubkey, + .setkeyid = akcipher_setkeyid, .accept = akcipher_accept_parent, .accept_nokey = akcipher_accept_parent_nokey, .ops = &algif_akcipher_ops, diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 6c3e6e7..09c99ab 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -53,6 +53,7 @@ struct af_alg_type { void (*release)(void *private); int (*setkey)(void *private, const u8 *key, unsigned int keylen); int (*setpubkey)(void *private, const u8 *key, unsigned int keylen); + int (*setkeyid)(void *private, const u8 *key, unsigned int keylen); int (*accept)(void *private, struct sock *sk); int (*accept_nokey)(void *private, struct sock *sk); int (*setauthsize)(void *private, unsigned int authsize); diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h index 02e6162..0379766 100644 --- a/include/uapi/linux/if_alg.h +++ b/include/uapi/linux/if_alg.h @@ -35,6 +35,8 @@ struct af_alg_iv { #define ALG_SET_AEAD_ASSOCLEN 4 #define ALG_SET_AEAD_AUTHSIZE 5 #define ALG_SET_PUBKEY 6 +#define ALG_SET_PUBKEY_ID 7 +#define ALG_SET_KEY_ID 8 /* Operations */ #define ALG_OP_DECRYPT 0
This patch adds support for asymmetric key type to AF_ALG. It will work as follows: A new PF_ALG socket options are added on top of existing ALG_SET_KEY and ALG_SET_PUBKEY, namely ALG_SET_KEY_ID and ALG_SET_PUBKEY_ID for setting public and private keys respectively. When these new options will be used the user, instead of providing the key material, will provide a key id and the key itself will be obtained from kernel keyring subsystem. The user will use the standard tools (keyctl tool or the keyctl syscall) for key instantiation and to obtain the key id. The key id can also be obtained by reading the /proc/keys file. When a key corresponding to the given keyid is found, it is stored in the socket context and subsequent crypto operation invoked by the user will use the new asymmetric accessor functions instead of akcipher api. The asymmetric subtype can internally use akcipher api or invoke operations defined by a given subtype, depending on the key type. Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com> --- crypto/af_alg.c | 10 ++ crypto/algif_akcipher.c | 212 ++++++++++++++++++++++++++++++++++++++++++- include/crypto/if_alg.h | 1 include/uapi/linux/if_alg.h | 2 4 files changed, 220 insertions(+), 5 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html