Hi Tudor,

> That Bluetooth SMP knows about the private key is pointless, since the
> detection of debug key usage is actually via the public key portion.
> With this patch, the Bluetooth SMP will stop keeping a copy of the
> ecdh private key, except when using debug keys. This way we let the
> crypto subsystem to generate and handle the ecdh private key,
> potentially benefiting of hardware ecc private key generation and
> retention.
> 
> The loop that tries to generate a correct private key is now removed and
> we trust the crypto subsystem to generate a correct private key. This
> backup logic should be done in crypto, if really needed.
> 
> Keeping the private key in the crypto subsystem implies that we can't
> check if we accidentally generated a debug key. As this event is
> unlikely we can live with it when comparing with the benefit of the
> overall change: hardware private key generation and retention.
> 
> Signed-off-by: Tudor Ambarus <tudor.amba...@microchip.com>
> ---
> net/bluetooth/ecdh_helper.c | 102 +++++++++++++++++++++-----------------------
> net/bluetooth/smp.c         |  55 +++++++++---------------
> 2 files changed, 67 insertions(+), 90 deletions(-)
> 
> diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c
> index ac2c708..56e9374 100644
> --- a/net/bluetooth/ecdh_helper.c
> +++ b/net/bluetooth/ecdh_helper.c
> @@ -53,10 +53,10 @@ bool compute_ecdh_secret(struct crypto_kpp *tfm, const u8 
> public_key[64],
>                        const u8 private_key[32], u8 secret[32])
> {
>       struct kpp_request *req;
> -     struct ecdh p;
> +     struct ecdh p = {0};
>       struct ecdh_completion result;
>       struct scatterlist src, dst;
> -     u8 *tmp, *buf;
> +     u8 *tmp, *buf = NULL;
>       unsigned int buf_len;
>       int err = -ENOMEM;
> 
> @@ -70,25 +70,29 @@ bool compute_ecdh_secret(struct crypto_kpp *tfm, const u8 
> public_key[64],
> 
>       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);
> +     if (private_key) {
> +             /* Security Manager Protocol holds digits in little-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 A private Key */
> -     err = crypto_kpp_set_secret(tfm, (void *)buf, buf_len);
> -     if (err)
> -             goto free_all;
> +             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 */
> @@ -126,14 +130,12 @@ bool generate_ecdh_keys(struct crypto_kpp *tfm, u8 
> public_key[64],
>                       u8 private_key[32])
> {
>       struct kpp_request *req;
> -     struct ecdh p;
> +     struct ecdh p = {0};
>       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)
> @@ -145,56 +147,48 @@ bool generate_ecdh_keys(struct crypto_kpp *tfm, u8 
> public_key[64],
> 
>       init_completion(&result.completion);
> 
> +     /* Set private Key */
> +     if (private_key) {
> +             p.key = (char *)private_key;
> +             p.key_size = 32;
> +     }
> +
>       /* 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;
> -             }
> +     crypto_ecdh_encode_key(buf, buf_len, &p);
> +     err = crypto_kpp_set_secret(tfm, buf, buf_len);
> +     if (err)
> +             goto free_all;
> 
> -             /* Private key is not valid. Regenerate */
> -             if (err == -EINVAL)
> -                     continue;
> +     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);
> 
> -             if (err < 0)
> -                     goto free_all;
> -             else
> -                     break;
> +     err = crypto_kpp_generate_public_key(req);
> 
> -     } while (true);
> +     if (err == -EINPROGRESS) {
> +             wait_for_completion(&result.completion);
> +             err = result.err;
> +     }
> +     if (err < 0)
> +             goto free_all;
> 
>       /* 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);
> +     if (private_key) {
> +             swap_digits((u64 *)private_key, (u64 *)tmp, 4);
> +             memcpy(private_key, tmp, 32);
> +     }
> 
> free_all:
>       kzfree(buf);
> diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
> index 6532689..9a8e826 100644
> --- a/net/bluetooth/smp.c
> +++ b/net/bluetooth/smp.c
> @@ -571,28 +571,16 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], 
> u8 rand[16])
>               memcpy(smp->local_pk, debug_pk, 64);
>               memcpy(smp->local_sk, debug_sk, 32);
>               smp->debug_key = true;
> +             SMP_DBG("OOB Private Key:  %32phN", smp->local_sk);
>       } 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))
> -                             return -EIO;
> -
> -                     /* 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))
> -                             break;

I would prefer if we do not loose this part. I mean if the crypto hardware 
accidentally generated the same private key as the private debug key, then we 
want to just redo it. We opted for comparing the private key instead of the 
public key because it is shorter, however just comparing the public key against 
the debug_pk is also just fine.

This is really just some sort of paranoia, but it would be bad if we locally 
classify the generated long term key as real key and the remote side declares 
it as debug key.

> -             }
> +             /* Generate local key pair for Secure Connections */
> +             if (!generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk, NULL))
> +                     return -EIO;
>               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);
> 
> @@ -1899,11 +1887,13 @@ static u8 sc_send_public_key(struct smp_chan *smp)
>               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 (smp_dev->debug_key) {
>                       set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
> +                     memcpy(smp->local_sk, smp_dev->local_sk, 32);
> +                     SMP_DBG("Local Private Key:  %32phN", smp->local_sk);
> +             }

Do we need to keep the smp->local_sk variable at all? I would prefer that this 
is hidden the KPP. Since even in case of debug key generation (software 
fallback), we really do not care about it either. The only thing we tell the 
KPP is that we want it to use debug_sk as its private key and debug_pk as 
public key. So essentially key generation with fixed a fixed key-pair. I think 
it would make the Bluetooth SMP code a lot simpler.

> 
>               goto done;
>       }
> @@ -1913,28 +1903,16 @@ static u8 sc_send_public_key(struct smp_chan *smp)
>               memcpy(smp->local_pk, debug_pk, 64);
>               memcpy(smp->local_sk, debug_sk, 32);
>               set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
> +             SMP_DBG("Local Private Key:  %32phN", smp->local_sk);
>       } 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))
> -                             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))
> -                             break;
> -             }
> +             /* Generate local key pair for Secure Connections */
> +             if (!generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk, NULL))
> +                     return SMP_UNSPECIFIED;
>       }
> 
> 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);
> 
> @@ -2663,6 +2641,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, 
> struct sk_buff *skb)
>       struct smp_chan *smp = chan->data;
>       struct hci_dev *hdev = hcon->hdev;
>       struct crypto_kpp *tfm;
> +     const u8 *local_sk;
>       struct smp_cmd_pairing_confirm cfm;
>       int err;
> 
> @@ -2703,8 +2682,12 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, 
> struct sk_buff *skb)
>               tfm = smp->tfm_ecdh;
>       }
> 
> -     if (!compute_ecdh_secret(tfm, smp->remote_pk, smp->local_sk,
> -                              smp->dhkey))
> +     if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS))
> +             local_sk = smp->local_sk;
> +     else
> +             local_sk = NULL;
> +
> +     if (!compute_ecdh_secret(tfm, smp->remote_pk, local_sk, smp->dhkey))
>               return SMP_UNSPECIFIED;

As with my comment above, should we make the practice a lot simpler here to 
make code less complex. I mean the difference is really in the key generation 
portion.

        if (debug_key)
                genarate_ecdh_debug_keys(tfm_ecdh, smp->local_pk)
        else
                generate_ecdh_keys(tfm_ecdh, smp->local_pk)

And here the generate_ecdh_debug_keys is really just a dumb tfm_ecdh that we 
already know the private and public key pair. We do not have to actually 
generate anything.

However now the secret computation can become simpler.

        compute_ecdh_secret(tfm_ecdh, smp->remote_pk, smp->dhkey)

No matter if it is a debug key or operational key usage, the tfm_ecdh has all 
the key information. If that private key is in hardware or not, or if hardware 
can be used or not, that is a KPP detail.

Regards

Marcel

Reply via email to