2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
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
8 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 * @brief This file contains implementation of Pseudo Random Function based on ANSI X9.31 Appendix A.2.4.
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"
34 using namespace Tizen::Base;
37 namespace Tizen { namespace Security
41 _Prng::GetRandomBytesN(const EVP_CIPHER* pAlg, long outLen)
47 SysTryReturn(NID_SEC, pAlg != null && outLen > 0, null, E_INVALID_ARG, "[E_INVALID_ARG] Input data are not valid.");
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.");
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.");
57 pPrng->lenKey = pAlg->key_len;
58 pPrng->blockSize = pAlg->block_size;
59 pPrng->lenSeed = pPrng->blockSize;
61 r = GenerateKey(*pPrng);
62 SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to generate key.", GetErrorMessage(r));
64 r = GenerateSeed(*pPrng);
65 SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to do generate seed.", GetErrorMessage(r));
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.");
70 pPrng->pRand = const_cast< byte* >(pOutBuf->GetPointer());
71 pPrng->randSize = outLen;
74 r = GenerateRandomBytes(*pPrng, null);
75 SysTryCatch(NID_SEC, !IsFailed(r), , r, "[%s] Failed to generate random bytes.", GetErrorMessage(r));
79 return pOutBuf.release();
83 _Prng::GenerateKey(PrngContext& prng)
86 unsigned long index = 0;
87 unsigned long offset = 0;
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.");
94 index = sizeof(clock_t);
96 while (offset < prng.lenKey)
98 if ((offset + sizeof(clock_t)) > prng.lenKey)
100 index = prng.lenKey - offset;
103 PerformXor(prng.pKey.get() + offset, reinterpret_cast< byte* >(&tick), index, prng.pKey.get() + offset);
107 prng.lenKey = offset;
112 _Prng::GenerateSeed(PrngContext& prng)
114 result r = E_SUCCESS;
115 unsigned long offset = 0;
116 unsigned long index = 0;
118 time_t miliSecond = 0;
120 miliSecond = time(null);
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.");
126 index = sizeof(clock_t);
128 while (offset < prng.lenSeed)
130 if ((offset + sizeof(clock_t)) > prng.lenSeed)
132 index = prng.lenSeed - offset;
136 tick = tick + miliSecond;
137 PerformXor(prng.pSeed.get() + offset, reinterpret_cast< byte* >(&tick), index, prng.pSeed.get() + offset);
141 prng.lenSeed = offset;
146 _Prng::PerformXor(byte* pIn1, byte* pIn2, unsigned long inLen, byte* pOut)
148 unsigned long index = 0;
150 for (index = 0; index < inLen; index++)
152 pOut[index] = pIn1[index] ^ pIn2[index];
158 _Prng::GenerateRandomBytes(PrngContext& prng, ByteBuffer* pSeed)
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;
178 EVP_CIPHER_CTX cipherCtx;
179 const EVP_CIPHER* pEncryptionAlgorithm = null;
183 pDt = const_cast< byte* >(pSeed->GetPointer());
184 dtLen = pSeed->GetRemaining();
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.");
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.");
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.");
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.");
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.");
202 lenInterVal1 = blockSize;
203 lenInterVal2 = blockSize;
204 lenInterVal1XorBlockLen = blockSize;
205 lenInterVal2XorInterVal1 = blockSize;
207 while (prng.curOffset < randSize)
209 blockSize = prng.blockSize;
213 time_t sttime = null;
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));
219 offset += sizeof(time_t);
220 tmp = sizeof(clock_t);
221 while (offset < blockSize)
223 if ((offset + sizeof(clock_t)) > blockSize)
225 tmp = blockSize - offset;
229 PerformXor(pBlock.get() + offset, reinterpret_cast< byte* >(&tick), tmp, pBlock.get() + offset);
235 if (dtLen != blockSize)
238 SysLogException(NID_SEC, r, "The seed length do not match the data block size.");
242 memcpy(pBlock.get(), pDt, dtLen);
245 // Selects the encryption algorithm using prng.pAlg
246 pEncryptionAlgorithm = prng.pAlg;
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.");
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.");
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 ////////////////////////////////////////////////////////////////////////////////////////////////////
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.");
263 PerformXor(pInterVal1.get(), prng.pSeed.get(), blockSize, pInterVal1XorBlock.get());
264 lenInterVal1XorBlockLen = lenInterVal1;
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.");
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.");
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 ////////////////////////////////////////////////////////////////////////////////////////////////////
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.");
281 PerformXor(pInterVal2.get(), pInterVal1.get(), blockSize, pInterVal2XorInterVal1.get());
282 lenInterVal2XorInterVal1 = blockSize;
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.");
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.");
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 ////////////////////////////////////////////////////////////////////////////////////////////////////
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.");
299 if ((prng.curOffset + lenInterVal2) > prng.randSize)
301 lenInterVal2 = prng.randSize - prng.curOffset;
304 memcpy(prng.pRand + prng.curOffset, pInterVal2.get(), lenInterVal2);
305 prng.curOffset += lenInterVal2;
310 EVP_CIPHER_CTX_cleanup(&cipherCtx);
316 _Prng::CreatePrngContextN(void)
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.");
323 memset(pPrng.get(), 0, sizeof(PrngContext));
325 return pPrng.release();
328 } } //Tizen::Security