Add PKIX provisioning
[platform/upstream/iotivity.git] / resource / csdk / security / provisioning / src / credentialgenerator.c
1 /* *****************************************************************
2  *
3  * Copyright 2015 Samsung Electronics All Rights Reserved.
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 #include <string.h>
21 #include "credentialgenerator.h"
22 #include "oic_malloc.h"
23 #include "oic_string.h"
24 #include "logger.h"
25 #include "credresource.h"
26 #include "ocrandom.h"
27 #include "base64.h"
28 #include "stdbool.h"
29 #include "securevirtualresourcetypes.h"
30 #ifdef __WITH_X509__
31 #include "ck_manager.h"
32
33 #define CHAIN_LEN (2) //TODO: replace by external define or a runtime value
34 #endif  //__WITH_X509__
35
36 #define TAG "SRPAPI-CG"
37
38 /**
39  * @def PM_VERIFY_SUCCESS
40  * @brief Macro to verify success of operation.
41  *        eg: PM_VERIFY_SUCCESS(TAG, OC_STACK_OK == foo(), OC_STACK_ERROR, ERROR);
42  * @note Invoking function must define "bail:" label for goto functionality to work correctly and
43  *       must define "OCStackResult res" for setting error code.
44  * */
45 #define PM_VERIFY_SUCCESS(tag, op, errCode, logLevel) { if (!(op)) \
46                        {OC_LOG((logLevel), tag, PCF(#op " failed!!")); res = errCode; goto bail;} }
47 /**
48  * @def PM_VERIFY_NON_NULL
49  * @brief Macro to verify argument is not equal to NULL.
50  *        eg: PM_VERIFY_NON_NULL(TAG, ptrData, ERROR);
51  * @note Invoking function must define "bail:" label for goto functionality to work correctly.
52  * */
53 #define PM_VERIFY_NON_NULL(tag, arg, errCode, logLevel) { if (NULL == (arg)) \
54                    { OC_LOG((logLevel), tag, PCF(#arg " is NULL")); res = errCode; goto bail;} }
55
56 OCStackResult PMGeneratePairWiseCredentials(OicSecCredType_t type, size_t keySize,
57                                     const OicUuid_t *ptDeviceId,
58                                     const OicUuid_t *firstDeviceId, const OicUuid_t *secondDeviceId,
59                                     OicSecCred_t **firstCred, OicSecCred_t **secondCred)
60 {
61
62     if (NULL == ptDeviceId || NULL == firstDeviceId || NULL != *firstCred || \
63         NULL == secondDeviceId || NULL != *secondCred)
64     {
65         OC_LOG(INFO, TAG, "Invalid params");
66         return OC_STACK_INVALID_PARAM;
67     }
68     if(!(keySize == OWNER_PSK_LENGTH_128 || keySize == OWNER_PSK_LENGTH_256))
69     {
70         OC_LOG(INFO, TAG, "Invalid key size");
71         return OC_STACK_INVALID_PARAM;
72     }
73     OCStackResult res = OC_STACK_ERROR;
74     uint8_t* privData = NULL;
75     char* base64Buff = NULL;
76     OicSecCred_t *tempFirstCred = NULL;
77     OicSecCred_t *tempSecondCred = NULL;
78
79     size_t privDataKeySize = keySize;
80
81     privData = (uint8_t*) OICCalloc(privDataKeySize,sizeof(uint8_t));
82     PM_VERIFY_NON_NULL(TAG, privData, OC_STACK_NO_MEMORY, ERROR);
83
84     OCFillRandomMem(privData,privDataKeySize);
85
86     uint32_t outLen = 0;
87
88     base64Buff = (char*) OICCalloc(B64ENCODE_OUT_SAFESIZE(privDataKeySize) + 1, sizeof(char));
89     PM_VERIFY_NON_NULL(TAG, base64Buff, OC_STACK_NO_MEMORY, ERROR);
90     int memReq = (B64ENCODE_OUT_SAFESIZE(privDataKeySize) + 1) * sizeof(char);
91     B64Result b64Ret = b64Encode(privData, privDataKeySize*sizeof(uint8_t), base64Buff,
92                                  memReq, &outLen);
93     PM_VERIFY_SUCCESS(TAG, B64_OK == b64Ret, OC_STACK_ERROR, ERROR);
94
95     // TODO: currently owner array is 1. only provisioning tool's id.
96     tempFirstCred =  GenerateCredential(secondDeviceId, type, NULL, base64Buff, 1, ptDeviceId);
97     PM_VERIFY_NON_NULL(TAG, tempFirstCred, OC_STACK_ERROR, ERROR);
98
99     // TODO: currently owner array is 1. only provisioning tool's id.
100     tempSecondCred =  GenerateCredential(firstDeviceId, type, NULL, base64Buff, 1, ptDeviceId);
101     PM_VERIFY_NON_NULL(TAG, tempSecondCred, OC_STACK_ERROR, ERROR);
102
103     *firstCred = tempFirstCred;
104     *secondCred = tempSecondCred;
105     res = OC_STACK_OK;
106
107 bail:
108     OICFree(privData);
109     OICFree(base64Buff);
110
111     if(res != OC_STACK_OK)
112     {
113         OICFree(tempFirstCred);
114         OICFree(tempSecondCred);
115         *firstCred = NULL;
116         *secondCred = NULL;
117     }
118
119     return res;
120 }
121
122 #ifdef __WITH_X509__
123 /**
124  * Function to compose JSON Web Key (JWK) string from a certificate and a public key.
125  *
126  * @param[in]  certificateChain    Array of Base64 encoded certificate strings.
127  * @param[in]  chainLength         Number of the certificates in certificateChain.
128  * @return     Valid JWK string on success, or NULL on fail.
129  */
130 static char *CreateCertificatePublicJWK(const char *const *certificateChain,
131                                         const size_t chainLength)
132 {
133     if (NULL == certificateChain || chainLength == 0)
134     {
135         OC_LOG(ERROR, TAG, "Error CreateCertificatePublicJWK: Invalid params");
136         return NULL;
137     }
138
139     size_t certChainSize = 0;
140     for (size_t i = 0; i < chainLength; ++i)
141     {
142         if (NULL != certificateChain[i])
143         {
144             certChainSize += strlen(certificateChain[i]);
145         }
146         else
147         {
148             OC_LOG(ERROR, TAG, "Error CreateCertificatePublicJWK: Invalid params");
149             return NULL;
150         }
151
152     }
153     /* certificates in the json array taken in quotes and separated by a comma
154      * so we have to count the number of characters (number of commas and quotes) required
155      * for embedding certificates in the array depending on the number of certificates in chain
156      * each certificate except last embeded in  "\"%s\"," */
157     const int numCommasAndQuotes = chainLength * 3 - 1;
158     const char firstPart[] = "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x5c\":[";
159     const char secondPart[] = "]}";
160     /* to calculate the size of JWK public part we need to add the value of first and  second parts,
161      * size of certificate chain, number of additional commas and quotes and 1 for string termination symbol */
162     size_t certPubJWKLen = strlen(firstPart) + strlen(secondPart)
163                                              + certChainSize + numCommasAndQuotes + 1;
164     char *certPubJWK = (char *)OICMalloc(certPubJWKLen);
165
166     if (NULL != certPubJWK)
167     {
168         OICStrcpy(certPubJWK, certPubJWKLen, firstPart);
169         size_t offset = strlen(firstPart);
170         for (size_t i = 0; i < chainLength; ++i)
171         {
172             offset += sprintf(certPubJWK + offset, "\"%s\",", certificateChain[i]);
173         }
174         sprintf(certPubJWK + offset - 1, secondPart);
175     }
176     else
177     {
178         OC_LOG(ERROR, TAG, "Error while memory allocation");
179     }
180     return certPubJWK;
181 }
182
183 /**
184  * Function to compose JWK string from a private key.
185  *
186  * @param[in]  privateKey    Base64 encoded private key.
187  * @return     Valid JWK string on success, or NULL on fail.
188  */
189 static char *CreateCertificatePrivateJWK(const char *privateKey)
190 {
191     if (NULL == privateKey)
192     {
193         OC_LOG(ERROR, TAG, "Error privateKey is NULL");
194         return NULL;
195     }
196     const char firstPart[] = "{\"kty\":\"EC\",\"crv\":\"P-256\",\"d\":\"";
197     const char secondPart[] = "\"}";
198     char *certPrivJWK = (char *)OICMalloc(strlen(firstPart) + strlen(secondPart) + strlen(
199             privateKey) + 1);
200
201     if (NULL != certPrivJWK)
202     {
203         sprintf(certPrivJWK, "%s%s%s", firstPart, privateKey, secondPart);
204     }
205     else
206     {
207         OC_LOG(ERROR, TAG, "Error while memory allocation");
208     }
209     return certPrivJWK;
210 }
211
212
213 /**
214  * Function to generate Base64 encoded credential data for device.
215  *
216  * @param[in]   subject             Device id.
217  * @param[out]  certificateChain    Pointer to Array of Base64 encoded certificate strings.
218  * @param[out]  chainLength         Pointer to number of the certificates in certificateChain.
219  * @param[out]  privKey             Pointer to Base64 encoded private key.
220  * @return  OC_STACK_OK on success
221  */
222 static OCStackResult GenerateCertificateAndKeys(const OicUuid_t * subject, char *** const certificateChain,
223         size_t * const chainLength, char ** const privKey)
224 {
225     if (NULL == subject || NULL == certificateChain || NULL == chainLength || NULL == privKey)
226     {
227         return  OC_STACK_INVALID_PARAM;
228     }
229     *certificateChain = NULL;
230     *privKey     = NULL;
231
232     ByteArray pubKeyBA  = BYTE_ARRAY_INITIALIZER;
233     ByteArray privKeyBA = BYTE_ARRAY_INITIALIZER;
234     ByteArray cert[CHAIN_LEN];
235
236     uint8_t pubKeyData[PUBLIC_KEY_SIZE] = {0};
237     uint8_t privKeyData[PRIVATE_KEY_SIZE] = {0};
238     uint8_t certData[ISSUER_MAX_CERT_SIZE * CHAIN_LEN] = {0};
239     uint8_t subjName[UUID_LENGTH + 1] = {0};
240
241     pubKeyBA.data  = pubKeyData;
242     pubKeyBA.len   = PUBLIC_KEY_SIZE;
243     privKeyBA.data = privKeyData;
244     privKeyBA.len  = PRIVATE_KEY_SIZE;
245     for (size_t i = 0; i < CHAIN_LEN; ++i)
246     {
247         cert[i].data      = certData + ISSUER_MAX_CERT_SIZE * i;
248         cert[i].len       = ISSUER_MAX_CERT_SIZE;
249     }
250
251     memcpy(subjName, subject->id, UUID_LENGTH);
252     subjName[UUID_LENGTH] = '\0';
253
254     if (PKI_SUCCESS != GenerateKeyPair(&privKeyBA, &pubKeyBA))
255     {
256         OC_LOG(ERROR, TAG, "Error generating keys.");
257         return OC_STACK_ERROR;
258     }
259     if (PKI_SUCCESS != CKMIssueDeviceCertificate(subjName, NULL, NULL, pubKeyBA.data, cert))
260     {
261         OC_LOG(ERROR, TAG, "Error generating certificate.");
262         return OC_STACK_ERROR;
263     }
264
265     char privB64buf[B64ENCODE_OUT_SAFESIZE(PRIVATE_KEY_SIZE) + 1] = {0};
266     uint32_t privB64len = 0;
267     if (B64_OK != b64Encode(privKeyBA.data,  privKeyBA.len, privB64buf,
268                              B64ENCODE_OUT_SAFESIZE(PRIVATE_KEY_SIZE) + 1, &privB64len))
269     {
270         OC_LOG(ERROR, TAG, "Error while encoding key");
271         return OC_STACK_ERROR;
272     }
273
274     if (PKI_SUCCESS != GetCAChain(chainLength , cert + 1))
275     {
276         OC_LOG(ERROR, TAG, "Error getting CA certificate chain.");
277         return OC_STACK_ERROR;
278     }
279
280     ++(*chainLength);
281     *certificateChain = (char **)OICMalloc(sizeof(char *) * (*chainLength));
282
283     OCStackResult ret = OC_STACK_NO_MEMORY;
284     if (NULL == *certificateChain)
285     {
286         goto memclean;
287     }
288
289
290     for (size_t i = 0; i < *chainLength; ++i)
291     {
292         (*certificateChain)[i] = NULL;
293
294         char certB64buf[B64ENCODE_OUT_SAFESIZE(ISSUER_MAX_CERT_SIZE) + 1] = {0};
295         uint32_t certB64len = 0;
296         if (B64_OK != b64Encode(cert[i].data, cert[i].len, certB64buf,
297                                 B64ENCODE_OUT_SAFESIZE(ISSUER_MAX_CERT_SIZE) + 1, &certB64len))
298         {
299             OC_LOG(ERROR, TAG, "Error while encoding certificate");
300             ret = OC_STACK_ERROR;
301             goto memclean;
302         }
303
304         (*certificateChain)[i] = (char *) OICMalloc(certB64len + 1);
305         if (NULL == (*certificateChain)[i])
306         {
307             goto memclean;
308         }
309
310         memcpy((*certificateChain)[i], certB64buf, certB64len + 1);
311     }
312
313
314     *privKey     = (char *)OICMalloc(privB64len + 1);
315
316     if (NULL == *privKey)
317     {
318 memclean:
319         if (NULL != *certificateChain)
320         {
321             for (size_t i = 0; i < *chainLength; ++i)
322             {
323                 OICFree((*certificateChain)[i]);
324             }
325         }
326         OICFree(*certificateChain);
327         *certificateChain = NULL;
328         *privKey     = NULL;
329         *chainLength = 0;
330         if (OC_STACK_NO_MEMORY == ret)
331         {
332             OC_LOG(ERROR, TAG, "Error while memory allocation");
333         }
334         return ret;
335     }
336
337     memcpy(*privKey, privB64buf, privB64len + 1);
338
339     return OC_STACK_OK;
340 }
341
342
343 OCStackResult PMGenerateCertificateCredentials(const OicUuid_t *ptDeviceId,
344         const OicUuid_t *deviceId, OicSecCred_t **const cred)
345 {
346     if (NULL == ptDeviceId || NULL == deviceId || NULL == cred)
347     {
348         return OC_STACK_INVALID_PARAM;
349     }
350     char **certificateChain = NULL;
351     char *privKey = NULL;
352     size_t certChainLen = 0;
353     if (OC_STACK_OK != GenerateCertificateAndKeys(deviceId, &certificateChain,
354             &certChainLen, &privKey))
355     {
356         OC_LOG(ERROR, TAG, "Error while generating credential data.");
357         return OC_STACK_ERROR;
358     }
359
360     char *publicJWK = CreateCertificatePublicJWK(certificateChain, certChainLen);
361     char *privateJWK = CreateCertificatePrivateJWK(privKey);
362     for (size_t i = 0; i < certChainLen; ++i)
363     {
364         OICFree(certificateChain[i]);
365     }
366     OICFree(certificateChain);
367     OICFree(privKey);
368     if (NULL == publicJWK || NULL == privateJWK)
369     {
370         OICFree(publicJWK);
371         OICFree(privateJWK);
372         OC_LOG(ERROR, TAG, "Error while converting keys to JWK format.");
373         return OC_STACK_ERROR;
374     }
375
376     OicSecCred_t *tempCred =  GenerateCredential(deviceId, SIGNED_ASYMMETRIC_KEY, publicJWK,
377                               privateJWK, 1, ptDeviceId);
378     OICFree(publicJWK);
379     OICFree(privateJWK);
380     if (NULL == tempCred)
381     {
382         OC_LOG(ERROR, TAG, "Error while generating credential.");
383         return OC_STACK_ERROR;
384     }
385     *cred = tempCred;
386     return OC_STACK_OK;
387 }
388 #endif // __WITH_X509__