d83a05bed66e5012afd28947c17414848367f2ef166cdb0916f5230733f0cbaf63870c3db7359dd212053dc84b3f121ed7200ccb8cb6ab35b8881c23e39eff263821eb02a5cb954a48e84eddc081d2e9d8d4b87d1e809780fbb6cad88773be3e8a3f2533a5cd8c80f428d268d1805b7565aed4912244cb7343a4264c499f521e2a2d4ae1fa42e9127bcc2b2a5f54aa4d123b6c2abc9f669b548e3483fe4a0178b589b8ab60c6c19c91c0a5af22e66e9f705dc39d2402bf8318212d703b4567b1b6adaae4b7a150a665e003a920e9c31adfd04bcb1480eede5d39e183008339d3e866f22eb472a9f3bfc11a24c1240891a09e4f67cff5d3376e7eb0c8e37455ca1c67b1a13bc0f88049f8b850d0b567c002d811542fe317cf6d154a23658eca75d6f654542e5c537b3cfc039e67b22db8f3bd0dcc4f0b3bc7a56cfdf7c0f7f39ecf893b5bde605aa904c19605a686e341cfe952c34f105b4eb12e42a87f9c452b033a2e165e11d07fe6154bb2cecfac1d54fec4c0c5de066bdbefee0a78625eee7abedc5a06da90e65616db07532562de4e7546550bc246a111ef6dd9e9fe106105fdc3fe2023a3ee778c939e7ac47e6b2344c0612bad7e4bea5acf125fe52feff520675067b8255c72c10e463cbe69fd867fe3806e56128d710720fb7bbfb987a78ee8f725a3375e0fc1 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1a69aa43a6c172d Directory build CREATED Directory package CREATED Directory service CREATED Directory service/rngrecv CREATED Directory service/rngrecv/log CREATED Directory service/rngsend CREATED Directory service/rngsend/log CREATED Directory src CREATED --- manifest.vamp 7f10c202766ab113271ba0292123e0d039074708fc90be7daa9a9cb872b9f7ce82fa8555865a3c8c2aac7e31e85ceea891772161d1df04c2ecb8f3b8f266c346f3b6eb9027993e783f80734662882ce6fd7f9fc8ea24e18b0bd30b5c9e38d467f4016cffe19a72b771f79cf4783dde0bf3ed712c8f8262babe514c4ef3cb8d37249fcf5ec7a6fed9409ece0eb9360f96c653b3080ab8ee04c9d5f1184e60c1deb61f5f690e21ab8f6e2cf4da91d4ebbcdeaf7e73b9ce40e1208adf2a50369f1c0aa926745a693c6f4c09f6e7ed7f92353714fe682e1e65fd27e113701d634c278a87f89b03b1a47339007072b2e33a8cc92a4213c3b4e98fe61dcefb8f41b8c5e8e3056d630da604ad00c7f32e41beb40b56cd37f47f1ee2d2a69b5c4dab078ad04afbf5b35f567c43718d09f1c2f10a7a4a66173431171c439516d76b110133e3a26e1d029d4eea454f103fbea750d74adc7192fd6403fda452b7913ebe64673203f0a8bfbb202fe21c4f67ca6e132951ec26a25dbc7cf58eda108cf5b75a5fecc71e9f7ef74c0b1ecad928b67ee79a1affb25c9c6d8abe28be7defac93ea2741309e5a0672577ee86412176e121a61add6c3bca07be7d1cc4fd879895433d4b85be2fec2efa5cb3df73e152f867944c4f50134d9ad57dd540a5caf419ec01d1d5cc91b3478fcd8f5a9 +++ manifest.vamp 9204865ef1ddceb94b74576541b1ea60b2dc2bcf109aa10ecfe96e399b9908d0b8beb230dff7c9b7def9a498947fb49e27f897c89374a0acca2ece1aafd9adbde049cc70383faaf88d20e096b21815e2da50ba8da69e7cb95559d45fa20114a99790301461516481e76dd802064508e4f9f00324fa4afe6d65f7a8567149fcdbc8aac494b2468be6c3a35b1701ddd93baa2faca1d77bb6668a4487a735b7f217894dc9972956ff463bd379a8acddc9fa747a7bbfafaa87e6920c195d7d40a0682766f62ef62122e616b9aba4cdb64afab9c045fbf503fb5f6a11b0438d97b72e09c0cd0a4d2efc105fe14e21640f7e1d13e0d202b6e832aaddd9542024e1410e0d3ff19c4aaa23845a6e9af408133d817189ddfea07ff3493fe5f47a94b7022bf7787617b6c93f118dab481626696f38de60aa22fa998dbf20225bc47c27ea12ad8aba4d2777dcebacb675310323a637d53d7c67144bd40e949dfef35a9260ab6d23b2a6402b5dc498de0a25c5701401f5a9bcdc1b1e7ce1fec3661f36c1d4667f647ed32a2c75e36895520c864fbda66408af33617b1ee2aabd9395ba70608e600b574ce68cc111944f3538822b1d5c40f0b6c4e3d84ef51aab419e9e884e7dab4def2f02c0fe411f53be6baa2b98e326e6d279139d1dfbf77604a26eeac3232600a4ba73a896d7a666 @@ -1,1 +1,2 @@ A VaMP project from JWRD Computing. +826750 rngnet_v2 jfw Initial patch for RNG network facilities, dubbed rngnet version 2; includes rngsend and rngrecv in C with a /package installation script, README and example services. ... Makefile ABSENT +++ Makefile 5f2eaeaba8e76a271e7f2198be99f43e0bed92abda625983f5951f9f5dae74aadbb1dd4d80f2285ae6884e29ab455db83c6009a14130b48a5392e85e161b7fa8ec7ec03afe73fa5d3c29fb271af68f1c5ef07d12e2845b7ed6c8a564c659b1a8335db2641f50bd3e12a81dbc1ace4a5104da53c8879ef7de6661f144c62cbb9e26e1de5c32df461e0c28362f3412ce54e5853fa53f04c1af2e2f6b4d2125f3618c53def874ceafc76070d9198492a09e00d20cfc70065f1d8c35486f12d945c0c5b21b1af487a70e2700f1565286f7557faadaa9548903cb1c1d6f9aaeff154a7c63d82000fbd17536139b162aac7af08533d190c7ec554e22e245b80f6a833b2f5fbb8f54f1765f51078d7521bfe5f88db96035e86dca17e84b7bd74498bac91eb01d37038ca1a1dce9a492a2a082ed522eb7bf70d0009d349453705bc3d6c2cfdd83073f7312cb7b091e9bb89357c231fbfc26925fb26a6f355f1aedbcf77e69053388099f5e2be7d00f9e0e5caa19dff44a8e277d4197d128f31c03e91b6332b1407912d3c4172cafd0a5982601d2ac707ee98e782ce7a915a5c98577562422fa007a3c0b41369ded7a07f91d8c72d5a33dd53462ae6189172aac93dbca4a7b74385d1bac844fa702c52c6cd5b1e5ce7791acc309e575b9e208a854163f73ee8576d7ecc1a956814e @@ -0,0 +1,18 @@ +P = rngnet +V = $(shell head -1 version) + +all: + $(MAKE) -C build + +clean: + $(MAKE) -C build clean + +dist: + mkdir $P-$V + mkdir $P-$V/build + cp build/Makefile $P-$V/build/ + cp -R Makefile README package service src version $P-$V/ + tar -czf $P-$V.tar.gz --sterilize $P-$V + rm -rf $P-$V + +.PHONY: all clean dist ... README ABSENT +++ README 1e8eac8ac2646315d29b396e72605aebb43158aa70cb788c844f057be596bc980e1617d9823e29ed1a3b79d1579ab94ffc1339d4207c6f00343c8d3f10c2c4ca6f3a25c9c8524326e0c0ead3be174b3e93327c617b34e8e8509435e717b865250c16d953dcd8319c6aab206322eaafc345e2df6d299d77d402d0e5c1de10edcaa8ebe36bda59da4324598a23b2416028c4fb30a3a0931e86d5bdd2ef0b0629a5bfa4ba698ebd6c68f87cb879991ba7e7b1c43365ab85a421fd3656f7970b8b6f9028484a00f143909a0e18bf18534323caf55ae900fdf150abda51cb2dc20f4ca52e0fe9a891622f7698739361e679451e20e7deb4b9fbd8402ab01c8b4ebc8fb138ebf21c6cfc514dc43325925562ff19cdf37d516c75e49dbf44b88188898f6a758a88b475e3da2360490359a8c3ae578c10439ab51081032fee65ea621da1ac0c120b546c42adf13565768d83ba38c436a4274dbe68fd9d38272f7e8cddd8ac0848620bffc40bf27ddc6edb8910ffba71792d01e6c9789fbd00c1920d7ba3575fb964a367d4d29eaaaa311f14a4845ad7e41027cff9e8452276e6fae5bb66e0d6ce5aad10426fb9c6fa5f3b9cfb79c7cbd4f9f9906730d0d410df2db907d9e91400cafa8deb3727c403264fe42274a0f7e327769686b997dc11f67cf75814b5f45c968b35d9f553e1 @@ -0,0 +1,84 @@ +About +----- + +RNGnet distributes a byte stream from a hardware random number generator (RNG) to one or more recipient machines, using UDP datagrams over a trusted local network, mixing it continuously into their kernel entropy pools. + +It consists of two complementary daemons, the sender "rngsend" and receiver "rngrecv", configured through their command-line options. The receiver logs packet counters once a minute to standard output, providing a ready indicator of system health. + +Written by Jacob Welsh for JWRD Computing. + +Prerequisites +------------- + +- daemontools (recommended; example service definitions are included) +- for the sender, a hardware RNG readable through some device node + +Installation +------------ + +(Generic instructions.) + +This software ignores some historical Unix conventions in favor of a simplified variant of Bernstein's /package scheme ( http://cr.yp.to/slashpackage.html ). Installation paths and command names are not configurable, which amounts to a global namespace claim, such that people and programs can count on finding components at known paths if they are to be found at all. User commands are symlinked into /usr/bin and will replace prior files in case of conflict; see "package/commands" for the list. + +You will need root privileges to install. + +1. Create the top-level /package directory if necessary and unpack the tree at its versioned path (here, NNN will represent the actual version number): + + mkdir -p /package + tar -xzf rngnet-NNN.tar.gz -C /package + +or, supposing you already have it unpacked somewhere (you're reading this, after all), + + cp -r rngnet-NNN /package/ + +2. Run the install script from the resulting directory: + + cd /package/rngnet-NNN + sh package/install + +To revert to this version after installing a different one, simply repeat step 2. + +Operation +--------- + +Run "rngsend -h" or "rngrecv -h" for usage help on each program. They run in the foreground and log to standard output and/or standard error. + +To use the example daemontools services, it's recommended to first copy them to wherever you keep your service collection, for instance: + + cp -r /package/rngnet/service/* /etc/svc/ + +Then edit the "run" scripts as required to configure the sender and receivers (e.g. /etc/svc/rngsend/run, /etc/svc/rngrecv/run). + +The "run" scripts for the logging sub-services will automatically set up the required directories for multilog under /var/log. They are written for Gales Linux; if using stock daemontools, you will need to change the "log" username to "multilog" (in the chown and setuidgid commands). The rngrecv logger illustrates how to capture the packet counters in a fixed status file; this can be viewed using "head -1 /var/log/rngrecv/status". + +When ready, activate the services by creating symlinks: + + ln -s /etc/svc/rng* /service/ + +After waiting a few seconds for svscan to start them up, check the logs for success. + +UDP port 25385 is used by default; you may wish to define this in /etc/services for your own reference (e.g. so the port number resolves in netstat or tcpdump output): + + rngnet 25385/udp # JWRD TRNG network sharing + +Note that there is no flow control, or indeed any communication from receiver back to sender; thus the load placed on the network depends entirely on the bitrate of the source device and transmission capacity of the sender machine. + +Packet counters +--------------- + +rngrecv periodically logs a status line to show its various packet counters. Most are formatted as NAME=CURRENT/LIFE. The CURRENT value resets to zero each time it is logged, giving a view of current rates, while LIFE accumulates for the lifetime of the process (kept as 64-bit unsigned integers). + +"ok" counts valid packets received. To count the bytes of entropy, multiply it by the packet size (fixed at 512 bytes). + +"recv_err" indicates an external error from the network stack when receiving a packet, while "bad_size" indicates a protocol error (incorrectly sized packet). + +One out of every ten valid packets received is diverted for rudimentary statistical testing; tested packets are never injected as entropy. Those that fail i.e. appear less likely to be random are counted under "weak". "weak_rate" measures per hundred packets tested rather than by time; thus it may be slower or faster to update but may show a more meaningful figure. With current thresholds, on the order of 2% "weak" packets are to be expected from a healthy source. + +Security considerations +----------------------- + +An adversary able to read network traffic along the path will clearly see the entropy data being sent, undermining its value to the receiver. Whether this gives them any advantage over the situation of not using a hardware RNG at all, depends on the kernel's pseudorandom number generator into which the entropy is mixed as one of multiple inputs. + +There is no verification of sender identity, thus any entity able to send UDP packets to the receiver on the configured port will be able to influence its PRNG inputs. As above, this might not be a problem, depending on the strength of the PRNG. In any case the mixing will consume system processing resources, so consider employing network-level access controls (firewall rules). + +Needless to say, statistical testing does not catch all possible defects, and is easily fooled by deliberate tampering. It is intended only to provide warning of failing hardware. ... build/Makefile ABSENT +++ build/Makefile 4b4de6698802d10d07c176dcfa261970cbf60b6ca7e00b800856fec839199c1502674938cfe75be32e85d66649a6bbd55f24e49a79a2ed2819d9d14125702f7967d848f8d4f83e600cbd9e2c06869fa8ebaa3238ca7a70eade464e79669be1c810630af0c701c8d63f5ed7f738b1578ab74c61d1f9f3b6b5757f67b76d84025612edad53516050e81a504eb9e3c7d6838e10dabe955278bb597ef82cdfcfae77299d63101f7fd1bd2ed0ee1a1d8ff5644828583fabc5812548e51f054831432ac84fadc21bf184ca459efbf705ea059b48e8718d74cbf8d6c48b2e0842b1a78713b020bda43003f78b337f3e4e709414cc43a45d868819ab6b90840831b282e3cdd67dd61f5d7ef5d09bc2a3a2ff4ff3c4b0252c370ae83c8c66191ffc46f51232bf668d6260ddc79e06d4c01b68c068fb9171d0e2d2938ae5d32067e4a8ea96f287803c7f9bda1bcdf0180872474c6aac040484d7a46509e9a476a012d325c64cecde421f8289211cec2961c8cc4dc8d9e5d789bfb6e021d58e4526170cc94c12b663b0a0ce765ad6b047da89b55de59e96469513df2619fc91f5b42d664f82e2fb618e1d1ef038305f1048e19c458b92a1e1d00d86d83e6b9e2b4e4304a10e6900e128ee4c03cb56c662a376d35488ec194b268cd63031d5be9373df082f60b0d7074e665df89e795a @@ -0,0 +1,26 @@ +CFLAGS = -g -O1 -ansi -pedantic -Wall -Wextra -Winit-self + +PROGS = rngsend rngrecv + +all: $(PROGS) + +LINK = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +rngsend: rngsend.o io.o + $(LINK) + +rngrecv: rngrecv.o io.o + $(LINK) + +%.o: ../src/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $< + +clean: + rm -f $(PROGS) *.o + +.PHONY: all clean + +# Header deps +rngsend.o: ../src/io.h +rngrecv.o: ../src/io.h +io.o: ../src/io.h ... package/commands ABSENT +++ package/commands acbdb5182870a6318b6ef8a9e323872939764f2cac734361944693bd7ce940ba32a9de9fccfe948097e93e7892053f745af4e2eeeab5e8cda3c060754818afc1d9cfb58f2ee0d51f91a2338eb16fec1d4dd687122f05d468c41e57b14ca7e7285a4dda3a3f3d2340a2ca05162d144818ee815e8c49c1e74449521e1a585e1e141a77ea0431e16d22100fe8ecdb23757531959c876d7a8d40ce4bcfa457dec560de0863cc46eed0c7708aadee0ac8ea3b7c6348c204548e4960019567913e4262031b75b7314afd2bd0d5f2581c0a9ff9ab316f8fca239a778524b641f817ab1b797d03255f72b9407fd5bdf391625cdd19aa70d4b1364b3245f19911af5e67f91285fc7a5cec8132dc4f3808365427ef69fca0a7eda00e79a1daba1fb82928b495d2f4b66d38b12680fabfab432a58b756a7f1198e662a657508a1a06ce7ff806816df576b5db1b06155e63234e261f0b9519d9061dcbd67a1246ed1f69f2ea93c591e2e30637c204303dbbf8ad780323ef707a7b3beda903bb93e9b14659d3cc35b71ef76a9533ddb07e74271171ff8a66851927127afcc1e2d9de3f212d3baa47590d602ed51577dc9d5903f966871e14663d9784c5d25bb86f889e36d5ac5a088c1ba10f2988d494e384fa01d0be711cfaabcc1925e85b425c3147a061dc1c0e8c33b3896d07946f1 @@ -0,0 +1,2 @@ +rngrecv +rngsend ... package/install ABSENT +++ package/install 9e62ba88eaf4181e989baee2910fb0d0b5753a4cf7adad739ffd38c21e995e4ea9694bcef95cf3c546006c838e238eb999841e71c6ff0fd29b4b8e4f3a1b5a24dbffca2d327563220299b6e739836a7b4fc43437960e2fc544ba7b3357c258dd6aced7142b562b828d5f5f49fa12b6846ccd4df205436a327d1fbce1aea070dd23a38c3f7ab62a7a45f499a286af53b8dde9f7c33547874c53d4249eba1d1455d40508d76cf3ae04ee9251e478ce59fc0138e32916edd408b9e1c57b38f32619c1ae020c308b98de0362e2d75514faeab1dcf719a2b0f89e28bedd59bda2e827752ca6e99e2cd5b034d8d377cdd634aa6c28a9217bb2aecd415560ebfaa0e4bfa13116fbb2c89eabd16dbeb8b15e90b88001f21f24a8e68fb361ecc2cd642541486d7d0da5c7eb867e378cf91433db7acc8fdcc0b66639ec69d9ad0af05a1767c3c80a5976c5ac0c379cc20ff4d84c8e889de1bfbe115a2075089eb288494ac103ee61a479b2616bff321f2e9ff730d84fa906553c9ce3ba3cbf892ced3535cf1b200beba3edf94eda9736528e0e807dc9f0c03cce80426e241729e707a13affcc1dfdf88add1261e7cbc2136ac4ae85232d9c4df28a2d61a967b6eddff55047e7c0ccafb32b1765aab195f074c38df8a83337b04c571936eb144983928cdce240a9c47287c2e225f058 @@ -0,0 +1,44 @@ +#!/bin/sh +set -e +# Abort on error. + +if ! [ -d package -a -d build -a -d service -a -f version ]; then + echo 'Wrong working directory.' + exit 1 +fi + +P=rngnet +V=`head -1 version` + +# Ensure sane permissions for newly created files. +umask 022 + +echo 'Setting script permissions...' +chmod 755 service/*/run service/*/log/run + +echo 'Cleaning any prior build artifacts...' +# In theory this isn't necessary; in practice there are too many ways to break the theory. +make clean + +echo 'Compiling...' +make + +echo 'Installing commands...' +# Atomically replace any live ones. +mkdir -p command +for i in `cat package/commands` ; do + mv -f build/$i command/ +done + +echo "Creating version symlink $P -> $P-$V..." +rm -f $P +ln -s $P-$V $P +mv -f $P .. + +echo 'Creating command symlinks in /usr/bin...' +mkdir -p /usr/bin +for i in `cat package/commands` ; do + rm -f /usr/bin/$i'{new}' + ln -s /package/$P/command/$i /usr/bin/$i'{new}' + mv -f /usr/bin/$i'{new}' /usr/bin/$i +done ... service/rngnet-services.idx ABSENT +++ service/rngnet-services.idx 15f01f10b0f4ed3f0fa29f1e2700ac6544fc849203c8b58bd071e671f9be295d84e6c0b11b2eb0c22f669771f187179ceffa6263b3b457a2d2e863a453a8c4cdd56274b2e50d681c87d20ba226c65628021fba34e7f37769d0e5a8cb40c815a7535b163878d8958f59f226b834df9e1f3992537f1e91610fea93ec2f16790a36e1d45dc8cca394d9258d9baba25fb19a3617aaab5edb28ebfc85674650e67f9f9d343f1ed04dd6db71b51815c26196e05fd2bdd7575dd83229beec588bf279b3a73465893654e2524fb061bdd4a382d4a90cd93f7b4f08e69feaf6d0f02cbbf218f70e51982fff903e84b381fcdc20b1b2aac1b26ba2ec7db8bdd1243a40c1cbbeea72015557015f186b21a07640994c700e62f1469dbe49f2edaa08f9868ae33417cc68572962a3159870d9c2776c506623951035ee5fb03b0246bce134e6f900e3b18315027bf9be10de9e0ce9672c7ae4711394be063ae0762a5d04082754cda02999ae67cf2ad2f23ac8ea9128d58fea684a80fde7c5717a8ac9b281fb5e43909ef670f96a020a2ad859805b8b4df0875924a48c698738f7a24eb05639ec1084f2b69d4ef9fa313bc6d7d2cdac08842e79b46e3a2dc7561aa3aeb6a22a6270b077d14f086b12d8f94cef3b753dcf4b16ba32cc3b7fdce81f74a04663b7b316fb002d8f938a04f5e9 @@ -0,0 +1,6 @@ +The rngnet package includes example daemontools services: + +rngsend +rngrecv + +See /package/rngnet/README for more information. ... service/rngrecv/log/run ABSENT +++ service/rngrecv/log/run 200a0febf80410d9fbb0100d91dfff1439628fb9ab066173fa35dc53adf382239373c85d7955dd2650ccf6ce7fe08fdfd68ab45bc2e9da344f456c9a2c77fe4210180d9aeb44a7c2560ca74011b2bade407684a65eb416c3c97395322a4bd1a3c4cf5181908409467383d3b5b76d111c7b30b23e57f163a89942184e8d52f6b9b41b7dd2384746a381117fade76b37bb83ea80ed47c6903a0c97b5033a70a7f98dc10d270f570771e3b39d18b50f1c2b595da7b9822eba690f4358ccf1064d7c753b73d60be5b915a088841785ecf942508605a1b1aa970b298f7d7d31a0ccc7624a384fd1f7f7cffbc0327bef0cd0c6dd34decda7a21161c0cf44878c1c8cfd6e78ad2b0b3e125435cb792cc38cefc775585567cefe5be99fc61fc22d65256e36ff86fbfa488832e601c9a00ed3aeecf0078112dcc9855e8d89b2e8fbfa1962e087becd287ecd7877ffcb78b0a64a1a93a3fb5fe8289f6396bdadce85c9064ea7b8a4343511ebb1acf2c23858419a488ab90404108223b19cd4a00b22693131bbbf567d949aa58fbbd971b423ceb011327afcbacb7badc1514e39924ae983a37974d16673c7646aacaa566b590fd91a8162a03fc5b6dfe33fd1493e8c7788738fd62f4d12fcdc0b8be1a677e5eb9e0608bd78c140cd93fbaebf43b35962414a9beb0402f861f7b5751f @@ -0,0 +1,7 @@ +#!/bin/sh -e +log=/var/log/rngrecv +if [ ! -d $log ] ; then + mkdir -m 750 $log + chown log:log $log +fi +exec setuidgid log multilog t '-* STAT*' $log '-*' '+* STAT*' =$log/status ... service/rngrecv/run ABSENT +++ service/rngrecv/run 7af0724ddd349584fa6c3c5ac4009a1dbc62969e25c70e4f6caeb6bc858227b3771e159adc3522866d7d6d76915792b0b71073a6b5e786a7bcdfbd828c81cbfd62851c28f0dbd5301664f58ccad4dd3f71c5c331d4d077cdb8d99da04e104fe4879c11d3e631fa388e545817d164b962db73056817484844c8a87ff6df82870265bd0b81a643e45f9aa3afba84982a3ecd3218067d99f8031be7ca1a38439a551411298905b1690efd17026c3e200202d8f979ab282a1d78db54abeabc51291f01f0f005e48eff5af7d2525ff7b9396b16c61765484642deb79c1aaea13710674bf28b12c1896a43ff1da76978c803e984524733e27d6fcd0b8173e2735e4bfcdc8280a0dd6bb641f889e522c136eec3fa22f3b39c69e65e3dd8f79663032d79409ecdb27ca2fa8b6f799177b5c22fa17181203a529fc6ad8ddcbdd6b0309d78f3f3717e37ca7cdcb42982727ff3a8306467f4e29f8f9f79d2461c8e090c919e12fdc9b625bc6e6d860f4cb5e2731df074bff9895ecfc3fb269bdfd46fccb115589229b6cfd2c9d278558b4ee48cdef3aed7a587a0b7b3ec6a51257de07f5565696e7106caca0aa6a43dffe0cc79f73687d76882c6fec405703c611bb1ba892b801d41ad10d1168e22f25f8bd8f20f662083c7cf8fddc956f377c1f670360621d4b5c304be9fdb69eceb @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec rngrecv ... service/rngsend/log/run ABSENT +++ service/rngsend/log/run 9ed41f45f7b212e913d0379ec85ed26b6124bba7ee91d3c12c0a0c2e8336bacbe5ee2dcf8c8c24d5d1501e6e71f7509e50d57f719032289b7285a407df395756fd0e67e6dc5a7f0202bff5807c9c20a4b62c4f05ad969544416a9a706b7e60928b64bc9a0c2ddfc6df6e7aebd090bd96c17dc3e67d96d9f46e9abb715cf0d97917dd0af929c781f454fec5b7b46f9ea15580ce8a0b045b8b79ff9a280abe4b68e5d1fdb8056a55b14e41e02c3c4f7b2cdc272a9aa331f7faa53edc3231c19dfeaff6bd40272e8b49012f0da2ad77cd92c52b5baf602ea243cbfa7d28e22c8f31e41facd1f95c1ee4a3564c2a2af6da0ac56ef783af184817049fb5f4e1ab5478b086648729fe39b81e66b23d2f52aaeb35f70c2bb3da3eb5613def463a50781cf21935c78ebe65813ad462238b702afee0f825df4c692909db612f5fce2c22c998f33935d67772b95a8477631fcc88e5b50e02d4071dd40b0585cb35ee85f537fc489a91fcf51089f1dc394b8595e40f16f6e04f8c4d98201c966b7459e6b683b40a288da196873eff9ba8ac38dacae62295c38d5d1f69af215181a1b096c8f089d3239a5460f2163358d3ab9f0e0ee61902f9e2cde88f7b3ab1228d2570b4ebcc87892a415d196ac558833d4d164be3b30246e93d25d85bc34cd1a151516084b68cef239ae6c2550078 @@ -0,0 +1,7 @@ +#!/bin/sh -e +log=/var/log/rngsend +if [ ! -d $log ] ; then + mkdir -m 750 $log + chown log:log $log +fi +exec setuidgid log multilog t $log ... service/rngsend/run ABSENT +++ service/rngsend/run 67d05201243fcc0967e445fca6f7a8080f69698764dfd06229032bbbb8a3569b81835d4d6b0ab12a924d4981a0ee5fd85126ab5f0af96152397815a277cf27fc2a32130b746afe1239d4417b774a4bfb947371c28a341247588aa7d596111017bb3726f448fbafd75bc53a3a903e73d30f46a70ae168052d357d87042028d002b8f1eb24efe1bd7fc258971aaddfe14e899a65035e9532417bfca03f3039d6b8e748015bca74baf46780ce8df0d05f3355b3674b345e4fbc98979cd9a924e380b44e3d6e71152bfbefd30a7740256dfdf1d5d79f6e5bdcb485a673b562410a31b3b85ab6ae093e0c43f84c0f7589f9c85b15ec45b0aa300b4197f9d96fc7e0d42b930f52fd9244215feea4df2b95ebbd2f4603d9d4da7120230049d7901cd1f7b39998b78a27ff7a95b04e1f2020886fb47eac93ea8f530341a993536d5fa7ce6258cb31f24cf96f8032d722bf2338b13e0568b2172bbb8e20ddb9a93c414b597d61fa412ed6ffde2caf5abac29d0ed67c03a599ed5ff08e362672cd1942227952ba524d1e31a31a50af1d2d7499272aeaaacb85d39c70b24b3875b97fa655f0cb459b66cdb2447c8a6fc655569d5f44cce61f37cfebcf99f6725b84cec839224e884464b68f46b8e941549cb82bdc5390c31c111888a4b55b85c587c0220673d2629f4c9734258ef9ff @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec rngsend -d /dev/trng 127.0.0.1 ... src/io.c ABSENT +++ src/io.c 931dc99a96828e75438ca8f49d9299793593417ce856b4b7cd21f1fe4fc00ce5e895d2581b13bcb908848b0f50153b3fb73912aa8e97ba2f562ad90a53767171016cfd5689eb687b4c5e1d9966d788218291dbe881043147111320daf16657b18520bcf0a5b04022c7d17b7ff386bb6178f650fe115761937ea31ec9f586711237b6e7c2ab1d296d49b23a49bdace416c52ca087696602aa2ab09b2fc29841b0142ddaa3a4b970092e1831d388c577f51420370381fbbcb1cb55e2d9222c28b21cad09f3a021ac25204800d043a5bff05911e2d2d92a806d4e967dd01b7b487bd4444793589f0cbefed27d1ed5a55dd4cadbf8cf3693ac496904e7c1cb736787b29ddcfc092e79727e40f778a73bdefce4817d640c49b4c71674a609c0dbfbf76e408993f766f6ef4466773468490804b576354a365c01f81b1ab8de70d8d7d51842700b09f04f1000feab961897ac8bb873b516472f4c34e21a5a76e1d7edfa11adfb9f37a2177f482a767c53c85582b7771a7e14ba1cf7dbbbaed52d004fec7a0a40684a2478b17f8122f607048fbaec08e13c8df1cbc4c839704fef56997118ee3f2cce03b97f0648f64c0da547b48803003bafb7fc8e9c73476d0403ca964df20c87aa897402b474b947117ce043d15e77b3099f2fe9e151aced93d80693f117f1f5da5313ea81db @@ -0,0 +1,188 @@ +/* Minimalist POSIX I/O - unbuffered, death on error, no heap + * J. Welsh + * Adapted from keksum and gscm, April & August 2021 + */ + +#include +#include +#include +#include +#include "io.h" + +/* Global initialization */ + +char const *progname; +unsigned argc; +char **argv; + +void io_init(char const *progname_in, int argc_in, char **argv_in) { + progname = progname_in; + argc = argc_in; + argv = argv_in; + + /* Standard unix-haters' setup: let EPIPE be handled the same as any other error. */ + signal(SIGPIPE, SIG_IGN); +} + +/* I/O primitives */ + +void write_all(int fd, char const *buf, size_t len) { + while (len) { + ssize_t n = write(fd, buf, len); + if (n < 0) { + if (errno == EINTR) continue; + /* XXX arguably we should handle EAGAIN / EWOULDBLOCK too. */ + if (fd != 2) /* prevent loop */ + perr("write_all"); + _exit(errno); + } + buf += n; + len -= n; + } +} + +size_t read_all(int fd, char *buf, size_t len) { + size_t olen = len; + while (len) { + ssize_t n = read(fd, buf, len); + if (n <= 0) { + if (!n) break; /* EOF */ + if (errno == EINTR) continue; + /* XXX arguably we should handle EAGAIN / EWOULDBLOCK too. */ + perr("read_all"); + _exit(errno); + } + buf += n; + len -= n; + } + return olen - len; +} + +/* Conveniences */ + +void write_str(int fd, char const *s) { + write_all(fd, s, strlen(s)); +} + +void newline(int fd) { + write_all(fd, "\n", 1); +} + +void write_line(int fd, char const *s) { + write_str(fd, s); + newline(fd); +} + +void write_u64_dec(int fd, uint64_t val) { + write_str(fd, fmt_u64_dec(val)); +} + +/* Error reporting */ + +void write_err(char const *context, char const *msg) { + if (progname) { + write_str(2, progname); + write_str(2, ": "); + } + if (context) { + write_str(2, context); + write_str(2, ": "); + } + write_line(2, msg); +} + +void assert_fail(char const *expr) { + write_err("Assertion failed", expr); + raise(SIGABRT); + _exit(2); +} + +void perr(char const *context) { + /* ala "perror" */ + write_err(context, strerror(errno)); +} + +int chkp(char const *context, int ret) { + if (ret == -1) { + int e = errno; + perr(context); + _exit(e); + } + return ret; +} + +void usage_err(char const *msg) { + write_err(NULL, msg); + write_str(2, usage); + _exit(2); +} + +/* Number formatting */ + +static char fmt_buf[3*sizeof(unsigned long) + 1]; + +char const *fmt_u64_dec(uint64_t val) { + unsigned i = sizeof(fmt_buf) - 1; + fmt_buf[i] = 0; + do { + assert(i); + --i; + fmt_buf[i] = '0' + (char) (val % 10); + val /= 10; + } while (val); + return &fmt_buf[i]; +} + +/* Arglist & option parsing */ + +char *pop_arg(void) { + assert(argc > 0); + --argc; + return *argv++; +} + +int end_of_options(void) { + if (argc) { + char *arg = *argv; + if (arg[0] != '-' || !arg[1]) { + /* implicit start of positional parameters */ + return 1; + } + if (arg[1] == '-' && !arg[2]) { + /* explicit start of positional parameters by '--' separator */ + pop_arg(); + return 1; + } + /* else it's an option */ + return 0; + } + /* end of arglist */ + return 1; +} + +static unsigned dec_value(unsigned char c) { + unsigned r = c - '0'; + if (r > 9) usage_err("Bad digit in decimal option value"); + return r; +} + +unsigned parse_uint_option(char const *s) { + unsigned acc; + + if (!*s) usage_err("Missing integer option value"); + acc = dec_value(*s++); + if (!acc && *s) usage_err("Bad integer option value (leading zero)"); + + while (*s) { + unsigned digit = dec_value(*s++); + + if (acc > ((unsigned) -1)/10) + usage_err("Integer option value out of range"); + acc *= 10; + + if (acc > ((unsigned) -1) - digit) + usage_err("Integer option value out of range"); + acc += digit; + } + return acc; +} ... src/io.h ABSENT +++ src/io.h 23adba99329cc8ac0af60483e12d5563f4b120223eb070f18cae9710e16ea746121f63d2821899ae3559bd1e820fc903f51673b01ae2a642326af0c8f85afe47db69bc732267653abeb00700497a5d8ead76a9271e29f65128b70ad4ebd2d1c39c80a3c8fe32472e7ac9744c3904e5c3872870fae9522ea1308da9cbe5a53e8b379b318365fd53dc488162a98b2d509ee66e5a4cdc247d4e270f44891183703a2def2726d6eb12e34c1c80c6fa25dfb3994f562dfd49904d770eb56df17cc881a5d5043adc34dba644c428dc6c083a88b8bab316cf90830af4cc1098ade2a17fc43feadb751ee60879e75b112d8cbc6e07c2b995f5935b425ab2134c188645edde2da34925ca8ef1297d03ecf5b6507023c6c9954642a00d10d248facff402b6af934e00d745e14bdf0bf35f254c4a0642f109b0e58e7d3f7acd04ebbc3b1be512e1700a85b5a7fb2efc057a5f94235d0b3f2912cf19af8fe745ff4b9ad8eb14dad4a0c49450ff873ba08fd4742d7db2bfa1ccb477b247a20794673ced9015cbc4fce6a7ca00efe2263f5a4290baffaee7b4db6f6af73871cc473a127073a519c1adf3a5473e64f20c8346b92937b158b56d9d40c015606423442be5e435e42bb7af1e5d1b744b7205902657acc086479a7cbfd96434b48622b18d9657cebf2168a21f7aff12526697f0 @@ -0,0 +1,28 @@ +#include +#include + +/* Defined by the main program. */ +extern char const usage[]; + +/* Defined by the library. */ +extern char const *progname; +extern unsigned argc; +extern char **argv; + +void io_init(char const *, int, char **); +void write_all(int, char const *, size_t); +size_t read_all(int, char *, size_t); +void write_str(int, char const *); +void newline(int); +void write_line(int, char const *); +void write_u64_dec(int, uint64_t); +void write_err(char const *, char const *); +#define assert(expr) ((void) ((expr) || (assert_fail(#expr), 0))) +void assert_fail(char const *); +void perr(char const *); +int chkp(char const *, int); +void usage_err(char const *); +char const *fmt_u64_dec(uint64_t); +char *pop_arg(void); +int end_of_options(void); +unsigned parse_uint_option(char const *); ... src/rngrecv.c ABSENT +++ src/rngrecv.c 16c72d762681990406da429525c8c556755e6d6b90df844e3b3e41169a3b94d67591d04c0c0984962703442f206b8a491d7c6b3acfe98e277bd327dc9547f615d8d55384a729328b3feb88de1da3dcfe94cad1ac647f0896ff1009a645fe1f0a4471b22817b626db9ea6b03595689fd4782dd0f9d3cdb6e7d994381fb1a29d35715fae8633dd0a21ae8593996da3a7ecc557e06d32c500485a9f38301887ecad91b5aeca22f0184f9d9164b64155417adafc5663f1415eeb5ffb621f4297265af19f539bc95ab583ff9b800c4879d9daa0df793461635f8f8b8910639a45f054f9c64c78e8e557b05577e96319eca4e316a68acd0fcdac1a38c3806669cddcb4bf65bcc4f69d25e6f6a6482e74ab3f1ca28fe96beaa2c1ab07b3018e50491efeee0d592bf640516fa186f445277e2273fdc5ffeaa4f47a7568969bd0546b5a36d5c402a37aa59a8836176ae43309e92f9bb6609bd251b01be57a6305bb10f65e14eac2c9569ecd92544ff40e984b35af1199fcbb665a40844cc1b9709699ebe1edd7655b9cf2fd4f164fc0fd735d78a5311542dcb3d269d9c135547b89c888d7b33323f443a7d075bcc8dbe6a495aac8e960fd7ac1b4cdb709936754ef2b84a21d7c4896aefe1133617d1b4720808c31bd0f6c78490fecb8958e8c1a6288519fd58c84415801251045bd @@ -0,0 +1,205 @@ +/* UDP receiver to mix entropy into the system PRNG from a remote TRNG. + * J. Welsh, April & August 2021 + */ + +#define _POSIX_C_SOURCE 1 /* required for sigaction on glibc */ +#define _BSD_SOURCE /* further required for setitimer on musl in strict-ansi mode (weird) */ +#include +#include +#include +#include +#include /* for memset */ +#include +#include +#include "io.h" + +char const usage[] = +"Usage: rngrecv [-d PRNG_DEV] [-p PORT]\n" +"\n" +"Receives 512-byte UDP packets of raw entropy and writes them to the system pseudorandom number generator device to mix them into its internal state.\n" +"\n" +"Options:\n" +" -d PRNG_DEV : output device path (default /dev/urandom)\n" +" -p PORT : port number (default 25385)\n" +" -q : quiet (don't print status updates each minute; they can still be triggered by SIGALRM)\n"; + +#define PKSIZE 512 /* packet size defined by the protocol */ + +static uint64_t ctr_ok, ctr_ok_life; +static uint64_t ctr_weak, ctr_weak_life; +static uint64_t ctr_recv_err, ctr_recv_err_life; +static uint64_t ctr_bad_size, ctr_bad_size_life; +static int last_weak_rate; + +static void print_status(void) { + write_str(1, "STAT ok="); write_u64_dec(1, ctr_ok); + write_str(1, "/"); write_u64_dec(1, ctr_ok_life); + ctr_ok = 0; + + write_str(1, " weak="); write_u64_dec(1, ctr_weak); + write_str(1, "/"); write_u64_dec(1, ctr_weak_life); + ctr_weak = 0; + + write_str(1, " weak_rate="); write_u64_dec(1, last_weak_rate); + + write_str(1, " recv_err="); write_u64_dec(1, ctr_recv_err); + write_str(1, "/"); write_u64_dec(1, ctr_recv_err_life); + ctr_recv_err = 0; + + write_str(1, " bad_size="); write_u64_dec(1, ctr_bad_size); + write_str(1, "/"); write_u64_dec(1, ctr_bad_size_life); + ctr_bad_size = 0; + + newline(1); +} + +static volatile sig_atomic_t alarm_pending; +static void handle_alarm() { + alarm_pending = 1; +} + +static void check_entropy(char const *buf) { + static int test_ctr, weak_rate; + + /* Split packet into 4-bit blocks and count frequency of each value. */ + uint16_t counts[16]; /* 2^4 block values, max count is 2*PKSIZE */ + int i; + + memset(counts, 0, sizeof counts); + for (i=0; i> 4]; /* high 4 bits */ + } + for (i=0; i<16; ++i) { + /* expected count is 2*PKSIZE / 2^4 */ + if ((counts[i] < (2*PKSIZE/16 * 2/3)) || + (counts[i] > (2*PKSIZE/16 * 3/2))) { + ++ctr_weak; + ++ctr_weak_life; + ++weak_rate; + break; + } + } + if (++test_ctr == 100) { + /* weak_rate measures weak packets per quantity tested, as this should be more meaningful than per time unit. The local variable counts them up, then the global latches it when done so as to provide a valid rate on demand (with the other time-based counters). */ + test_ctr = 0; + last_weak_rate = weak_rate; + weak_rate = 0; + } +} + +int main(int argc_in, char *argv_in[]) { + int sockfd, prngfd; + /* packet size defined by the protocol, +1 to notice over-length packets */ + char buf[PKSIZE + 1]; + char const *prng_path = "/dev/urandom"; + unsigned port = 25385; + int quiet_mode = 0; + int sample_ctr = 0; + + io_init("rngrecv", argc_in, argv_in); + sockfd = chkp("socket", socket(AF_INET, SOCK_DGRAM, 0)); + + if (argc) pop_arg(); +next_arg: + while (!end_of_options()) { + char const *arg = pop_arg(); + while (*++arg) { + switch (*arg) { + case 'd': + ++arg; + if (!*arg) { + if (!argc) usage_err("-d missing value"); + arg = pop_arg(); + } + prng_path = arg; + goto next_arg; + case 'h': + write_str(1, usage); + return 0; + case 'p': + ++arg; + if (!*arg) { + if (!argc) usage_err("-p missing value"); + arg = pop_arg(); + } + port = parse_uint_option(arg); + if (port > 65535) usage_err("port number out of range"); + goto next_arg; + case 'q': + quiet_mode = 1; + break; + default: + usage_err("bad option"); + } + } + } + if (argc) usage_err("excess arguments"); + + prngfd = chkp(prng_path, open(prng_path, O_WRONLY)); + + { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = 0; + chkp("bind", bind(sockfd, (struct sockaddr *)&addr, sizeof addr)); + } + + { + struct sigaction sact; + sact.sa_handler = handle_alarm; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; /* no SA_RESTART as we want to receive EINTR. */ + chkp("sigaction", sigaction(SIGALRM, &sact, NULL)); + } + + if (!quiet_mode) { + struct itimerval itv; + itv.it_interval.tv_sec = itv.it_value.tv_sec = 60; + itv.it_interval.tv_usec = itv.it_value.tv_usec = 0; + + chkp("setitimer", setitimer(ITIMER_REAL, &itv, NULL)); + alarm_pending = 1; + } + + write_line(1, "receiver started"); + for (;;) { + ssize_t len; + + if (alarm_pending) { + alarm_pending = 0; + print_status(); + } + /* An alarm can be missed if it fires at this point (between check and recv blocking). No big deal; even if no packets arrive, we'll still get the next alarm. (Otherwise there is the nonblock + select + self-pipe trick, but all that setup doesn't quite seem warranted here.) */ + + len = recv(sockfd, buf, sizeof buf, 0); + if (len == PKSIZE) { + /* Good packet */ + if (++sample_ctr == 10) { + /* Run statistical tests on every 10th packet. As this might leak data through timing variance, these packets are used for testing ONLY and discarded. */ + check_entropy(buf); + sample_ctr = 0; + } + else { + write_all(prngfd, buf, len); + } + ++ctr_ok; + ++ctr_ok_life; + } + else if (len < 0) { + if (errno != EINTR) { + perr("recv"); + ++ctr_recv_err; + ++ctr_recv_err_life; + } + } + else { + ++ctr_bad_size; + ++ctr_bad_size_life; + } + } + + return 0; +} ... src/rngsend.c ABSENT +++ src/rngsend.c d09019a69472ce5cb9db0856e3f4449e9b122c0b52cc59e96947d95f01078b73e29d8eb51c0c48b1e33cd42e476c6419e08792cefaddc3d5326c4233051aabc7b6872fc39dfb57a2f007001295cef6dc3e316bfcc55de41cc0e04408f5bbc70eeeefec5533fa7f32f8b47a4f247e107387aa1b4970a897195d5e9df5d3cd5a1926045e815a052fd8cfa2696bf8c48500b79d451e245c81bbd45b4b699648a505e779b086b8f02fdeb3493f33f120c908199d92c3a33c8519d8d6a9a8e7ff99964596077ed2fe19eb02bd497a38af37ff0cfb8582bfdf08e8794364771e4691070f1a16df6987542ea6a59ad806edacfabf4b513711b195fa57df5e197a707219294e47cb2d7571ef91f291cb1bd561bc987dd971ddd563513b65b7f466aedd2d1a6f68f06c1e3506495bd904ba425d8961fb5aa16c0d5f4d7ad30f277a0a33ef43df0d2c7abf0bd3afe4cebd1d4d346dfffd2a596488af24c38422c4a92d8213aadb1c2d605b3b31140712e44ae69e44af11f2899b837ed954434d74299f07b1d22eb805e0aba5ee2f4e29b82d537e9f461181a03dd3426aedb1d718c99e1a0e7f88cc3e8bb4fb18278b79f88a70a0d8355db65710d1888c4259f0b3ebb4dec60a61830a3ace5c338d438c63940b3d10944c98d788fa8cf0b7fa1dfddf063dcc1a37a0ce7164d91ee46c @@ -0,0 +1,125 @@ +/* UDP sender to export entropy from a TRNG device. + * J. Welsh, April & August 2021 + */ + +#define _BSD_SOURCE /* for gethostbyname */ + +#include +#include +#include /* for memcpy */ + +#include +#include +#include +#include +#include +#include "io.h" + +char const usage[] = +"Usage: rngsend -d TRNG_DEV [-p PORT] [--] RECEIVER ...\n" +"\n" +"Reads from a True Random Number Generator device and transmits in 512-byte UDP packets to one or more receivers in a simple rotation as the buffer is filled.\n" +"\n" +"Options:\n" +"\t-d TRNG_DEV : device path (required)\n" +"\t-p PORT : port number (default 25385)\n" +"\n" +"Positional parameters:\n" +"\tRECEIVER : IP address (or hostname, resolved at startup) for each receiver in the rotation\n"; + +#define PKSIZE 512 /* packet size defined by the protocol */ + +void load_address(struct sockaddr_in *dest, char const *name, unsigned port) { + struct hostent *h = gethostbyname(name); + if (!h) { + write_err(name, hstrerror(h_errno)); + _exit(1); + } + if (!h->h_addr_list) { + write_err(name, "no addresses for name"); + _exit(1); + } + if (h->h_addrtype != AF_INET || h->h_length != sizeof dest->sin_addr) { + write_err("gethostbyname", "BUG: bad length or type"); + _exit(1); + } + dest->sin_family = AF_INET; + dest->sin_port = htons(port); + memcpy(&(dest->sin_addr), h->h_addr_list[0], sizeof dest->sin_addr); +} + +int main(int argc_in, char *argv_in[]) { + int trngfd, sockfd; + struct sockaddr_in *recv_addrs; + char buf[PKSIZE]; + unsigned port = 25385; + char const *trng_path = 0; + unsigned i; + + io_init("rngsend", argc_in, argv_in); + sockfd = chkp("socket", socket(AF_INET, SOCK_DGRAM, 0)); + + if (argc) pop_arg(); +next_arg: + while (!end_of_options()) { + char const *arg = pop_arg(); + while (*++arg) { + switch (*arg) { + case 'd': + ++arg; + if (!*arg) { + if (!argc) usage_err("-d missing value"); + arg = pop_arg(); + } + trng_path = arg; + goto next_arg; + case 'h': + write_str(1, usage); + return 0; + case 'p': + ++arg; + if (!*arg) { + if (!argc) usage_err("-p missing value"); + arg = pop_arg(); + } + port = parse_uint_option(arg); + if (port > 65535) usage_err("port number out of range"); + goto next_arg; + default: + usage_err("bad option"); + } + } + } + + if (!trng_path) usage_err("missing TRNG device path"); + trngfd = chkp(trng_path, open(trng_path, O_RDONLY)); + + if (!argc) usage_err("missing receiver address list"); + recv_addrs = calloc(argc, sizeof *recv_addrs); + if (!recv_addrs) { + perr("calloc"); + _exit(1); + } + for (i=0; i