1 /* vim: set expandtab ts=4 sw=4: */
2 /*
3  * You may redistribute this program and/or modify it under the terms of
4  * the GNU General Public License as published by the Free Software Foundation,
5  * either version 3 of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
14  */
15 #include "crypto/random/Random.h"
16 #include "crypto/random/seed/RandomSeed.h"
17 #include "crypto/random/seed/SystemRandomSeed.h"
18 #include "memory/Allocator.h"
19 #include "util/Bits.h"
20 #include "util/Assert.h"
21 #include "util/Base32.h"
22 #include "util/Identity.h"
23 #include "util/Endian.h"
24 
25 #include <crypto_hash_sha256.h>
26 #include <crypto_stream_salsa20.h>
27 
28 /**
29  * cjdns random generator:
30  * It is with great apprehension that I have decided to go forward with this random generator.
31  * Sadly there doesn't exist any plain-and-simple random generation library for C without
32  * bundling libevent, openssl or some other megalyth.
33  *
34  * Additionally most random generators use a feedback loop which is difficult to validate as
35  * it has a period which is not immedietly obvious by looking at it. Additionally, this
36  * feedback loop design leads to issues like:
37  * http://www.openssl.org/news/secadv_prng.txt
38  *
39  * How this random generator works:
40  * 1. All available random sources such as dev/urandom and sysctl(RANDOM_UUID) are combined
41  *    with a rolling SHA-512 hash, the result is placed in the Random_SeedGen union.
42  *
43  * 2. Random_SeedGen is SHA-256 hashed into Random.tempSeed
44  *
45  * 3. Random numbers are generated by running salsa20 with Random.tempSeed as the key, and
46  *    Random.nonce 64 bit counter which is incremented each run, never reset, and assumed
47  *    never to wrap.
48  *
49  * Adding entropy to the generator is as follows:
50  * Random_addRandom() adds a sample of randomness by rotating and XORing it into
51  * Random_SeedGen.collectedEntropy.
52  * Every 256 calls to Random_addRandom(), Random_SeedGen is again hashed into Random.tempSeed.
53  * Note that Random.nonce is *not* reset ever during the operation of the generator because
54  * otherwise, 512 successive calls to Random_addRandom() with the same input would cause the
55  * random generator to repeat.
56  *
57  *
58  * State-compromize extension:
59  * It is acknoliged that a compromize of the generator's internal state will result in the
60  * attacker knowing every output which has been and will be generated or with the current
61  * tempSeed. After a further 256 calls to Random_addRandom(), the generator should recover.
62  *
63  * While using a feedback loop with a one-way hash function to frustrate backtracking seems
64  * enticing, it stands to reason that the only way a hash function can be one-way is by
65  * destroying entropy, destruction of entropy in a feedback system could lead to an oscillation
66  * effect when it becomes entropy starved. Though this issue does not seem to have been
67  * exploited in other prngs, proving that it cannot be exploited is beyond my abilities and the
68  * devil you know is better than the devil you don't.
69  *
70  *
71  * Iterative Guessing:
72  * This generator only introduces the entropy given by Random_addRandom() once every 256 calls.
73  * Assuming each call introduces at least 1 bit of good entropy, iterative guessing requires
74  * guessing a 256 bit value for each iteration.
75  *
76  *
77  * Input based Attacks:
78  * The generator is as conservitive as possible about the entropy provided in calls to
79  * Random_addRandom(), valuing each at 1 bit of entropy. Since the number is rotated and XORd
80  * into collectedEntropy, some calls with 0 bits of entropy can be smoothed over by other calls
81  * with > 1 bit of entropy. If Random_addRandom() is called arbitrarily many times with 0 bits
82  * of entropy, since the inputs are XORd into collectedEntropy the entropy level of
83  * collectedEntropy will remain unchanged.
84  *
85  * Even if the attacker is able to gather information from the generator's output and craft
86  * inputs to Random_addRandom() which *decrease* the entropy in collectedEntropy, this will not
87  * decrease the performance of the generator itself because the 256 bit Random_SeedGen.seed
88  * is seeded with the primary seed meterial (eg dev/urandom) and never altered for duration of
89  * the generator's operation.
90  */
91 
92 /** How many bytes to buffer so requests for a small amount of random do not invoke salsa20. */
93 #define BUFFSIZE 128
94 
95 /** The key material which is used to generate the temporary seed. */
96 union Random_SeedGen
97 {
98     struct {
99         /**
100          * Read directly from the seed supplier (dev/urandom etc.),
101          * same for the whole run of the generator.
102          */
103         uint64_t seed[4];
104 
105         /**
106          * Initialized by the seed supplier
107          * then XORd with the input given to Random_addRandom().
108          */
109         uint32_t collectedEntropy[8];
110     } elements;
111 
112     /** Used to generate tempSeed. */
113     uint64_t buff[8];
114 };
115 
116 struct Random
117 {
118     /** The random seed which is used to generate random numbers. */
119     uint64_t tempSeed[4];
120 
121     /** Incremented every call to salsa20, never reset. */
122     uint64_t nonce;
123 
124     /** buffer of random generated in the last rand cycle. */
125     uint8_t buff[BUFFSIZE];
126 
127     /** the next number to read out of buff. */
128     int nextByte;
129 
130     /** A counter which Random_addRandom() uses to rotate the random input. */
131     int addRandomCounter;
132 
133     /** The seed generator for generating new temporary random seeds. */
134     union Random_SeedGen* seedGen;
135 
136     /** The collector for getting the original permanent random seed from the operating system. */
137     struct RandomSeed* seed;
138 
139     Identity
140 };
141 
142 /**
143  * Add a random number to the entropy pool.
144  * 1 bit of entropy is extracted from each call to addRandom(), every 256 calls
145  * this function will generate a new temporary seed using the permanent seed and
146  * the collected entropy.
147  *
148  * Worst case scenario, Random_addRandom() is completely broken, the original
149  * seed is still used and the nonce is never reset so the only loss is forward secrecy.
150  */
Random_addRandom(struct Random * rand,uint32_t randomNumber)151 void Random_addRandom(struct Random* rand, uint32_t randomNumber)
152 {
153     Identity_check(rand);
154     #define rotl(a,b) (((a) << (b)) | ((a) >> (31 - (b))))
155     rand->seedGen->elements.collectedEntropy[rand->addRandomCounter % 8] ^=
156         rotl(randomNumber, rand->addRandomCounter / 8);
157     if (++rand->addRandomCounter >= 256) {
158         crypto_hash_sha256((uint8_t*)rand->tempSeed,
159                            (uint8_t*)rand->seedGen->buff,
160                            sizeof(union Random_SeedGen));
161         rand->addRandomCounter = 0;
162     }
163 }
164 
stir(struct Random * rand)165 static void stir(struct Random* rand)
166 {
167     uint64_t nonce = Endian_hostToLittleEndian64(rand->nonce);
168     crypto_stream_salsa20_xor((uint8_t*)rand->buff,
169                               (uint8_t*)rand->buff,
170                               BUFFSIZE,
171                               (uint8_t*)&nonce,
172                               (uint8_t*)rand->tempSeed);
173     rand->nonce++;
174     rand->nextByte = 0;
175 }
176 
randomCopy(struct Random * rand,uint8_t * location,uint64_t count)177 static uintptr_t randomCopy(struct Random* rand, uint8_t* location, uint64_t count)
178 {
179     uintptr_t num = (uintptr_t) count;
180     if (num > (uintptr_t)(BUFFSIZE - rand->nextByte)) {
181         num = (BUFFSIZE - rand->nextByte);
182     }
183     Bits_memcpy(location, &rand->buff[rand->nextByte], num);
184     rand->nextByte += num;
185     return num;
186 }
187 
Random_bytes(struct Random * rand,uint8_t * location,uint64_t count)188 void Random_bytes(struct Random* rand, uint8_t* location, uint64_t count)
189 {
190     Identity_check(rand);
191     if (count > BUFFSIZE) {
192         // big request, don't buffer it.
193         crypto_stream_salsa20_xor((uint8_t*)location,
194                                   (uint8_t*)location,
195                                   count,
196                                   (uint8_t*)&rand->nonce,
197                                   (uint8_t*)rand->tempSeed);
198         rand->nonce++;
199         return;
200     }
201 
202     for (;;) {
203         uintptr_t sz = randomCopy(rand, location, count);
204         location += sz;
205         count -= sz;
206         if (count == 0) {
207             return;
208         }
209         stir(rand);
210     }
211 }
212 
Random_base32(struct Random * rand,uint8_t * output,uint32_t length)213 void Random_base32(struct Random* rand, uint8_t* output, uint32_t length)
214 {
215     Identity_check(rand);
216     uint64_t index = 0;
217     for (;;) {
218         uint8_t bin[16];
219         Random_bytes(rand, bin, 16);
220         int ret = Base32_encode(&output[index], length - index, (uint8_t*)bin, 16);
221         if (ret == Base32_TOO_BIG || index + ret == length) {
222             break;
223         }
224         index += ret;
225     }
226     output[length - 1] = '\0';
227 }
228 
Random_newWithSeed(struct Allocator * alloc,struct Log * logger,struct RandomSeed * seed,struct Except * eh)229 struct Random* Random_newWithSeed(struct Allocator* alloc,
230                                   struct Log* logger,
231                                   struct RandomSeed* seed,
232                                   struct Except* eh)
233 {
234     union Random_SeedGen* seedGen = Allocator_calloc(alloc, sizeof(union Random_SeedGen), 1);
235 
236     if (RandomSeed_get(seed, seedGen->buff)) {
237         Except_throw(eh, "Unable to initialize secure random number generator");
238     }
239 
240     struct Random* rand = Allocator_calloc(alloc, sizeof(struct Random), 1);
241     rand->seedGen = seedGen;
242     rand->seed = seed;
243     rand->nextByte = BUFFSIZE;
244 
245     Identity_set(rand);
246 
247     rand->addRandomCounter = 255;
248     Random_addRandom(rand, 0);
249     stir(rand);
250 
251     return rand;
252 }
253 
Random_new(struct Allocator * alloc,struct Log * logger,struct Except * eh)254 struct Random* Random_new(struct Allocator* alloc, struct Log* logger, struct Except* eh)
255 {
256     struct RandomSeed* rs = SystemRandomSeed_new(NULL, 0, logger, alloc);
257     return Random_newWithSeed(alloc, logger, rs, eh);
258 }
259