bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]) { struct crypto_kpp *tfm; struct kpp_request *req; struct ecdh p; struct ecdh_completion result; struct scatterlist dst; u8 *tmp, *buf; unsigned int buf_len; int err = -ENOMEM; const unsigned short max_tries = 16; unsigned short tries = 0; tmp = kmalloc(64, GFP_KERNEL); if (!tmp) return false; tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(tfm)) { pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", PTR_ERR(tfm)); goto free_tmp; } req = kpp_request_alloc(tfm, GFP_KERNEL); if (!req) goto free_kpp; init_completion(&result.completion); /* Set curve_id */ p.curve_id = ECC_CURVE_NIST_P256; p.key_size = 32; buf_len = crypto_ecdh_key_len(&p); buf = kmalloc(buf_len, GFP_KERNEL); if (!buf) goto free_req; do { if (tries++ >= max_tries) goto free_all; /* Set private Key */ p.key = (char *)private_key; crypto_ecdh_encode_key(buf, buf_len, &p); err = crypto_kpp_set_secret(tfm, buf, buf_len); if (err) goto free_all; sg_init_one(&dst, tmp, 64); kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, &dst, 64); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ecdh_complete, &result); err = crypto_kpp_generate_public_key(req); if (err == -EINPROGRESS) { wait_for_completion(&result.completion); err = result.err; } /* Private key is not valid. Regenerate */ if (err == -EINVAL) continue; if (err < 0) goto free_all; else break; } while (true); /* Keys are handed back in little endian as expected by Security * Manager Protocol */ swap_digits((u64 *)tmp, (u64 *)public_key, 4); /* x */ swap_digits((u64 *)&tmp[32], (u64 *)&public_key[32], 4); /* y */ swap_digits((u64 *)private_key, (u64 *)tmp, 4); memcpy(private_key, tmp, 32); free_all: kzfree(buf); free_req: kpp_request_free(req); free_kpp: crypto_free_kpp(tfm); free_tmp: kfree(tmp); return (err == 0); }
bool compute_ecdh_secret(const u8 public_key[64], const u8 private_key[32], u8 secret[32]) { struct crypto_kpp *tfm; struct kpp_request *req; struct ecdh p; struct ecdh_completion result; struct scatterlist src, dst; u8 *tmp, *buf; unsigned int buf_len; int err = -ENOMEM; tmp = kmalloc(64, GFP_KERNEL); if (!tmp) return false; tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(tfm)) { pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", PTR_ERR(tfm)); goto free_tmp; } req = kpp_request_alloc(tfm, GFP_KERNEL); if (!req) goto free_kpp; init_completion(&result.completion); /* Security Manager Protocol holds digits in litte-endian order * while ECC API expect big-endian data */ swap_digits((u64 *)private_key, (u64 *)tmp, 4); p.key = (char *)tmp; p.key_size = 32; /* Set curve_id */ p.curve_id = ECC_CURVE_NIST_P256; buf_len = crypto_ecdh_key_len(&p); buf = kmalloc(buf_len, GFP_KERNEL); if (!buf) goto free_req; crypto_ecdh_encode_key(buf, buf_len, &p); /* Set A private Key */ err = crypto_kpp_set_secret(tfm, (void *)buf, buf_len); if (err) goto free_all; swap_digits((u64 *)public_key, (u64 *)tmp, 4); /* x */ swap_digits((u64 *)&public_key[32], (u64 *)&tmp[32], 4); /* y */ sg_init_one(&src, tmp, 64); sg_init_one(&dst, secret, 32); kpp_request_set_input(req, &src, 64); kpp_request_set_output(req, &dst, 32); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ecdh_complete, &result); err = crypto_kpp_compute_shared_secret(req); if (err == -EINPROGRESS) { wait_for_completion(&result.completion); err = result.err; } if (err < 0) { pr_err("alg: ecdh: compute shared secret failed. err %d\n", err); goto free_all; } swap_digits((u64 *)secret, (u64 *)tmp, 4); memcpy(secret, tmp, 32); free_all: kzfree(buf); free_req: kpp_request_free(req); free_kpp: crypto_free_kpp(tfm); free_tmp: kfree(tmp); return (err == 0); }
long __keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, struct keyctl_kdf_params *kdfcopy) { long ret; ssize_t dlen; int secretlen; int outlen; struct keyctl_dh_params pcopy; struct dh dh_inputs; struct scatterlist outsg; struct dh_completion compl; struct crypto_kpp *tfm; struct kpp_request *req; uint8_t *secret; uint8_t *outbuf; struct kdf_sdesc *sdesc = NULL; if (!params || (!buffer && buflen)) { ret = -EINVAL; goto out1; } if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { ret = -EFAULT; goto out1; } if (kdfcopy) { char *hashname; if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) { ret = -EINVAL; goto out1; } if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { ret = -EMSGSIZE; goto out1; } /* get KDF name string */ hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); if (IS_ERR(hashname)) { ret = PTR_ERR(hashname); goto out1; } /* allocate KDF from the kernel crypto API */ ret = kdf_alloc(&sdesc, hashname); kfree(hashname); if (ret) goto out1; } memset(&dh_inputs, 0, sizeof(dh_inputs)); dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p); if (dlen < 0) { ret = dlen; goto out1; } dh_inputs.p_size = dlen; dlen = dh_data_from_key(pcopy.base, &dh_inputs.g); if (dlen < 0) { ret = dlen; goto out2; } dh_inputs.g_size = dlen; dlen = dh_data_from_key(pcopy.private, &dh_inputs.key); if (dlen < 0) { ret = dlen; goto out2; } dh_inputs.key_size = dlen; secretlen = crypto_dh_key_len(&dh_inputs); secret = kmalloc(secretlen, GFP_KERNEL); if (!secret) { ret = -ENOMEM; goto out2; } ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs); if (ret) goto out3; tfm = crypto_alloc_kpp("dh", CRYPTO_ALG_TYPE_KPP, 0); if (IS_ERR(tfm)) { ret = PTR_ERR(tfm); goto out3; } ret = crypto_kpp_set_secret(tfm, secret, secretlen); if (ret) goto out4; outlen = crypto_kpp_maxsize(tfm); if (!kdfcopy) { /* * When not using a KDF, buflen 0 is used to read the * required buffer length */ if (buflen == 0) { ret = outlen; goto out4; } else if (outlen > buflen) { ret = -EOVERFLOW; goto out4; } } outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen, GFP_KERNEL); if (!outbuf) { ret = -ENOMEM; goto out4; } sg_init_one(&outsg, outbuf, outlen); req = kpp_request_alloc(tfm, GFP_KERNEL); if (!req) { ret = -ENOMEM; goto out5; } kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, &outsg, outlen); init_completion(&compl.completion); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, dh_crypto_done, &compl); /* * For DH, generate_public_key and generate_shared_secret are * the same calculation */ ret = crypto_kpp_generate_public_key(req); if (ret == -EINPROGRESS) { wait_for_completion(&compl.completion); ret = compl.err; if (ret) goto out6; } if (kdfcopy) { /* * Concatenate SP800-56A otherinfo past DH shared secret -- the * input to the KDF is (DH shared secret || otherinfo) */ if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo, kdfcopy->otherinfolen) != 0) { ret = -EFAULT; goto out6; } ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, outbuf, req->dst_len + kdfcopy->otherinfolen, outlen - req->dst_len); } else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) { ret = req->dst_len; } else { ret = -EFAULT; } out6: kpp_request_free(req); out5: kzfree(outbuf); out4: crypto_free_kpp(tfm); out3: kzfree(secret); out2: dh_free_data(&dh_inputs); out1: kdf_dealloc(sdesc); return ret; }