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