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