LCOV - code coverage report
Current view: top level - journal - fsprg.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 214 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 21 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 94 0.0 %

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

Generated by: LCOV version 1.14