out[i] = __swab64(in[ndigits - 1 - i]);
 }
 
+/* compute_ecdh_secret() - function assumes that the private key was
+ *                         already set.
+ * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
+ * @public_key:   pair's ecc public key.
+ * secret:        memory where the ecdh computed shared secret will be saved.
+ *
+ * Return: zero on success; error code in case of error.
+ */
 int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 public_key[64],
-                       const u8 private_key[32], u8 secret[32])
+                       u8 secret[32])
 {
        struct kpp_request *req;
-       struct ecdh p;
+       u8 *tmp;
        struct ecdh_completion result;
        struct scatterlist src, dst;
-       u8 *tmp, *buf;
-       unsigned int buf_len;
        int err;
 
        tmp = kmalloc(64, GFP_KERNEL);
 
        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) {
-               err = -ENOMEM;
-               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 */
 
        memcpy(secret, tmp, 32);
 
 free_all:
-       kzfree(buf);
-free_req:
        kpp_request_free(req);
 free_tmp:
        kzfree(tmp);
        return err;
 }
 
-int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64],
-                      u8 private_key[32])
+/* set_ecdh_privkey() - set or generate ecc private key.
+ *
+ * Function generates an ecc private key in the crypto subsystem when receiving
+ * a NULL private key or sets the received key when not NULL.
+ *
+ * @tfm:           KPP tfm handle allocated with crypto_alloc_kpp().
+ * @private_key:   user's ecc private key. When not NULL, the key is expected
+ *                 in little endian format.
+ *
+ * Return: zero on success; error code in case of error.
+ */
+int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 private_key[32])
+{
+       u8 *buf, *tmp = NULL;
+       unsigned int buf_len;
+       int err;
+       struct ecdh p = {0};
+
+       p.curve_id = ECC_CURVE_NIST_P256;
+
+       if (private_key) {
+               tmp = kmalloc(32, GFP_KERNEL);
+               if (!tmp)
+                       return -ENOMEM;
+               swap_digits((u64 *)private_key, (u64 *)tmp, 4);
+               p.key = tmp;
+               p.key_size = 32;
+       }
+
+       buf_len = crypto_ecdh_key_len(&p);
+       buf = kmalloc(buf_len, GFP_KERNEL);
+       if (!buf) {
+               err = -ENOMEM;
+               goto free_tmp;
+       }
+
+       err = crypto_ecdh_encode_key(buf, buf_len, &p);
+       if (err)
+               goto free_all;
+
+       err = crypto_kpp_set_secret(tfm, buf, buf_len);
+       /* fall through */
+free_all:
+       kzfree(buf);
+free_tmp:
+       kzfree(tmp);
+       return err;
+}
+
+/* generate_ecdh_public_key() - function assumes that the private key was
+ *                              already set.
+ *
+ * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
+ * @public_key:   memory where the computed ecc public key will be saved.
+ *
+ * Return: zero on success; error code in case of error.
+ */
+int generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64])
 {
        struct kpp_request *req;
-       struct ecdh p;
+       u8 *tmp;
        struct ecdh_completion result;
        struct scatterlist dst;
-       u8 *tmp, *buf;
-       unsigned int buf_len;
        int err;
-       const unsigned short max_tries = 16;
-       unsigned short tries = 0;
 
        tmp = kmalloc(64, GFP_KERNEL);
        if (!tmp)
        }
 
        init_completion(&result.completion);
+       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);
 
-       /* 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
+       err = crypto_kpp_generate_public_key(req);
+       if (err == -EINPROGRESS) {
+               wait_for_completion(&result.completion);
+               err = result.err;
+       }
+       if (err < 0)
+               goto free_all;
+
+       /* The public key is handed back in little endian as expected by
+        * the 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_tmp:
        kfree(tmp);
        return err;
 }
+
+/* generate_ecdh_keys() - generate ecc key pair.
+ *
+ * @tfm:          KPP tfm handle allocated with crypto_alloc_kpp().
+ * @public_key:   memory where the computed ecc public key will be saved.
+ *
+ * Return: zero on success; error code in case of error.
+ */
+int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64])
+{
+       int err;
+
+       err = set_ecdh_privkey(tfm, NULL);
+       if (err)
+               return err;
+
+       return generate_ecdh_public_key(tfm, public_key);
+}
 
 #include <crypto/kpp.h>
 #include <linux/types.h>
 
-int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 pub_a[64],
-                       const u8 priv_b[32], u8 secret[32]);
-int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64],
-                      u8 private_key[32]);
+int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 pair_public_key[64],
+                       u8 secret[32]);
+int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 *private_key);
+int generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64]);
+int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64]);
 
        dhkey_a = &tmp[0];
        dhkey_b = &tmp[32];
 
-       ret = compute_ecdh_secret(tfm, pub_b, priv_a, dhkey_a);
+       ret = set_ecdh_privkey(tfm, priv_a);
        if (ret)
                goto out;
 
-       ret = compute_ecdh_secret(tfm, pub_a, priv_b, dhkey_b);
+       ret = compute_ecdh_secret(tfm, pub_b, dhkey_a);
        if (ret)
                goto out;
 
                goto out;
        }
 
+       ret = set_ecdh_privkey(tfm, priv_b);
+       if (ret)
+               goto out;
+
+       ret = compute_ecdh_secret(tfm, pub_a, dhkey_b);
+       if (ret)
+               goto out;
+
        if (memcmp(dhkey_b, dhkey, 32))
                ret = -EINVAL;
-
+       /* fall through*/
 out:
        kfree(tmp);
        return ret;
 
 struct smp_dev {
        /* Secure Connections OOB data */
        u8                      local_pk[64];
-       u8                      local_sk[32];
        u8                      local_rand[16];
        bool                    debug_key;
 
 
        /* Secure Connections variables */
        u8                      local_pk[64];
-       u8                      local_sk[32];
        u8                      remote_pk[64];
        u8                      dhkey[32];
        u8                      mackey[16];
 
        if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
                BT_DBG("Using debug keys");
+               err = set_ecdh_privkey(smp->tfm_ecdh, debug_sk);
+               if (err)
+                       return err;
                memcpy(smp->local_pk, debug_pk, 64);
-               memcpy(smp->local_sk, debug_sk, 32);
                smp->debug_key = true;
        } else {
                while (true) {
-                       /* Seed private key with random number */
-                       get_random_bytes(smp->local_sk, 32);
-
-                       /* Generate local key pair for Secure Connections */
-                       err = generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk,
-                                                smp->local_sk);
+                       /* Generate key pair for Secure Connections */
+                       err = generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk);
                        if (err)
                                return err;
 
                        /* This is unlikely, but we need to check that
                         * we didn't accidentially generate a debug key.
                         */
-                       if (crypto_memneq(smp->local_sk, debug_sk, 32))
+                       if (crypto_memneq(smp->local_pk, debug_pk, 64))
                                break;
                }
                smp->debug_key = false;
 
        SMP_DBG("OOB Public Key X: %32phN", smp->local_pk);
        SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32);
-       SMP_DBG("OOB Private Key:  %32phN", smp->local_sk);
 
        get_random_bytes(smp->local_rand, 16);
 
                smp_dev = chan->data;
 
                memcpy(smp->local_pk, smp_dev->local_pk, 64);
-               memcpy(smp->local_sk, smp_dev->local_sk, 32);
                memcpy(smp->lr, smp_dev->local_rand, 16);
 
                if (smp_dev->debug_key)
 
        if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
                BT_DBG("Using debug keys");
+               if (set_ecdh_privkey(smp->tfm_ecdh, debug_sk))
+                       return SMP_UNSPECIFIED;
                memcpy(smp->local_pk, debug_pk, 64);
-               memcpy(smp->local_sk, debug_sk, 32);
                set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
        } else {
                while (true) {
-                       /* Seed private key with random number */
-                       get_random_bytes(smp->local_sk, 32);
-
-                       /* Generate local key pair for Secure Connections */
-                       if (generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk,
-                                              smp->local_sk))
+                       /* Generate key pair for Secure Connections */
+                       if (generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk))
                                return SMP_UNSPECIFIED;
 
                        /* This is unlikely, but we need to check that
                         * we didn't accidentially generate a debug key.
                         */
-                       if (crypto_memneq(smp->local_sk, debug_sk, 32))
+                       if (crypto_memneq(smp->local_pk, debug_pk, 64))
                                break;
                }
        }
 done:
        SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
        SMP_DBG("Local Public Key Y: %32phN", smp->local_pk + 32);
-       SMP_DBG("Local Private Key:  %32phN", smp->local_sk);
 
        smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
 
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
        struct hci_dev *hdev = hcon->hdev;
+       struct crypto_kpp *tfm_ecdh;
        struct smp_cmd_pairing_confirm cfm;
        int err;
 
        SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
        SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32);
 
-       if (!compute_ecdh_secret(smp->tfm_ecdh, smp->remote_pk, smp->local_sk,
-                                smp->dhkey))
+       /* Compute the shared secret on the same crypto tfm on which the private
+        * key was set/generated.
+        */
+       if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) {
+               struct smp_dev *smp_dev = chan->data;
+
+               tfm_ecdh = smp_dev->tfm_ecdh;
+       } else {
+               tfm_ecdh = smp->tfm_ecdh;
+       }
+
+       if (compute_ecdh_secret(tfm_ecdh, smp->remote_pk, smp->dhkey))
                return SMP_UNSPECIFIED;
 
        SMP_DBG("DHKey %32phN", smp->dhkey);
 
 #if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
 
-static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits)
-{
-       int i;
-
-       for (i = 0; i < ndigits; i++)
-               out[i] = __swab64(in[ndigits - 1 - i]);
-}
-
 static int __init test_debug_key(struct crypto_kpp *tfm_ecdh)
 {
-       u8 pk[64], sk[32];
+       u8 pk[64];
        int err;
 
-       swap_digits((u64 *)debug_sk, (u64 *)sk, 4);
-
-       err = generate_ecdh_keys(tfm_ecdh, pk, sk);
+       err = set_ecdh_privkey(tfm_ecdh, debug_sk);
        if (err)
                return err;
 
-       if (crypto_memneq(sk, debug_sk, 32))
-               return -EINVAL;
+       err = generate_ecdh_public_key(tfm_ecdh, pk);
+       if (err)
+               return err;
 
        if (crypto_memneq(pk, debug_pk, 64))
                return -EINVAL;