diff --git a/wireless/bluetooth/btsak/Makefile b/wireless/bluetooth/btsak/Makefile index 1a48938c4..0d5417ced 100644 --- a/wireless/bluetooth/btsak/Makefile +++ b/wireless/bluetooth/btsak/Makefile @@ -46,7 +46,7 @@ STACKSIZE = 4096 # Bluetooth SAK (Swiss Army Knife) ASRCS = -CSRCS = btsak_advertise.c btsak_scan.c +CSRCS = btsak_advertise.c btsak_scan.c btsak_security.c MAINSRC = btsak_main.c AOBJS = $(ASRCS:.S=$(OBJEXT)) diff --git a/wireless/bluetooth/btsak/btsak.h b/wireless/bluetooth/btsak/btsak.h index 338f447c5..3aa05671e 100644 --- a/wireless/bluetooth/btsak/btsak.h +++ b/wireless/bluetooth/btsak/btsak.h @@ -118,7 +118,7 @@ static inline void btsak_update_ipv6addr(FAR struct btsak_s *btsak) * ****************************************************************************/ -uint8_t btsak_char2nibble(char ch); +int btsak_char2nibble(char ch); /**************************************************************************** * Name: btsak_str2long @@ -177,11 +177,35 @@ int btsak_str2payload(FAR const char *str, FAR uint8_t *buf); * Name: btsak_str2addr * * Description: - * Convert a string 8-byte EADDR array. + * Convert a string of the form "xx:xx:xx:xx:xx:xx" 6-byte Bluetooth + * address (where xx is a one or two character hexadecimal number sub- + * string) * ****************************************************************************/ -void btsak_str2addr(FAR const char *str, FAR uint8_t *addr); +int btsak_str2addr(FAR const char *str, FAR uint8_t *addr); + +/**************************************************************************** + * Name: btsak_str2addrtype + * + * Description: + * Convert a string to an address type. String options are "public" or + * "private". + * + ****************************************************************************/ + +int btsak_str2addrtype(FAR const char *str, FAR uint8_t *addrtype); + +/**************************************************************************** + * Name: btsak_str2seclevel + * + * Description: + * Convert a string to a security level. String options are "low", + * "medium", "high", or "fips" + * + ****************************************************************************/ + +int btsak_str2seclevel(FAR const char *str, FAR enum bt_security_e *level); /**************************************************************************** * Name: btsak_socket diff --git a/wireless/bluetooth/btsak/btsak_advertise.c b/wireless/bluetooth/btsak/btsak_advertise.c index 57a40a6e0..3e115047e 100644 --- a/wireless/bluetooth/btsak/btsak_advertise.c +++ b/wireless/bluetooth/btsak/btsak_advertise.c @@ -197,9 +197,10 @@ static void btsak_cmd_advertisestop(FAR struct btsak_s *btsak, FAR char *cmd, ****************************************************************************/ /**************************************************************************** - * Name: + * Name: btsak_cmd_advertise * * Description: + * advertise [-h] command * ****************************************************************************/ @@ -226,7 +227,7 @@ void btsak_cmd_advertise(FAR struct btsak_s *btsak, int argc, FAR char *argv[]) { btsak_cmd_advertisestart(btsak, argv[0], argc - argind, &argv[argind]); } - else if (strcmp(argv[argind], "-h") == 0) + else if (strcmp(argv[argind], "stop") == 0) { btsak_cmd_advertisestop(btsak, argv[0], argc - argind, &argv[argind]); } diff --git a/wireless/bluetooth/btsak/btsak_main.c b/wireless/bluetooth/btsak/btsak_main.c index 3deea992d..6ef772c2e 100644 --- a/wireless/bluetooth/btsak/btsak_main.c +++ b/wireless/bluetooth/btsak/btsak_main.c @@ -47,6 +47,7 @@ #include #include +#include #include #include "btsak.h" @@ -300,7 +301,7 @@ int bt_main(int argc, char *argv[]) * ****************************************************************************/ -uint8_t btsak_char2nibble(char ch) +int btsak_char2nibble(char ch) { if (ch >= '0' && ch <= '9') { @@ -316,13 +317,13 @@ uint8_t btsak_char2nibble(char ch) } else if (ch == '\0') { - fprintf(stderr, "ERROR: Unexpected end hex\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "ERROR: Unexpected NUL terminator in hex string\n"); + return -EPIPE; } else { - fprintf(stderr, "ERROR: Unexpected character in hex value: %02x\n", ch); - exit(EXIT_FAILURE); + fprintf(stderr, "ERROR: Unexpected end character in hex string\n"); + return -EINVAL; } } @@ -480,38 +481,114 @@ int btsak_str2payload(FAR const char *str, FAR uint8_t *buf) * Name: btsak_str2addr * * Description: - * Convert a string 8-byte EADDR array. + * Convert a string of the form "xx:xx:xx:xx:xx:xx" 6-byte Bluetooth + * address (where xx is a one or two character hexadecimal number sub- + * string) * ****************************************************************************/ -void btsak_str2addr(FAR const char *str, FAR uint8_t *addr) +int btsak_str2addr(FAR const char *str, FAR uint8_t *addr) { FAR const char *src = str; - uint8_t bvalue; + int nibble; + uint8_t hex; char ch; int i; - for (i = 0; i < 8; i++) + for (i = 0; i < 6; i++) { ch = (char)*src++; - bvalue = btsak_char2nibble(ch) << 4; + nibble = btsak_char2nibble(ch) << 4; + if (nibble < 0) + { + return nibble; + } + + hex = (uint8_t)nibble << 4; ch = (char)*src++; - bvalue |= btsak_char2nibble(ch); + nibble = btsak_char2nibble(ch); + if (nibble < 0) + { + return nibble; + } - *addr++ = bvalue; + hex |= (uint8_t)nibble; + *addr++ = hex; - if (i < 7) + if (i < 5) { ch = (char)*src++; if (ch != ':') { - fprintf(stderr, "ERROR: Missing colon separator: %s\n", str); - fprintf(stderr, " Expected xx:xx:xx:xx:xx:xx:xx:xx\n"); - exit(EXIT_FAILURE); + return -EINVAL; } } } + + return OK; +} + +/**************************************************************************** + * Name: btsak_str2addrtype + * + * Description: + * Convert a string to an address type. String options are "public" or + * "private". + * + ****************************************************************************/ + +int btsak_str2addrtype(FAR const char *str, FAR uint8_t *addrtype) +{ + if (!strcasecmp(str, "public") == 0) + { + *addrtype = BT_ADDR_LE_PUBLIC; + } + else if (!strcasecmp(str, "random")) + { + *addrtype = BT_ADDR_LE_RANDOM; + } + else + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: btsak_str2seclevel + * + * Description: + * Convert a string to a security level. String options are "low", + * "medium", "high", or "fips" + * + ****************************************************************************/ + +int btsak_str2seclevel(FAR const char *str, FAR enum bt_security_e *level) +{ + if (!strcasecmp(str, "low") == 0) + { + *level = BT_SECURITY_LOW; + } + else if (!strcasecmp(str, "medium")) + { + *level = BT_SECURITY_MEDIUM; + } + else if (!strcasecmp(str, "high")) + { + *level = BT_SECURITY_HIGH; + } + else if (!strcasecmp(str, "fips")) + { + *level = BT_SECURITY_FIPS; + } + else + { + return -EINVAL; + } + + return OK; } /**************************************************************************** diff --git a/wireless/bluetooth/btsak/btsak_scan.c b/wireless/bluetooth/btsak/btsak_scan.c index 12fb3c249..69e551c68 100644 --- a/wireless/bluetooth/btsak/btsak_scan.c +++ b/wireless/bluetooth/btsak/btsak_scan.c @@ -249,9 +249,10 @@ static void btsak_cmd_scanstop(FAR struct btsak_s *btsak, FAR char *cmd, ****************************************************************************/ /**************************************************************************** - * Name: + * Name: btsak_cmd_scan * * Description: + * scan [-h] command * ****************************************************************************/ diff --git a/wireless/bluetooth/btsak/btsak_security.c b/wireless/bluetooth/btsak/btsak_security.c new file mode 100644 index 000000000..88c02571c --- /dev/null +++ b/wireless/bluetooth/btsak/btsak_security.c @@ -0,0 +1,201 @@ +/**************************************************************************** + * apps/wireless/bluetooth/btsak/btsak_security.c + * Bluetooth Swiss Army Knife -- Security command + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Based loosely on the i8sak IEEE 802.15.4 program by Anthony Merlino and + * Sebastien Lorquet. Commands inspired for btshell example in the + * Intel/Zephyr Arduino 101 package (BSD license). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "btsak.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if !defined(CONFIG_BTSAK_NINSTANCES) || CONFIG_BTSAK_NINSTANCES <= 0 +# undef CONFIG_BTSAK_NINSTANCES +# define CONFIG_BTSAK_NINSTANCES 3 +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: btsak_security_showusage + * + * Description: + * Show usage of the security command + * + ****************************************************************************/ + +static void btsak_security_showusage(FAR const char *progname, + FAR const char *cmd, int exitcode) +{ + fprintf(stderr, "%s:\tEnable security (encryption) for a connection:\n", + cmd); + fprintf(stderr, + "\tIf device is paired, key encryption will be enabled. If the link\n"); + fprintf(stderr, + "\tis already encrypted with sufficiently strong key this function\n"); + fprintf(stderr, + "\tdoes nothing.\n\n"); + fprintf(stderr, + "\tIf the device is not paired pairing will be initiated. If the device\n"); + fprintf(stderr, + "\tis paired and keys are too weak but input output capabilities allow\n"); + fprintf(stderr, + "\tfor strong enough keys pairing will be initiated.\n\n"); + fprintf(stderr, + "\tThis function may return error if required level of security is not\n"); + fprintf(stderr, + "\tpossible to achieve due to local or remote device limitation (eg input\n"); + fprintf(stderr, + "\toutput capabilities).\n\n"); + fprintf(stderr, "Usage:\n\n"); + fprintf(stderr, "\t%s %s [-h] \n", + progname, cmd); + fprintf(stderr, + "\nWhere:\n\n"); + fprintf(stderr, + "\t\t- The 6-byte address of the connected peer\n"); + fprintf(stderr, + "\t\t- Either \"public\" or \"random\"\n"); + fprintf(stderr, + "\t\t- Security level, on of:\n\n"); + fprintf(stderr, + "\t\tlow\t- No encryption and no authentication\n"); + fprintf(stderr, + "\t\tmedium\t- Encryption and no authentication (no MITM)\n"); + fprintf(stderr, + "\t\thigh\t- Encryption and authentication (MITM)\n"); + fprintf(stderr, + "\t\tfips\t- Authenticated LE secure connections and encryption\n"); + exit(exitcode); +} + +/**************************************************************************** + * Public functions + ****************************************************************************/ + +/**************************************************************************** + * Name: btsak_cmd_security + * + * Description: + * security [-h] command + * + ****************************************************************************/ + +void btsak_cmd_security(FAR struct btsak_s *btsak, int argc, FAR char *argv[]) +{ + struct bt_security_s sec; + int sockfd; + int ret; + + /* Check for help */ + + if (argc < 2) + { + fprintf(stderr, "ERROR: Missing required arguments/n"); + btsak_security_showusage(btsak->progname, argv[0], EXIT_FAILURE); + } + + if (strcmp(argv[1], "-h") == 0) + { + btsak_security_showusage(btsak->progname, argv[0], EXIT_SUCCESS); + } + + /* Verify that all required arguments were provided */ + + if (argc < 4) + { + fprintf(stderr, "ERROR: Missing required arguments/n"); + btsak_security_showusage(btsak->progname, argv[0], EXIT_FAILURE); + } + + /* The first argument must be an address of the form xx:xx:xx:xx:xx:xx */ + + ret = btsak_str2addr(argv[1], sec.se_addr.val); + if (ret < 0) + { + fprintf(stderr, "ERROR: Invalid address string: %s/n", argv[1]); + btsak_security_showusage(btsak->progname, argv[0], EXIT_FAILURE); + } + + /* The second address is the address type, either "public" or "random" */ + + ret = btsak_str2addrtype(argv[2], &sec.se_addr.type); + if (ret < 0) + { + fprintf(stderr, "ERROR: Invalid address type: %s/n", argv[2]); + btsak_security_showusage(btsak->progname, argv[0], EXIT_FAILURE); + } + + /* The third argument is the security level */ + + ret = btsak_str2seclevel(argv[3], &sec.se_level); + + /* Perform the IOCTL to stop advertising */ + + strncpy(sec.se_name, btsak->ifname, HCI_DEVNAME_SIZE); + + sockfd = btsak_socket(btsak); + if (sockfd >= 0) + { + ret = ioctl(sockfd, SIOCBT_SECURITY, (unsigned long)((uintptr_t)&sec)); + if (ret < 0) + { + fprintf(stderr, "ERROR: ioctl(SIOCBT_SECURITY) failed: %d\n", + errno); + } + } + + close(sockfd); +}