From 953de26f6b80983a419eb8d7265f72889a3034c3 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Sat, 9 Oct 2021 23:05:39 -0400 Subject: [PATCH] Add random number generation support. --- pdfio-crypto.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++ pdfio-private.h | 1 + 2 files changed, 183 insertions(+) diff --git a/pdfio-crypto.c b/pdfio-crypto.c index 1653311..d5ac001 100644 --- a/pdfio-crypto.c +++ b/pdfio-crypto.c @@ -12,6 +12,188 @@ // #include "pdfio-private.h" +#if !_WIN32 +# include +#endif // !_WIN32 +#ifdef __has_include +# if __has_include() +# define HAVE_GETRANDOM 1 +# include +# endif // __has_include() +#endif // __has_include + + +// +// '_pdfioCryptoMakeRandom()' - Fill a buffer with good random numbers. +// + +void +_pdfioCryptoMakeRandom(uint8_t *buffer, // I - Buffer + size_t bytes) // I - Number of bytes +{ +#ifdef __APPLE__ + // macOS/iOS provide the arc4random function which is seeded with entropy + // from the system... + while (bytes > 0) + { + // Just collect 8 bits from each call to fill the buffer... + *buffer++ = (uint8_t)arc4random(); + bytes --; + } + +#else +# if _WIN32 + // Windows provides the CryptGenRandom function... + HCRYPTPROV prov; // Cryptographic provider + + if (CryptAcquireContextA(&prov, NULL, NULL, PROV_RSA_FULL, 0)) + { + // Got the default crypto provider, try to get random data... + BOOL success = CryptGenRandom(prov, (DWORD)bytes, buffer); + + // Release the crypto provider and return on success... + CryptReleaseContext(prov, 0); + + if (success) + return; + } + +# elif HAVE_GETRANDOM + // Linux provides a system call called getrandom that uses system entropy ... + ssize_t rbytes; // Bytes read + + while (bytes > 0) + { + if ((rbytes = getrandom(buffer, bytes, 0)) < 0) + { + if (errno != EINTR && errno != EAGAIN) + break; + } + bytes -= (size_t)rbytes; + buffer += rbytes; + } + + if (bytes == 0) + return; + +# else + // Other UNIX-y systems have /dev/urandom... + int fd; // Random number file + ssize_t rbytes; // Bytes read + + + // Fall back on /dev/urandom... + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) + { + while (bytes > 0) + { + if ((rbytes = read(fd, buffer, bytes)) < 0) + { + if (errno != EINTR && errno != EAGAIN) + break; + } + bytes -= (size_t)rbytes; + buffer += rbytes; + } + + close(fd); + + if (bytes == 0) + return; + } +# endif // _WIN32 + + // If we get here then we were unable to get enough random data or the local + // system doesn't have enough entropy. Make some up... + uint32_t i, // Looping var + mt_state[624], // Mersenne twister state + mt_index, // Mersenne twister index + temp; // Temporary value +# if _WIN32 + struct _timeb curtime; // Current time + + _ftime(&curtime); + mt_state[0] = (uint32_t)(curtime.time + curtime.millitm); + +# else + struct timeval curtime; // Current time + + gettimeofday(&curtime, NULL); + mt_state[0] = (uint32_t)(curtime.tv_sec + curtime.tv_usec); +# endif // _WIN32 + + // Seed the random number state... + mt_index = 0; + + for (i = 1; i < 624; i ++) + mt_state[i] = (uint32_t)((1812433253 * (mt_state[i - 1] ^ (mt_state[i - 1] >> 30))) + i); + + // Fill the buffer with random numbers... + while (bytes > 0) + { + if (mt_index == 0) + { + // Generate a sequence of random numbers... + uint32_t i1 = 1, i397 = 397; // Looping vars + + for (i = 0; i < 624; i ++) + { + temp = (mt_state[i] & 0x80000000) + (mt_state[i1] & 0x7fffffff); + mt_state[i] = mt_state[i397] ^ (temp >> 1); + + if (temp & 1) + mt_state[i] ^= 2567483615u; + + i1 ++; + i397 ++; + + if (i1 == 624) + i1 = 0; + + if (i397 == 624) + i397 = 0; + } + } + + // Pull 32-bits of random data... + temp = mt_state[mt_index ++]; + temp ^= temp >> 11; + temp ^= (temp << 7) & 2636928640u; + temp ^= (temp << 15) & 4022730752u; + temp ^= temp >> 18; + + if (mt_index == 624) + mt_index = 0; + + // Copy to the buffer... + switch (bytes) + { + case 1 : + *buffer++ = (uint8_t)(temp >> 24); + bytes --; + break; + case 2 : + *buffer++ = (uint8_t)(temp >> 24); + *buffer++ = (uint8_t)(temp >> 16); + bytes -= 2; + break; + case 3 : + *buffer++ = (uint8_t)(temp >> 24); + *buffer++ = (uint8_t)(temp >> 16); + *buffer++ = (uint8_t)(temp >> 8); + bytes -= 3; + break; + default : + *buffer++ = (uint8_t)(temp >> 24); + *buffer++ = (uint8_t)(temp >> 16); + *buffer++ = (uint8_t)(temp >> 8); + *buffer++ = (uint8_t)temp; + bytes -= 4; + break; + } + } +#endif // __APPLE__ +} // diff --git a/pdfio-private.h b/pdfio-private.h index df3b609..c97075f 100644 --- a/pdfio-private.h +++ b/pdfio-private.h @@ -339,6 +339,7 @@ extern bool _pdfioArrayWrite(pdfio_array_t *a) _PDFIO_INTERNAL; extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL; extern void _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *buffer, size_t len) _PDFIO_INTERNAL; extern void _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *buffer, size_t len) _PDFIO_INTERNAL; +extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL; extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL; extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL; extern void _pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes) _PDFIO_INTERNAL;