]> git.proxmox.com Git - systemd.git/blob - src/journal/fsprg.c
Merge tag 'upstream/229'
[systemd.git] / src / journal / fsprg.c
1 /*
2 * fsprg v0.1 - (seekable) forward-secure pseudorandom generator
3 * Copyright (C) 2012 B. Poettering
4 * Contact: fsprg@point-at-infinity.org
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 */
21
22 /*
23 * See "Practical Secure Logging: Seekable Sequential Key Generators"
24 * by G. A. Marson, B. Poettering for details:
25 *
26 * http://eprint.iacr.org/2013/397
27 */
28
29 #include <gcrypt.h>
30 #include <string.h>
31
32 #include "fsprg.h"
33
34 #define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384))
35 #define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar));
36
37 #define RND_HASH GCRY_MD_SHA256
38 #define RND_GEN_P 0x01
39 #define RND_GEN_Q 0x02
40 #define RND_GEN_X 0x03
41
42 /******************************************************************************/
43
44 static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) {
45 unsigned len;
46 size_t nwritten;
47
48 assert(gcry_mpi_cmp_ui(x, 0) >= 0);
49 len = (gcry_mpi_get_nbits(x) + 7) / 8;
50 assert(len <= buflen);
51 memzero(buf, buflen);
52 gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
53 assert(nwritten == len);
54 }
55
56 static gcry_mpi_t mpi_import(const void *buf, size_t buflen) {
57 gcry_mpi_t h;
58 unsigned len;
59
60 gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL);
61 len = (gcry_mpi_get_nbits(h) + 7) / 8;
62 assert(len <= buflen);
63 assert(gcry_mpi_cmp_ui(h, 0) >= 0);
64
65 return h;
66 }
67
68 static void uint64_export(void *buf, size_t buflen, uint64_t x) {
69 assert(buflen == 8);
70 ((uint8_t*) buf)[0] = (x >> 56) & 0xff;
71 ((uint8_t*) buf)[1] = (x >> 48) & 0xff;
72 ((uint8_t*) buf)[2] = (x >> 40) & 0xff;
73 ((uint8_t*) buf)[3] = (x >> 32) & 0xff;
74 ((uint8_t*) buf)[4] = (x >> 24) & 0xff;
75 ((uint8_t*) buf)[5] = (x >> 16) & 0xff;
76 ((uint8_t*) buf)[6] = (x >> 8) & 0xff;
77 ((uint8_t*) buf)[7] = (x >> 0) & 0xff;
78 }
79
80 _pure_ static uint64_t uint64_import(const void *buf, size_t buflen) {
81 assert(buflen == 8);
82 return
83 (uint64_t)(((uint8_t*) buf)[0]) << 56 |
84 (uint64_t)(((uint8_t*) buf)[1]) << 48 |
85 (uint64_t)(((uint8_t*) buf)[2]) << 40 |
86 (uint64_t)(((uint8_t*) buf)[3]) << 32 |
87 (uint64_t)(((uint8_t*) buf)[4]) << 24 |
88 (uint64_t)(((uint8_t*) buf)[5]) << 16 |
89 (uint64_t)(((uint8_t*) buf)[6]) << 8 |
90 (uint64_t)(((uint8_t*) buf)[7]) << 0;
91 }
92
93 /* deterministically generate from seed/idx a string of buflen pseudorandom bytes */
94 static void det_randomize(void *buf, size_t buflen, const void *seed, size_t seedlen, uint32_t idx) {
95 gcry_md_hd_t hd, hd2;
96 size_t olen, cpylen;
97 uint32_t ctr;
98
99 olen = gcry_md_get_algo_dlen(RND_HASH);
100 gcry_md_open(&hd, RND_HASH, 0);
101 gcry_md_write(hd, seed, seedlen);
102 gcry_md_putc(hd, (idx >> 24) & 0xff);
103 gcry_md_putc(hd, (idx >> 16) & 0xff);
104 gcry_md_putc(hd, (idx >> 8) & 0xff);
105 gcry_md_putc(hd, (idx >> 0) & 0xff);
106
107 for (ctr = 0; buflen; ctr++) {
108 gcry_md_copy(&hd2, hd);
109 gcry_md_putc(hd2, (ctr >> 24) & 0xff);
110 gcry_md_putc(hd2, (ctr >> 16) & 0xff);
111 gcry_md_putc(hd2, (ctr >> 8) & 0xff);
112 gcry_md_putc(hd2, (ctr >> 0) & 0xff);
113 gcry_md_final(hd2);
114 cpylen = (buflen < olen) ? buflen : olen;
115 memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen);
116 gcry_md_close(hd2);
117 buf += cpylen;
118 buflen -= cpylen;
119 }
120 gcry_md_close(hd);
121 }
122
123 /* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */
124 static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint32_t idx) {
125 size_t buflen = bits / 8;
126 uint8_t buf[buflen];
127 gcry_mpi_t p;
128
129 assert(bits % 8 == 0);
130 assert(buflen > 0);
131
132 det_randomize(buf, buflen, seed, seedlen, idx);
133 buf[0] |= 0xc0; /* set upper two bits, so that n=pq has maximum size */
134 buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */
135
136 p = mpi_import(buf, buflen);
137 while (gcry_prime_check(p, 0))
138 gcry_mpi_add_ui(p, p, 4);
139
140 return p;
141 }
142
143 /* deterministically generate from seed/idx a quadratic residue (mod n) */
144 static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen, uint32_t idx, unsigned secpar) {
145 size_t buflen = secpar / 8;
146 uint8_t buf[buflen];
147 gcry_mpi_t x;
148
149 det_randomize(buf, buflen, seed, seedlen, idx);
150 buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */
151 x = mpi_import(buf, buflen);
152 assert(gcry_mpi_cmp(x, n) < 0);
153 gcry_mpi_mulm(x, x, x, n);
154 return x;
155 }
156
157 /* compute 2^m (mod phi(p)), for a prime p */
158 static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) {
159 gcry_mpi_t phi, r;
160 int n;
161
162 phi = gcry_mpi_new(0);
163 gcry_mpi_sub_ui(phi, p, 1);
164
165 /* count number of used bits in m */
166 for (n = 0; (1ULL << n) <= m; n++)
167 ;
168
169 r = gcry_mpi_new(0);
170 gcry_mpi_set_ui(r, 1);
171 while (n) { /* square and multiply algorithm for fast exponentiation */
172 n--;
173 gcry_mpi_mulm(r, r, r, phi);
174 if (m & ((uint64_t)1 << n)) {
175 gcry_mpi_add(r, r, r);
176 if (gcry_mpi_cmp(r, phi) >= 0)
177 gcry_mpi_sub(r, r, phi);
178 }
179 }
180
181 gcry_mpi_release(phi);
182 return r;
183 }
184
185 /* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */
186 static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) {
187 *xp = gcry_mpi_new(0);
188 *xq = gcry_mpi_new(0);
189 gcry_mpi_mod(*xp, x, p);
190 gcry_mpi_mod(*xq, x, q);
191 }
192
193 /* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */
194 static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) {
195 gcry_mpi_t a, u;
196
197 a = gcry_mpi_new(0);
198 u = gcry_mpi_new(0);
199 *x = gcry_mpi_new(0);
200 gcry_mpi_subm(a, xq, xp, q);
201 gcry_mpi_invm(u, p, q);
202 gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
203 gcry_mpi_mul(*x, p, a);
204 gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
205 gcry_mpi_release(a);
206 gcry_mpi_release(u);
207 }
208
209 static void initialize_libgcrypt(void) {
210 const char *p;
211 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
212 return;
213
214 p = gcry_check_version("1.4.5");
215 assert(p);
216
217 /* Turn off "secmem". Clients which whish to make use of this
218 * feature should initialize the library manually */
219 gcry_control(GCRYCTL_DISABLE_SECMEM);
220 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
221 }
222
223 /******************************************************************************/
224
225 size_t FSPRG_mskinbytes(unsigned _secpar) {
226 VALIDATE_SECPAR(_secpar);
227 return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */
228 }
229
230 size_t FSPRG_mpkinbytes(unsigned _secpar) {
231 VALIDATE_SECPAR(_secpar);
232 return 2 + _secpar / 8; /* to store header,n */
233 }
234
235 size_t FSPRG_stateinbytes(unsigned _secpar) {
236 VALIDATE_SECPAR(_secpar);
237 return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */
238 }
239
240 static void store_secpar(void *buf, uint16_t secpar) {
241 secpar = secpar / 16 - 1;
242 ((uint8_t*) buf)[0] = (secpar >> 8) & 0xff;
243 ((uint8_t*) buf)[1] = (secpar >> 0) & 0xff;
244 }
245
246 static uint16_t read_secpar(const void *buf) {
247 uint16_t secpar;
248 secpar =
249 (uint16_t)(((uint8_t*) buf)[0]) << 8 |
250 (uint16_t)(((uint8_t*) buf)[1]) << 0;
251 return 16 * (secpar + 1);
252 }
253
254 void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
255 uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
256 gcry_mpi_t n, p, q;
257 uint16_t secpar;
258
259 VALIDATE_SECPAR(_secpar);
260 secpar = _secpar;
261
262 initialize_libgcrypt();
263
264 if (!seed) {
265 gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
266 seed = iseed;
267 seedlen = FSPRG_RECOMMENDED_SEEDLEN;
268 }
269
270 p = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_P);
271 q = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_Q);
272
273 if (msk) {
274 store_secpar(msk + 0, secpar);
275 mpi_export(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8, p);
276 mpi_export(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8, q);
277 }
278
279 if (mpk) {
280 n = gcry_mpi_new(0);
281 gcry_mpi_mul(n, p, q);
282 assert(gcry_mpi_get_nbits(n) == secpar);
283
284 store_secpar(mpk + 0, secpar);
285 mpi_export(mpk + 2, secpar / 8, n);
286
287 gcry_mpi_release(n);
288 }
289
290 gcry_mpi_release(p);
291 gcry_mpi_release(q);
292 }
293
294 void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
295 gcry_mpi_t n, x;
296 uint16_t secpar;
297
298 initialize_libgcrypt();
299
300 secpar = read_secpar(mpk + 0);
301 n = mpi_import(mpk + 2, secpar / 8);
302 x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
303
304 memcpy(state, mpk, 2 + secpar / 8);
305 mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
306 memzero(state + 2 + 2 * secpar / 8, 8);
307
308 gcry_mpi_release(n);
309 gcry_mpi_release(x);
310 }
311
312 void FSPRG_Evolve(void *state) {
313 gcry_mpi_t n, x;
314 uint16_t secpar;
315 uint64_t epoch;
316
317 initialize_libgcrypt();
318
319 secpar = read_secpar(state + 0);
320 n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
321 x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8);
322 epoch = uint64_import(state + 2 + 2 * secpar / 8, 8);
323
324 gcry_mpi_mulm(x, x, x, n);
325 epoch++;
326
327 mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
328 uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
329
330 gcry_mpi_release(n);
331 gcry_mpi_release(x);
332 }
333
334 uint64_t FSPRG_GetEpoch(const void *state) {
335 uint16_t secpar;
336 secpar = read_secpar(state + 0);
337 return uint64_import(state + 2 + 2 * secpar / 8, 8);
338 }
339
340 void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
341 gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
342 uint16_t secpar;
343
344 initialize_libgcrypt();
345
346 secpar = read_secpar(msk + 0);
347 p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
348 q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8);
349
350 n = gcry_mpi_new(0);
351 gcry_mpi_mul(n, p, q);
352
353 x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
354 CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */
355
356 kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */
357 kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */
358
359 gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
360 gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
361
362 CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */
363
364 store_secpar(state + 0, secpar);
365 mpi_export(state + 2 + 0 * secpar / 8, secpar / 8, n);
366 mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm);
367 uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
368
369 gcry_mpi_release(p);
370 gcry_mpi_release(q);
371 gcry_mpi_release(n);
372 gcry_mpi_release(x);
373 gcry_mpi_release(xp);
374 gcry_mpi_release(xq);
375 gcry_mpi_release(kp);
376 gcry_mpi_release(kq);
377 gcry_mpi_release(xm);
378 }
379
380 void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
381 uint16_t secpar;
382
383 initialize_libgcrypt();
384
385 secpar = read_secpar(state + 0);
386 det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
387 }