Adding more randomised token generation logic
[platform/upstream/iotivity.git] / resource / c_common / ocrandom / src / ocrandom.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH 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
21 // Defining _POSIX_C_SOURCE macro with 199309L (or greater) as value
22 // causes header files to expose definitions
23 // corresponding to the POSIX.1b, Real-time extensions
24 // (IEEE Std 1003.1b-1993) specification
25 //
26 // For this specific file, see use of clock_gettime,
27 // Refer to http://pubs.opengroup.org/stage7tc1/functions/clock_gettime.html
28 // and to http://man7.org/linux/man-pages/man2/clock_gettime.2.html
29 #ifndef _POSIX_C_SOURCE
30 #define _POSIX_C_SOURCE 200809L
31 #endif
32
33 #include "iotivity_config.h"
34
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #ifdef HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif
44 #ifdef HAVE_STRING_H
45 #include <string.h>
46 #elif defined(HAVE_STRINGS_H)
47 #include <strings.h>
48 #endif
49 #ifdef HAVE_SYS_TIME_H
50 #include <sys/time.h>
51 #endif
52 #ifdef HAVE_TIME_H
53 #include <time.h>
54 #endif
55 #if defined(__ANDROID__)
56 #include <ctype.h>
57 #include <linux/time.h>
58 #endif
59 #ifdef HAVE_WINDOWS_H
60 #include <windows.h>
61 #endif
62 #include "ocrandom.h"
63 #include <stdio.h>
64
65 #ifdef HAVE_UUID_UUID_H
66 #include <uuid/uuid.h>
67 #endif
68
69 #define NANO_SEC 1000000000
70
71 #ifdef ARDUINO
72 #include "Arduino.h"
73
74 // ARM GCC compiler doesnt define srandom function.
75 #if defined(ARDUINO) && !defined(ARDUINO_ARCH_SAM)
76 #define HAVE_SRANDOM 1
77 #endif
78
79 uint8_t GetRandomBitRaw()
80 {
81     return analogRead((uint8_t)ANALOG_IN) & 0x1;
82 }
83
84 uint8_t GetRandomBitRaw2()
85 {
86     int a = 0;
87     for (;;)
88     {
89         a = GetRandomBitRaw() | (GetRandomBitRaw()<<1);
90         if (a==1)
91         {
92             return 0; // 1 to 0 transition: log a zero bit
93         }
94         if (a==2)
95         {
96             return 1;// 0 to 1 transition: log a one bit
97         }
98         // For other cases, try again.
99     }
100 }
101
102 uint8_t GetRandomBit()
103 {
104     int a = 0;
105     for (;;)
106     {
107         a = GetRandomBitRaw2() | (GetRandomBitRaw2()<<1);
108         if (a==1)
109         {
110             return 0; // 1 to 0 transition: log a zero bit
111         }
112         if (a==2)
113         {
114             return 1;// 0 to 1 transition: log a one bit
115         }
116         // For other cases, try again.
117     }
118 }
119 #endif
120
121 int8_t OCSeedRandom()
122 {
123 #ifndef ARDUINO
124     // Get current time to Seed.
125     uint64_t currentTime = 0;
126 #ifdef __ANDROID__
127     struct timespec getTs;
128     clock_gettime(CLOCK_MONOTONIC, &getTs);
129     currentTime = (getTs.tv_sec * (uint64_t)NANO_SEC + getTs.tv_nsec)/1000;
130 #elif _WIN32
131     LARGE_INTEGER count;
132     if (QueryPerformanceCounter(&count)) {
133         currentTime = count.QuadPart;
134     }
135 #elif  _POSIX_TIMERS > 0
136     struct timespec ts;
137     clock_gettime(CLOCK_MONOTONIC, &ts);
138     currentTime = (ts.tv_sec * (uint64_t)NANO_SEC + ts.tv_nsec)/ 1000;
139 #else
140     struct timeval tv;
141     gettimeofday(&tv, NULL);
142     currentTime = tv.tv_sec * (uint64_t)1000000 + tv.tv_usec;
143 #endif
144 #if defined(__unix__) || defined(__APPLE__) || defined(__TIZENRT__)
145     int32_t fd = open("/dev/urandom", O_RDONLY);
146     if (fd >= 0)
147     {
148         uint32_t randomSeed = 0;
149         uint32_t totalRead = 0; //how many integers were read
150         int32_t currentRead = 0;
151         while (totalRead < sizeof(randomSeed))
152         {
153             currentRead = read(fd, (uint8_t*) &randomSeed + totalRead,
154                     sizeof(randomSeed) - totalRead);
155             if (currentRead > 0)
156             {
157                 totalRead += currentRead;
158             }
159         }
160         close(fd);
161         srand(randomSeed | currentTime);
162     }
163     else
164 #endif
165     {
166         // Do time based seed when problem in accessing "/dev/urandom"
167         srand(currentTime);
168     }
169
170     return 0;
171 #elif defined ARDUINO
172     uint32_t result =0;
173     uint8_t i;
174     for (i=32; i--;)
175     {
176         result += result + GetRandomBit();
177     }
178 #if HAVE_SRANDOM
179     srandom(result);
180 #else
181     srand(result);
182 #endif
183     return 0;
184 #endif
185
186 }
187
188 void OCFillRandomMem(uint8_t * location, uint16_t len)
189 {
190     if (!location)
191     {
192         return;
193     }
194     for (; len--;)
195     {
196         *location++ = OCGetRandomByte();
197     }
198     uint8_t *temp = (char *) OICCalloc(len, sizeof(char));
199     int *mask = (int *) OICCalloc(len, sizeof(int));
200     int i, j, rand_idx;
201     for (i = 0; i < len; i++)
202     {
203         mask[i] = 0;
204     }
205     j = 0;
206     for (i = 0; i < len; i++)
207     {
208         rand_idx = lrand48() % len;
209         while((rand_idx < len) && (mask[rand_idx] != 0))
210         {
211             rand_idx++;
212         }
213         if(rand_idx == len)
214         {
215             rand_idx = 0;
216             while(mask[rand_idx] != 0)
217             {
218                 rand_idx++;
219             }
220         }
221         temp[rand_idx] = location[j];
222         mask[rand_idx] = 1;
223         j++;
224     }
225     for (i = 0; i < len; i++)
226     {
227         location[i] = temp[i];
228     }
229     OICFree(temp);
230     OICFree(mask);
231 }
232
233 uint32_t OCGetRandom()
234 {
235     uint32_t result = 0;
236     OCFillRandomMem((uint8_t*) &result, 4);
237     return result;
238 }
239
240 uint8_t OCGetRandomByte(void)
241 {
242 #ifdef HAVE_SRANDOM
243     return random() & 0x00FF;
244 #else
245     return rand() & 0x00FF;
246 #endif
247 }
248
249 uint32_t OCGetRandomRange(uint32_t firstBound, uint32_t secondBound)
250 {
251     uint32_t base;
252     uint32_t diff;
253     uint32_t result;
254     if (firstBound > secondBound)
255     {
256         base = secondBound;
257         diff = firstBound - secondBound;
258     }
259     else if (firstBound < secondBound)
260     {
261         base = firstBound;
262         diff = secondBound - firstBound;
263     }
264     else
265     {
266         return secondBound;
267     }
268     result = ((float)OCGetRandom()/((float)(0xFFFFFFFF))*(float)diff) + (float) base;
269     return result;
270 }
271
272 #if defined(__ANDROID__)
273 uint8_t parseUuidChar(char c)
274 {
275     if (isdigit(c))
276     {
277         return c - '0';
278     }
279     else
280     {
281         return c - 'a' + 10;
282     }
283 }
284 uint8_t parseUuidPart(const char *c)
285 {
286     return (parseUuidChar(c[0])<<4) + parseUuidChar(c[1]);
287 }
288 #endif
289
290 OCRandomUuidResult OCGenerateUuid(uint8_t uuid[UUID_SIZE])
291 {
292     if (!uuid)
293     {
294         return RAND_UUID_INVALID_PARAM;
295     }
296 #if defined(__ANDROID__)
297     char uuidString[UUID_STRING_SIZE];
298     int8_t ret = OCGenerateUuidString(uuidString);
299
300     if (RAND_UUID_OK == ret)
301     {
302         uuid[ 0] = parseUuidPart(&uuidString[0]);
303         uuid[ 1] = parseUuidPart(&uuidString[2]);
304         uuid[ 2] = parseUuidPart(&uuidString[4]);
305         uuid[ 3] = parseUuidPart(&uuidString[6]);
306
307         uuid[ 4] = parseUuidPart(&uuidString[9]);
308         uuid[ 5] = parseUuidPart(&uuidString[11]);
309
310         uuid[ 6] = parseUuidPart(&uuidString[14]);
311         uuid[ 7] = parseUuidPart(&uuidString[16]);
312
313         uuid[ 8] = parseUuidPart(&uuidString[19]);
314         uuid[ 9] = parseUuidPart(&uuidString[21]);
315
316         uuid[10] = parseUuidPart(&uuidString[24]);
317         uuid[11] = parseUuidPart(&uuidString[26]);
318         uuid[12] = parseUuidPart(&uuidString[28]);
319         uuid[13] = parseUuidPart(&uuidString[30]);
320         uuid[14] = parseUuidPart(&uuidString[32]);
321         uuid[15] = parseUuidPart(&uuidString[34]);
322
323         return RAND_UUID_OK;
324     }
325 #endif
326 #if defined(HAVE_UUID_UUID_H)
327     // note: uuid_t is typedefed as unsigned char[16] on linux/apple
328     uuid_generate(uuid);
329     return RAND_UUID_OK;
330 #else
331     // Fallback for all platforms is filling the array with random data
332     OCFillRandomMem(uuid, UUID_SIZE);
333     return RAND_UUID_OK;
334 #endif
335 }
336
337 OCRandomUuidResult OCGenerateUuidString(char uuidString[UUID_STRING_SIZE])
338 {
339     if (!uuidString)
340     {
341         return RAND_UUID_INVALID_PARAM;
342     }
343 #if defined(__ANDROID__)
344     int32_t fd = open("/proc/sys/kernel/random/uuid", O_RDONLY);
345     if (fd > 0)
346     {
347         ssize_t readResult = read(fd, uuidString, UUID_STRING_SIZE - 1);
348         close(fd);
349         if (readResult < 0)
350         {
351             return RAND_UUID_READ_ERROR;
352         }
353         else if (readResult < UUID_STRING_SIZE - 1)
354         {
355             uuidString[0] = '\0';
356             return RAND_UUID_READ_ERROR;
357         }
358
359         uuidString[UUID_STRING_SIZE - 1] = '\0';
360         for (char* p = uuidString; *p; ++p)
361         {
362             *p = tolower(*p);
363         }
364         return RAND_UUID_OK;
365     }
366     else
367     {
368         return RAND_UUID_READ_ERROR;
369     }
370 #elif defined(HAVE_UUID_UUID_H)
371     uint8_t uuid[UUID_SIZE];
372     int8_t ret = OCGenerateUuid(uuid);
373
374     if (ret != 0)
375     {
376         return ret;
377     }
378
379     uuid_unparse_lower(uuid, uuidString);
380     return RAND_UUID_OK;
381
382 #else
383     uint8_t uuid[UUID_SIZE];
384     OCGenerateUuid(uuid);
385
386     return OCConvertUuidToString(uuid, uuidString);
387 #endif
388 }
389
390 OCRandomUuidResult OCConvertUuidToString(const uint8_t uuid[UUID_SIZE],
391                                          char uuidString[UUID_STRING_SIZE])
392 {
393     if (uuid == NULL || uuidString == NULL)
394     {
395         return RAND_UUID_INVALID_PARAM;
396     }
397
398
399     int ret = snprintf(uuidString, UUID_STRING_SIZE,
400             "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
401             uuid[0], uuid[1], uuid[2], uuid[3],
402             uuid[4], uuid[5], uuid[6], uuid[7],
403             uuid[8], uuid[9], uuid[10], uuid[11],
404             uuid[12], uuid[13], uuid[14], uuid[15]
405             );
406
407     if (ret != UUID_STRING_SIZE - 1)
408     {
409         return RAND_UUID_CONVERT_ERROR;
410     }
411
412     return RAND_UUID_OK;
413 }
414
415 OCRandomUuidResult OCConvertStringToUuid(const char uuidString[UUID_STRING_SIZE],
416                                          uint8_t uuid[UUID_SIZE])
417 {
418     if(NULL == uuidString || NULL == uuid)
419     {
420         return RAND_UUID_INVALID_PARAM;
421     }
422
423     size_t urnIdx = 0;
424     size_t uuidIdx = 0;
425     size_t strUuidLen = 0;
426     char convertedUuid[UUID_SIZE * 2] = {0};
427
428     strUuidLen = strlen(uuidString);
429     if((UUID_STRING_SIZE - 1) == strUuidLen)
430     {
431         for(uuidIdx=0, urnIdx=0; uuidIdx < UUID_SIZE ; uuidIdx++, urnIdx+=2)
432         {
433             if(*(uuidString + urnIdx) == '-')
434             {
435                 urnIdx++;
436             }
437             sscanf(uuidString + urnIdx, "%2hhx", &convertedUuid[uuidIdx]);
438         }
439     }
440     else
441     {
442         return RAND_UUID_CONVERT_ERROR;
443     }
444
445     memcpy(uuid, convertedUuid, UUID_SIZE);
446
447     return RAND_UUID_OK;
448 }
449