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