mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2026-01-27 10:22:15 +00:00
Merge pull request #478 from OrdnanceNetworks/fix-low-entrypy-at-boot
ordnance: Initialize Linux Random Number Generator (RNG) early
This commit is contained in:
230
packages/base/all/initrds/loader-initrd-files/src/bin/initrng.py
Normal file
230
packages/base/all/initrds/loader-initrd-files/src/bin/initrng.py
Normal file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Distributed under MIT License.
|
||||
See LICENSE file for full license text.
|
||||
"""
|
||||
|
||||
import fcntl
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
import hashlib
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
RNDADDENTROPY = 0x40085203 # from linux/random.h
|
||||
|
||||
def sha512sum(digest, fileName, blockSize = 16 * 1024):
|
||||
"""
|
||||
Take SHA512 digest from file given by it's name contents.
|
||||
|
||||
File opened in os.O_NONBLOCK mode to support FIFOs and
|
||||
special files like /dev/kmsg. Read data from files in
|
||||
blockSize (default 16kB) chunks to keep memory usage at
|
||||
minimum when used with large files.
|
||||
|
||||
Return True on success and False otherwise.
|
||||
"""
|
||||
|
||||
def read():
|
||||
try:
|
||||
return fp.read(blockSize)
|
||||
except IOError:
|
||||
return b''
|
||||
#end def
|
||||
|
||||
if digest is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
fp = open(fileName, 'rb')
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
flags = fcntl.fcntl(fp, fcntl.F_GETFL) | os.O_NONBLOCK
|
||||
if fcntl.fcntl(fp, fcntl.F_SETFL, flags):
|
||||
fp.close()
|
||||
return False
|
||||
|
||||
for block in iter(read, b''):
|
||||
digest.update(block)
|
||||
|
||||
fp.close()
|
||||
return True
|
||||
|
||||
def sha512(digest, fileName):
|
||||
"""
|
||||
Computes sha512sum() optionally logging status.
|
||||
|
||||
For internal use only.
|
||||
|
||||
Return 1 on success and 0 on failure.
|
||||
"""
|
||||
|
||||
rc = sha512sum(digest, fileName)
|
||||
logging.debug("SHA512 (%s) = %s", fileName, 'ok' if rc else 'fail')
|
||||
return int(rc)
|
||||
|
||||
def add_entropy(digest, fileName = "/dev/urandom"):
|
||||
"""
|
||||
Adds digest.digest_size * 8 bits of entropy using ioctl(RNDADDENTROPY, ...)
|
||||
to increase entropy count if fileName is a character special file node and
|
||||
CAP_SYS_ADMIN is available (otherwise IOError is raised by fnctl.fnctl()).
|
||||
|
||||
Writes entropy to fileName if IOError exception is raised by fnctl.fnctl()
|
||||
or fileName is a regular file. Update entropy pool without incrementing
|
||||
entropy count if fileName is special character device like "/dev/urandom".
|
||||
"""
|
||||
|
||||
try:
|
||||
fp = open(fileName, 'ab')
|
||||
except IOError as e:
|
||||
return None, str(e)
|
||||
|
||||
size = digest.digest_size
|
||||
digest = digest.digest()
|
||||
|
||||
method = None
|
||||
err = None
|
||||
|
||||
do_ioctl = not os.path.isfile(fileName)
|
||||
|
||||
try:
|
||||
if do_ioctl:
|
||||
# from random(4):
|
||||
#
|
||||
# struct rand_pool_info {
|
||||
# int entropy_count;
|
||||
# int buf_size;
|
||||
# __u32 buf[0];
|
||||
# };
|
||||
#
|
||||
fmt = "ii{:d}s".format(size)
|
||||
rand_pool_info = struct.pack(fmt, size * 8, size, digest)
|
||||
fcntl.ioctl(fp, RNDADDENTROPY, rand_pool_info)
|
||||
else:
|
||||
raise IOError()
|
||||
except IOError:
|
||||
try:
|
||||
fp.write(digest)
|
||||
except IOError as e:
|
||||
err = str(e)
|
||||
else:
|
||||
method = "write"
|
||||
else:
|
||||
method = "ioctl"
|
||||
|
||||
return method, err
|
||||
|
||||
def init():
|
||||
prog_name = os.path.splitext(os.path.basename(__file__))[0]
|
||||
if not prog_name:
|
||||
prog_name = "initrng"
|
||||
|
||||
parser = argparse.ArgumentParser(description = 'Linux RNG early init')
|
||||
|
||||
# loglevel
|
||||
loglevels = {
|
||||
'crit' : 'CRITICAL',
|
||||
'err' : 'ERROR',
|
||||
'warn' : 'WARNING',
|
||||
'info' : 'INFO',
|
||||
'debug': 'DEBUG',
|
||||
}
|
||||
parser.add_argument('-l', '--loglevel', default = 'info',
|
||||
choices = list(loglevels.keys()),
|
||||
help = 'set program loging severity (level)')
|
||||
|
||||
# entropy_files
|
||||
dflt_entropy_files = [
|
||||
"/proc/timer_list",
|
||||
"/proc/buddyinfo",
|
||||
"/proc/interrupts",
|
||||
"/proc/softirqs",
|
||||
]
|
||||
|
||||
sched_debug = "/proc/sched_debug"
|
||||
if os.path.isfile(sched_debug):
|
||||
dflt_entropy_files.append(sched_debug)
|
||||
else:
|
||||
dflt_entropy_files.append("/proc/schedstat")
|
||||
|
||||
parser.add_argument('-e', '--entropy-file', default = [],
|
||||
action = 'append', dest = 'entropy_files', type = str,
|
||||
help = 'files to use as source of entropy ({:s})'.format(', '.join(dflt_entropy_files)))
|
||||
|
||||
# repeat
|
||||
dflt_repeat = 8
|
||||
parser.add_argument('-r', '--repeat', default = dflt_repeat,
|
||||
action = 'store', type = int,
|
||||
help = 'repeat entropy updates # times (default {:d})'.format(dflt_repeat))
|
||||
|
||||
# output
|
||||
dflt_output = "/dev/urandom"
|
||||
parser.add_argument('-o', '--output', default = dflt_output,
|
||||
action = 'store', type = str,
|
||||
help = 'file to output entropy (default "{:s}")'.format(dflt_output))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(format = "{:s}: %(message)s".format(prog_name),
|
||||
level = getattr(logging, loglevels[args.loglevel]))
|
||||
|
||||
# adjust entropy_files
|
||||
if not args.entropy_files:
|
||||
args.entropy_files = dflt_entropy_files
|
||||
logging.debug("using default list of files as entropy source")
|
||||
|
||||
# adjust repeat
|
||||
repeat = args.repeat
|
||||
|
||||
if repeat <= 0:
|
||||
repeat = 1
|
||||
elif repeat > 65536:
|
||||
repeat = 65536
|
||||
|
||||
if repeat != args.repeat:
|
||||
logging.debug("adjust repeat count from %d to %d", args.repeat, repeat)
|
||||
args.repeat = repeat
|
||||
|
||||
# truncate entropy file
|
||||
output = args.output
|
||||
try:
|
||||
with open(output, 'wb'):
|
||||
pass
|
||||
except IOError:
|
||||
logging.debug("entropy file '%s' isn't writable", output)
|
||||
args = None
|
||||
else:
|
||||
logging.debug("entropy file '%s' is writable", output)
|
||||
|
||||
return args
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = init()
|
||||
|
||||
if args is None:
|
||||
logging.debug("fail to adjust/validate args")
|
||||
sys.exit(1)
|
||||
|
||||
logging.info("Linux Random Number Generator (RNG) early init")
|
||||
|
||||
entropy_files = args.entropy_files
|
||||
repeat = args.repeat
|
||||
|
||||
for x in range(1, repeat + 1):
|
||||
digest_sha512 = hashlib.sha512()
|
||||
|
||||
i = 0
|
||||
for f in entropy_files:
|
||||
i += sha512(digest_sha512, f)
|
||||
if not i:
|
||||
logging.debug("digest error for all entropy files, step %d", x)
|
||||
continue
|
||||
|
||||
method, err = add_entropy(digest_sha512, args.output)
|
||||
if err:
|
||||
logging.debug("error seeding Linux RNG, step %d: %s", x, err)
|
||||
else:
|
||||
logging.debug("seeded Linux RNG, method '%s', step %d", method, x)
|
||||
@@ -47,8 +47,10 @@ case "$(stat -f -c "%T" /tmp)" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# Grab cmdline settings
|
||||
# Initialize RNG early
|
||||
python /bin/initrng.py
|
||||
|
||||
# Grab cmdline settings
|
||||
touch /etc/onl/boot-config
|
||||
tr -s " " "\n" </proc/cmdline |
|
||||
while read -r l; do
|
||||
|
||||
Reference in New Issue
Block a user