Crypto pseudo random number generator enhancement
problem
- The OpenSSL PRNG entropy is not preserved during Gajim restart.
- In
src/common/crypto.py
the functionrandom_bytes
uses only os.urandom, which is fine for Linux, but on Windows platform this function uses onlyCryptGenRandom
. There has been some issues withCryptGenRandom
in the past. https://en.wikipedia.org/wiki/CryptGenRandom#Security
analysis
- The OpenSSL functions RAND_load_file and RAND_save_file allow persisting of the collected entropy.
- For Windows platform, the OpenSSL PRNG mixes
CryptGenRandom
with other entropy sources, this is more reliable than just to depend on the quality ofCryptGenRandom
for cryptographically secure random numbers.
https://wiki.openssl.org/index.php/Random_Numbers
enhancement recommendation
This enhancement will preserve OpenSSL PRNG entropy during restarts in the data directory in file "rng_seed". The add_entropy_sources_OpenSSL
will add miscellaneous Python sources of randomness (only those which are available on all platforms) and for Windows platform the OpenSSL.rand.screen will add the current contents of the screen to the PRNG state. For Unix systems files in /proc, /proc/net, /proc/self will provide addition entropy.
The function random_bytes
will draw random bytes from OpenSSL PRNG
diff -r 8073f9ceabb5 src/common/configpaths.py
--- a/src/common/configpaths.py Tue Nov 05 11:21:56 2013 +0100
+++ b/src/common/configpaths.py Tue Nov 05 14:46:09 2013 +0100
@@ -141,7 +141,8 @@
d = {'MY_DATA': '', 'LOG_DB': u'logs.db', 'MY_CACERTS': u'cacerts.pem',
'MY_EMOTS': u'emoticons', 'MY_ICONSETS': u'iconsets',
'MY_MOOD_ICONSETS': u'moods', 'MY_ACTIVITY_ICONSETS': u'activities',
- 'PLUGINS_USER': u'plugins', 'MY_PEER_CERTS': u'certs'}
+ 'PLUGINS_USER': u'plugins', 'MY_PEER_CERTS': u'certs',
+ 'RNG_SEED': u'rng_seed'}
for name in d:
self.add(name, TYPE_DATA, windowsify(d[name]))
diff -r 8073f9ceabb5 src/common/crypto.py
--- a/src/common/crypto.py Tue Nov 05 11:21:56 2013 +0100
+++ b/src/common/crypto.py Tue Nov 05 14:46:09 2013 +0100
@@ -19,6 +19,7 @@
## along with Gajim. If not, see \<http://www.gnu.org/licenses/>.
##
+import sys
import os
import math
@@ -75,8 +76,54 @@
else:
return base28_chr[n]
+def add_entropy_sources_OpenSSL():
+ # Other possibly variable data. This are very low quality sources of
+ # entropy, but some of them are installation dependent and can be hard
+ # to guess for the attacker.
+ # Data available on all platforms Unix, Windows
+ sources = [sys.argv, sys.builtin_module_names,
+ sys.copyright, sys.getfilesystemencoding(), sys.hexversion,
+ sys.modules, sys.path, sys.version, sys.api_version,
+ os.environ, os.getcwd(), os.getpid()]
+
+ for s in sources:
+ OpenSSL.rand.add(str(s), 0.01)
+
+ # On Windows add the current contents of the screen to the PRNG state.
+ if os.name == 'nt':
+ OpenSSL.rand.screen()
+ # The /proc filesystem on POSIX systems contains many random variables:
+ # memory statistics, interrupt counts, network packet counts
+ if os.name == 'posix':
+ dirs = ['/proc', '/proc/net', '/proc/self']
+ for d in dirs:
+ if os.access(d, os.R_OK):
+ for filename in os.listdir(d):
+ OpenSSL.rand.add(filename, 0)
+ try:
+ with open(d + os.sep + filename, "r") as fp:
+ # Limit the ammount of read bytes, in case a memory file
+ # was opened
+ OpenSSL.rand.add(str(fp.read(5000000)), 0.01)
+ except:
+ # Ignore all read and access errors
+ pass
+
+PYOPENSSL_PRNG_PRESENT = False
+try:
+ import OpenSSL.rand
+ PYOPENSSL_PRNG_PRESENT = True
+ add_entropy_sources_OpenSSL()
+except ImportError:
+ # PyOpenSSL PRNG not available
+ pass
+
def random_bytes(bytes_):
- return os.urandom(bytes_)
+ if PYOPENSSL_PRNG_PRESENT:
+ OpenSSL.rand.add(os.urandom(bytes_), bytes_)
+ return OpenSSL.rand.bytes(bytes_)
+ else:
+ return os.urandom(bytes_)
def generate_nonce():
return random_bytes(8)
diff -r 8073f9ceabb5 src/gajim.py
--- a/src/gajim.py Tue Nov 05 11:21:56 2013 +0100
+++ b/src/gajim.py Tue Nov 05 14:46:09 2013 +0100
@@ -293,6 +293,19 @@
pid_filename = gajimpaths['PID_FILE']
config_filename = gajimpaths['CONFIG_FILE']
+#Seed the OpenSSL pseudo random number generator from file and initialize
+RNG_SEED = gajimpaths['RNG_SEED']
+PYOPENSSL_PRNG_PRESENT = False
+try:
+ import OpenSSL.rand
+ from common import crypto
+ PYOPENSSL_PRNG_PRESENT = True
+ # Seed from file
+ OpenSSL.rand.load_file(RNG_SEED)
+ crypto.add_entropy_sources_OpenSSL()
+except ImportError:
+ log.info("PyOpenSSL PRNG not available")
+
import traceback
import errno
import dialogs
@@ -439,6 +452,9 @@
del pid_dir
def on_exit():
+ # Save the entropy from OpenSSL PRNG
+ if PYOPENSSL_PRNG_PRESENT:
+ OpenSSL.rand.write_file(RNG_SEED)
# delete pid file on normal exit
if os.path.exists(pid_filename):
os.remove(pid_filename)