crosses initialization fix
[iotivity.git] / resource / csdk / security / src / csrresource.c
1 //******************************************************************
2 //
3 // Copyright 2017 Microsoft
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
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
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
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.
18 //
19 //******************************************************************
20
21 #include "iotivity_config.h"
22 #include <stdlib.h>
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26 #ifdef HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <inttypes.h>
32 #include "oic_string.h"
33 #include "cainterface.h"
34 #include "experimental/payload_logging.h"
35 #include "ocstack.h"
36 #include "experimental/ocrandom.h"
37 #include "cacommon.h"
38 #include "srmresourcestrings.h"
39 #include "ocpayload.h"
40 #include "ocpayloadcbor.h"
41 #include "credresource.h"
42 #include "experimental/doxmresource.h"
43 #include "srmutility.h"
44 #include "certhelpers.h"
45 #include "csrresource.h"
46 #include "resourcemanager.h"
47
48 #define TAG  "OIC_SRM_CSR"
49
50 static const uint8_t CSR_MAP_SIZE = 4; // csr, encoding, RT, and IF
51
52 static OCResourceHandle    gCsrHandle = NULL;
53
54 /** Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
55  * The value of payload size is increased until reaching belox max cbor size.
56  */
57 static const uint16_t CBOR_SIZE = 2048;
58
59 static const char* EncodingValueToString(OicEncodingType_t encoding)
60 {
61     switch (encoding)
62     {
63     case OIC_ENCODING_RAW:    return OIC_SEC_ENCODING_RAW;
64     case OIC_ENCODING_BASE64: return OIC_SEC_ENCODING_BASE64;
65     case OIC_ENCODING_DER:    return OIC_SEC_ENCODING_DER;
66     case OIC_ENCODING_PEM:    return OIC_SEC_ENCODING_PEM;
67     default:                  return NULL;
68     }
69 }
70
71
72 static OCStackResult StoreKeyPair(mbedtls_pk_context *keyPair, const OicUuid_t *myUuid)
73 {
74     int ret = 0;
75     OicSecCred_t *cred = NULL;
76     OicSecKey_t publicData;
77     OicSecKey_t privateData;
78     uint8_t publicBuf[1024];
79     uint8_t privateBuf[1024];
80
81     /* These DER writing APIs write at the END of the buffers, hence the pointer arithmetic. See the API
82      * documentation for mbedTLS.
83      */
84
85     ret = mbedtls_pk_write_pubkey_der(keyPair, publicBuf, sizeof(publicBuf));
86     VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
87     publicData.data = publicBuf + sizeof(publicBuf) - ret;
88     publicData.len = ret;
89     publicData.encoding = OIC_ENCODING_DER;
90
91     ret = mbedtls_pk_write_key_der(keyPair, privateBuf, sizeof(privateBuf));
92     VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
93     privateData.data = privateBuf + sizeof(privateBuf) - ret;
94     privateData.len = ret;
95     privateData.encoding = OIC_ENCODING_DER;
96
97     cred = GenerateCredential(myUuid, ASYMMETRIC_KEY, &publicData, &privateData,  NULL);
98     VERIFY_NOT_NULL(TAG, cred, ERROR);
99     cred->credUsage = OICStrdup(PRIMARY_CERT);
100     VERIFY_NOT_NULL(TAG, cred->credUsage, ERROR);
101
102     VERIFY_SUCCESS(TAG, OC_STACK_OK == AddCredential(cred), ERROR);
103
104     return OC_STACK_OK;
105
106 exit:
107
108     if (NULL != cred)
109     {
110         DeleteCredList(cred);
111     }
112
113     OICClearMemory(privateBuf, sizeof(privateBuf));
114
115     return OC_STACK_ERROR;
116 }
117
118 static OCStackResult CSRToCBORPayload(const uint8_t *csr, size_t csrLen, OicEncodingType_t encoding, uint8_t **cborPayload, size_t *cborSize)
119 {
120     OCStackResult ret = OC_STACK_ERROR;
121
122     CborError cborEncoderResult = CborNoError;
123     uint8_t *outPayload = NULL;
124     size_t cborLen = *cborSize;
125     CborEncoder encoder;
126     CborEncoder csrRootMap;
127     const char *strEncoding = NULL;
128
129     if ((NULL == csr) || (0 == csrLen) || (NULL == cborPayload) || (NULL == cborSize))
130     {
131         return OC_STACK_INVALID_PARAM;
132     }
133
134     if ((OIC_ENCODING_DER != encoding) && (OIC_ENCODING_PEM != encoding))
135     {
136         return OC_STACK_INVALID_PARAM;
137     }
138
139     *cborSize = 0;
140     *cborPayload = NULL;
141
142     if (0 == cborLen)
143     {
144         cborLen = CBOR_SIZE;
145     }
146
147     outPayload = (uint8_t *)OICCalloc(1, cborLen);
148     VERIFY_NOT_NULL_RETURN(TAG, outPayload, ERROR, OC_STACK_NO_MEMORY);
149     cbor_encoder_init(&encoder, outPayload, cborLen, 0);
150
151     // Create CSR Root Map (csr)
152     cborEncoderResult = cbor_encoder_create_map(&encoder, &csrRootMap, CSR_MAP_SIZE);
153     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Adding CSR Root Map");
154
155     // Create CSR string entry
156     cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_CSR_NAME, strlen(OIC_JSON_CSR_NAME));
157     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed adding CSR name.");
158     cborEncoderResult = cbor_encode_byte_string(&csrRootMap, csr, csrLen);
159     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed adding CSR value.");
160
161     // Create encoding entry
162     cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_ENCODING_NAME, strlen(OIC_JSON_ENCODING_NAME));
163     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed adding encoding name.");
164     strEncoding = EncodingValueToString(encoding);
165     assert(strEncoding != NULL);
166     cborEncoderResult = cbor_encode_text_string(&csrRootMap, strEncoding, strlen(strEncoding));
167     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed adding encoding value.");
168
169     //RT -- Mandatory
170     CborEncoder rtArray;
171     cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_RT_NAME,
172         strlen(OIC_JSON_RT_NAME));
173     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Addding RT Name Tag.");
174     cborEncoderResult = cbor_encoder_create_array(&csrRootMap, &rtArray, 1);
175     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Addding RT Value.");
176     for (size_t i = 0; i < 1; i++)
177     {
178         cborEncoderResult = cbor_encode_text_string(&rtArray, OIC_RSRC_TYPE_SEC_CSR,
179             strlen(OIC_RSRC_TYPE_SEC_CSR));
180         VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Adding RT Value.");
181     }
182     cborEncoderResult = cbor_encoder_close_container(&csrRootMap, &rtArray);
183     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Closing RT.");
184
185     //IF-- Mandatory
186     CborEncoder ifArray;
187     cborEncoderResult = cbor_encode_text_string(&csrRootMap, OIC_JSON_IF_NAME,
188         strlen(OIC_JSON_IF_NAME));
189     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Addding IF Name Tag.");
190     cborEncoderResult = cbor_encoder_create_array(&csrRootMap, &ifArray, 1);
191     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Addding IF Value.");
192     for (size_t i = 0; i < 1; i++)
193     {
194         cborEncoderResult = cbor_encode_text_string(&ifArray, OC_RSRVD_INTERFACE_DEFAULT,
195             strlen(OC_RSRVD_INTERFACE_DEFAULT));
196         VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Adding IF Value.");
197     }
198     cborEncoderResult = cbor_encoder_close_container(&csrRootMap, &ifArray);
199     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Closing IF.");
200
201     // Close CSR Map
202     cborEncoderResult = cbor_encoder_close_container(&encoder, &csrRootMap);
203     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborEncoderResult, "Failed Closing CSR root Map.");
204
205     if (CborNoError == cborEncoderResult)
206     {
207         OIC_LOG(DEBUG, TAG, "CSRToCBORPayload Succeeded");
208         *cborSize = cbor_encoder_get_buffer_size(&encoder, outPayload);
209         *cborPayload = outPayload;
210         ret = OC_STACK_OK;
211     }
212 exit:
213     if (CborErrorOutOfMemory == cborEncoderResult)
214     {
215         OIC_LOG(DEBUG, TAG, "CSRToCBORPayload:CborErrorOutOfMemory : retry with more memory");
216
217         // reallocate and try again!
218         OICFree(outPayload);
219         // Since the allocated initial memory failed, double the memory.
220         cborLen += cbor_encoder_get_buffer_size(&encoder, encoder.end);
221         cborEncoderResult = CborNoError;
222         ret = CSRToCBORPayload(csr, csrLen, encoding, cborPayload, &cborLen);
223         *cborSize = cborLen;
224     }
225     else if (cborEncoderResult != CborNoError)
226     {
227         OIC_LOG(ERROR, TAG, "Failed to CSRToCBORPayload");
228         OICFree(outPayload);
229         outPayload = NULL;
230         *cborSize = 0;
231         *cborPayload = NULL;
232         ret = OC_STACK_ERROR;
233     }
234
235     return ret;
236 }
237
238 OCStackResult CBORPayloadToCSR(const uint8_t *cborPayload, size_t size, uint8_t **csr, size_t *csrLen, OicEncodingType_t *encoding)
239 {
240     if (NULL == cborPayload || 0 == size || NULL == csr || NULL == csrLen)
241     {
242         return OC_STACK_INVALID_PARAM;
243     }
244
245     OCStackResult ret = OC_STACK_ERROR;
246     CborValue csrCbor = { .parser = NULL };
247     CborParser parser = { .end = NULL };
248     CborError cborFindResult = CborNoError;
249     uint8_t* cborCsr = NULL;
250     size_t cborCsrLen = 0;
251     char* tagName = NULL;
252     size_t len = 0;
253
254     CborValue csrRootMap = { .parser = NULL, .ptr = NULL, .remaining = 0, .extra = 0, .type = 0, .flags = 0 };
255
256     cborFindResult = cbor_parser_init(cborPayload, size, 0, &parser, &csrCbor);
257     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed to initialize parser.");
258
259     if (!cbor_value_is_container(&csrCbor))
260     {
261         return OC_STACK_ERROR;
262     }
263
264     // Enter CSR Root Map
265     cborFindResult = cbor_value_enter_container(&csrCbor, &csrRootMap);
266     VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed Entering CSR Root Map.");
267
268     while (cbor_value_is_valid(&csrRootMap))
269     {
270         if (NULL != tagName)
271         {
272             free(tagName);
273             tagName = NULL;
274         }
275         len = 0;
276         CborType type = cbor_value_get_type(&csrRootMap);
277         if (type == CborTextStringType && cbor_value_is_text_string(&csrRootMap))
278         {
279             cborFindResult = cbor_value_dup_text_string(&csrRootMap, &tagName, &len, NULL);
280             VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed finding name in CSR Root Map.");
281             cborFindResult = cbor_value_advance(&csrRootMap);
282             VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed advancing value in CSR Root Map.");
283         }
284         if (NULL != tagName)
285         {
286             if (strcmp(tagName, OIC_JSON_CSR_NAME) == 0 && cbor_value_is_byte_string(&csrRootMap))
287             {
288                 cborFindResult = cbor_value_dup_byte_string(&csrRootMap, &cborCsr, &cborCsrLen, NULL);
289                 VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed finding name in CSR Map");
290                 // Use our own heap allocator and copy the result in so callers can use OICFree.
291                 *csr = (uint8_t *)OICCalloc(1, cborCsrLen);
292                 VERIFY_NOT_NULL(TAG, *csr, ERROR);
293                 memcpy(*csr, cborCsr, cborCsrLen);
294                 free(cborCsr);
295                 cborCsr = NULL;
296                 *csrLen = cborCsrLen;
297                 cborCsrLen = 0;
298             }
299             else if (strcmp(tagName, OIC_JSON_ENCODING_NAME) == 0 && cbor_value_is_text_string(&csrRootMap))
300             {
301                 char *strEncoding = NULL;
302                 cborFindResult = cbor_value_dup_text_string(&csrRootMap, &strEncoding, &len, NULL);
303                 VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed finding encoding type");
304
305                 if (strcmp(strEncoding, OIC_SEC_ENCODING_DER) == 0)
306                 {
307                     *encoding = OIC_ENCODING_DER;
308                 }
309                 else if (strcmp(strEncoding, OIC_SEC_ENCODING_PEM) == 0)
310                 {
311                     *encoding = OIC_ENCODING_PEM;
312                 }
313                 else
314                 {
315                     OIC_LOG_V(ERROR, TAG, "Invalid encoding type %s", strEncoding);
316                     ret = OC_STACK_ERROR;
317                     free(strEncoding);
318                     goto exit;
319                 }
320
321                 free(strEncoding);
322             }
323             else
324             {
325                 // Ignore any other tag type for now.
326                 OIC_LOG_V(WARNING, TAG, "Unknown tag %s", tagName);
327             }
328
329         }
330         if (cbor_value_is_valid(&csrRootMap))
331         {
332             cborFindResult = cbor_value_advance(&csrRootMap);
333             VERIFY_CBOR_SUCCESS_OR_OUT_OF_MEMORY(TAG, cborFindResult, "Failed advancing CSR Root Map");
334         }
335     }
336
337     ret = OC_STACK_OK;
338
339 exit:
340     if (CborNoError != cborFindResult)
341     {
342         if (NULL != *csr)
343         {
344             OICFreeAndSetToNull((void **)csr);
345         }
346
347         *csrLen = 0;
348
349         ret = OC_STACK_ERROR;
350     }
351
352     // tagName and cborCsr are both allocated internally by tinycbor, which uses malloc, not OICMalloc.
353     // Therefore, they must be freed with free, not OICFree.
354     if (NULL != tagName)
355     {
356         free(tagName);
357     }
358     if (NULL != cborCsr)
359     {
360         free(cborCsr);
361     }
362
363     return ret;
364
365 }
366
367 static OCEntityHandlerResult HandleCsrGetRequest(OCEntityHandlerRequest * ehRequest)
368 {
369     OCStackResult res = OC_STACK_OK;
370     OCEntityHandlerResult ehRet = OC_EH_OK;
371     int ret = 0;
372     mbedtls_pk_context keyPair;
373     OicUuid_t myUuid;
374     char *myUuidStr = NULL;
375     char *myDNStr = NULL;
376     size_t myDNStrLen = 0;
377     ByteArray_t keyData = { .data = NULL, .len = 0 };
378     OCByteString csr = { .bytes = NULL, .len = 0 };
379     size_t size = 0;
380     uint8_t *payload = NULL;
381
382     OIC_LOG(INFO, TAG, "HandleCsrGetRequest  processing GET request");
383
384     res = GetDoxmDeviceID(&myUuid);
385     VERIFY_SUCCESS(TAG, OC_STACK_OK == res, ERROR);
386     res = ConvertUuidToStr(&myUuid, &myUuidStr);
387     VERIFY_SUCCESS(TAG, OC_STACK_OK == res, ERROR);
388
389     // Retrieve the key from our current certificate if present, otherwise create a new key
390     GetDerKey(&keyData, PRIMARY_CERT);
391     mbedtls_pk_init(&keyPair);
392     if (0 < keyData.len)
393     {
394         ret = mbedtls_pk_parse_key(&keyPair, keyData.data, keyData.len, NULL, 0);
395         if (0 > ret)
396         {
397             res = OC_STACK_ERROR;
398             // Because we need to set res before failing out, this is a redundant check of ret, but gives better logging.
399             VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
400         }
401     }
402     else
403     {
404         ret = OCInternalGenerateKeyPair(&keyPair);
405         if (0 > ret)
406         {
407             res = OC_STACK_ERROR;
408             VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
409         }
410         VERIFY_SUCCESS(TAG, OC_STACK_OK == (res = StoreKeyPair(&keyPair, &myUuid)), ERROR);
411     }
412
413     // Generate CSR
414     myDNStrLen = strlen(myUuidStr) + sizeof(SUBJECT_PREFIX); // sizeof(SUBJECT_PREFIX) will also count the byte for terminating null.
415     myDNStr = (char *)OICMalloc(myDNStrLen);
416     VERIFY_NOT_NULL(TAG, myDNStr, ERROR);
417     ret = snprintf(myDNStr, myDNStrLen, SUBJECT_PREFIX "%s", myUuidStr);
418     VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
419     /* ret >= myDNStrLen means there was truncation; assert because this means a code bug. */
420     assert((size_t)ret < myDNStrLen);
421     ret = OCInternalCSRRequest(myDNStr, &keyPair, OIC_ENCODING_DER, &csr);
422     if (0 > ret)
423     {
424         res = OC_STACK_ERROR;
425         VERIFY_SUCCESS(TAG, 0 <= ret, ERROR);
426     }
427
428     // Convert CSR data into CBOR for transmission
429     res = CSRToCBORPayload(csr.bytes, csr.len, OIC_ENCODING_DER, &payload, &size);
430
431 exit:
432
433     ehRet = (OC_STACK_OK == res) ? OC_EH_OK : OC_EH_ERROR;
434
435     //Send payload to request originator
436     ehRet = ((SendSRMResponse(ehRequest, ehRet, payload, size)) == OC_STACK_OK) ?
437                        OC_EH_OK : OC_EH_ERROR;
438
439     mbedtls_pk_free(&keyPair);
440     OICFree(myDNStr);
441     OICFree(myUuidStr);
442     OICFree(keyData.data);
443     OICFree(csr.bytes);
444     OICFree(payload);
445     return ehRet;
446 }
447
448 static OCEntityHandlerResult CredCsrEntityHandler(OCEntityHandlerFlag flag,
449                                                   OCEntityHandlerRequest * ehRequest,
450                                                   void* callbackParameter)
451 {
452     OC_UNUSED(callbackParameter);
453     OCEntityHandlerResult ret = OC_EH_ERROR;
454
455     if (!ehRequest)
456     {
457         return OC_EH_ERROR;
458     }
459
460     if (flag & OC_REQUEST_FLAG)
461     {
462         OIC_LOG(DEBUG, TAG, "Flag includes OC_REQUEST_FLAG");
463         if (ehRequest->method == OC_REST_GET)
464         {
465             ret = HandleCsrGetRequest(ehRequest);
466         }
467         else
468         {
469             /* Only GET supported on CSR resource. */
470             ret = OC_EH_ERROR;
471         }
472     }
473
474     return ret;
475 }
476
477 OCStackResult InitCSRResource(void)
478 {
479     OCStackResult ret = OCCreateResource(&gCsrHandle,
480         OIC_RSRC_TYPE_SEC_CSR,
481         OC_RSRVD_INTERFACE_DEFAULT,
482         OIC_RSRC_CSR_URI,
483         CredCsrEntityHandler,
484         NULL,
485         OC_SECURE |
486         OC_DISCOVERABLE);
487
488     if (OC_STACK_OK != ret)
489     {
490         OIC_LOG(FATAL, TAG, "Unable to instantiate CSR resource");
491     }
492
493     return ret;
494 }
495
496 OCStackResult DeInitCSRResource(void)
497 {
498     OCStackResult res = OCDeleteResource(gCsrHandle);
499
500     if (OC_STACK_OK != res)
501     {
502         OIC_LOG_V(ERROR, TAG, "Failed to delete CSR resource: %d", res);
503     }
504
505     gCsrHandle = NULL;
506
507     return res;
508 }