1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2 /* SPDX-License-Identifier: Unlicense */
3 
4 #include "tomcrypt_private.h"
5 
6 /**
7  @file prngs/sober128.c
8  Implementation of SOBER-128 by Tom St Denis.
9  Based on s128fast.c reference code supplied by Greg Rose of QUALCOMM.
10 */
11 
12 #ifdef LTC_SOBER128
13 
14 const struct ltc_prng_descriptor sober128_desc =
15 {
16    "sober128",
17    40,
18    &sober128_start,
19    &sober128_add_entropy,
20    &sober128_ready,
21    &sober128_read,
22    &sober128_done,
23    &sober128_export,
24    &sober128_import,
25    &sober128_test
26 };
27 
28 /**
29   Start the PRNG
30   @param prng     [out] The PRNG state to initialize
31   @return CRYPT_OK if successful
32 */
sober128_start(prng_state * prng)33 int sober128_start(prng_state *prng)
34 {
35    LTC_ARGCHK(prng != NULL);
36    prng->ready = 0;
37    XMEMSET(&prng->u.sober128.ent, 0, sizeof(prng->u.sober128.ent));
38    prng->u.sober128.idx = 0;
39    LTC_MUTEX_INIT(&prng->lock)
40    return CRYPT_OK;
41 }
42 
43 /**
44   Add entropy to the PRNG state
45   @param in       The data to add
46   @param inlen    Length of the data to add
47   @param prng     PRNG state to update
48   @return CRYPT_OK if successful
49 */
sober128_add_entropy(const unsigned char * in,unsigned long inlen,prng_state * prng)50 int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
51 {
52    unsigned char buf[40];
53    unsigned long i;
54    int err;
55 
56    LTC_ARGCHK(prng != NULL);
57    LTC_ARGCHK(in != NULL);
58    LTC_ARGCHK(inlen > 0);
59 
60    LTC_MUTEX_LOCK(&prng->lock);
61    if (prng->ready) {
62       /* sober128_ready() was already called, do "rekey" operation */
63       if ((err = sober128_stream_keystream(&prng->u.sober128.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
64       for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
65       /* key 32 bytes, 20 rounds */
66       if ((err = sober128_stream_setup(&prng->u.sober128.s, buf, 32)) != CRYPT_OK)     goto LBL_UNLOCK;
67       /* iv 8 bytes */
68       if ((err = sober128_stream_setiv(&prng->u.sober128.s, buf + 32, 8)) != CRYPT_OK) goto LBL_UNLOCK;
69       /* clear KEY + IV */
70       zeromem(buf, sizeof(buf));
71    }
72    else {
73       /* sober128_ready() was not called yet, add entropy to ent buffer */
74       while (inlen--) prng->u.sober128.ent[prng->u.sober128.idx++ % sizeof(prng->u.sober128.ent)] ^= *in++;
75    }
76    err = CRYPT_OK;
77 LBL_UNLOCK:
78    LTC_MUTEX_UNLOCK(&prng->lock);
79    return err;
80 }
81 
82 /**
83   Make the PRNG ready to read from
84   @param prng   The PRNG to make active
85   @return CRYPT_OK if successful
86 */
sober128_ready(prng_state * prng)87 int sober128_ready(prng_state *prng)
88 {
89    int err;
90 
91    LTC_ARGCHK(prng != NULL);
92 
93    LTC_MUTEX_LOCK(&prng->lock);
94    if (prng->ready)                                                            { err = CRYPT_OK; goto LBL_UNLOCK; }
95    /* key 32 bytes, 20 rounds */
96    if ((err = sober128_stream_setup(&prng->u.sober128.s, prng->u.sober128.ent, 32)) != CRYPT_OK)     goto LBL_UNLOCK;
97    /* iv 8 bytes */
98    if ((err = sober128_stream_setiv(&prng->u.sober128.s, prng->u.sober128.ent + 32, 8)) != CRYPT_OK) goto LBL_UNLOCK;
99    XMEMSET(&prng->u.sober128.ent, 0, sizeof(prng->u.sober128.ent));
100    prng->u.sober128.idx = 0;
101    prng->ready = 1;
102 LBL_UNLOCK:
103    LTC_MUTEX_UNLOCK(&prng->lock);
104    return err;
105 }
106 
107 /**
108   Read from the PRNG
109   @param out      Destination
110   @param outlen   Length of output
111   @param prng     The active PRNG to read from
112   @return Number of octets read
113 */
sober128_read(unsigned char * out,unsigned long outlen,prng_state * prng)114 unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng)
115 {
116    if (outlen == 0 || prng == NULL || out == NULL) return 0;
117    LTC_MUTEX_LOCK(&prng->lock);
118    if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
119    if (sober128_stream_keystream(&prng->u.sober128.s, out, outlen) != CRYPT_OK) outlen = 0;
120 LBL_UNLOCK:
121    LTC_MUTEX_UNLOCK(&prng->lock);
122    return outlen;
123 }
124 
125 /**
126   Terminate the PRNG
127   @param prng   The PRNG to terminate
128   @return CRYPT_OK if successful
129 */
sober128_done(prng_state * prng)130 int sober128_done(prng_state *prng)
131 {
132    int err;
133    LTC_ARGCHK(prng != NULL);
134    LTC_MUTEX_LOCK(&prng->lock);
135    prng->ready = 0;
136    err = sober128_stream_done(&prng->u.sober128.s);
137    LTC_MUTEX_UNLOCK(&prng->lock);
138    LTC_MUTEX_DESTROY(&prng->lock);
139    return err;
140 }
141 
142 /**
143   Export the PRNG state
144   @param out       [out] Destination
145   @param outlen    [in/out] Max size and resulting size of the state
146   @param prng      The PRNG to export
147   @return CRYPT_OK if successful
148 */
LTC_PRNG_EXPORT(sober128)149 LTC_PRNG_EXPORT(sober128)
150 
151 /**
152   Import a PRNG state
153   @param in       The PRNG state
154   @param inlen    Size of the state
155   @param prng     The PRNG to import
156   @return CRYPT_OK if successful
157 */
158 int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
159 {
160    int err;
161 
162    LTC_ARGCHK(prng != NULL);
163    LTC_ARGCHK(in   != NULL);
164    if (inlen < (unsigned long)sober128_desc.export_size) return CRYPT_INVALID_ARG;
165 
166    if ((err = sober128_start(prng)) != CRYPT_OK) return err;
167    if ((err = sober128_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
168    return CRYPT_OK;
169 }
170 
171 /**
172   PRNG self-test
173   @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
174 */
sober128_test(void)175 int sober128_test(void)
176 {
177 #ifndef LTC_TEST
178    return CRYPT_NOP;
179 #else
180    prng_state st;
181    unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
182                           0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
183                           0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
184                           0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
185                           0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
186    unsigned char dmp[300];
187    unsigned long dmplen = sizeof(dmp);
188    unsigned char out[500];
189    unsigned char t1[] = { 0x31, 0x82, 0xA7, 0xA5, 0x8B, 0xD7, 0xCB, 0x39, 0x86, 0x1A };
190    unsigned char t2[] = { 0x6B, 0x43, 0x9E, 0xBC, 0xE7, 0x62, 0x9B, 0xE6, 0x9B, 0x83 };
191    unsigned char t3[] = { 0x4A, 0x0E, 0x6C, 0xC1, 0xCF, 0xB4, 0x73, 0x49, 0x99, 0x05 };
192    int err;
193 
194    if ((err = sober128_start(&st)) != CRYPT_OK)                         return err;
195    /* add entropy to uninitialized prng */
196    if ((err = sober128_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
197    if ((err = sober128_ready(&st)) != CRYPT_OK)                         return err;
198    if (sober128_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
199    if (compare_testvector(out, 10, t1, sizeof(t1), "SOBER128-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
200    if (sober128_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
201    /* add entropy to already initialized prng */
202    if ((err = sober128_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
203    if (sober128_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
204    if ((err = sober128_export(dmp, &dmplen, &st)) != CRYPT_OK)          return err;
205    if (sober128_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
206    if (sober128_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
207    if (compare_testvector(out, 10, t2, sizeof(t2), "SOBER128-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
208    if ((err = sober128_done(&st)) != CRYPT_OK)                          return err;
209    if ((err = sober128_import(dmp, dmplen, &st)) != CRYPT_OK)           return err;
210    if ((err = sober128_ready(&st)) != CRYPT_OK)                         return err;
211    if (sober128_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
212    if (sober128_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
213    if (compare_testvector(out, 10, t3, sizeof(t3), "SOBER128-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
214    if ((err = sober128_done(&st)) != CRYPT_OK)                          return err;
215 
216    return CRYPT_OK;
217 #endif
218 }
219 
220 #endif
221