Commit 605e826d authored by Salvatore Benedetto's avatar Salvatore Benedetto Committed by Sultan Alsawaf

Bluetooth: convert smp and selftest to crypto kpp API

* Convert both smp and selftest to crypto kpp API
* Remove module ecc as no more required
* Add ecdh_helper functions for wrapping kpp async calls

This patch has been tested *only* with selftest, which is called on
module loading.
Signed-off-by: default avatarSalvatore Benedetto <salvatore.benedetto@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>

CVE-2018-5383

(cherry picked from commit 58771c1c)
Signed-off-by: default avatarPaolo Pisati <paolo.pisati@canonical.com>
Acked-by: default avatarStefan Bader <stefan.bader@canonical.com>
Acked-by: default avatarConnor Kuehl <connor.kuehl@canonical.com>
Signed-off-by: default avatarKhalid Elmously <khalid.elmously@canonical.com>
parent 04d384e6
...@@ -13,6 +13,7 @@ menuconfig BT ...@@ -13,6 +13,7 @@ menuconfig BT
select CRYPTO_CMAC select CRYPTO_CMAC
select CRYPTO_ECB select CRYPTO_ECB
select CRYPTO_SHA256 select CRYPTO_SHA256
select CRYPTO_ECDH
help help
Bluetooth is low-cost, low-power, short-range wireless technology. Bluetooth is low-cost, low-power, short-range wireless technology.
It was designed as a replacement for cables and other short-range It was designed as a replacement for cables and other short-range
......
...@@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o ...@@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
ecc.o hci_request.o mgmt_util.o ecdh_helper.o hci_request.o mgmt_util.o
bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
......
This diff is collapsed.
/*
* Copyright (c) 2013, Kenneth MacKay
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Create a public/private key pair.
* Outputs:
* public_key - Will be filled in with the public key.
* private_key - Will be filled in with the private key.
*
* Returns true if the key pair was generated successfully, false
* if an error occurred. The keys are with the LSB first.
*/
bool ecc_make_key(u8 public_key[64], u8 private_key[32]);
/* Compute a shared secret given your secret key and someone else's
* public key.
* Note: It is recommended that you hash the result of ecdh_shared_secret
* before using it for symmetric encryption or HMAC.
*
* Inputs:
* public_key - The public key of the remote party
* private_key - Your private key.
*
* Outputs:
* secret - Will be filled in with the shared secret value.
*
* Returns true if the shared secret was generated successfully, false
* if an error occurred. Both input and output parameters are with the
* LSB first.
*/
bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
u8 secret[32]);
/*
* ECDH helper functions - KPP wrappings
*
* Copyright (C) 2017 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
* CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
* COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
* SOFTWARE IS DISCLAIMED.
*/
#include "ecdh_helper.h"
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <crypto/kpp.h>
#include <crypto/ecdh.h>
struct ecdh_completion {
struct completion completion;
int err;
};
static void ecdh_complete(struct crypto_async_request *req, int err)
{
struct ecdh_completion *res = req->data;
if (err == -EINPROGRESS)
return;
res->err = err;
complete(&res->completion);
}
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]);
}
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[64];
u8 *buf;
unsigned int buf_len;
int err = -ENOMEM;
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));
return false;
}
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) {
pr_err("alg: kpp: Failed to allocate %d bytes for buf\n",
buf_len);
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);
return (err == 0);
}
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[64];
u8 *buf;
unsigned int buf_len;
int err = -ENOMEM;
const unsigned short max_tries = 16;
unsigned short tries = 0;
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));
return false;
}
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) {
pr_err("alg: kpp: Failed to allocate %d bytes for buf\n",
buf_len);
goto free_req;
}
do {
if (tries++ >= max_tries)
goto free_all;
get_random_bytes(private_key, 32);
/* 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_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);
return (err == 0);
}
/*
* ECDH helper functions - KPP wrappings
*
* Copyright (C) 2017 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
* CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
* COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
* SOFTWARE IS DISCLAIMED.
*/
#include <linux/types.h>
bool compute_ecdh_secret(const u8 pub_a[64], const u8 priv_b[32],
u8 secret[32]);
bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]);
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include "ecc.h" #include "ecdh_helper.h"
#include "smp.h" #include "smp.h"
#include "selftest.h" #include "selftest.h"
...@@ -144,8 +144,8 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], ...@@ -144,8 +144,8 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
{ {
u8 dhkey_a[32], dhkey_b[32]; u8 dhkey_a[32], dhkey_b[32];
ecdh_shared_secret(pub_b, priv_a, dhkey_a); compute_ecdh_secret(pub_b, priv_a, dhkey_a);
ecdh_shared_secret(pub_a, priv_b, dhkey_b); compute_ecdh_secret(pub_a, priv_b, dhkey_b);
if (memcmp(dhkey_a, dhkey, 32)) if (memcmp(dhkey_a, dhkey, 32))
return -EINVAL; return -EINVAL;
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include <net/bluetooth/l2cap.h> #include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h> #include <net/bluetooth/mgmt.h>
#include "ecc.h" #include "ecdh_helper.h"
#include "smp.h" #include "smp.h"
#define SMP_DEV(hdev) \ #define SMP_DEV(hdev) \
...@@ -572,7 +572,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) ...@@ -572,7 +572,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
} else { } else {
while (true) { while (true) {
/* Generate local key pair for Secure Connections */ /* Generate local key pair for Secure Connections */
if (!ecc_make_key(smp->local_pk, smp->local_sk)) if (!generate_ecdh_keys(smp->local_pk, smp->local_sk))
return -EIO; return -EIO;
/* This is unlikely, but we need to check that /* This is unlikely, but we need to check that
...@@ -1870,7 +1870,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) ...@@ -1870,7 +1870,7 @@ static u8 sc_send_public_key(struct smp_chan *smp)
} else { } else {
while (true) { while (true) {
/* Generate local key pair for Secure Connections */ /* Generate local key pair for Secure Connections */
if (!ecc_make_key(smp->local_pk, smp->local_sk)) if (!generate_ecdh_keys(smp->local_pk, smp->local_sk))
return SMP_UNSPECIFIED; return SMP_UNSPECIFIED;
/* This is unlikely, but we need to check that /* This is unlikely, but we need to check that
...@@ -2665,7 +2665,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -2665,7 +2665,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk); SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32); SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32);
if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) if (!compute_ecdh_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
return SMP_UNSPECIFIED; return SMP_UNSPECIFIED;
SMP_DBG("DHKey %32phN", smp->dhkey); SMP_DBG("DHKey %32phN", smp->dhkey);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment