Merge "Add Setting feature on system-server lib" into tizen_2.2
[platform/framework/native/appfw.git] / src / security / FSec_Prng.cpp
1 //
2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Apache License, Version 2.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 /**
18  *  @file       FSec_Prng.cpp
19  *  @brief      This file contains implementation of Pseudo Random Function based on ANSI X9.31 Appendix A.2.4.
20  *
21  */
22 #include <new>
23 #include <stdlib.h>
24 #include <sys/time.h>
25 #include <sys/timeb.h>
26 #include <openssl/crypto.h>
27 #include <openssl/evp.h>
28 #include <unique_ptr.h>
29 #include <FBaseResult.h>
30 #include <FBaseErrors.h>
31 #include <FBaseSysLog.h>
32 #include "FSec_Prng.h"
33
34 using namespace Tizen::Base;
35
36
37 namespace Tizen { namespace Security
38 {
39
40 ByteBuffer*
41 _Prng::GetRandomBytesN(const EVP_CIPHER* pAlg, long outLen)
42 {
43         result r = E_SUCCESS;
44
45         ClearLastResult();
46
47         SysTryReturn(NID_SEC, pAlg != null && outLen > 0, null, E_INVALID_ARG, "[E_INVALID_ARG] Input data are not valid.");
48
49         std::unique_ptr <PrngContext> pPrng (_Prng::CreatePrngContextN());
50         SysTryReturn(NID_SEC, pPrng != null, null, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new PrngContext object failed.");
51
52         std::unique_ptr <ByteBuffer> pOutBuf (new (std::nothrow) ByteBuffer());
53         SysTryCatch(NID_SEC, pOutBuf != null, r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new ByteBuffer failed.");
54
55         // init pAlg details
56         pPrng->pAlg = pAlg;
57         pPrng->lenKey = pAlg->key_len;
58         pPrng->blockSize = pAlg->block_size;
59         pPrng->lenSeed = pPrng->blockSize;
60
61         r = GenerateKey(*pPrng);
62         SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to generate key.", GetErrorMessage(r));
63
64         r = GenerateSeed(*pPrng);
65         SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to do generate seed.", GetErrorMessage(r));
66
67         r = pOutBuf->Construct(outLen);
68         SysTryCatch(NID_SEC, !IsFailed(r), r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Failed to allocate memory.");
69
70         pPrng->pRand = const_cast< byte* >(pOutBuf->GetPointer());
71         pPrng->randSize = outLen;
72         pPrng->curOffset = 0;
73
74         r = GenerateRandomBytes(*pPrng, null);
75         SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to generate random bytes.", GetErrorMessage(r));
76
77 CATCH:
78
79         return pOutBuf.release();
80 }
81
82 result
83 _Prng::GenerateKey(PrngContext& prng)
84 {
85         result r = E_SUCCESS;
86         unsigned long index = 0;
87         unsigned long offset = 0;
88         clock_t tick = null;
89
90         prng.pKey = std::unique_ptr <byte[]> (new (std::nothrow) byte[prng.lenKey]);
91         SysTryReturn(NID_SEC, prng.pKey != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
92
93         offset = 0;
94         index = sizeof(clock_t);
95
96         while (offset < prng.lenKey)
97         {
98                 if ((offset + sizeof(clock_t)) > prng.lenKey)
99                 {
100                         index = prng.lenKey - offset;
101                 }
102                 tick = clock();
103                 PerformXor(prng.pKey.get() + offset, reinterpret_cast< byte* >(&tick), index, prng.pKey.get() + offset);
104                 offset += index;
105         }
106
107         prng.lenKey = offset;
108         return r;
109 }
110
111 result
112 _Prng::GenerateSeed(PrngContext& prng)
113 {
114         result r = E_SUCCESS;
115         unsigned long offset = 0;
116         unsigned long index = 0;
117         clock_t tick = 0;
118         time_t miliSecond = 0;
119
120         miliSecond = time(null);
121
122         prng.pSeed = std::unique_ptr <byte[]> (new (std::nothrow) byte[prng.lenSeed]);
123         SysTryReturn(NID_SEC, prng.pSeed != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
124
125         offset = 0;
126         index = sizeof(clock_t);
127
128         while (offset < prng.lenSeed)
129         {
130                 if ((offset + sizeof(clock_t)) > prng.lenSeed)
131                 {
132                         index = prng.lenSeed - offset;
133                 }
134
135                 tick = clock();
136                 tick = tick + miliSecond;
137                 PerformXor(prng.pSeed.get() + offset, reinterpret_cast< byte* >(&tick), index, prng.pSeed.get() + offset);
138                 offset += index;
139         }
140
141         prng.lenSeed = offset;
142         return r;
143 }
144
145 void
146 _Prng::PerformXor(byte* pIn1, byte* pIn2, unsigned long inLen, byte* pOut)
147 {
148         unsigned long index = 0;
149
150         for (index = 0; index < inLen; index++)
151         {
152                 pOut[index] = pIn1[index] ^ pIn2[index];
153         }
154
155 }
156
157 result
158 _Prng::GenerateRandomBytes(PrngContext& prng, ByteBuffer* pSeed)
159 {
160         result r = E_SUCCESS;
161         unsigned int ret = 0;
162         unsigned long tmp = 0;
163         unsigned long offset = 0;
164         unsigned long lenInterVal1 = 0;
165         unsigned long lenInterVal2 = 0;
166         unsigned long dtLen = 0;
167         unsigned long blockSize = prng.blockSize;
168         unsigned long randSize = prng.randSize;
169         unsigned long lenInterVal1XorBlockLen = 0;
170         unsigned long lenInterVal2XorInterVal1 = 0;
171         std::unique_ptr <byte[]> pBlock;
172         std::unique_ptr <byte[]> pInterVal1;
173         std::unique_ptr <byte[]> pInterVal2;
174         std::unique_ptr <byte[]> pInterVal1XorBlock;
175         std::unique_ptr <byte[]> pInterVal2XorInterVal1;
176         byte* pDt = null;
177         clock_t tick = null;
178         EVP_CIPHER_CTX cipherCtx;
179         const EVP_CIPHER* pEncryptionAlgorithm = null;
180
181         if (pSeed != null)
182         {
183                 pDt = const_cast< byte* >(pSeed->GetPointer());
184                 dtLen = pSeed->GetRemaining();
185         }
186
187         pBlock = std::unique_ptr <byte[]> (new (std::nothrow) byte[blockSize]);
188         SysTryReturn(NID_SEC, pBlock != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
189
190         pInterVal1 = std::unique_ptr <byte[]> (new (std::nothrow) byte[blockSize]);
191         SysTryReturn(NID_SEC, pInterVal1 != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
192
193         pInterVal2 = std::unique_ptr <byte[]> (new (std::nothrow) byte[blockSize]);
194         SysTryReturn(NID_SEC, pInterVal2 != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
195
196         pInterVal1XorBlock = std::unique_ptr <byte[]> (new (std::nothrow) byte[blockSize]);
197         SysTryReturn(NID_SEC, pInterVal1XorBlock != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
198
199         pInterVal2XorInterVal1 = std::unique_ptr <byte[]> (new (std::nothrow) byte[blockSize]);
200         SysTryReturn(NID_SEC, pInterVal2XorInterVal1 != null, E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new byte array failed.");
201
202         lenInterVal1 = blockSize;
203         lenInterVal2 = blockSize;
204         lenInterVal1XorBlockLen = blockSize;
205         lenInterVal2XorInterVal1 = blockSize;
206
207         while (prng.curOffset < randSize)
208         {
209                 blockSize = prng.blockSize;
210
211                 if (pDt == null)
212                 {
213                         time_t sttime = null;
214                         sttime = time(null);
215
216                         //get D and append with xor of clock tick and random value in buffer till block size is reached
217                         memcpy(pBlock.get(), reinterpret_cast< byte* >(&sttime), sizeof(time_t));
218
219                         offset += sizeof(time_t);
220                         tmp = sizeof(clock_t);
221                         while (offset < blockSize)
222                         {
223                                 if ((offset + sizeof(clock_t)) > blockSize)
224                                 {
225                                         tmp = blockSize - offset;
226                                 }
227
228                                 tick = clock();
229                                 PerformXor(pBlock.get() + offset, reinterpret_cast< byte* >(&tick), tmp, pBlock.get() + offset);
230                                 offset += tmp;
231                         }
232                 }
233                 else
234                 {
235                         if (dtLen != blockSize)
236                         {
237                                 r = E_INVALID_ARG;
238                                 SysLogException(NID_SEC, r, "The seed length do not match the data block size.");
239                                 return r;
240                         }
241
242                         memcpy(pBlock.get(), pDt, dtLen);
243                 }
244
245                 // Selects the encryption algorithm using prng.pAlg
246                 pEncryptionAlgorithm = prng.pAlg;
247
248                 //Cipher init operation based on op mode
249                 ret = EVP_CipherInit(&cipherCtx, pEncryptionAlgorithm, prng.pKey.get(), null, 0);
250                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
251
252                 //if padding enabled or not
253                 ret = EVP_CIPHER_CTX_set_padding(&cipherCtx, 0);
254                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
255
256                 //cipher update operation
257                 ret = EVP_CipherUpdate(&cipherCtx, pInterVal1.get(), reinterpret_cast< int* >(&lenInterVal1), pBlock.get(), blockSize);
258                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
259                 ////////////////////////////////////////////////////////////////////////////////////////////////////
260
261                 SysTryCatch(NID_SEC, lenInterVal1 == blockSize, r = E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The input and output data lengths do not match.");
262
263                 PerformXor(pInterVal1.get(), prng.pSeed.get(), blockSize, pInterVal1XorBlock.get());
264                 lenInterVal1XorBlockLen = lenInterVal1;
265
266                 //Cipher init operation based on op mode
267                 ret = EVP_CipherInit(&cipherCtx, pEncryptionAlgorithm, prng.pKey.get(), null, 0);
268                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
269
270                 //if padding enabled or not
271                 ret = EVP_CIPHER_CTX_set_padding(&cipherCtx, 0);
272                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
273
274                 //cipher update operation
275                 ret = EVP_CipherUpdate(&cipherCtx, pInterVal2.get(), reinterpret_cast< int* >(&lenInterVal2), pInterVal1XorBlock.get(), lenInterVal1XorBlockLen);
276                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
277                 ////////////////////////////////////////////////////////////////////////////////////////////////////
278
279                 SysTryCatch(NID_SEC, lenInterVal2 == blockSize, r = E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The input and output data lengths do not match.");
280
281                 PerformXor(pInterVal2.get(), pInterVal1.get(), blockSize, pInterVal2XorInterVal1.get());
282                 lenInterVal2XorInterVal1 = blockSize;
283
284                 //Cipher init operation based on op mode
285                 ret = EVP_CipherInit(&cipherCtx, pEncryptionAlgorithm, prng.pKey.get(), null, 0);
286                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
287
288                 //if padding enabled or not
289                 ret = EVP_CIPHER_CTX_set_padding(&cipherCtx, 0);
290                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
291
292                 //cipher update operation
293                 ret = EVP_CipherUpdate(&cipherCtx, prng.pSeed.get(), reinterpret_cast< int* >(&prng.lenSeed), pInterVal2XorInterVal1.get(), lenInterVal2XorInterVal1);
294                 SysTryCatch(NID_SEC, ret == 1, r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] An unexpected system error occurred.");
295                 ////////////////////////////////////////////////////////////////////////////////////////////////////
296
297                 SysTryCatch(NID_SEC, prng.lenSeed == blockSize, r = E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The input and output data lengths do not match.");
298
299                 if ((prng.curOffset + lenInterVal2) > prng.randSize)
300                 {
301                         lenInterVal2 = prng.randSize - prng.curOffset;
302                 }
303
304                 memcpy(prng.pRand + prng.curOffset, pInterVal2.get(), lenInterVal2);
305                 prng.curOffset += lenInterVal2;
306         }
307
308 CATCH:
309
310         EVP_CIPHER_CTX_cleanup(&cipherCtx);
311
312         return r;
313 }
314
315 _Prng::PrngContext*
316 _Prng::CreatePrngContextN(void)
317 {
318         ClearLastResult();
319
320         std::unique_ptr <PrngContext> pPrng (new (std::nothrow) PrngContext());
321         SysTryReturn(NID_SEC, pPrng != null, null, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Allocating new PrngContext object failed.");
322
323         memset(pPrng.get(), 0, sizeof(PrngContext));
324
325         return pPrng.release();
326 }
327
328 } } //Tizen::Security