1 /* *****************************************************************
\r
3 * Copyright 2016 Samsung Electronics All Rights Reserved.
\r
7 * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * you may not use this file except in compliance with the License.
\r
9 * You may obtain a copy of the License at
\r
11 * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * Unless required by applicable law or agreed to in writing, software
\r
14 * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * See the License for the specific language governing permissions and
\r
17 * limitations under the License.
\r
19 * *****************************************************************/
\r
23 #include "ocstack.h"
\r
25 #include "oic_malloc.h"
\r
26 #include "oic_string.h"
\r
29 #include "resourcemanager.h"
\r
30 #include "dpairingresource.h"
\r
31 #include "psinterface.h"
\r
33 #include "srmresourcestrings.h"
\r
34 #include "cainterface.h"
\r
35 #include "doxmresource.h"
\r
36 #include "pconfresource.h"
\r
37 #include "credresource.h"
\r
38 #include "aclresource.h"
\r
39 #include "srmutility.h"
\r
40 #include "ocserverrequest.h"
\r
45 #include <strings.h>
\r
48 #ifdef __WITH_DTLS__
\r
52 #define TAG "SRM-DPAIRING"
\r
55 static OicSecDpairing_t *gDpair = NULL;
\r
56 static OCResourceHandle gDpairHandle = NULL;
\r
57 static OicSecDpairing_t gDefaultDpair =
\r
59 PRM_NOT_ALLOWED, /* OicSecPrm_t spm */
\r
60 {.id = {0}}, /* OicUuid_t pdeviceID */
\r
61 {.id = {0}}, /* OicUuid_t rowner */
\r
64 void DeleteDpairingBinData(OicSecDpairing_t* dpair)
\r
68 //Clean dpairing itself
\r
74 * Get the default value.
\r
75 * @retval the gDefaultDpair pointer;
\r
77 static OicSecDpairing_t* GetDpairingDefault()
\r
79 OIC_LOG (DEBUG, TAG, "GetDpairingDefault");
\r
81 return &gDefaultDpair;
\r
85 * This method is used by SRM to retrieve Dpairing resource data..
\r
87 void SetDpairingResourceOwner(OicUuid_t *rowner)
\r
89 OIC_LOG (DEBUG, TAG, "SetDpairingResourceOwner");
\r
92 memcpy(&gDpair->rowner, rowner, sizeof(OicUuid_t));
\r
96 #ifdef __WITH_DTLS__
\r
98 * Function to save PairingPSK.
\r
100 * @param[in] endpoint current endpoint.
\r
101 * @param[in] peerDevID peer device indentitiy.
\r
102 * @param[in] isPairingServer indicate if it generates PairingPSK for server or client.
\r
104 * @return OC_STACK_OK on success
\r
106 OCStackResult SavePairingPSK(OCDevAddr *endpoint,
\r
107 OicUuid_t *peerDevID, OicUuid_t *owner, bool isPairingServer)
\r
109 OIC_LOG(DEBUG, TAG, "IN SavePairingPSK");
\r
111 if(NULL == endpoint || NULL == peerDevID || NULL == owner)
\r
113 OIC_LOG_V(ERROR, TAG, "Invalid Input parameters in [%s]\n", __FUNCTION__);
\r
114 return OC_STACK_INVALID_PARAM;
\r
117 OCStackResult res = OC_STACK_ERROR;
\r
119 OicUuid_t ptDeviceID = {.id={0}};
\r
120 if (OC_STACK_OK != GetDoxmDeviceID(&ptDeviceID))
\r
122 OIC_LOG(ERROR, TAG, "Error while retrieving provisioning tool's device ID");
\r
126 uint8_t pairingPSK[OWNER_PSK_LENGTH_128] = {0};
\r
128 //Generating PairingPSK using OwnerPSK scheme
\r
129 CAResult_t pskRet = CAGenerateOwnerPSK((const CAEndpoint_t *)endpoint,
\r
130 (uint8_t *)OIC_RSRC_TYPE_SEC_DPAIRING,
\r
131 strlen(OIC_RSRC_TYPE_SEC_DPAIRING),
\r
132 (isPairingServer ? ptDeviceID.id : peerDevID->id), sizeof(OicUuid_t), // server
\r
133 (isPairingServer ? peerDevID->id : ptDeviceID.id), sizeof(OicUuid_t), // client
\r
134 pairingPSK, OWNER_PSK_LENGTH_128);
\r
136 if (CA_STATUS_OK == pskRet)
\r
138 OIC_LOG(INFO, TAG, "pairingPSK dump:\n");
\r
139 OIC_LOG_BUFFER(INFO, TAG, pairingPSK, OWNER_PSK_LENGTH_128);
\r
140 //Generating new credential for direct-pairing client
\r
142 uint32_t outLen = 0;
\r
144 char base64Buff[B64ENCODE_OUT_SAFESIZE(sizeof(pairingPSK)) + 1] = {};
\r
145 B64Result b64Ret = b64Encode(pairingPSK, sizeof(pairingPSK), base64Buff, sizeof(base64Buff),
\r
147 VERIFY_SUCCESS(TAG, B64_OK == b64Ret, ERROR);
\r
149 OicSecCred_t *cred = GenerateCredential(peerDevID,
\r
150 SYMMETRIC_PAIR_WISE_KEY, NULL,
\r
151 base64Buff, ownLen, owner);
\r
152 VERIFY_NON_NULL(TAG, cred, ERROR);
\r
154 res = AddCredential(cred);
\r
155 if(res != OC_STACK_OK)
\r
157 DeleteCredList(cred);
\r
163 OIC_LOG(ERROR, TAG, "CAGenerateOwnerPSK failed");
\r
166 OIC_LOG(DEBUG, TAG, "OUT SavePairingPSK");
\r
170 #endif // __WITH_DTLS__
\r
173 * This internal method converts DPairing data into JSON format.
\r
174 * Does not error-check here, but check it in caller
\r
176 * Note: Caller needs to invoke 'free' when finished done using
\r
179 char * BinToDpairingJSON(const OicSecDpairing_t * dpair)
\r
181 OIC_LOG(DEBUG, TAG, "BinToDpairingJSON() IN");
\r
188 char *jsonStr = NULL;
\r
189 cJSON *jsonDpair = NULL;
\r
190 char base64Buff[B64ENCODE_OUT_SAFESIZE(sizeof(((OicUuid_t*)0)->id)) + 1] = {};
\r
191 uint32_t outLen = 0;
\r
192 B64Result b64Ret = B64_OK;
\r
194 cJSON *jsonRoot = cJSON_CreateObject();
\r
195 VERIFY_NON_NULL(TAG, jsonRoot, ERROR);
\r
197 jsonDpair = cJSON_CreateObject();
\r
198 VERIFY_NON_NULL(TAG, jsonDpair, ERROR);
\r
199 cJSON_AddItemToObject(jsonRoot, OIC_JSON_DPAIRING_NAME, jsonDpair );
\r
202 if(PRM_RANDOM_PIN >= dpair->spm) // don't need to check "PRM_NOT_ALLOWED <= dpair->spm" because of always true
\r
204 cJSON_AddNumberToObject(jsonDpair, OIC_JSON_SPM_NAME, (int)dpair->spm);
\r
207 //PDeviceID -- Mandatory
\r
208 //There may not be paired devices if it did not be received pairing request
\r
209 if ('\0' != (char)dpair->pdeviceID.id[0])
\r
212 b64Ret = b64Encode(dpair->pdeviceID.id, sizeof(dpair->pdeviceID.id), base64Buff,
\r
213 sizeof(base64Buff), &outLen);
\r
214 VERIFY_SUCCESS(TAG, b64Ret == B64_OK, ERROR);
\r
215 cJSON_AddStringToObject(jsonDpair, OIC_JSON_PDEVICE_ID_NAME, base64Buff);
\r
218 //ROwner -- Mandatory
\r
219 if ('\0' != (char)dpair->rowner.id[0])
\r
222 b64Ret = b64Encode(dpair->rowner.id, sizeof(dpair->rowner.id), base64Buff,
\r
223 sizeof(base64Buff), &outLen);
\r
224 VERIFY_SUCCESS(TAG, b64Ret == B64_OK, ERROR);
\r
225 cJSON_AddStringToObject(jsonDpair, OIC_JSON_ROWNER_NAME, base64Buff);
\r
229 jsonStr = cJSON_PrintUnformatted(jsonRoot);
\r
234 cJSON_Delete(jsonRoot);
\r
240 * This internal method converts JSON Dpairing into binary Dpairing.
\r
241 * Does not error-check here, but check it in caller
\r
243 OicSecDpairing_t* JSONToDpairingBin(const char * jsonStr)
\r
245 OIC_LOG(DEBUG, TAG, "JSONToDpairingBin() IN");
\r
247 OCStackResult ret = OC_STACK_ERROR;
\r
248 OicSecDpairing_t *dpair = NULL;
\r
249 cJSON *jsonRoot = NULL;
\r
250 cJSON *jsonDpair = NULL;
\r
251 cJSON *jsonObj = NULL;
\r
253 unsigned char base64Buff[sizeof(((OicUuid_t*)0)->id)] = {};
\r
254 uint32_t outLen = 0;
\r
255 B64Result b64Ret = B64_OK;
\r
258 VERIFY_NON_NULL(TAG, jsonStr, ERROR);
\r
260 jsonRoot = cJSON_Parse(jsonStr);
\r
261 VERIFY_NON_NULL(TAG, jsonRoot, ERROR);
\r
263 jsonDpair = cJSON_GetObjectItem(jsonRoot, OIC_JSON_DPAIRING_NAME);
\r
264 VERIFY_NON_NULL(TAG, jsonDpair, ERROR);
\r
266 dpair = (OicSecDpairing_t*)OICCalloc(1, sizeof(OicSecDpairing_t));
\r
267 VERIFY_NON_NULL(TAG, dpair, ERROR);
\r
270 jsonObj = cJSON_GetObjectItem(jsonDpair, OIC_JSON_SPM_NAME);
\r
271 if (jsonObj && cJSON_Number == jsonObj->type)
\r
273 dpair->spm = (OicSecPrm_t)jsonObj->valueint;
\r
274 OIC_LOG_V (DEBUG, TAG, "jsonObj->valueint = %d", jsonObj->valueint);
\r
275 OIC_LOG_V (DEBUG, TAG, "dpair->spm = %d", dpair->spm);
\r
277 // don't need to check "PRM_NOT_ALLOWED <= dpair->spm" because of always true
\r
278 VERIFY_SUCCESS(TAG, (PRM_RANDOM_PIN >= dpair->spm), ERROR);
\r
282 dpair->spm = PRM_NOT_ALLOWED;
\r
285 //PDeviceId -- Mandatory
\r
287 jsonObj = cJSON_GetObjectItem(jsonDpair, OIC_JSON_PDEVICE_ID_NAME);
\r
288 if (jsonObj && cJSON_String == jsonObj->type)
\r
290 b64Ret = b64Decode(jsonObj->valuestring, strlen(jsonObj->valuestring), base64Buff,
\r
291 sizeof(base64Buff), &outLen);
\r
292 VERIFY_SUCCESS(TAG, (b64Ret == B64_OK && outLen <= sizeof(dpair->pdeviceID.id)), ERROR);
\r
293 memcpy(dpair->pdeviceID.id, base64Buff, outLen);
\r
297 memset(&dpair->pdeviceID, 0, sizeof(OicUuid_t));
\r
300 // ROwner -- Mandatory
\r
302 jsonObj = cJSON_GetObjectItem(jsonDpair, OIC_JSON_ROWNER_NAME);
\r
303 if (jsonObj && cJSON_String == jsonObj->type)
\r
305 b64Ret = b64Decode(jsonObj->valuestring, strlen(jsonObj->valuestring), base64Buff,
\r
306 sizeof(base64Buff), &outLen);
\r
307 VERIFY_SUCCESS(TAG, (b64Ret == B64_OK && outLen <= sizeof(dpair->rowner.id)), ERROR);
\r
308 memcpy(dpair->rowner.id, base64Buff, outLen);
\r
312 memset(&dpair->rowner, 0, sizeof(OicUuid_t));
\r
318 cJSON_Delete(jsonRoot);
\r
319 if (OC_STACK_OK != ret)
\r
321 DeleteDpairingBinData(dpair);
\r
325 OIC_LOG(DEBUG, TAG, "JSONToDpairingBin() OUT");
\r
330 * Function to handle the handshake result in Direct-Pairing.
\r
331 * This function will be invoked after DTLS handshake
\r
332 * @param endPoint [IN] The remote endpoint.
\r
333 * @param errorInfo [IN] Error information from the endpoint.
\r
336 void DPairingDTLSHandshakeCB(const CAEndpoint_t *endpoint, const CAErrorInfo_t *info)
\r
338 OIC_LOG_V(INFO, TAG, "IN DPairingDTLSHandshakeCB");
\r
340 if(gDpair && endpoint && info)
\r
342 OIC_LOG_V(INFO, TAG, "Received status from remote device(%s:%d) : %d",
\r
343 endpoint->addr, endpoint->port, info->result);
\r
345 if(CA_STATUS_OK == info->result)
\r
347 OIC_LOG(INFO, TAG, "DPairingDTLSHandshakeCB - Connection success.");
\r
349 else if(CA_DTLS_AUTHENTICATION_FAILURE == info->result)
\r
351 OIC_LOG(INFO, TAG, "DPairingDTLSHandshakeCB - Authentication failed");
\r
355 #ifdef __WITH_DTLS__
\r
356 CARegisterDTLSHandshakeCallback(NULL);
\r
357 #endif // __WITH_DTLS__
\r
359 // delete temporary key
\r
360 RemoveCredential(&gDpair->pdeviceID);
\r
363 OIC_LOG_V(INFO, TAG, "OUT DPairingDTLSHandshakeCB");
\r
366 static OCEntityHandlerResult HandleDpairingPostRequest (const OCEntityHandlerRequest * ehRequest)
\r
368 OIC_LOG (DEBUG, TAG, "Dpairing EntityHandle processing POST request");
\r
369 OCEntityHandlerResult ehRet = OC_EH_ERROR;
\r
370 OicSecDpairing_t* newDpair = NULL;
\r
372 const OicSecPconf_t *pconf = GetPconfResourceData();
\r
373 if (true == pconf->edp)
\r
375 // Convert JSON DPAIRING data into binary. This will also validate the DPAIRING data received.
\r
376 newDpair = JSONToDpairingBin(((OCSecurityPayload*)ehRequest->payload)->securityData);
\r
380 OIC_LOG (DEBUG, TAG, "EDP == false : Direct-Pairing Disabled");
\r
381 ehRet = OC_EH_ERROR;
\r
384 if (newDpair && false == IsPairedDevice(&newDpair->pdeviceID))
\r
386 // Check if valid Post request
\r
387 bool prmMached = false;
\r
388 for (size_t i=0; i<pconf->prmLen; i++)
\r
390 if (newDpair->spm == pconf->prm[i])
\r
396 OIC_LOG_V(DEBUG, TAG, "Parsed spm is %s", prmMached ? "valid" : "invalid, send error response");
\r
398 // Update local Dpairing with new Dpairing & prepare dtls session
\r
399 if (prmMached && '\0' != (char)newDpair->pdeviceID.id[0])
\r
403 gDpair = GetDpairingDefault();
\r
405 gDpair->spm = newDpair->spm;
\r
406 memcpy(&gDpair->pdeviceID, &newDpair->pdeviceID, sizeof(OicUuid_t));
\r
407 memcpy(&gDpair->rowner, &pconf->rowner, sizeof(OicUuid_t));
\r
409 #ifdef __WITH_DTLS__
\r
410 // Add temporary psk
\r
412 OicUuid_t subjectId = {.id={0}};
\r
413 res = AddTmpPskWithPIN(&gDpair->pdeviceID,
\r
414 SYMMETRIC_PAIR_WISE_KEY,
\r
415 (char*)pconf->pin.val, DP_PIN_LENGTH,
\r
416 1, &gDpair->rowner, &subjectId);
\r
417 if(res != OC_STACK_OK ||
\r
418 memcmp(&gDpair->pdeviceID, &subjectId, sizeof(OicUuid_t)))
\r
420 OIC_LOG_V(ERROR, TAG, "Failed to save the temporal PSK : %d", res);
\r
424 // Prepare to establish a secure channel with Pin-based PSK cipher suite
\r
425 if (CA_STATUS_OK != CAEnableAnonECDHCipherSuite(false) ||
\r
426 CA_STATUS_OK != CASelectCipherSuite(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA_256))
\r
428 OIC_LOG_V(ERROR, TAG, "Failed to select TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA_256");
\r
432 if(CA_STATUS_OK != CARegisterDTLSHandshakeCallback(DPairingDTLSHandshakeCB))
\r
434 OIC_LOG(WARNING, TAG, "DirectPairingHandler : Failed to register DTLS handshake callback.");
\r
437 #endif // __WITH_DTLS__
\r
439 // should be lock /oic/sec/dpairing resource if Direct-Pairing starts normally ?
\r
440 OIC_LOG (DEBUG, TAG, "/oic/sec/dpairing resource created");
\r
442 ehRet = OC_EH_RESOURCE_CREATED;
\r
446 OIC_LOG(ERROR, TAG, "Error in request check");
\r
451 #ifdef __WITH_DTLS__
\r
453 #endif // __WITH_DTLS__
\r
455 if (OC_EH_ERROR == ehRet && gDpair)
\r
457 RemoveCredential(&gDpair->pdeviceID);
\r
461 // Send payload to request originator
\r
462 if(OC_STACK_OK != SendSRMResponse(ehRequest, ehRet, NULL))
\r
464 OIC_LOG (ERROR, TAG, "SendSRMResponse failed in HandleDpairingPostRequest");
\r
467 DeleteDpairingBinData(newDpair);
\r
468 OIC_LOG_V (DEBUG, TAG, "%s RetVal %d", __func__ , ehRet);
\r
472 static OCEntityHandlerResult HandleDpairingPutRequest (const OCEntityHandlerRequest * ehRequest)
\r
474 OIC_LOG (DEBUG, TAG, "Dpairing EntityHandle processing PUT request (Comfirmation)");
\r
476 OCEntityHandlerResult ehRet = OC_EH_ERROR;
\r
477 OicSecDpairing_t* newDpair = NULL;
\r
479 const OicSecPconf_t *pconf = GetPconfResourceData();
\r
480 if (true == pconf->edp)
\r
482 // Convert JSON DPAIRING data into binary. This will also validate the DPAIRING data received.
\r
483 newDpair = JSONToDpairingBin(((OCSecurityPayload*)ehRequest->payload)->securityData);
\r
487 OIC_LOG (DEBUG, TAG, "EDP == false : Direct-Pairing Disabled");
\r
488 ehRet = OC_EH_ERROR;
\r
491 if (gDpair && newDpair)
\r
493 OIC_LOG(DEBUG, TAG, "Received direct-pairing finalization request");
\r
495 // Check if valid Put request
\r
496 VERIFY_SUCCESS(TAG, PRM_NOT_ALLOWED == newDpair->spm, ERROR);
\r
498 const OicSecPconf_t *pconf = GetPconfResourceData();
\r
499 VERIFY_NON_NULL(TAG, pconf, ERROR);
\r
501 #ifdef __WITH_DTLS__
\r
502 OCServerRequest * request = (OCServerRequest *)ehRequest->requestHandle;
\r
503 VERIFY_SUCCESS(TAG, (request->devAddr.flags | OC_FLAG_SECURE), ERROR);
\r
505 //Generate new credential
\r
506 OIC_LOG_V(INFO, TAG, "SavePairingPSK for %s(%d)", request->devAddr.addr, request->devAddr.port);
\r
507 OCStackResult res = SavePairingPSK(&request->devAddr, &newDpair->pdeviceID,
\r
508 (OicUuid_t *)&pconf->rowner, true);
\r
509 VERIFY_SUCCESS(TAG, OC_STACK_OK == res, ERROR);
\r
510 #endif //__WITH_DTLS__
\r
513 OicSecPdAcl_t *pdAcl;
\r
514 LL_FOREACH(pconf->pdacls, pdAcl)
\r
517 memset(&acl, 0, sizeof(OicSecAcl_t));
\r
518 memcpy(&acl.subject, &gDpair->pdeviceID, sizeof(OicUuid_t));
\r
519 acl.resources = pdAcl->resources;
\r
520 acl.resourcesLen = pdAcl->resourcesLen;
\r
521 acl.owners = (OicUuid_t*)&pconf->rowner;
\r
523 acl.permission = pdAcl->permission;
\r
524 acl.periods = pdAcl->periods;
\r
525 acl.recurrences = pdAcl->recurrences;
\r
526 acl.prdRecrLen = pdAcl->prdRecrLen;
\r
528 char* aclJson = BinToAclJSON(&acl);
\r
531 InstallNewACL(aclJson);
\r
536 //update pconf device list
\r
537 AddPairedDevice(&newDpair->pdeviceID);
\r
539 //Initialize dpairing resource
\r
542 OIC_LOG (DEBUG, TAG, "/oic/sec/dpairing resource updated, direct-pairing finalization success");
\r
548 //Send payload to request originator
\r
549 if(OC_STACK_OK != SendSRMResponse(ehRequest, ehRet, NULL))
\r
551 OIC_LOG (ERROR, TAG, "SendSRMResponse failed in HandleDpairingPutRequest");
\r
554 DeleteDpairingBinData(newDpair);
\r
555 OIC_LOG_V (DEBUG, TAG, "%s RetVal %d", __func__ , ehRet);
\r
559 * This internal method is the entity handler for Dpairing resources and
\r
560 * will handle REST request (GET/POST) for them.
\r
562 OCEntityHandlerResult DpairingEntityHandler (OCEntityHandlerFlag flag,
\r
563 OCEntityHandlerRequest * ehRequest,
\r
564 void* callbackParameter)
\r
566 OIC_LOG(DEBUG, TAG, "Received request DpairingEntityHandler");
\r
567 (void)callbackParameter;
\r
568 OCEntityHandlerResult ehRet = OC_EH_ERROR;
\r
575 if (flag & OC_REQUEST_FLAG)
\r
577 OIC_LOG (DEBUG, TAG, "Flag includes OC_REQUEST_FLAG");
\r
578 switch (ehRequest->method)
\r
584 ehRet = HandleDpairingPostRequest(ehRequest);
\r
588 ehRet = HandleDpairingPutRequest(ehRequest);
\r
591 case OC_REST_DELETE:
\r
595 ehRet = OC_EH_ERROR;
\r
596 SendSRMResponse(ehRequest, ehRet, NULL);
\r
604 * This internal method is used to create '/oic/sec/dpairing' resource.
\r
606 OCStackResult CreateDpairingResource()
\r
610 ret = OCCreateResource(&gDpairHandle,
\r
611 OIC_RSRC_TYPE_SEC_DPAIRING,
\r
613 OIC_RSRC_DPAIRING_URI,
\r
614 DpairingEntityHandler,
\r
616 OC_SECURE | OC_EXPLICIT_DISCOVERABLE);
\r
618 if (OC_STACK_OK != ret)
\r
620 OIC_LOG (ERROR, TAG, "Unable to instantiate Dpairing resource");
\r
621 DeInitDpairingResource();
\r
627 * Initialize Dpairing resource by loading data from persistent storage.
\r
629 * @retval OC_STACK_OK for Success, otherwise some error value
\r
631 OCStackResult InitDpairingResource()
\r
633 OCStackResult ret = OC_STACK_ERROR;
\r
635 // Instantiate 'oic.sec.dpairing'
\r
636 ret = CreateDpairingResource();
\r
637 if (OC_STACK_OK != ret)
\r
639 DeInitDpairingResource();
\r
645 * Perform cleanup for Dpairing resources.
\r
648 * OC_STACK_OK - no error
\r
649 * OC_STACK_ERROR - stack process error
\r
652 OCStackResult DeInitDpairingResource()
\r
654 OCStackResult ret = OCDeleteResource(gDpairHandle);
\r
657 if(OC_STACK_OK == ret)
\r
659 return OC_STACK_OK;
\r
663 return OC_STACK_ERROR;
\r