Commit cb18797b authored by Kishen Maloor's avatar Kishen Maloor

oc_obt:Add onboarding support with Random PIN OTM

This change adds new onboarding APIs to oc_obt for handling the Random PIN
OTM. Specifically, it introduces two new APIs: one for requesting the peer
device to generate (and display) a Random PIN, and one for executing the
Random PIN OTM sequence in a TLS_PSK handshake that employs the PIN in
deriving the symmetric key used for the handshake.

OBT implementations are expected to invoke the first API and following a
success response, accept the PIN via user input using any means defined by
the application. Subsequently, the OBT invokes the second API, supplying
the PIN for carrying out the entire OTM sequence.

The sample onboarding_tool has been updated to support Random PIN OTM using
these two APIs.

This change also adds a new API oc_obt_shutdown() to be called by OBT
implementations when they're about to terminate. This frees all internally
allocated resources.

Change-Id: I8ef2df9ffc08aa34c658c6d4c80ea1077a1bc9d9
Signed-off-by: Kishen Maloor's avatarKishen Maloor <kishen.maloor@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/29388Tested-by: default avatarIoTivity Jenkins <jenkins-daemon@iotivity.org>
parent ce2177ae
/*
// Copyright (c) 2017 Intel Corporation
// Copyright (c) 2017-2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
......@@ -34,6 +34,8 @@ typedef void (*oc_obt_status_cb_t)(int, void *);
/* Call once at startup for OBT initialization */
void oc_obt_init(void);
/* Called when the OBT terminates to free all resources */
void oc_obt_shutdown(void);
/* Device discovery */
int oc_obt_discover_unowned_devices(oc_obt_discovery_cb_t cb, void *data);
......@@ -42,6 +44,11 @@ int oc_obt_discover_owned_devices(oc_obt_discovery_cb_t cb, void *data);
/* Perform ownership transfer */
int oc_obt_perform_just_works_otm(oc_uuid_t *uuid, oc_obt_device_status_cb_t cb,
void *data);
int oc_obt_request_random_pin(oc_uuid_t *uuid, oc_obt_device_status_cb_t cb,
void *data);
int oc_obt_perform_random_pin_otm(oc_uuid_t *uuid, const unsigned char *pin,
size_t pin_len, oc_obt_device_status_cb_t cb,
void *data);
/* RESET device state */
int oc_obt_device_hard_reset(oc_uuid_t *uuid, oc_obt_device_status_cb_t cb,
......
......@@ -46,7 +46,6 @@ OC_LIST(unowned_devices);
#if defined (_WIN32)
static HANDLE event_thread;
//static HANDLE app_sync_lock;
static CRITICAL_SECTION app_sync_lock;
static CONDITION_VARIABLE cv;
static CRITICAL_SECTION cs;
......@@ -79,11 +78,13 @@ display_menu(void)
PRINT("[1] Discover un-owned devices\n");
PRINT("[2] Discover owned devices\n");
PRINT("-----------------------------------------------\n");
PRINT("[3] Take ownership of device (Just-works)\n");
PRINT("[4] Provision pair-wise credentials\n");
PRINT("[5] Provision ACE2\n");
PRINT("[3] Just-Works Ownership Transfer Method\n");
PRINT("[4] Request Random PIN from device for OTM\n");
PRINT("[5] Random PIN Ownership Transfer Method\n");
PRINT("[6] Provision pair-wise credentials\n");
PRINT("[7] Provision ACE2\n");
PRINT("-----------------------------------------------\n");
PRINT("[6] RESET device\n");
PRINT("[8] RESET device\n");
PRINT("-----------------------------------------------\n");
PRINT("[9] Exit\n");
PRINT("################################################\n");
......@@ -155,6 +156,7 @@ ocf_event_thread(LPVOID lpParam)
}
oc_main_shutdown();
oc_obt_shutdown();
return TRUE;
}
#elif defined(__linux__)
......@@ -179,6 +181,7 @@ ocf_event_thread(void *data)
otb_mutex_unlock(mutex);
}
oc_main_shutdown();
oc_obt_shutdown();
return NULL;
}
#endif
......@@ -273,6 +276,7 @@ discover_owned_devices(void)
otb_mutex_lock(app_sync_lock);
oc_obt_discover_owned_devices(owned_device_cb, NULL);
otb_mutex_unlock(app_sync_lock);
signal_event_loop();
}
static void
......@@ -284,6 +288,124 @@ discover_unowned_devices(void)
signal_event_loop();
}
static void
otm_rdp_cb(oc_uuid_t *uuid, int status, void *data)
{
(void)data;
char di[37];
oc_uuid_to_str(uuid, di, 37);
if (status >= 0) {
PRINT("\nSuccessfully performed OTM on device %s\n", di);
add_device_to_list(uuid, owned_devices);
} else {
PRINT("\nERROR performing ownership transfer on device %s\n", di);
}
}
static void
otm_rdp(void)
{
if (oc_list_length(unowned_devices) == 0) {
PRINT("\nPlease Re-discover Unowned devices\n");
return;
}
device_handle_t *device = (device_handle_t *)oc_list_head(unowned_devices);
device_handle_t *devices[MAX_NUM_DEVICES];
int i = 0, c;
PRINT("\nUnowned Devices:\n");
while (device != NULL) {
char di[OC_UUID_LEN];
oc_uuid_to_str(&device->uuid, di, OC_UUID_LEN);
PRINT("[%d]: %s\n", i, di);
devices[i] = device;
i++;
device = device->next;
}
PRINT("\n\nSelect device: ");
SCANF("%d", &c);
if (c < 0 || c >= i) {
PRINT("ERROR: Invalid selection\n");
return;
}
unsigned char pin[24];
PRINT("\nEnter Random PIN: ");
SCANF("%10s", pin);
otb_mutex_lock(app_sync_lock);
int ret = oc_obt_perform_random_pin_otm(
&devices[c]->uuid, pin, strlen((const char *)pin), otm_rdp_cb, NULL);
if (ret >= 0) {
PRINT("\nSuccessfully issued request to perform Random PIN OTM\n");
} else {
PRINT("\nERROR issuing request to perform Random PIN OTM\n");
}
/* Having issued an OTM request, remove this item from the unowned device list
*/
remove_device_from_list(&devices[c]->uuid, unowned_devices);
otb_mutex_unlock(app_sync_lock);
}
static void
random_pin_cb(oc_uuid_t *uuid, int status, void *data)
{
(void)data;
char di[37];
oc_uuid_to_str(uuid, di, 37);
if (status >= 0) {
PRINT("\nSuccessfully requested device %s to generate a Random PIN\n", di);
} else {
PRINT("\nERROR requesting device %s to generate a Random PIN\n", di);
}
}
static void
request_random_pin(void)
{
if (oc_list_length(unowned_devices) == 0) {
PRINT("\nPlease Re-discover Unowned devices\n");
return;
}
device_handle_t *device = (device_handle_t *)oc_list_head(unowned_devices);
device_handle_t *devices[MAX_NUM_DEVICES];
int i = 0, c;
PRINT("\nUnowned Devices:\n");
while (device != NULL) {
char di[OC_UUID_LEN];
oc_uuid_to_str(&device->uuid, di, OC_UUID_LEN);
PRINT("[%d]: %s\n", i, di);
devices[i] = device;
i++;
device = device->next;
}
PRINT("\n\nSelect device: ");
SCANF("%d", &c);
if (c < 0 || c >= i) {
PRINT("ERROR: Invalid selection\n");
return;
}
otb_mutex_lock(app_sync_lock);
int ret = oc_obt_request_random_pin(&devices[c]->uuid, random_pin_cb, NULL);
if (ret >= 0) {
PRINT("\nSuccessfully issued request to generate a random PIN\n");
} else {
PRINT("\nERROR issuing request to generate random PIN\n");
}
otb_mutex_unlock(app_sync_lock);
}
static void
otm_just_works_cb(oc_uuid_t *uuid, int status, void *data)
{
......@@ -293,13 +415,14 @@ otm_just_works_cb(oc_uuid_t *uuid, int status, void *data)
if (status >= 0) {
PRINT("\nSuccessfully performed OTM on device %s\n", di);
add_device_to_list(uuid, unowned_devices);
} else {
PRINT("\nERROR performing ownership transfer on device %s\n", di);
}
}
static void
take_ownership_of_device(void)
otm_just_works(void)
{
if (oc_list_length(unowned_devices) == 0) {
PRINT("\nPlease Re-discover Unowned devices\n");
......@@ -744,15 +867,21 @@ main(void)
discover_owned_devices();
break;
case 3:
take_ownership_of_device();
otm_just_works();
break;
case 4:
provision_credentials();
request_random_pin();
break;
case 5:
provision_ace2();
otm_rdp();
break;
case 6:
provision_credentials();
break;
case 7:
provision_ace2();
break;
case 8:
reset_device();
break;
case 9:
......@@ -768,5 +897,18 @@ main(void)
#elif defined(__linux__)
pthread_join(event_thread, NULL);
#endif
/* Free all device_handle_t objects allocated by this application */
device_handle_t *device = (device_handle_t *)oc_list_pop(owned_devices);
while (device) {
oc_memb_free(&device_handles, device);
device = (device_handle_t *)oc_list_pop(owned_devices);
}
device = (device_handle_t *)oc_list_pop(unowned_devices);
while (device) {
oc_memb_free(&device_handles, device);
device = (device_handle_t *)oc_list_pop(unowned_devices);
}
return 0;
}
......@@ -91,7 +91,7 @@ HEADERS_TINYCBOR = $(wildcard ../../deps/tinycbor/src/*.h)
CFLAGS=-fPIC -fno-asynchronous-unwind-tables -fno-omit-frame-pointer -ffreestanding -Os -fno-stack-protector -ffunction-sections -fdata-sections -fno-reorder-functions -fno-defer-pop -fno-strict-overflow -I./ -I../../include/ -I../../ -std=gnu99 -Wall -Wextra -Werror -pedantic #-Wl,-Map,client.map
OBJ_COMMON=$(addprefix obj/,$(notdir $(SRC_COMMON:.c=.o)))
OBJ_CLIENT=$(addprefix obj/client/,$(notdir $(SRC:.c=.o)))
OBJ_SERVER=$(addprefix obj/server/,$(filter-out oc_obt.o oc_obt_otm_justworks.o,$(notdir $(SRC:.c=.o))))
OBJ_SERVER=$(addprefix obj/server/,$(filter-out oc_obt.o oc_obt_otm_justworks.o oc_obt_otm_randompin.o,$(notdir $(SRC:.c=.o))))
OBJ_CLIENT_SERVER=$(addprefix obj/client_server/,$(notdir $(SRC:.c=.o)))
VPATH=../../messaging/coap/:../../util/:../../api/:../../deps/tinycbor/src/:../../deps/mbedtls/library:../../api/c-timestamp:
LIBS?= -lm -pthread -lrt
......@@ -123,7 +123,7 @@ ifneq ($(SECURE),0)
SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS})
MBEDTLS_PATCH_FILE := $(MBEDTLS_DIR)/patched.txt
ifeq ($(DYNAMIC),1)
SRC += ../../security/oc_obt.c ../../security/oc_obt_otm_justworks.c
SRC += ../../security/oc_obt.c ../../security/oc_obt_otm_justworks.c ../../security/oc_obt_otm_randompin.c
SAMPLES += ${OBT}
else
SRC_COMMON += $(MBEDTLS_DIR)/library/memory_buffer_alloc.c
......
/*
// Copyright (c) 2019 Intel Corporation
//
// Li!censed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#ifdef OC_SECURITY
#ifndef OC_DYNAMIC_ALLOCATION
#error "ERROR: Please rebuild with OC_DYNAMIC_ALLOCATION"
#endif /* !OC_DYNAMIC_ALLOCATION */
#include "oc_core_res.h"
#include "oc_obt.h"
#include "security/oc_acl.h"
#include "security/oc_cred.h"
#include "security/oc_doxm.h"
#include "security/oc_obt_internal.h"
#include "security/oc_pstat.h"
#include "security/oc_store.h"
#include "security/oc_tls.h"
/* Random PIN OTM */
static void
obt_rdp_11(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_11");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_11;
}
/** 11) <close DTLS>
*/
oc_dostype_t s = oc_obt_parse_dos(data->payload);
if (s == OC_DOS_RFNOP) {
oc_obt_free_otm_ctx(o, 0, OC_OBT_OTM_RDP);
return;
}
err_obt_rdp_11:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_10(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_10");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_10;
}
/** 10) get pstat s=rfnop?
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_do_get("/oic/sec/pstat", ep, NULL, &obt_rdp_11, HIGH_QOS, o)) {
return;
}
err_obt_rdp_10:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_9(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_9");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_9;
}
oc_dostype_t s = oc_obt_parse_dos(data->payload);
if (s == OC_DOS_RFPRO) {
/** 9) post pstat s=rfnop, isop=true
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_10, HIGH_QOS, o)) {
oc_rep_start_root_object();
oc_rep_set_object(root, dos);
oc_rep_set_int(dos, s, OC_DOS_RFNOP);
oc_rep_close_object(root, dos);
oc_rep_end_root_object();
if (oc_do_post()) {
return;
}
}
}
err_obt_rdp_9:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_8(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_8");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_8;
}
/** 8) get pstat s=rfpro?
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_do_get("/oic/sec/pstat", ep, NULL, &obt_rdp_9, HIGH_QOS, o)) {
return;
}
err_obt_rdp_8:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_7(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_7");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_7;
}
/** 7) post pstat s=rfpro
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_8, HIGH_QOS, o)) {
oc_rep_start_root_object();
oc_rep_set_object(root, dos);
oc_rep_set_int(dos, s, OC_DOS_RFPRO);
oc_rep_close_object(root, dos);
oc_rep_end_root_object();
if (oc_do_post()) {
return;
}
}
err_obt_rdp_7:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_6(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_6");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_6;
}
/** 6) post doxm owned = true
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_init_post("/oic/sec/doxm", ep, NULL, &obt_rdp_7, HIGH_QOS, o)) {
oc_rep_start_root_object();
oc_rep_set_boolean(root, owned, true);
oc_rep_end_root_object();
if (oc_do_post()) {
return;
}
}
err_obt_rdp_6:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_5(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_5");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_5;
}
oc_sec_dump_cred(0);
/** 5) post pstat rowneruuid
*/
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_6, HIGH_QOS, o)) {
oc_uuid_t *my_uuid = oc_core_get_device_id(0);
char uuid[OC_UUID_LEN];
oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN);
oc_rep_start_root_object();
oc_rep_set_text_string(root, rowneruuid, uuid);
oc_rep_end_root_object();
if (oc_do_post()) {
return;
}
}
err_obt_rdp_5:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_4(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_4");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_4;
}
oc_device_t *device = o->device;
oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
oc_uuid_t *my_uuid = oc_core_get_device_id(0);
char uuid[OC_UUID_LEN];
oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN);
char suuid[OC_UUID_LEN];
oc_uuid_to_str(&device->uuid, suuid, OC_UUID_LEN);
#define OXM_RDP "oic.sec.doxm.rdp"
uint8_t key[16];
bool derived =
oc_sec_derive_owner_psk(ep, (const uint8_t *)OXM_RDP, strlen(OXM_RDP),
device->uuid.id, 16, my_uuid->id, 16, key, 16);
#undef OXM_RDP
if (!derived) {
goto err_obt_rdp_4;
}
int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK,
OC_CREDUSAGE_NULL, suuid, OC_ENCODING_RAW,
16, key, 0, 0, NULL, NULL, NULL);
if (credid == -1) {
goto err_obt_rdp_4;
}
/** 4) post cred rowneruuid, cred
*/
if (oc_init_post("/oic/sec/cred", ep, NULL, &obt_rdp_5, HIGH_QOS, o)) {
oc_rep_start_root_object();
oc_rep_set_array(root, creds);
oc_rep_object_array_start_item(creds);
oc_rep_set_int(creds, credtype, 1);
oc_rep_set_text_string(creds, subjectuuid, uuid);
oc_rep_set_object(creds, privatedata);
oc_rep_set_text_string(privatedata, encoding, "oic.sec.encoding.raw");
oc_rep_close_object(creds, privatedata);
oc_rep_object_array_end_item(creds);
oc_rep_close_array(root, creds);
oc_rep_set_text_string(root, rowneruuid, uuid);
oc_rep_end_root_object();
if (oc_do_post()) {
return;
}
}
err_obt_rdp_4:
oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP);
}
static void
obt_rdp_3(oc_client_response_t *data)
{
if (!oc_obt_is_otm_ctx_valid(data->user_data)) {
return;
}
OC_DBG("In obt_rdp_3");
oc_otm_ctx_t *o = (oc_otm_ctx_t *)data->user_data;
if (data->code >= OC_STATUS_BAD_REQUEST) {
goto err_obt_rdp_3;
}
oc_uuid_t peer;
oc_rep_t *rep = data->payload;
while (rep != NULL) {
switch (rep->type) {
case OC_REP_STRING:
if (oc_string_len(rep->name) == 10 &&
memcmp(oc_string(rep->name), "deviceuuid", 10) == 0) {
oc_str_to_uuid(oc_string(rep->value.string), &peer);
}
break;
default:
break;
}
rep = rep->next;
}
/** 3) <store peer uuid> ; post acl rowneruuid
*/
oc_device_t *device = o->device;
/* Free temporary PSK credential that was created for this handshake
* and has served its purpose.
*/
char suuid[37];
oc_uuid_to_str(&device->uuid, suuid, 37);
oc_cred_remove_subject(suuid, 0);
/* Store peer device's now fixed uuid in local device object */
memcpy(device->uuid.id, peer.id, 16);
oc_endpoint_t *ep = device->endpoint;
while (ep) {
memcpy(ep->di.id, peer.id, 16);
ep = ep->next;
}
ep = oc_obt_get_secure_endpoint(device->endpoint);
if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_rdp_4, HIGH_QOS, o)) {
oc_uuid_t *my_uuid = oc_core_get_device_id(0);
char uuid[OC_UUID_LEN];
oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN);