| // SPDX-License-Identifier: BSD-2-Clause |
| /* LibTomCrypt, modular cryptographic library -- Tom St Denis |
| * |
| * LibTomCrypt is a library that provides various cryptographic |
| * algorithms in a highly modular and flexible manner. |
| * |
| * The library is free for all purposes without any express |
| * guarantee it works. |
| */ |
| #include "tomcrypt_private.h" |
| |
| /** |
| @file sober128_stream.c |
| Implementation of SOBER-128 by Tom St Denis. |
| Based on s128fast.c reference code supplied by Greg Rose of QUALCOMM. |
| */ |
| |
| #ifdef LTC_SOBER128 |
| |
| #define __LTC_SOBER128TAB_C__ |
| #include "sober128tab.c" |
| |
| /* don't change these... */ |
| #define N 17 |
| #define INITKONST 0x6996c53a /* value of KONST to use during key loading */ |
| #define KEYP 15 /* where to insert key words */ |
| #define FOLDP 4 /* where to insert non-linear feedback */ |
| |
| static ulong32 BYTE2WORD(const unsigned char *b) |
| { |
| ulong32 t; |
| LOAD32L(t, b); |
| return t; |
| } |
| |
| static void XORWORD(ulong32 w, const unsigned char *in, unsigned char *out) |
| { |
| ulong32 t; |
| LOAD32L(t, in); |
| t ^= w; |
| STORE32L(t, out); |
| } |
| |
| /* give correct offset for the current position of the register, |
| * where logically R[0] is at position "zero". |
| */ |
| #define OFF(zero, i) (((zero)+(i)) % N) |
| |
| /* step the LFSR */ |
| /* After stepping, "zero" moves right one place */ |
| #define STEP(R,z) \ |
| R[OFF(z,0)] = R[OFF(z,15)] ^ R[OFF(z,4)] ^ (R[OFF(z,0)] << 8) ^ Multab[(R[OFF(z,0)] >> 24) & 0xFF]; |
| |
| static void cycle(ulong32 *R) |
| { |
| ulong32 t; |
| int i; |
| |
| STEP(R,0); |
| t = R[0]; |
| for (i = 1; i < N; ++i) { |
| R[i-1] = R[i]; |
| } |
| R[N-1] = t; |
| } |
| |
| /* Return a non-linear function of some parts of the register. |
| */ |
| #define NLFUNC(st,z) \ |
| { \ |
| t = st->R[OFF(z,0)] + st->R[OFF(z,16)]; \ |
| t ^= Sbox[(t >> 24) & 0xFF]; \ |
| t = RORc(t, 8); \ |
| t = ((t + st->R[OFF(z,1)]) ^ st->konst) + st->R[OFF(z,6)]; \ |
| t ^= Sbox[(t >> 24) & 0xFF]; \ |
| t = t + st->R[OFF(z,13)]; \ |
| } |
| |
| static ulong32 nltap(const sober128_state *st) |
| { |
| ulong32 t; |
| NLFUNC(st, 0); |
| return t; |
| } |
| |
| /* Save the current register state |
| */ |
| static void s128_savestate(sober128_state *st) |
| { |
| int i; |
| for (i = 0; i < N; ++i) { |
| st->initR[i] = st->R[i]; |
| } |
| } |
| |
| /* initialise to previously saved register state |
| */ |
| static void s128_reloadstate(sober128_state *st) |
| { |
| int i; |
| |
| for (i = 0; i < N; ++i) { |
| st->R[i] = st->initR[i]; |
| } |
| } |
| |
| /* Initialise "konst" |
| */ |
| static void s128_genkonst(sober128_state *st) |
| { |
| ulong32 newkonst; |
| |
| do { |
| cycle(st->R); |
| newkonst = nltap(st); |
| } while ((newkonst & 0xFF000000) == 0); |
| st->konst = newkonst; |
| } |
| |
| /* Load key material into the register |
| */ |
| #define ADDKEY(k) \ |
| st->R[KEYP] += (k); |
| |
| #define XORNL(nl) \ |
| st->R[FOLDP] ^= (nl); |
| |
| /* nonlinear diffusion of register for key */ |
| #define DROUND(z) STEP(st->R,z); NLFUNC(st,(z+1)); st->R[OFF((z+1),FOLDP)] ^= t; |
| static void s128_diffuse(sober128_state *st) |
| { |
| ulong32 t; |
| /* relies on FOLD == N == 17! */ |
| DROUND(0); |
| DROUND(1); |
| DROUND(2); |
| DROUND(3); |
| DROUND(4); |
| DROUND(5); |
| DROUND(6); |
| DROUND(7); |
| DROUND(8); |
| DROUND(9); |
| DROUND(10); |
| DROUND(11); |
| DROUND(12); |
| DROUND(13); |
| DROUND(14); |
| DROUND(15); |
| DROUND(16); |
| } |
| |
| /** |
| Initialize an Sober128 context (only the key) |
| @param st [out] The destination of the Sober128 state |
| @param key The secret key |
| @param keylen The length of the secret key (octets) |
| @return CRYPT_OK if successful |
| */ |
| int sober128_stream_setup(sober128_state *st, const unsigned char *key, unsigned long keylen) |
| { |
| ulong32 i, k; |
| |
| LTC_ARGCHK(st != NULL); |
| LTC_ARGCHK(key != NULL); |
| LTC_ARGCHK(keylen > 0); |
| |
| /* keylen must be multiple of 4 bytes */ |
| if ((keylen & 3) != 0) { |
| return CRYPT_INVALID_KEYSIZE; |
| } |
| |
| /* Register initialised to Fibonacci numbers */ |
| st->R[0] = 1; |
| st->R[1] = 1; |
| for (i = 2; i < N; ++i) { |
| st->R[i] = st->R[i-1] + st->R[i-2]; |
| } |
| st->konst = INITKONST; |
| |
| for (i = 0; i < keylen; i += 4) { |
| k = BYTE2WORD((unsigned char *)&key[i]); |
| ADDKEY(k); |
| cycle(st->R); |
| XORNL(nltap(st)); |
| } |
| |
| /* also fold in the length of the key */ |
| ADDKEY(keylen); |
| |
| /* now diffuse */ |
| s128_diffuse(st); |
| s128_genkonst(st); |
| s128_savestate(st); |
| st->nbuf = 0; |
| |
| return CRYPT_OK; |
| } |
| |
| /** |
| Set IV to the Sober128 state |
| @param st The Sober12820 state |
| @param iv The IV data to add |
| @param ivlen The length of the IV (must be 12) |
| @return CRYPT_OK on success |
| */ |
| int sober128_stream_setiv(sober128_state *st, const unsigned char *iv, unsigned long ivlen) |
| { |
| ulong32 i, k; |
| |
| LTC_ARGCHK(st != NULL); |
| LTC_ARGCHK(iv != NULL); |
| LTC_ARGCHK(ivlen > 0); |
| |
| /* ok we are adding an IV then... */ |
| s128_reloadstate(st); |
| |
| /* ivlen must be multiple of 4 bytes */ |
| if ((ivlen & 3) != 0) { |
| return CRYPT_INVALID_KEYSIZE; |
| } |
| |
| for (i = 0; i < ivlen; i += 4) { |
| k = BYTE2WORD((unsigned char *)&iv[i]); |
| ADDKEY(k); |
| cycle(st->R); |
| XORNL(nltap(st)); |
| } |
| |
| /* also fold in the length of the key */ |
| ADDKEY(ivlen); |
| |
| /* now diffuse */ |
| s128_diffuse(st); |
| st->nbuf = 0; |
| |
| return CRYPT_OK; |
| } |
| |
| /* XOR pseudo-random bytes into buffer |
| */ |
| #define SROUND(z) STEP(st->R,z); NLFUNC(st,(z+1)); XORWORD(t, in+(z*4), out+(z*4)); |
| |
| /** |
| Encrypt (or decrypt) bytes of ciphertext (or plaintext) with Sober128 |
| @param st The Sober128 state |
| @param in The plaintext (or ciphertext) |
| @param inlen The length of the input (octets) |
| @param out [out] The ciphertext (or plaintext), length inlen |
| @return CRYPT_OK if successful |
| */ |
| int sober128_stream_crypt(sober128_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out) |
| { |
| ulong32 t; |
| |
| if (inlen == 0) return CRYPT_OK; /* nothing to do */ |
| LTC_ARGCHK(out != NULL); |
| LTC_ARGCHK(st != NULL); |
| |
| /* handle any previously buffered bytes */ |
| while (st->nbuf != 0 && inlen != 0) { |
| *out++ = *in++ ^ (unsigned char)(st->sbuf & 0xFF); |
| st->sbuf >>= 8; |
| st->nbuf -= 8; |
| --inlen; |
| } |
| |
| #ifndef LTC_SMALL_CODE |
| /* do lots at a time, if there's enough to do */ |
| while (inlen >= N*4) { |
| SROUND(0); |
| SROUND(1); |
| SROUND(2); |
| SROUND(3); |
| SROUND(4); |
| SROUND(5); |
| SROUND(6); |
| SROUND(7); |
| SROUND(8); |
| SROUND(9); |
| SROUND(10); |
| SROUND(11); |
| SROUND(12); |
| SROUND(13); |
| SROUND(14); |
| SROUND(15); |
| SROUND(16); |
| out += 4*N; |
| in += 4*N; |
| inlen -= 4*N; |
| } |
| #endif |
| |
| /* do small or odd size buffers the slow way */ |
| while (4 <= inlen) { |
| cycle(st->R); |
| t = nltap(st); |
| XORWORD(t, in, out); |
| out += 4; |
| in += 4; |
| inlen -= 4; |
| } |
| |
| /* handle any trailing bytes */ |
| if (inlen != 0) { |
| cycle(st->R); |
| st->sbuf = nltap(st); |
| st->nbuf = 32; |
| while (st->nbuf != 0 && inlen != 0) { |
| *out++ = *in++ ^ (unsigned char)(st->sbuf & 0xFF); |
| st->sbuf >>= 8; |
| st->nbuf -= 8; |
| --inlen; |
| } |
| } |
| |
| return CRYPT_OK; |
| } |
| |
| int sober128_stream_keystream(sober128_state *st, unsigned char *out, unsigned long outlen) |
| { |
| if (outlen == 0) return CRYPT_OK; /* nothing to do */ |
| LTC_ARGCHK(out != NULL); |
| XMEMSET(out, 0, outlen); |
| return sober128_stream_crypt(st, out, outlen, out); |
| } |
| |
| /** |
| Terminate and clear Sober128 state |
| @param st The Sober128 state |
| @return CRYPT_OK on success |
| */ |
| int sober128_stream_done(sober128_state *st) |
| { |
| LTC_ARGCHK(st != NULL); |
| XMEMSET(st, 0, sizeof(sober128_state)); |
| return CRYPT_OK; |
| } |
| |
| #endif |
| |
| /* ref: $Format:%D$ */ |
| /* git commit: $Format:%H$ */ |
| /* commit time: $Format:%ai$ */ |