diff options
Diffstat (limited to 'sal/rtl/random.cxx')
-rw-r--r-- | sal/rtl/random.cxx | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/sal/rtl/random.cxx b/sal/rtl/random.cxx new file mode 100644 index 000000000..418358b22 --- /dev/null +++ b/sal/rtl/random.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cmath> + +#include <sal/types.h> +#include <o3tl/temporary.hxx> +#include <osl/thread.h> +#include <osl/thread.hxx> +#include <osl/time.h> +#include <rtl/alloc.h> +#include <rtl/digest.h> +#include <rtl/random.h> +#include <oslrandom.h> + +#define RTL_RANDOM_RNG_1(a) ((a) * 16807L) +#define RTL_RANDOM_RNG_2(a) ((a) * 65539L) + +#define RTL_RANDOM_RNG(x, y, z) \ +{ \ + (x) = 170 * ((x) % 178) - 63 * ((x) / 178); \ + if ((x) < 0) (x) += 30328; \ + \ + (y) = 171 * ((y) % 177) - 2 * ((y) / 177); \ + if ((y) < 0) (y) += 30269; \ + \ + (z) = 172 * ((z) % 176) - 35 * ((z) / 176); \ + if ((z) < 0) (z) += 30307; \ +} + +namespace { + +struct RandomData_Impl +{ + sal_Int16 m_nX; + sal_Int16 m_nY; + sal_Int16 m_nZ; +}; + +} + +static double data (RandomData_Impl *pImpl); + +#define RTL_RANDOM_DIGEST rtl_Digest_AlgorithmMD5 +#define RTL_RANDOM_SIZE_DIGEST RTL_DIGEST_LENGTH_MD5 +#define RTL_RANDOM_SIZE_POOL 1023 + +namespace { + +struct RandomPool_Impl +{ + rtlDigest m_hDigest; + sal_uInt8 m_pDigest[RTL_RANDOM_SIZE_DIGEST]; + sal_uInt8 m_pData[RTL_RANDOM_SIZE_POOL + 1]; + sal_uInt32 m_nData; + sal_uInt32 m_nIndex; + sal_uInt32 m_nCount; +}; + +} + +static bool initPool(RandomPool_Impl *pImpl); + +static void seedPool( + RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen); + +static void readPool( + RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen); + +static double data(RandomData_Impl *pImpl) +{ + double random; + + RTL_RANDOM_RNG (pImpl->m_nX, pImpl->m_nY, pImpl->m_nZ); + random = ((static_cast<double>(pImpl->m_nX) / 30328.0) + + (static_cast<double>(pImpl->m_nY) / 30269.0) + + (static_cast<double>(pImpl->m_nZ) / 30307.0) ); + + return std::modf(random, &o3tl::temporary(double())); +} + +static bool initPool(RandomPool_Impl *pImpl) +{ + pImpl->m_hDigest = rtl_digest_create(RTL_RANDOM_DIGEST); + if (pImpl->m_hDigest) + { + oslThreadIdentifier tid; + TimeValue tv; + RandomData_Impl rd; + double seed; + + /* The use of uninitialized stack variables as a way to + * enhance the entropy of the random pool triggers + * memory checkers like purify and valgrind. + */ + + /* + seedPool (pImpl, (sal_uInt8*)&tid, sizeof(tid)); + seedPool (pImpl, (sal_uInt8*)&tv, sizeof(tv)); + seedPool (pImpl, (sal_uInt8*)&rd, sizeof(rd)); + */ + + tid = osl::Thread::getCurrentIdentifier(); + tid = RTL_RANDOM_RNG_2(RTL_RANDOM_RNG_1(tid)); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tid), sizeof(tid)); + + osl_getSystemTime (&tv); + tv.Seconds = RTL_RANDOM_RNG_2(tv.Seconds); + tv.Nanosec = RTL_RANDOM_RNG_2(tv.Nanosec); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tv), sizeof(tv)); + + rd.m_nX = static_cast<sal_Int16>(((tid >> 1) << 1) + 1); + rd.m_nY = static_cast<sal_Int16>(((tv.Seconds >> 1) << 1) + 1); + rd.m_nZ = static_cast<sal_Int16>(((tv.Nanosec >> 1) << 1) + 1); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&rd), sizeof(rd)); + + while (pImpl->m_nData < RTL_RANDOM_SIZE_POOL) + { + seed = data (&rd); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&seed), sizeof(seed)); + } + return true; + } + return false; +} + +static void seedPool( + RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + sal_Size i; + sal_sSize j, k; + + for (i = 0; i < nBufLen; i += RTL_RANDOM_SIZE_DIGEST) + { + j = nBufLen - i; + if (j > RTL_RANDOM_SIZE_DIGEST) + j = RTL_RANDOM_SIZE_DIGEST; + + rtl_digest_update( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + + k = (pImpl->m_nIndex + j) - RTL_RANDOM_SIZE_POOL; + if (k > 0) + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k); + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[0]), k); + } + else + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j); + } + + rtl_digest_update(pImpl->m_hDigest, pBuffer, j); + pBuffer += j; + + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + for (k = 0; k < j; k++) + { + pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k]; + if (pImpl->m_nIndex >= RTL_RANDOM_SIZE_POOL) + { + pImpl->m_nData = RTL_RANDOM_SIZE_POOL; + pImpl->m_nIndex = 0; + } + } + } + + if (pImpl->m_nIndex > pImpl->m_nData) + pImpl->m_nData = pImpl->m_nIndex; +} + +static void readPool ( + RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + sal_Int32 j, k; + + while (nBufLen > 0) + { + j = nBufLen; + if (j > RTL_RANDOM_SIZE_DIGEST/2) + j = RTL_RANDOM_SIZE_DIGEST/2; + nBufLen -= j; + + rtl_digest_update( + pImpl->m_hDigest, + &(pImpl->m_pDigest[RTL_RANDOM_SIZE_DIGEST/2]), + RTL_RANDOM_SIZE_DIGEST/2); + + k = (pImpl->m_nIndex + j) - pImpl->m_nData; + if (k > 0) + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k); + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[0]), k); + } + else + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j); + } + + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + for (k = 0; k < j; k++) + { + if (pImpl->m_nIndex >= pImpl->m_nData) + pImpl->m_nIndex = 0; + + pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k]; + *pBuffer++ = pImpl->m_pDigest[k + RTL_RANDOM_SIZE_DIGEST/2]; + } + } + + pImpl->m_nCount++; + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_nCount), sizeof(pImpl->m_nCount)); + rtl_digest_update( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); +} + +rtlRandomPool SAL_CALL rtl_random_createPool() SAL_THROW_EXTERN_C() +{ + /* try to get system random number, if it fail fall back on own pool */ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(rtl_allocateZeroMemory(sizeof(RandomPool_Impl))); + if (pImpl) + { + char sanity[4]; + if (!osl_get_system_random_data(sanity, 4)) + { + if (!initPool(pImpl)) + { + rtl_freeZeroMemory(pImpl, sizeof(RandomPool_Impl)); + pImpl = nullptr; + } + } + } + return static_cast< rtlRandomPool >(pImpl); +} + +void SAL_CALL rtl_random_destroyPool(rtlRandomPool Pool) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + if (pImpl) + { + if (pImpl->m_hDigest) + rtl_digest_destroy(pImpl->m_hDigest); + + rtl_freeZeroMemory (pImpl, sizeof(RandomPool_Impl)); + } +} + +rtlRandomError SAL_CALL rtl_random_addBytes( + rtlRandomPool Pool, const void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + const sal_uInt8 *pBuffer = static_cast< const sal_uInt8* >(Buffer); + + if (!pImpl || !pBuffer) + return rtl_Random_E_Argument; + + if (pImpl->m_hDigest) + seedPool (pImpl, pBuffer, Bytes); + + return rtl_Random_E_None; +} + +rtlRandomError SAL_CALL rtl_random_getBytes ( + rtlRandomPool Pool, void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + sal_uInt8 *pBuffer = static_cast< sal_uInt8* >(Buffer); + + if (!pImpl || !pBuffer) + return rtl_Random_E_Argument; + + if (pImpl->m_hDigest || !osl_get_system_random_data(static_cast< char* >(Buffer), Bytes)) + { + if (!pImpl->m_hDigest && !initPool(pImpl)) + return rtl_Random_E_Unknown; + readPool(pImpl, pBuffer, Bytes); + } + return rtl_Random_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |