diff --git a/changes b/changes index e74b1d8..cd5ef23 100644 --- a/changes +++ b/changes @@ -1,3 +1,45 @@ +August 6th, 2004 +v0.98 -- Update to hmac_init to free all allocated memory on error + -- Update to PRNG API to fix import/export functions of Fortuna and Yarrow + -- Added test functions to PRNG api, RC4 now conforms ;-) [was a minor issue] + -- Added the SOBER-128 PRNG based off of code donated by Greg Rose. + -- Added Tech Note #4 [notes/tech0004.txt] + -- Changed RC4 back [due to request]. It will now XOR the output so you can use it like + a stream cipher easily. + -- Update Fortuna's export() to emit a hash of each pool. This means that the accumulated + entropy that was spread over all the pools isn't entirely lost when you export/import. + -- Zhi Chen suggested a comment for rsa_encrypt_key() to let users know [easily] that it was + PKCS #1 v2.0 padding. (updated other rsa_* functions) + -- Cleaned up Noekeon to remove unrolling [wasn't required, was messy and actually slower with GCC/ICC] + -- Updated RC4 so that when you feed it >256 bytes of entropy it quietly ignores additional + bytes. Also removed the % from the key setup to speed it up a bit. + -- Added cipher/hash/prng tests to x86_prof to help catch bugs while testing + -- Made the PRNG "done" return int, fixed sprng_done to not require prng* to be non-null + -- Spruced up mycrypt_custom.h to trap more errors and also help prevent LTMSSE from being defined + on non-i386 platforms by accident. + -- Added RSA/ECC/DH speed tests to x86_prof and cleaned it up to build with zero warnings + -- Changed Fortuna to count only entropy [not the 2 byte header] added to pool[0] into the + reseed mechanism. + -- Added "export_size" member to prng_descriptor tables so you can know in advance the size of + the exported state for any given PRNG. + -- Ported over patch on LTM 0.30 [not ready to release LTM 0.31] that fixes bug in mp_mul()/mp_div() + that used to result in negative zeroes when you multiplied zero by a negative integer. + (patch due to "Wolfgang Ehrhardt" ) + -- Fixed rsa_*decrypt_key() and rsa_*verify_hash() to default to invalid "stat" or "res". This way + if any of the higher level functions fail [before you get to the padding] the result will be in + a known state]. Applied to both v2 and v1.5 padding helpers. + -- Added MACs to x86_prof + -- Fixed up "warnings" in x86_prof and tv_gen + -- Added a "profiled" target back [for GCC 3.4 and ICC v8]. Doesn't seem to help but might be worth + tinkering with. + -- Beefed up load/store test in demos/test + + ++ New note, in order to use the optimized LOAD/STORE macros your platform + must support unaligned 32/64 bit load/stores. The x86s support this + but some [ARM for instance] do not. If your platform cannot perform + unaligned operations you must use the endian neutral code which is safe for + any sort of platform. + July 23rd, 2004 v0.97b -- Added PKCS #1 v1.5 RSA encrypt/sign helpers (like rsa_sign_hash, etc...) -- Added missing prng check to rsa_decrypt_key() [not critical as I don't use @@ -62,7 +104,7 @@ v0.96 -- Removed GF and Keyring code -- Changed PSS/OAEP API slightly to be more consistent with other PK functions (order of arguments) -- rsa_exptmod() now pads with leading zeroes as per I2OSP. -- added error checking to yarrow code - -- Mike Frysinger pointed out that tommath.h from this distro will overwrite tommath.h + -- pointed out that tommath.h from this distro will overwrite tommath.h from libtommath. I changed this to ltc_tommath.h to avoid any such problems. -- Fixed bug in PSS encoder/decoder that didn't handle the MSB properly -- refactored AES, now sports an "encrypt only" descriptor which uses half as much code space. diff --git a/crypt.c b/crypt.c index d71066c..56b5c5a 100644 --- a/crypt.c +++ b/crypt.c @@ -141,6 +141,26 @@ const char *crypt_build_settings = " CTR\n" #endif + "\nMACs:\n" +#if defined(HMAC) + " HMAC\n" +#endif +#if defined(OMAC) + " OMAC\n" +#endif +#if defined(PMAC) + " PMAC\n" +#endif + + "\nENC + AUTH modes:\n" +#if defined(EAX_MODE) + " EAX_MODE\n" +#endif +#if defined(OCB_MODE) + " OCB_MODE\n" +#endif + + "\nPRNG:\n" #if defined(YARROW) " Yarrow\n" @@ -151,6 +171,12 @@ const char *crypt_build_settings = #if defined(RC4) " RC4\n" #endif +#if defined(FORTUNA) + " Fortuna\n" +#endif +#if defined(SOBER128) + " SOBER128\n" +#endif "\nPK Algs:\n" #if defined(MRSA) @@ -197,21 +223,6 @@ const char *crypt_build_settings = #if defined(MPI) " MPI " #endif -#if defined(HMAC) - " HMAC " -#endif -#if defined(OMAC) - " OMAC " -#endif -#if defined(PMAC) - " PMAC " -#endif -#if defined(EAX_MODE) - " EAX_MODE " -#endif -#if defined(OCB_MODE) - " OCB_MODE " -#endif #if defined(TRY_UNRANDOM_FIRST) " TRY_UNRANDOM_FIRST " #endif @@ -229,6 +240,9 @@ const char *crypt_build_settings = #endif #if defined(NO_FILE) " NO_FILE " +#endif +#if defined(LTMSSE) + " LTMSSE " #endif "\n" "\n\n\n" diff --git a/crypt.tex b/crypt.tex index 4b3d103..875d903 100644 --- a/crypt.tex +++ b/crypt.tex @@ -47,7 +47,7 @@ \def\gap{\vspace{0.5ex}} \makeindex \begin{document} -\title{LibTomCrypt \\ Version 0.97b} +\title{LibTomCrypt \\ Version 0.98} \author{Tom St Denis \\ \\ tomstdenis@iahu.ca \\ @@ -1687,12 +1687,15 @@ int pmac_test(void); Which returns {\bf CRYPT\_OK} if the code passes otherwise it returns an error code. + + + \chapter{Pseudo-Random Number Generators} \section{Core Functions} - The library provides an array of core functions for Pseudo-Random Number Generators (PRNGs) as well. A cryptographic PRNG is used to expand a shorter bit string into a longer bit string. PRNGs are used wherever random data is required such as Public Key (PK) key generation. There is a universal structure called ``prng\_state''. To initialize a PRNG call: +\index{PRNG start} \begin{verbatim} int XXX_start(prng_state *prng); \end{verbatim} @@ -1700,6 +1703,7 @@ int XXX_start(prng_state *prng); This will setup the PRNG for future use and not seed it. In order for the PRNG to be cryptographically useful you must give it entropy. Ideally you'd have some OS level source to tap like in UNIX (see section 5.3). To add entropy to the PRNG call: +\index{PRNG add\_entropy} \begin{verbatim} int XXX_add_entropy(const unsigned char *in, unsigned long len, prng_state *prng); @@ -1707,17 +1711,77 @@ int XXX_add_entropy(const unsigned char *in, unsigned long len, Which returns {\bf CRYPTO\_OK} if the entropy was accepted. Once you think you have enough entropy you call another function to put the entropy into action. +\index{PRNG ready} \begin{verbatim} int XXX_ready(prng_state *prng); \end{verbatim} Which returns {\bf CRYPTO\_OK} if it is ready. Finally to actually read bytes call: +\index{PRNG read} \begin{verbatim} unsigned long XXX_read(unsigned char *out, unsigned long len, prng_state *prng); \end{verbatim} -Which returns the number of bytes read from the PRNG. +Which returns the number of bytes read from the PRNG. When you are finished with a PRNG state you call +the following. + +\index{PRNG done} +\begin{verbatim} +void XXX_done(prng_state *prng); +\end{verbatim} + +This will terminate a PRNG state and free any memory (if any) allocated. To export a PRNG state +so that you can later resume the PRNG call the following. + +\index{PRNG export} +\begin{verbatim} +int XXX_export(unsigned char *out, unsigned long *outlen, + prng_state *prng); +\end{verbatim} + +This will write a ``PRNG state'' to the buffer ``out'' of length ``outlen'' bytes. The idea of +the export is meant to be used as a ``seed file''. That is, when the program starts up there will not likely +be that much entropy available. To import a state to seed a PRNG call the following function. + +\index{PRNG import} +\begin{verbatim} +int XXX_import(const unsigned char *in, unsigned long inlen, + prng_state *prng); +\end{verbatim} + +This will call the start and add\_entropy functions of the given PRNG. It will use the state in +``in'' of length ``inlen'' as the initial seed. You must pass the same seed length as was exported +by the corresponding export function. + +Note that importing a state will not ``resume'' the PRNG from where it left off. That is, if you export +a state, emit (say) 8 bytes and then import the previously exported state the next 8 bytes will not +specifically equal the 8 bytes you generated previously. + +When a program is first executed the normal course of operation is + +\begin{enumerate} + \item Gather entropy from your sources for a given period of time or number of events. + \item Start, use your entropy via add\_entropy and ready the PRNG yourself. +\end{enumerate} + +When your program is finished you simply call the export function and save the state to a medium (disk, +flash memory, etc). The next time your application starts up you can detect the state, feed it to the +import function and go on your way. It is ideal that (as soon as possible) after startup you export a +fresh state. This helps in the case that the program aborts or the machine is powered down without +being given a chance to exit properly. + +Note that even if you have a state to import it is important to add new entropy to the state. However, +there is less pressure to do so. + +To test a PRNG for operational conformity call the following functions. + +\index{PRNG test} +\begin{verbatim} +int XXX_test(void); +\end{verbatim} + +This will return \textbf{CRYPT\_OK} if PRNG is operating properly. \subsection{Remarks} @@ -1728,8 +1792,8 @@ checking is guaranteed to see if the entropy is sufficient or if the PRNG is eve \subsection{Example} -Below is a simple snippet to read 10 bytes from yarrow. Its important to note that this snippet is {\bf NOT} secure since -the entropy added is not random. +Below is a simple snippet to read 10 bytes from yarrow. Its important to note that this snippet is +{\bf NOT} secure since the entropy added is not random. \begin{verbatim} #include @@ -1762,10 +1826,15 @@ PRNGs have descriptors too (surprised?). Stored in the structure ``prng\_descrip \begin{verbatim} struct _prng_descriptor { char *name; + int export_size; /* size in bytes of exported state */ int (*start) (prng_state *); int (*add_entropy)(const unsigned char *, unsigned long, prng_state *); int (*ready) (prng_state *); unsigned long (*read)(unsigned char *, unsigned long len, prng_state *); + void (*done)(prng_state *); + int (*export)(unsigned char *, unsigned long *, prng_state *); + int (*import)(const unsigned char *, unsigned long, prng_state *); + int (*test)(void); }; \end{verbatim} @@ -1779,16 +1848,82 @@ int register_prng(const struct _prng_descriptor *prng); int unregister_prng(const struct _prng_descriptor *prng); \end{verbatim} -\subsubsection{PRNGs Provided} -Currently Yarrow (yarrow\_desc), RC4 (rc4\_desc) and the secure RNG (sprng\_desc) are provided as PRNGs within the -library. +\subsection{PRNGs Provided} +\begin{figure}[here] +\begin{center} +\begin{small} +\begin{tabular}{|c|c|l|} +\hline \textbf{Name} & \textbf{Descriptor} & \textbf{Usage} \\ +\hline Yarrow & yarrow\_desc & Fast short-term PRNG \\ +\hline Fortuna & fortuna\_desc & Fast long-term PRNG (recommended) \\ +\hline RC4 & rc4\_desc & Stream Cipher \\ +\hline SOBER-128 & sober128\_desc & Stream Cipher (also very fast PRNG) \\ +\hline +\end{tabular} +\end{small} +\end{center} +\caption{List of Provided PRNGs} +\end{figure} -RC4 is provided with a PRNG interface because it is a stream cipher and not well suited for the symmetric block cipher -interface. You provide the key for RC4 via the rc4\_add\_entropy() function. By calling rc4\_ready() the key will be used -to setup the RC4 state for encryption or decryption. The rc4\_read() function has been modified from RC4 since it will -XOR the output of the RC4 keystream generator against the input buffer you provide. The following snippet will demonstrate -how to encrypt a buffer with RC4: +\subsubsection{Yarrow} +Yarrow is fast PRNG meant to collect an unspecified amount of entropy from sources +(keyboard, mouse, interrupts, etc) and produce an unbounded string of random bytes. +\textit{Note:} This PRNG is still secure for most taskings but is no longer recommended. Users +should use Fortuna instead. + +\subsubsection{Fortuna} + +Fortuna is a fast attack tolerant and more thoroughly designed PRNG suitable for long term +usage. It is faster than the default implementation of Yarrow\footnote{Yarrow has been implemented +to work with most cipher and hash combos based on which you have chosen to build into the library.} while +providing more security. + +Fortuna is slightly less flexible than Yarrow in the sense that it only works with the AES block cipher +and SHA--256 hash function. Technically Fortuna will work with any block cipher that accepts a 256--bit +key and any hash that produces at least a 256--bit output. However, to make the implementation simpler +it has been fixed to those choices. + +Fortuna is more secure than Yarrow in the sense that attackers who learn parts of the entropy being +added to the PRNG learn far less about the state than that of Yarrow. Without getting into to many +details Fortuna has the ability to recover from state determination attacks where the attacker starts +to learn information from the PRNGs output about the internal state. Yarrow on the other hand cannot +recover from that problem until new entropy is added to the pool and put to use through the ready() function. + +\subsubsection{RC4} + +RC4 is an old stream cipher that can also double duty as a PRNG in a pinch. You ``key'' it by +calling add\_entropy() and setup the key by calling ready(). You can only add upto 256 bytes via +add\_entropy(). + +When you read from RC4 the output of the RC4 algorithm is XOR'd against your buffer you provide. In this +manner you can use rc4\_read() as an encrypt (and decrypt) function. + +You really shouldn't use RC4 anymore. This isn't because RC4 is weak (though biases are known to exist) just +simply that faster alternatives exist. + +\subsubsection{SOBER-128} + +SOBER-128 is a stream cipher designed by the QUALCOMM Australia team. Like RC4 you ``key'' it by +calling add\_entropy(). There is no need to call ready() for this PRNG as it does not do anything. + +Note that this cipher has several oddities about how it operates. The first time you call +add\_entropy() that sets the cipher's key. Every other time you call the same function it sets +the cipher's IV variable. The IV mechanism allows you to encrypt several messages with the same +key and not re--use the same key material. + +Unlike Yarrow and Fortuna all of the entropy (and hence security) of this algorithm rests in the data +you pass it on the first call to add\_entropy(). All buffers sent to add\_entropy() must have a length +that is a multiple of four bytes. + +Like RC4 the output of SOBER--128 is XOR'ed against the buffer you provide it. In this manner you can use +sober128\_read() as an encrypt (and decrypt) function. + +Since SOBER-128 has a fixed keying scheme and is very fast (faster than RC4) the ideal usage of SOBER-128 is to +key it from the output of Fortuna (or Yarrow) and use it to encrypt messages. It is also ideal for +simulations which need a high quality (and fast) stream of bytes. + +\subsubsection{Example Usage} \begin{small} \begin{verbatim} #include diff --git a/demos/test/cipher_hash_test.c b/demos/test/cipher_hash_test.c index 52e9e65..046454b 100644 --- a/demos/test/cipher_hash_test.c +++ b/demos/test/cipher_hash_test.c @@ -4,7 +4,10 @@ int cipher_hash_test(void) { - int x; + int x; + unsigned char buf[4096]; + unsigned long n; + prng_state nprng; /* test ciphers */ for (x = 0; cipher_descriptor[x].name != NULL; x++) { @@ -15,6 +18,24 @@ int cipher_hash_test(void) for (x = 0; hash_descriptor[x].name != NULL; x++) { DO(hash_descriptor[x].test()); } + + /* test prngs (test, import/export */ + for (x = 0; prng_descriptor[x].name != NULL; x++) { + DO(prng_descriptor[x].test()); + DO(prng_descriptor[x].start(&nprng)); + DO(prng_descriptor[x].add_entropy("helloworld12", 12, &nprng)); + DO(prng_descriptor[x].ready(&nprng)); + n = sizeof(buf); + DO(prng_descriptor[x].export(buf, &n, &nprng)); + prng_descriptor[x].done(&nprng); + DO(prng_descriptor[x].import(buf, n, &nprng)); + DO(prng_descriptor[x].ready(&nprng)); + if (prng_descriptor[x].read(buf, 100, &nprng) != 100) { + fprintf(stderr, "Error reading from imported PRNG!\n"); + exit(EXIT_FAILURE); + } + prng_descriptor[x].done(&nprng); + } return 0; } diff --git a/demos/test/store_test.c b/demos/test/store_test.c index 278d52f..e033594 100644 --- a/demos/test/store_test.c +++ b/demos/test/store_test.c @@ -1,43 +1,44 @@ #include "test.h" +/* Test store/load macros with offsets */ int store_test(void) { - unsigned char buf[8]; - unsigned long L; - ulong64 LL; + unsigned char buf[24]; + unsigned long L, L1; + int y; + ulong64 LL, LL1; L = 0x12345678UL; - STORE32L (L, &buf[0]); - L = 0; - LOAD32L (L, &buf[0]); - if (L != 0x12345678UL) { - printf ("LOAD/STORE32 Little don't work"); - return 1; - } - LL = CONST64 (0x01020304050607); - STORE64L (LL, &buf[0]); - LL = 0; - LOAD64L (LL, &buf[0]) - if (LL != CONST64 (0x01020304050607)) { - printf ("LOAD/STORE64 Little don't work"); - return 1; + for (y = 0; y < 4; y++) { + STORE32L(L, buf + y); + LOAD32L(L1, buf + y); + if (L1 != L) { + fprintf(stderr, "\n32L failed at offset %d\n", y); + return 1; + } + STORE32H(L, buf + y); + LOAD32H(L1, buf + y); + if (L1 != L) { + fprintf(stderr, "\n32H failed at offset %d\n", y); + return 1; + } } - L = 0x12345678UL; - STORE32H (L, &buf[0]); - L = 0; - LOAD32H (L, &buf[0]); - if (L != 0x12345678UL) { - printf ("LOAD/STORE32 High don't work, %08lx", L); - return 1; - } LL = CONST64 (0x01020304050607); - STORE64H (LL, &buf[0]); - LL = 0; - LOAD64H (LL, &buf[0]) - if (LL != CONST64 (0x01020304050607)) { - printf ("LOAD/STORE64 High don't work"); - return 1; + for (y = 0; y < 8; y++) { + STORE64L(LL, buf + y); + LOAD64L(LL1, buf + y); + if (LL1 != LL) { + fprintf(stderr, "\n64L failed at offset %d\n", y); + return 1; + } + STORE64H(LL, buf + y); + LOAD64H(LL1, buf + y); + if (LL1 != LL) { + fprintf(stderr, "\n64H failed at offset %d\n", y); + return 1; + } } + return 0; } diff --git a/demos/test/test.c b/demos/test/test.c index 2ffac3c..b516804 100644 --- a/demos/test/test.c +++ b/demos/test/test.c @@ -112,15 +112,21 @@ void register_algs(void) register_hash (&whirlpool_desc); #endif - if (register_prng(&yarrow_desc) == -1) { - printf("Error registering yarrow PRNG\n"); - exit(-1); - } - - if (register_prng(&sprng_desc) == -1) { - printf("Error registering sprng PRNG\n"); - exit(-1); - } +#ifdef YARROW + register_prng(&yarrow_desc); +#endif +#ifdef FORTUNA + register_prng(&fortuna_desc); +#endif +#ifdef RC4 + register_prng(&rc4_desc); +#endif +#ifdef SPRNG + register_prng(&sprng_desc); +#endif +#ifdef SOBER128 + register_prng(&sober128_desc); +#endif } /* sort tests based on their requirement/services. Helps make sure dependencies are tested first */ diff --git a/demos/tv_gen.c b/demos/tv_gen.c index 17f7ad2..101976e 100644 --- a/demos/tv_gen.c +++ b/demos/tv_gen.c @@ -203,7 +203,7 @@ void cipher_gen(void) void hmac_gen(void) { unsigned char key[MAXBLOCKSIZE], output[MAXBLOCKSIZE], *input; - int x, y, z, kl, err; + int x, y, z, err; FILE *out; unsigned long len; diff --git a/demos/x86_prof.c b/demos/x86_prof.c index a478614..7293bd6 100644 --- a/demos/x86_prof.c +++ b/demos/x86_prof.c @@ -191,16 +191,26 @@ void reg_algs(void) register_hash (&whirlpool_desc); #endif +#ifndef YARROW + #error This demo requires Yarrow. +#endif register_prng(&yarrow_desc); +#ifdef FORTUNA register_prng(&fortuna_desc); +#endif +#ifdef RC4 register_prng(&rc4_desc); +#endif +#ifdef SOBER128 +register_prng(&sober128_desc); +#endif rng_make_prng(128, find_prng("yarrow"), &prng, NULL); } int time_keysched(void) { - unsigned long x, i, y1; + unsigned long x, y1; ulong64 t1, c1; symmetric_key skey; int kl; @@ -241,7 +251,7 @@ int time_cipher(void) symmetric_key skey; void (*func) (const unsigned char *, unsigned char *, symmetric_key *); unsigned char key[MAXBLOCKSIZE], pt[MAXBLOCKSIZE]; - + int err; printf ("\n\nECB Time Trials for the Symmetric Ciphers:\n"); no_results = 0; @@ -249,6 +259,12 @@ int time_cipher(void) cipher_descriptor[x].setup (key, cipher_descriptor[x].min_key_length, 0, &skey); + /* sanity check on cipher */ + if ((err = cipher_descriptor[x].test()) != CRYPT_OK) { + fprintf(stderr, "\n\nERROR: Cipher %s failed self-test %s\n", cipher_descriptor[x].name, error_to_string(err)); + exit(EXIT_FAILURE); + } + #define DO1 func(pt,pt,&skey); #define DO2 DO1 DO1 @@ -303,13 +319,20 @@ int time_hash(void) unsigned long x, y1, len; ulong64 t1, t2, c1, c2; hash_state md; - int (*func)(hash_state *, const unsigned char *, unsigned long); + int (*func)(hash_state *, const unsigned char *, unsigned long), err; unsigned char pt[MAXBLOCKSIZE]; printf ("\n\nHASH Time Trials for:\n"); no_results = 0; for (x = 0; hash_descriptor[x].name != NULL; x++) { + + /* sanity check on hash */ + if ((err = hash_descriptor[x].test()) != CRYPT_OK) { + fprintf(stderr, "\n\nERROR: Hash %s failed self-test %s\n", hash_descriptor[x].name, error_to_string(err)); + exit(EXIT_FAILURE); + } + hash_descriptor[x].init(&md); #define DO1 func(&md,pt,len); @@ -365,7 +388,7 @@ void time_mult(void) t1 = (t_read() - t1)>>1; if (t1 < t2) t2 = t1; } - printf("%3d digits: %9llu cycles\n", x, t2); + printf("%3lu digits: %9llu cycles\n", x, t2); } mp_clear_multi(&a,&b,&c,NULL); @@ -395,7 +418,7 @@ void time_sqr(void) t1 = (t_read() - t1)>>1; if (t1 < t2) t2 = t1; } - printf("%3d digits: %9llu cycles\n", x, t2); + printf("%3lu digits: %9llu cycles\n", x, t2); } mp_clear_multi(&a,&b,NULL); @@ -407,20 +430,27 @@ void time_prng(void) { ulong64 t1, t2; unsigned char buf[4096]; - prng_state prng; + prng_state tprng; unsigned long x, y; + int err; - printf("Timing PRNGs:\n"); + printf("Timing PRNGs (cycles/byte output, cycles add_entropy (32 bytes) :\n"); for (x = 0; prng_descriptor[x].name != NULL; x++) { - prng_descriptor[x].start(&prng); + + /* sanity check on prng */ + if ((err = prng_descriptor[x].test()) != CRYPT_OK) { + fprintf(stderr, "\n\nERROR: PRNG %s failed self-test %s\n", prng_descriptor[x].name, error_to_string(err)); + exit(EXIT_FAILURE); + } + + prng_descriptor[x].start(&tprng); zeromem(buf, 256); - prng_descriptor[x].add_entropy(buf, 256, &prng); - prng_descriptor[x].ready(&prng); + prng_descriptor[x].add_entropy(buf, 256, &tprng); + prng_descriptor[x].ready(&tprng); t2 = -1; -#define DO1 prng_descriptor[x].read(buf, 4096, &prng); +#define DO1 if (prng_descriptor[x].read(buf, 4096, &tprng) != 4096) { printf("\n\nERROR READ != 4096\n\n"); exit(EXIT_FAILURE); } #define DO2 DO1 DO1 - for (y = 0; y < 10000; y++) { t_start(); t1 = t_read(); @@ -428,14 +458,255 @@ void time_prng(void) t1 = (t_read() - t1)>>1; if (t1 < t2) t2 = t1; } - printf("%20s: %llu\n", prng_descriptor[x].name, t2>>12); - } + printf("%20s: %5llu ", prng_descriptor[x].name, t2>>12); #undef DO2 #undef DO1 +#define DO1 prng_descriptor[x].start(&tprng); prng_descriptor[x].add_entropy(buf, 32, &tprng); prng_descriptor[x].ready(&tprng); prng_descriptor[x].done(&tprng); +#define DO2 DO1 DO1 + for (y = 0; y < 10000; y++) { + t_start(); + t1 = t_read(); + DO2; + t1 = (t_read() - t1)>>1; + if (t1 < t2) t2 = t1; + } + printf("%5llu\n", t2); +#undef DO2 +#undef DO1 + + } } +/* time various RSA operations */ +void time_rsa(void) +{ + rsa_key key; + ulong64 t1, t2; + unsigned char buf[2][4096]; + unsigned long x, y, z, zzz; + int err, zz; + for (x = 1024; x <= 2048; x += 512) { + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + if ((err = rsa_make_key(&prng, find_prng("yarrow"), x/8, 65537, &key)) != CRYPT_OK) { + fprintf(stderr, "\n\nrsa_make_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + + if (y < 15) { + rsa_free(&key); + } + } + t2 >>= 4; + printf("RSA-%lu make_key took %15llu cycles\n", x, t2); + + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + z = sizeof(buf[1]); + if ((err = rsa_encrypt_key(buf[0], 32, buf[1], &z, "testprog", 8, &prng, + find_prng("yarrow"), find_hash("sha1"), + &key)) != CRYPT_OK) { + fprintf(stderr, "\n\nrsa_encrypt_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + } + t2 >>= 4; + printf("RSA-%lu encrypt_key took %15llu cycles\n", x, t2); + + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + zzz = sizeof(buf[0]); + if ((err = rsa_decrypt_key(buf[1], z, buf[0], &zzz, "testprog", 8, &prng, + find_prng("yarrow"), find_hash("sha1"), + &zz, &key)) != CRYPT_OK) { + fprintf(stderr, "\n\nrsa_decrypt_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + } + t2 >>= 4; + printf("RSA-%lu decrypt_key took %15llu cycles\n", x, t2); + + + rsa_free(&key); + } +} + +/* time various ECC operations */ +void time_ecc(void) +{ + ecc_key key; + ulong64 t1, t2; + unsigned char buf[2][4096]; + unsigned long i, x, y, z; + int err; + static unsigned long sizes[] = {160/8, 256/8, 521/8, 100000}; + + for (x = sizes[i=0]; x < 100000; x = sizes[++i]) { + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + if ((err = ecc_make_key(&prng, find_prng("yarrow"), x, &key)) != CRYPT_OK) { + fprintf(stderr, "\n\necc_make_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + + if (y < 15) { + ecc_free(&key); + } + } + t2 >>= 4; + printf("ECC-%lu make_key took %15llu cycles\n", x*8, t2); + + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + z = sizeof(buf[1]); + if ((err = ecc_encrypt_key(buf[0], 20, buf[1], &z, &prng, find_prng("yarrow"), find_hash("sha1"), + &key)) != CRYPT_OK) { + fprintf(stderr, "\n\necc_encrypt_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + } + t2 >>= 4; + printf("ECC-%lu encrypt_key took %15llu cycles\n", x*8, t2); + ecc_free(&key); + } +} + +/* time various DH operations */ +void time_dh(void) +{ + dh_key key; + ulong64 t1, t2; + unsigned char buf[2][4096]; + unsigned long i, x, y, z; + int err; + static unsigned long sizes[] = {768/8, 1024/8, 1536/8, 2048/8, 3072/8, 4096/8, 100000}; + + for (x = sizes[i=0]; x < 100000; x = sizes[++i]) { + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + if ((err = dh_make_key(&prng, find_prng("yarrow"), x, &key)) != CRYPT_OK) { + fprintf(stderr, "\n\ndh_make_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + + if (y < 15) { + dh_free(&key); + } + } + t2 >>= 4; + printf("DH-%4lu make_key took %15llu cycles\n", x*8, t2); + + t2 = 0; + for (y = 0; y < 16; y++) { + t_start(); + t1 = t_read(); + z = sizeof(buf[1]); + if ((err = dh_encrypt_key(buf[0], 20, buf[1], &z, &prng, find_prng("yarrow"), find_hash("sha1"), + &key)) != CRYPT_OK) { + fprintf(stderr, "\n\ndh_encrypt_key says %s, wait...no it should say %s...damn you!\n", error_to_string(err), error_to_string(CRYPT_OK)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + t2 += t1; + } + t2 >>= 4; + printf("DH-%4lu encrypt_key took %15llu cycles\n", x*8, t2); + dh_free(&key); + } +} + +#define MAC_SIZE 32 +void time_macs(void) +{ + unsigned char *buf, key[16], tag[16]; + ulong64 t1, t2; + unsigned long x, z; + int err, cipher_idx, hash_idx; + + printf("\nMAC Timings (cycles/byte on %dKB blocks):\n", MAC_SIZE); + + buf = XMALLOC(MAC_SIZE*1024); + if (buf == NULL) { + fprintf(stderr, "\n\nout of heap yo\n\n"); + exit(EXIT_FAILURE); + } + + cipher_idx = find_cipher("aes"); + hash_idx = find_hash("md5"); + + yarrow_read(buf, MAC_SIZE*1024, &prng); + yarrow_read(key, 16, &prng); + + t2 = -1; + for (x = 0; x < 10000; x++) { + t_start(); + t1 = t_read(); + z = 16; + if ((err = omac_memory(cipher_idx, key, 16, buf, MAC_SIZE*1024, tag, &z)) != CRYPT_OK) { + fprintf(stderr, "\n\nomac error... %s\n", error_to_string(err)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + if (t1 < t2) t2 = t1; + } + printf("OMAC-AES\t\t%9llu\n", t2/(MAC_SIZE*1024)); + + t2 = -1; + for (x = 0; x < 10000; x++) { + t_start(); + t1 = t_read(); + z = 16; + if ((err = pmac_memory(cipher_idx, key, 16, buf, MAC_SIZE*1024, tag, &z)) != CRYPT_OK) { + fprintf(stderr, "\n\npmac error... %s\n", error_to_string(err)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + if (t1 < t2) t2 = t1; + } + printf("PMAC-AES\t\t%9llu\n", t2/(MAC_SIZE*1024)); + + t2 = -1; + for (x = 0; x < 10000; x++) { + t_start(); + t1 = t_read(); + z = 16; + if ((err = hmac_memory(hash_idx, key, 16, buf, MAC_SIZE*1024, tag, &z)) != CRYPT_OK) { + fprintf(stderr, "\n\nhmac error... %s\n", error_to_string(err)); + exit(EXIT_FAILURE); + } + t1 = t_read() - t1; + if (t1 < t2) t2 = t1; + } + printf("HMAC-MD5\t\t%9llu\n", t2/(MAC_SIZE*1024)); + + XFREE(buf); +} int main(void) { @@ -446,10 +717,14 @@ int main(void) // init_timer(); time_mult(); time_sqr(); + time_rsa(); + time_dh(); + time_ecc(); time_prng(); time_cipher(); time_keysched(); time_hash(); + time_macs(); return EXIT_SUCCESS; } diff --git a/doc/crypt.pdf b/doc/crypt.pdf index b7555f5..778017a 100644 Binary files a/doc/crypt.pdf and b/doc/crypt.pdf differ diff --git a/fortuna.c b/fortuna.c index 32cfa08..44ba044 100644 --- a/fortuna.c +++ b/fortuna.c @@ -19,22 +19,36 @@ we reseed automatically when len(pool0) >= 64 or every FORTUNA_WD calls to the r #ifdef FORTUNA +/* requries SHA256 and AES */ +#if !(defined(RIJNDAEL) && defined(SHA256)) + #error FORTUNA requires SHA256 and RIJNDAEL (AES) +#endif + +#ifndef FORTUNA_POOLS + #warning FORTUNA_POOLS was not previously defined (old headers?) + #define FORTUNA_POOLS 32 +#endif + +#if FORTUNA_POOLS < 4 || FORTUNA_POOLS > 32 + #error FORTUNA_POOLS must be in [4..32] +#endif + const struct _prng_descriptor fortuna_desc = { - "fortuna", + "fortuna", 1024, &fortuna_start, &fortuna_add_entropy, &fortuna_ready, &fortuna_read, &fortuna_done, &fortuna_export, - &fortuna_import - + &fortuna_import, + &fortuna_test }; /* update the IV */ static void fortuna_update_iv(prng_state *prng) { - int x; + int x; unsigned char *IV; /* update IV */ IV = prng->fortuna.IV; @@ -47,7 +61,7 @@ static void fortuna_update_iv(prng_state *prng) /* reseed the PRNG */ static int fortuna_reseed(prng_state *prng) { - unsigned char tmp[32]; + unsigned char tmp[MAXBLOCKSIZE]; hash_state md; int err, x; @@ -59,7 +73,7 @@ static int fortuna_reseed(prng_state *prng) return err; } - for (x = 0; x < 32; x++) { + for (x = 0; x < FORTUNA_POOLS; x++) { if (x == 0 || ((prng->fortuna.reset_cnt >> (x-1)) & 1) == 0) { /* terminate this hash */ if ((err = sha256_done(&prng->fortuna.pool[x], tmp)) != CRYPT_OK) { @@ -105,7 +119,7 @@ int fortuna_start(prng_state *prng) _ARGCHK(prng != NULL); /* initialize the pools */ - for (x = 0; x < 32; x++) { + for (x = 0; x < FORTUNA_POOLS; x++) { sha256_init(&prng->fortuna.pool[x]); } prng->fortuna.pool_idx = prng->fortuna.pool0_len = prng->fortuna.reset_cnt = @@ -144,9 +158,11 @@ int fortuna_add_entropy(const unsigned char *buf, unsigned long len, prng_state return err; } if (prng->fortuna.pool_idx == 0) { - prng->fortuna.pool0_len += len + 2; + prng->fortuna.pool0_len += len; + } + if (++(prng->fortuna.pool_idx) == FORTUNA_POOLS) { + prng->fortuna.pool_idx = 0; } - prng->fortuna.pool_idx = (prng->fortuna.pool_idx + 1) & 31; return CRYPT_OK; } @@ -160,7 +176,7 @@ unsigned long fortuna_read(unsigned char *dst, unsigned long len, prng_state *pr { unsigned char tmp[16]; int err; - unsigned long tlen, n; + unsigned long tlen; _ARGCHK(dst != NULL); _ARGCHK(prng != NULL); @@ -174,18 +190,21 @@ unsigned long fortuna_read(unsigned char *dst, unsigned long len, prng_state *pr /* now generate the blocks required */ tlen = len; - while (len > 0) { - if (len >= 16) { - /* encrypt the IV and store it */ - rijndael_ecb_encrypt(prng->fortuna.IV, dst, &prng->fortuna.skey); - dst += 16; - len -= 16; - } else { - rijndael_ecb_encrypt(prng->fortuna.IV, tmp, &prng->fortuna.skey); - XMEMCPY(dst, tmp, len); - len = 0; - } - fortuna_update_iv(prng); + + /* handle whole blocks without the extra memcpy */ + while (len >= 16) { + /* encrypt the IV and store it */ + rijndael_ecb_encrypt(prng->fortuna.IV, dst, &prng->fortuna.skey); + dst += 16; + len -= 16; + fortuna_update_iv(prng); + } + + /* left over bytes? */ + if (len > 0) { + rijndael_ecb_encrypt(prng->fortuna.IV, tmp, &prng->fortuna.skey); + XMEMCPY(dst, tmp, len); + fortuna_update_iv(prng); } /* generate new key */ @@ -201,33 +220,77 @@ unsigned long fortuna_read(unsigned char *dst, unsigned long len, prng_state *pr return tlen; } -void fortuna_done(prng_state *prng) +int fortuna_done(prng_state *prng) { + int err, x; + unsigned char tmp[32]; + _ARGCHK(prng != NULL); + + /* terminate all the hashes */ + for (x = 0; x < FORTUNA_POOLS; x++) { + if ((err = sha256_done(&(prng->fortuna.pool[x]), tmp)) != CRYPT_OK) { + return err; + } + } /* call cipher done when we invent one ;-) */ + +#ifdef CLEAN_STACK + zeromem(tmp, sizeof(tmp)); +#endif + + return CRYPT_OK; } int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng) { - int x; + int x, err; + hash_state *md; _ARGCHK(out != NULL); _ARGCHK(outlen != NULL); _ARGCHK(prng != NULL); - /* we'll write 2048 bytes for s&g's */ - if (*outlen < 2048) { + /* we'll write bytes for s&g's */ + if (*outlen < 32*FORTUNA_POOLS) { return CRYPT_BUFFER_OVERFLOW; } - for (x = 0; x < 32; x++) { - if (fortuna_read(out+x*64, 64, prng) != 64) { - return CRYPT_ERROR_READPRNG; + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + /* to emit the state we copy each pool, terminate it then hash it again so + * an attacker who sees the state can't determine the current state of the PRNG + */ + for (x = 0; x < FORTUNA_POOLS; x++) { + /* copy the PRNG */ + XMEMCPY(md, &(prng->fortuna.pool[x]), sizeof(*md)); + + /* terminate it */ + if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) { + goto __ERR; + } + + /* now hash it */ + sha256_init(md); + if ((err = sha256_process(md, out+x*32, 32)) != CRYPT_OK) { + goto __ERR; + } + if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) { + goto __ERR; } } - *outlen = 2048; + *outlen = 32*FORTUNA_POOLS; + err = CRYPT_OK; - return CRYPT_OK; +__ERR: +#ifdef CLEAN_STACK + zeromem(md, sizeof(*md)); +#endif + XFREE(md); + return err; } int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng) @@ -237,19 +300,33 @@ int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prn _ARGCHK(in != NULL); _ARGCHK(prng != NULL); - if (inlen != 2048) { + if (inlen != 32*FORTUNA_POOLS) { return CRYPT_INVALID_ARG; } if ((err = fortuna_start(prng)) != CRYPT_OK) { return err; } - for (x = 0; x < 32; x++) { - if ((err = fortuna_add_entropy(in+x*64, 64, &prng)) != CRYPT_OK) { + for (x = 0; x < FORTUNA_POOLS; x++) { + if ((err = fortuna_add_entropy(in+x*32, 32, prng)) != CRYPT_OK) { return err; } } - return fortuna_ready(&prng); + return err; +} + +int fortuna_test(void) +{ +#ifndef LTC_TEST + return CRYPT_NOP; +#else + int err; + + if ((err = sha256_test()) != CRYPT_OK) { + return err; + } + return rijndael_test(); +#endif } #endif diff --git a/hmac_done.c b/hmac_done.c index 7ded6a1..62ebe47 100644 --- a/hmac_done.c +++ b/hmac_done.c @@ -64,17 +64,17 @@ int hmac_done(hmac_state *hmac, unsigned char *hashOut, unsigned long *outlen) return CRYPT_MEM; } - // Get the hash of the first HMAC vector plus the data + /* Get the hash of the first HMAC vector plus the data */ if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) { goto __ERR; } - // Create the second HMAC vector vector for step (3) + /* Create the second HMAC vector vector for step (3) */ for(i=0; i < HMAC_BLOCKSIZE; i++) { buf[i] = hmac->key[i] ^ 0x5C; } - // Now calculate the "outer" hash for step (5), (6), and (7) + /* Now calculate the "outer" hash for step (5), (6), and (7) */ hash_descriptor[hash].init(&hmac->md); if ((err = hash_descriptor[hash].process(&hmac->md, buf, HMAC_BLOCKSIZE)) != CRYPT_OK) { goto __ERR; @@ -86,7 +86,7 @@ int hmac_done(hmac_state *hmac, unsigned char *hashOut, unsigned long *outlen) goto __ERR; } - // copy to output + /* copy to output */ for (i = 0; i < hashsize && i < *outlen; i++) { hashOut[i] = buf[i]; } diff --git a/hmac_init.c b/hmac_init.c index 2cbf001..a1cc0b5 100644 --- a/hmac_init.c +++ b/hmac_init.c @@ -68,9 +68,9 @@ int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned lon return CRYPT_MEM; } - // (1) make sure we have a large enough key + /* (1) make sure we have a large enough key */ if(keylen > HMAC_BLOCKSIZE) { - z = (unsigned long)HMAC_BLOCKSIZE; + z = HMAC_BLOCKSIZE; if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) { goto __ERR; } @@ -85,15 +85,21 @@ int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned lon } } - // Create the initial vector for step (3) + /* Create the initial vector for step (3) */ for(i=0; i < HMAC_BLOCKSIZE; i++) { buf[i] = hmac->key[i] ^ 0x36; } - // Pre-pend that to the hash data + /* Pre-pend that to the hash data */ hash_descriptor[hash].init(&hmac->md); - err = hash_descriptor[hash].process(&hmac->md, buf, HMAC_BLOCKSIZE); + if ((err = hash_descriptor[hash].process(&hmac->md, buf, HMAC_BLOCKSIZE)) != CRYPT_OK) { + goto __ERR; + } + goto done; __ERR: + /* free the key since we failed */ + XFREE(hmac->key); +done: #ifdef CLEAN_STACK zeromem(buf, HMAC_BLOCKSIZE); #endif diff --git a/hmac_test.c b/hmac_test.c index c0f2185..2b97777 100644 --- a/hmac_test.c +++ b/hmac_test.c @@ -291,6 +291,7 @@ Key First" } if(memcmp(digest, cases[i].digest, (size_t)hash_descriptor[hash].hashsize) != 0) { + failed++; #if 0 unsigned int j; printf("\nHMAC-%s test #%d:\n", cases[i].algo, cases[i].num); @@ -303,9 +304,8 @@ Key First" printf("%2x ", cases[i].digest[j]); } printf("\n"); + return CRYPT_ERROR; #endif - failed++; - //return CRYPT_ERROR; } else { /* printf("HMAC-%s test #%d: Passed\n", cases[i].algo, cases[i].num); */ } diff --git a/makefile b/makefile index b9e810f..f65aa5c 100644 --- a/makefile +++ b/makefile @@ -4,7 +4,7 @@ # Modified by Clay Culver # The version -VERSION=0.97b +VERSION=0.98 # Compiler and Linker Names #CC=gcc @@ -63,7 +63,7 @@ crypt_find_cipher_id.o crypt_find_prng.o crypt_prng_is_valid.o \ crypt_unregister_cipher.o crypt_cipher_is_valid.o crypt_find_hash.o \ crypt_hash_descriptor.o crypt_register_cipher.o crypt_unregister_hash.o \ \ -fortuna.o sprng.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ +sober128.o fortuna.o sprng.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ \ rand_prime.o is_prime.o \ \ @@ -171,10 +171,10 @@ small: library $(SMALLOBJECTS) $(CC) $(SMALLOBJECTS) $(LIBNAME) -o $(SMALL) $(WARN) x86_prof: library $(PROFS) - $(CC) $(PROFS) $(LIBNAME) -o $(PROF) + $(CC) $(PROFS) $(LIBNAME) $(EXTRALIBS) -o $(PROF) tv_gen: library $(TVS) - $(CC) $(TVS) $(LIBNAME) -o $(TV) + $(CC) $(TVS) $(LIBNAME) $(EXTRALIBS) -o $(TV) #This rule installs the library and the header files. This must be run #as root in order to have a high enough permission to write to the correct @@ -215,13 +215,21 @@ docdvi: crypt.tex echo hello > crypt.ind latex crypt > /dev/null latex crypt > /dev/null - makeindex.idx crypt + makeindex crypt.idx latex crypt > /dev/null #pretty build pretty: perl pretty.build +#for GCC 3.4+ +profiled: + make clean + make CFLAGS="$(CFLAGS) -fprofile-generate" EXTRALIBS=-lgcov x86_prof + ./x86_prof + rm *.o *.a x86_prof + make CFLAGS="$(CFLAGS) -fprofile-use" EXTRALIBS=-lgcov x86_prof + #beta beta: clean cd .. ; rm -rf crypt* libtomcrypt-$(VERSION)-beta ; mkdir libtomcrypt-$(VERSION)-beta ; \ diff --git a/makefile.cygwin_dll b/makefile.cygwin_dll index 0dece2f..5683dc6 100644 --- a/makefile.cygwin_dll +++ b/makefile.cygwin_dll @@ -28,7 +28,7 @@ crypt_find_cipher_id.o crypt_find_prng.o crypt_prng_is_valid.o \ crypt_unregister_cipher.o crypt_cipher_is_valid.o crypt_find_hash.o \ crypt_hash_descriptor.o crypt_register_cipher.o crypt_unregister_hash.o \ \ -sprng.o fortuna.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ +sprng.o fortuna.o sober128.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ \ rand_prime.o is_prime.o \ \ diff --git a/makefile.icc b/makefile.icc index 4019e2f..f73a323 100644 --- a/makefile.icc +++ b/makefile.icc @@ -79,7 +79,7 @@ crypt_find_cipher_id.o crypt_find_prng.o crypt_prng_is_valid.o \ crypt_unregister_cipher.o crypt_cipher_is_valid.o crypt_find_hash.o \ crypt_hash_descriptor.o crypt_register_cipher.o crypt_unregister_hash.o \ \ -sprng.o fortuna.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ +sober128.o fortuna.o sprng.o yarrow.o rc4.o rng_get_bytes.o rng_make_prng.o \ \ rand_prime.o is_prime.o \ \ @@ -188,6 +188,13 @@ x86_prof: library $(PROFS) tv_gen: library $(TVS) $(CC) $(TVS) $(LIBNAME) -o $(TV) +profiled: + make clean + make CFLAGS="$(CFLAGS) -prof_gen" x86_prof + ./x86_prof + rm *.o *.a x86_prof + make CFLAGS="$(CFLAGS) -prof_use" x86_prof + #This rule installs the library and the header files. This must be run #as root in order to have a high enough permission to write to the correct diff --git a/makefile.msvc b/makefile.msvc index 6fd60ef..6209827 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -18,7 +18,7 @@ crypt_find_cipher_id.obj crypt_find_prng.obj crypt_prng_is_valid.obj crypt_unregister_cipher.obj crypt_cipher_is_valid.obj crypt_find_hash.obj \ crypt_hash_descriptor.obj crypt_register_cipher.obj crypt_unregister_hash.obj \ \ -sprng.obj fortuna.obj yarrow.obj rc4.obj rng_get_bytes.obj rng_make_prng.obj \ +sprng.obj fortuna.obj sober128.obj yarrow.obj rc4.obj rng_get_bytes.obj rng_make_prng.obj \ \ rand_prime.obj is_prime.obj \ \ diff --git a/mpi.c b/mpi.c index ae120d8..dc99927 100644 --- a/mpi.c +++ b/mpi.c @@ -4703,7 +4703,7 @@ int mp_mul (mp_int * a, mp_int * b, mp_int * c) res = s_mp_mul (a, b, c); } } - c->sign = neg; + c->sign = (c->used == 0) ? MP_ZPOS : neg; return res; } diff --git a/mycrypt_argchk.h b/mycrypt_argchk.h index 46b0e30..090d4d0 100644 --- a/mycrypt_argchk.h +++ b/mycrypt_argchk.h @@ -1,6 +1,4 @@ /* Defines the _ARGCHK macro used within the library */ - -/* ch1-01-1 */ /* ARGTYPE is defined in mycrypt_cfg.h */ #if ARGTYPE == 0 @@ -20,5 +18,4 @@ #define _ARGCHK(x) #endif -/* ch1-01-1 */ diff --git a/mycrypt_cfg.h b/mycrypt_cfg.h index d24824e..4d40c70 100644 --- a/mycrypt_cfg.h +++ b/mycrypt_cfg.h @@ -20,12 +20,16 @@ void XFREE(void *p); void *XMEMCPY(void *dest, const void *src, size_t n); int XMEMCMP(const void *s1, const void *s2, size_t n); -/* ch1-01-1 */ /* type of argument checking, 0=default, 1=fatal and 2=none */ #define ARGTYPE 0 -/* ch1-01-1 */ -/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code */ +/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code + * + * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. + * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST** + * use the portable [slower] macros. + */ + /* detect x86-32 machines somewhat */ #if defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__))) #define ENDIAN_LITTLE @@ -52,12 +56,6 @@ int XMEMCMP(const void *s1, const void *s2, size_t n); #define ENDIAN_NEUTRAL #endif -#ifdef YARROW - #ifndef CTR - #error YARROW requires CTR chaining mode to be defined! - #endif -#endif - /* packet code */ #if defined(MRSA) || defined(MDH) || defined(MECC) #define PACKET diff --git a/mycrypt_custom.h b/mycrypt_custom.h index 1e00830..c71579f 100644 --- a/mycrypt_custom.h +++ b/mycrypt_custom.h @@ -82,18 +82,29 @@ /* Various tidbits of modern neatoness */ #define BASE64 + +/* Yarrow */ #define YARROW // which descriptor of AES to use? // 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] #define YARROW_AES 0 + +#if defined(YARROW) && !defined(CTR) + #error YARROW requires CTR chaining mode to be defined! +#endif + #define SPRNG #define RC4 -/* Fortuna */ +/* Fortuna PRNG */ #define FORTUNA /* reseed every N calls to the read function */ -#define FORTUNA_WD 1024 +#define FORTUNA_WD 10 +/* number of pools (4..32) can save a bit of ram by lowering the count */ +#define FORTUNA_POOLS 32 +/* Greg's SOBER128 PRNG ;-0 */ +#define SOBER128 #define DEVRANDOM #define TRY_URANDOM_FIRST @@ -139,6 +150,12 @@ /* Use SSE2 optimizations in LTM? Requires GCC or ICC and a P4 or K8 processor */ // #define LTMSSE +/* prevents the code from being "unportable" at least to non i386 platforms */ +#if defined(LTMSSE) && !( (defined(__GNUC__) && defined(__i386__)) || defined(INTEL_CC)) + #warning LTMSSE is only available for GNU CC (i386) or Intel CC + #undef LTMSSE +#endif + /* PKCS #1 and #5 stuff */ #define PKCS_1 #define PKCS_5 diff --git a/mycrypt_prng.h b/mycrypt_prng.h index 3ded2cc..76d342e 100644 --- a/mycrypt_prng.h +++ b/mycrypt_prng.h @@ -11,7 +11,7 @@ struct rc4_prng { }; struct fortuna_prng { - hash_state pool[32]; /* the 32 pools */ + hash_state pool[FORTUNA_POOLS]; /* the pools */ symmetric_key skey; @@ -25,21 +25,44 @@ struct fortuna_prng { ulong64 reset_cnt; /* number of times we have reset */ }; +struct sober128_prng { + ulong32 R[17], /* Working storage for the shift register */ + initR[17], /* saved register contents */ + konst, /* key dependent constant */ + sbuf; /* partial word encryption buffer */ + + int nbuf, /* number of part-word stream bits buffered */ + flag, /* first add_entropy call or not? */ + set; /* did we call add_entropy to set key? */ + +}; + typedef union Prng_state { +#ifdef YARROW struct yarrow_prng yarrow; +#endif +#ifdef RC4 struct rc4_prng rc4; +#endif +#ifdef FORTUNA struct fortuna_prng fortuna; +#endif +#ifdef SOBER128 + struct sober128_prng sober128; +#endif } prng_state; extern struct _prng_descriptor { char *name; + int export_size; /* size in bytes of exported state */ int (*start)(prng_state *); int (*add_entropy)(const unsigned char *, unsigned long, prng_state *); int (*ready)(prng_state *); unsigned long (*read)(unsigned char *, unsigned long, prng_state *); - void (*done)(prng_state *); + int (*done)(prng_state *); int (*export)(unsigned char *, unsigned long *, prng_state *); int (*import)(const unsigned char *, unsigned long, prng_state *); + int (*test)(void); } prng_descriptor[]; #ifdef YARROW @@ -47,9 +70,10 @@ extern struct _prng_descriptor { int yarrow_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng); int yarrow_ready(prng_state *prng); unsigned long yarrow_read(unsigned char *buf, unsigned long len, prng_state *prng); - void yarrow_done(prng_state *prng); + int yarrow_done(prng_state *prng); int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); + int yarrow_test(void); extern const struct _prng_descriptor yarrow_desc; #endif @@ -58,9 +82,10 @@ extern struct _prng_descriptor { int fortuna_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng); int fortuna_ready(prng_state *prng); unsigned long fortuna_read(unsigned char *buf, unsigned long len, prng_state *prng); - void fortuna_done(prng_state *prng); + int fortuna_done(prng_state *prng); int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); + int fortuna_test(void); extern const struct _prng_descriptor fortuna_desc; #endif @@ -69,9 +94,10 @@ extern struct _prng_descriptor { int rc4_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng); int rc4_ready(prng_state *prng); unsigned long rc4_read(unsigned char *buf, unsigned long len, prng_state *prng); - void rc4_done(prng_state *prng); + int rc4_done(prng_state *prng); int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); + int rc4_test(void); extern const struct _prng_descriptor rc4_desc; #endif @@ -80,26 +106,36 @@ extern struct _prng_descriptor { int sprng_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng); int sprng_ready(prng_state *prng); unsigned long sprng_read(unsigned char *buf, unsigned long len, prng_state *prng); - void sprng_done(prng_state *prng); + int sprng_done(prng_state *prng); int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); + int sprng_test(void); extern const struct _prng_descriptor sprng_desc; #endif +#ifdef SOBER128 + int sober128_start(prng_state *prng); + int sober128_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng); + int sober128_ready(prng_state *prng); + unsigned long sober128_read(unsigned char *buf, unsigned long len, prng_state *prng); + int sober128_done(prng_state *prng); + int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); + int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); + int sober128_test(void); + extern const struct _prng_descriptor sober128_desc; +#endif + int find_prng(const char *name); int register_prng(const struct _prng_descriptor *prng); int unregister_prng(const struct _prng_descriptor *prng); int prng_is_valid(int idx); - /* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this * might not work on all platforms as planned */ -/* ch2-02-1 */ - unsigned long rng_get_bytes(unsigned char *buf, +unsigned long rng_get_bytes(unsigned char *buf, unsigned long len, void (*callback)(void)); -/* ch2-02-1 */ - int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); +int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); diff --git a/noekeon.c b/noekeon.c index 7c6e87f..ef4796c 100644 --- a/noekeon.c +++ b/noekeon.c @@ -33,7 +33,6 @@ static const ulong32 RC[] = { 0x000000d4UL }; - #define kTHETA(a, b, c, d) \ temp = a^c; temp = temp ^ ROL(temp, 8) ^ ROR(temp, 8); \ b ^= temp; d ^= temp; \ @@ -97,9 +96,7 @@ void noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_k #endif { ulong32 a,b,c,d,temp; -#ifdef SMALL_CODE int r; -#endif _ARGCHK(key != NULL); _ARGCHK(pt != NULL); @@ -115,16 +112,9 @@ void noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_k GAMMA(a,b,c,d); \ PI2(a,b,c,d); -#ifdef SMALL_CODE for (r = 0; r < 16; ++r) { ROUND(r); } -#else - ROUND( 0); ROUND( 1); ROUND( 2); ROUND( 3); - ROUND( 4); ROUND( 5); ROUND( 6); ROUND( 7); - ROUND( 8); ROUND( 9); ROUND(10); ROUND(11); - ROUND(12); ROUND(13); ROUND(14); ROUND(15); -#endif #undef ROUND @@ -150,9 +140,7 @@ void noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_k #endif { ulong32 a,b,c,d, temp; -#ifdef SMALL_CODE int r; -#endif _ARGCHK(key != NULL); _ARGCHK(pt != NULL); @@ -169,17 +157,10 @@ void noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_k GAMMA(a,b,c,d); \ PI2(a,b,c,d); -#ifdef SMALL_CODE for (r = 16; r > 0; --r) { ROUND(r); } -#else - ROUND(16); ROUND(15); ROUND(14); ROUND(13); - ROUND(12); ROUND(11); ROUND(10); ROUND( 9); - ROUND( 8); ROUND( 7); ROUND( 6); ROUND( 5); - ROUND( 4); ROUND( 3); ROUND( 2); ROUND( 1); -#endif - + #undef ROUND THETA(key->noekeon.dK, a,b,c,d); diff --git a/notes/tech0004.txt b/notes/tech0004.txt new file mode 100644 index 0000000..2acd378 --- /dev/null +++ b/notes/tech0004.txt @@ -0,0 +1,91 @@ +Tech Note 0004 +Using Yarrow, Fortuna and SOBER-128 +Tom St Denis + +Introduction +------------ + +This tech note explains how to use three of the more useful pseudo random number generators and their +own little "issues". While all of the PRNGs have the same API and are roughly used in the same +manner their effectiveness really depends on the user knowing how they work. + + +Yarrow +------ + +Yarrow is by far the simplest of the PRNGs. It gathers bits of entropy by hashing the pool state +plus the additional bits storing the message digest back in the pool. E.g. + +pool = hash(pool || newbits) + +Simply dump bits into the PRNG via yarrow_add_entropy() and call yarrow_ready() when you want to +put them to use. This PRNG while simple is not entirely safe. An attacker who learns the state +of the pool and can control future events can control the PRNG. This requires an active attacker but +isn't entire impossible. + +The pool is then used as a key for a cipher that is used in CTR mode. + +Yarrow is mostly meant for short-term programs [e.g. like file utils]. This particular implementation +is not meant for long-term usage. + +Fortuna +------- + +Fortuna was designed by Niels Fergusson and Bruce Schneier [Bruce is also the guy who invented Yarrow]. It +operates on a more defensive level than Yarrow. Instead of 1 entropy pool it has 32 and the new entropy +is spread [round robin] in all of the pools. + +That is, each call to fortuna_add_entropy() puts the bits in the next [in the sequenece] pool of entropy. +Effective bits are added to the pool by sending them through a hash [but not terminating the hash]. + +Here's the main catch though. When the PRNG must be reseeded [so that you can extract bits from it] only +certain pools are used. More precisely the i'th pool is used every 2**i'th reseeding. For example, pool[0] +is always used. pool[1] is used every second reseeding, pool[2] every fourth. + +The pools are hashed together along with the current key and the result is the new key for a cipher which +operates in CTR mode [more about that in a sec]. + +Now this may seem odd at first however there is a good reason behind it. An attacker who learns pool[0] won't +strictly know the other pools. So the recovery rate of is not 0. In fact pool[0] can be completely +compromised and the PRNG will still eventually recover. The value FORTUNA_WD is the "WatchDog" counter. +Every FORTUNA_WD calls to fortuna_read will invoke the reseed operation. By default this is set to 10 which +means after 10 calls the PRNG will reseed itself. + +The pools are combined with the running cipher key [256 bits] so that a cipher in CTR mode can produce +the stream. Unlike Yarrow the cipher is re-keyed after every call to fortuna_read() [so one big call +would be faster than many smaller calls]. This prevents too much data being encrypted under the same +key [and mitigates a flaw in CTR mode that the same block can't be emitted twice under the same key]. + +Fortuna is really meant for a kernel-level PRNG. The more sources [and often] you feed into it the +healthier it will be. It's also meant to be used for long term purposes. Since it can recover from +compromises it is harder to control it. + +SOBER-128 +------ + +SOBER-128 is actually a stream cipher but like most ciphers can easily be modelled in the context of a PRNG. +This PRNG is extremely fast [4 cycles/byte on a P4] and was designed by a well known cryptographer [Greg Rose]. + +SOBER-128 doesn't really "act" like the other two PRNGs. It's meant to be seeded once and then read as +required. In such a sense it isn't a "system PRNG" but useful short term purposes. In particular +the sober128_read() function actually XORs against the input buffer you specify. This allows the +read() function to be used as an "encrypt" function as well. + +You can only key SOBER-128 once [by calling sober128_add_entropy()]. Once it it is keyed subsequent +calls to add_entropy() will be considered a "re-IV" operation. Changing the IV allows you to use same +initial key and not produce the same output stream. It also lets you differentiate packets. E.g. each +packet has it's own IV. + +All inputs to sober128_add_entropy() must have a length that is a multiple of four. + +Overall +------- + +Since SOBER-128 is *much* faster than the other two PRNGs a good setup would be to use Fortuna as your +system-wide PRNG and use SOBER-128 [key'ed from Fortuna] for encrypting streams or as a PRNG for +simulations. + +Yarrow is still a good candidate but only for "short lived" programs. However, since Fortuna is faster +[by about 10 cycles/byte on a P4] I'd use Fortuna anyways... + +Tom \ No newline at end of file diff --git a/pretty.build b/pretty.build index 0046d34..3eac213 100644 --- a/pretty.build +++ b/pretty.build @@ -21,8 +21,9 @@ foreach my $filename (glob "*.c") { if (!($filename =~ "sha384.c")) { if (!($filename =~ "dh_sys.c")) { if (!($filename =~ "ecc_sys.c")) { + if (!($filename =~ "sober128tab.c")) { ++$count; - }}}}}}} + }}}}}}}} } print "Source files to build: $count\nBuilding...\n"; my $i = 0; @@ -36,6 +37,7 @@ foreach my $filename (glob "*.c") { if (!($filename =~ "sha384.c")) { if (!($filename =~ "dh_sys.c")) { if (!($filename =~ "ecc_sys.c")) { + if (!($filename =~ "sober128tab.c")) { printf("Building %3.2f%%, ", (++$i/$count)*100.0); if ($i % 4 == 0) { print "/, "; } if ($i % 4 == 1) { print "-, "; } @@ -71,7 +73,7 @@ foreach my $filename (glob "*.c") { my $delay = time - $starttime; $rate = $i/$delay; } - }}}}}}} + }}}}}}}} } # finish building the library diff --git a/rc4.c b/rc4.c index b340204..e8139ad 100644 --- a/rc4.c +++ b/rc4.c @@ -14,14 +14,15 @@ const struct _prng_descriptor rc4_desc = { - "rc4", + "rc4", 32, &rc4_start, &rc4_add_entropy, &rc4_ready, &rc4_read, &rc4_done, &rc4_export, - &rc4_import + &rc4_import, + &rc4_test }; int rc4_start(prng_state *prng) @@ -36,11 +37,18 @@ int rc4_start(prng_state *prng) int rc4_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng) { - _ARGCHK(buf != NULL); + _ARGCHK(buf != NULL); _ARGCHK(prng != NULL); - + + /* trim as required */ if (prng->rc4.x + len > 256) { - return CRYPT_INVALID_KEYSIZE; + if (prng->rc4.x == 256) { + /* I can't possibly accept another byte, ok maybe a mint wafer... */ + return CRYPT_OK; + } else { + /* only accept part of it */ + len = 256 - prng->rc4.x; + } } while (len--) { @@ -53,26 +61,30 @@ int rc4_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prn int rc4_ready(prng_state *prng) { - unsigned char key[256], tmp; - int keylen, x, y; + unsigned char key[256], tmp, *s; + int keylen, x, y, j; _ARGCHK(prng != NULL); /* extract the key */ - XMEMCPY(key, prng->rc4.buf, 256); + s = prng->rc4.buf; + XMEMCPY(key, s, 256); keylen = prng->rc4.x; /* make RC4 perm and shuffle */ for (x = 0; x < 256; x++) { - prng->rc4.buf[x] = x; + s[x] = x; } - for (x = y = 0; x < 256; x++) { - y = (y + prng->rc4.buf[x] + key[x % keylen]) & 255; - tmp = prng->rc4.buf[x]; prng->rc4.buf[x] = prng->rc4.buf[y]; prng->rc4.buf[y] = tmp; + for (j = x = y = 0; x < 256; x++) { + y = (y + prng->rc4.buf[x] + key[j++]) & 255; + if (j == keylen) { + j = 0; + } + tmp = s[x]; s[x] = s[y]; s[y] = tmp; } - prng->rc4.x = x; - prng->rc4.y = y; + prng->rc4.x = 0; + prng->rc4.y = 0; #ifdef CLEAN_STACK zeromem(key, sizeof(key)); @@ -83,8 +95,7 @@ int rc4_ready(prng_state *prng) unsigned long rc4_read(unsigned char *buf, unsigned long len, prng_state *prng) { - int x, y; - unsigned char *s, tmp; + unsigned char x, y, *s, tmp; unsigned long n; _ARGCHK(buf != NULL); @@ -99,31 +110,99 @@ unsigned long rc4_read(unsigned char *buf, unsigned long len, prng_state *prng) y = (y + s[x]) & 255; tmp = s[x]; s[x] = s[y]; s[y] = tmp; tmp = (s[x] + s[y]) & 255; - *buf++ = s[tmp]; + *buf++ ^= s[tmp]; } prng->rc4.x = x; prng->rc4.y = y; return n; } -void rc4_done(prng_state *prng) +int rc4_done(prng_state *prng) { _ARGCHK(prng != NULL); + return CRYPT_OK; } int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng) { _ARGCHK(outlen != NULL); + _ARGCHK(out != NULL); + _ARGCHK(prng != NULL); + + if (*outlen < 32) { + return CRYPT_BUFFER_OVERFLOW; + } + + if (rc4_read(out, 32, prng) != 32) { + return CRYPT_ERROR_READPRNG; + } + *outlen = 32; - *outlen = 0; return CRYPT_OK; } int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng) { - return CRYPT_OK; + int err; + _ARGCHK(in != NULL); + _ARGCHK(prng != NULL); + + if (inlen != 32) { + return CRYPT_INVALID_ARG; + } + + if ((err = rc4_start(prng)) != CRYPT_OK) { + return err; + } + return rc4_add_entropy(in, 32, prng); } +int rc4_test(void) +{ +#ifndef LTC_TEST + return CRYPT_NOP; +#else + static const struct { + unsigned char key[8], pt[8], ct[8]; + } tests[] = { +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + { 0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 } +} +}; + prng_state prng; + unsigned char dst[8]; + int err, x; + + for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) { + if ((err = rc4_start(&prng)) != CRYPT_OK) { + return err; + } + if ((err = rc4_add_entropy(tests[x].key, 8, &prng)) != CRYPT_OK) { + return err; + } + if ((err = rc4_ready(&prng)) != CRYPT_OK) { + return err; + } + XMEMCPY(dst, tests[x].pt, 8); + if (rc4_read(dst, 8, &prng) != 8) { + return CRYPT_ERROR_READPRNG; + } + rc4_done(&prng); + if (memcmp(dst, tests[x].ct, 8)) { +#if 0 + int y; + printf("\n\nRC4 failed, I got:\n"); + for (y = 0; y < 8; y++) printf("%02x ", dst[y]); + printf("\n"); +#endif + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; +#endif +} #endif diff --git a/rsa_decrypt_key.c b/rsa_decrypt_key.c index ccdc8b1..47a922c 100644 --- a/rsa_decrypt_key.c +++ b/rsa_decrypt_key.c @@ -13,7 +13,7 @@ #ifdef MRSA -/* decrypt then OAEP depad */ +/* (PKCS #1 v2.0) decrypt then OAEP depad */ int rsa_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *outkey, unsigned long *keylen, const unsigned char *lparam, unsigned long lparamlen, @@ -30,6 +30,9 @@ int rsa_decrypt_key(const unsigned char *in, unsigned long inlen, _ARGCHK(key != NULL); _ARGCHK(res != NULL); + /* default to invalid */ + *res = 0; + /* valid hash/prng ? */ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { return err; diff --git a/rsa_encrypt_key.c b/rsa_encrypt_key.c index fc5076b..c0c8400 100644 --- a/rsa_encrypt_key.c +++ b/rsa_encrypt_key.c @@ -13,7 +13,7 @@ #ifdef MRSA -/* OAEP pad then encrypt */ +/* (PKCS #1 v2.0) OAEP pad then encrypt */ int rsa_encrypt_key(const unsigned char *inkey, unsigned long inlen, unsigned char *outkey, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, diff --git a/rsa_export.c b/rsa_export.c index cedd26d..a5c20c0 100644 --- a/rsa_export.c +++ b/rsa_export.c @@ -13,6 +13,7 @@ #ifdef MRSA +/* Export an RSA key */ int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key) { unsigned long y, z; diff --git a/rsa_exptmod.c b/rsa_exptmod.c index 1f6daae..e94fad7 100644 --- a/rsa_exptmod.c +++ b/rsa_exptmod.c @@ -14,6 +14,7 @@ #ifdef MRSA +/* compute an RSA modular exponentiation */ int rsa_exptmod(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, prng_state *prng, int prng_idx, diff --git a/rsa_make_key.c b/rsa_make_key.c index e96bea3..ded68cf 100644 --- a/rsa_make_key.c +++ b/rsa_make_key.c @@ -69,7 +69,7 @@ int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) if ((err = mp_invmod(&key->e, &tmp1, &key->d)) != MP_OKAY) { goto error2; } /* key->d = 1/e mod lcm(p-1,q-1) */ if ((err = mp_mul(&p, &q, &key->N)) != MP_OKAY) { goto error2; } /* key->N = pq */ -/* optimize for CRT now */ + /* optimize for CRT now */ /* find d mod q-1 and d mod p-1 */ if ((err = mp_sub_d(&p, 1, &tmp1)) != MP_OKAY) { goto error2; } /* tmp1 = q-1 */ if ((err = mp_sub_d(&q, 1, &tmp2)) != MP_OKAY) { goto error2; } /* tmp2 = p-1 */ diff --git a/rsa_sign_hash.c b/rsa_sign_hash.c index decac6c..a5d2f95 100644 --- a/rsa_sign_hash.c +++ b/rsa_sign_hash.c @@ -13,7 +13,7 @@ #ifdef MRSA -/* PSS pad then sign */ +/* (PKCS #1, v2.0) PSS pad then sign */ int rsa_sign_hash(const unsigned char *msghash, unsigned long msghashlen, unsigned char *sig, unsigned long *siglen, prng_state *prng, int prng_idx, diff --git a/rsa_v15_decrypt_key.c b/rsa_v15_decrypt_key.c index fa187ba..e8c496b 100644 --- a/rsa_v15_decrypt_key.c +++ b/rsa_v15_decrypt_key.c @@ -26,6 +26,9 @@ int rsa_v15_decrypt_key(const unsigned char *in, unsigned long inlen, _ARGCHK(outkey != NULL); _ARGCHK(key != NULL); _ARGCHK(res != NULL); + + /* default to invalid */ + *res = 0; /* valid prng ? */ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { diff --git a/rsa_v15_verify_hash.c b/rsa_v15_verify_hash.c index 80ca72a..6f6ee99 100644 --- a/rsa_v15_verify_hash.c +++ b/rsa_v15_verify_hash.c @@ -13,7 +13,7 @@ #ifdef MRSA -/* design then PKCS v1.5 depad */ +/* de-sign then PKCS v1.5 depad */ int rsa_v15_verify_hash(const unsigned char *sig, unsigned long siglen, const unsigned char *msghash, unsigned long msghashlen, prng_state *prng, int prng_idx, @@ -28,6 +28,9 @@ int rsa_v15_verify_hash(const unsigned char *sig, unsigned long siglen, _ARGCHK(sig != NULL); _ARGCHK(stat != NULL); _ARGCHK(key != NULL); + + /* default to invalid */ + *stat = 0; /* valid hash ? */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { diff --git a/rsa_verify_hash.c b/rsa_verify_hash.c index 5e3664b..1da1af3 100644 --- a/rsa_verify_hash.c +++ b/rsa_verify_hash.c @@ -13,7 +13,7 @@ #ifdef MRSA -/* design then PSS depad */ +/* (PKCS #1, v2.0) de-sign then PSS depad */ int rsa_verify_hash(const unsigned char *sig, unsigned long siglen, const unsigned char *msghash, unsigned long msghashlen, prng_state *prng, int prng_idx, @@ -28,6 +28,9 @@ int rsa_verify_hash(const unsigned char *sig, unsigned long siglen, _ARGCHK(sig != NULL); _ARGCHK(stat != NULL); _ARGCHK(key != NULL); + + /* default to invalid */ + *stat = 0; /* valid hash ? */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { diff --git a/sober128.c b/sober128.c new file mode 100644 index 0000000..dee7471 --- /dev/null +++ b/sober128.c @@ -0,0 +1,444 @@ +/* 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. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtomcrypt.org + */ +#include "mycrypt.h" + +/* Implementation of SOBER-128 by Tom St Denis. + * Based on s128fast.c reference code supplied by Greg Rose of QUALCOMM. + */ + +#ifdef SOBER128 + +#include "sober128tab.c" + +const struct _prng_descriptor sober128_desc = +{ + "sober128", 64, + &sober128_start, + &sober128_add_entropy, + &sober128_ready, + &sober128_read, + &sober128_done, + &sober128_export, + &sober128_import, + &sober128_test +}; + +/* don't change these... */ +#define N 17 +#define FOLD N /* how many iterations of folding to do */ +#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 */ + +#define B(x,i) ((unsigned char)(((x) >> (8*i)) & 0xFF)) + +static ulong32 BYTE2WORD(unsigned char *b) +{ + ulong32 t; + LOAD32L(t, b); + return t; +} + +#define WORD2BYTE(w, b) STORE32L(b, w) + +static void XORWORD(ulong32 w, unsigned char *b) +{ + ulong32 t; + LOAD32L(t, b); + t ^= w; + STORE32L(t, b); +} + +/* 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(c,z) \ +{ \ + t = c->R[OFF(z,0)] + c->R[OFF(z,16)]; \ + t ^= Sbox[(t >> 24) & 0xFF]; \ + t = ROR(t, 8); \ + t = ((t + c->R[OFF(z,1)]) ^ c->konst) + c->R[OFF(z,6)]; \ + t ^= Sbox[(t >> 24) & 0xFF]; \ + t = t + c->R[OFF(z,13)]; \ +} + +static ulong32 nltap(struct sober128_prng *c) +{ + ulong32 t; + NLFUNC(c, 0); + return t; +} + +/* initialise to known state + */ +int sober128_start(prng_state *prng) +{ + int i; + struct sober128_prng *c; + + _ARGCHK(prng != NULL); + + c = &(prng->sober128); + + /* Register initialised to Fibonacci numbers */ + c->R[0] = 1; + c->R[1] = 1; + for (i = 2; i < N; ++i) { + c->R[i] = c->R[i-1] + c->R[i-2]; + } + c->konst = INITKONST; + + /* next add_entropy will be the key */ + c->flag = 1; + c->set = 0; + + return CRYPT_OK; +} + +/* Save the current register state + */ +static void s128_savestate(struct sober128_prng *c) +{ + int i; + for (i = 0; i < N; ++i) { + c->initR[i] = c->R[i]; + } +} + +/* initialise to previously saved register state + */ +static void s128_reloadstate(struct sober128_prng *c) +{ + int i; + + for (i = 0; i < N; ++i) { + c->R[i] = c->initR[i]; + } +} + +/* Initialise "konst" + */ +static void s128_genkonst(struct sober128_prng *c) +{ + ulong32 newkonst; + + do { + cycle(c->R); + newkonst = nltap(c); + } while ((newkonst & 0xFF000000) == 0); + c->konst = newkonst; +} + +/* Load key material into the register + */ +#define ADDKEY(k) \ + c->R[KEYP] += (k); + +#define XORNL(nl) \ + c->R[FOLDP] ^= (nl); + +/* nonlinear diffusion of register for key */ +#define DROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); c->R[OFF((z+1),FOLDP)] ^= t; +static void s128_diffuse(struct sober128_prng *c) +{ + 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); +} + +int sober128_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng) +{ + struct sober128_prng *c; + ulong32 i, k; + + _ARGCHK(buf != NULL); + _ARGCHK(prng != NULL); + c = &(prng->sober128); + + if (c->flag == 1) { + /* this is the first call to the add_entropy so this input is the key */ + /* len must be multiple of 4 bytes */ + if ((len & 3) != 0) { + return CRYPT_INVALID_KEYSIZE; + } + + for (i = 0; i < len; i += 4) { + k = BYTE2WORD((unsigned char *)&buf[i]); + ADDKEY(k); + cycle(c->R); + XORNL(nltap(c)); + } + + /* also fold in the length of the key */ + ADDKEY(len); + + /* now diffuse */ + s128_diffuse(c); + + s128_genkonst(c); + s128_savestate(c); + c->nbuf = 0; + c->flag = 0; + c->set = 1; + } else { + /* ok we are adding an IV then... */ + s128_reloadstate(c); + + /* len must be multiple of 4 bytes */ + if ((len & 3) != 0) { + return CRYPT_INVALID_KEYSIZE; + } + + for (i = 0; i < len; i += 4) { + k = BYTE2WORD((unsigned char *)&buf[i]); + ADDKEY(k); + cycle(c->R); + XORNL(nltap(c)); + } + + /* also fold in the length of the key */ + ADDKEY(len); + + /* now diffuse */ + s128_diffuse(c); + c->nbuf = 0; + } + + return CRYPT_OK; +} + +int sober128_ready(prng_state *prng) +{ + return prng->sober128.set == 1 ? CRYPT_OK : CRYPT_ERROR; +} + +/* XOR pseudo-random bytes into buffer + */ +#define SROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); XORWORD(t, buf+(z*4)); + +unsigned long sober128_read(unsigned char *buf, unsigned long nbytes, prng_state *prng) +{ + struct sober128_prng *c; + ulong32 t, tlen; + + _ARGCHK(buf != NULL); + _ARGCHK(prng != NULL); + + c = &(prng->sober128); + t = 0; + tlen = nbytes; + + /* handle any previously buffered bytes */ + while (c->nbuf != 0 && nbytes != 0) { + *buf++ ^= c->sbuf & 0xFF; + c->sbuf >>= 8; + c->nbuf -= 8; + --nbytes; + } + +#ifndef SMALL_CODE + /* do lots at a time, if there's enough to do */ + while (nbytes >= 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); + buf += 4*N; + nbytes -= 4*N; + } +#endif + + /* do small or odd size buffers the slow way */ + while (4 <= nbytes) { + cycle(c->R); + t = nltap(c); + XORWORD(t, buf); + buf += 4; + nbytes -= 4; + } + + /* handle any trailing bytes */ + if (nbytes != 0) { + cycle(c->R); + c->sbuf = nltap(c); + c->nbuf = 32; + while (c->nbuf != 0 && nbytes != 0) { + *buf++ ^= c->sbuf & 0xFF; + c->sbuf >>= 8; + c->nbuf -= 8; + --nbytes; + } + } + + return tlen; +} + +int sober128_done(prng_state *prng) +{ + _ARGCHK(prng != NULL); + return CRYPT_OK; +} + +int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng) +{ + _ARGCHK(outlen != NULL); + _ARGCHK(out != NULL); + _ARGCHK(prng != NULL); + + if (*outlen < 64) { + return CRYPT_BUFFER_OVERFLOW; + } + + if (sober128_read(out, 64, prng) != 64) { + return CRYPT_ERROR_READPRNG; + } + *outlen = 64; + + return CRYPT_OK; +} + +int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng) +{ + int err; + _ARGCHK(in != NULL); + _ARGCHK(prng != NULL); + + if (inlen != 64) { + return CRYPT_INVALID_ARG; + } + + if ((err = sober128_start(prng)) != CRYPT_OK) { + return err; + } + if ((err = sober128_add_entropy(in, 64, prng)) != CRYPT_OK) { + return err; + } + return sober128_ready(prng); +} + +int sober128_test(void) +{ +#ifndef LTC_TEST + return CRYPT_NOP; +#else + static const struct { + int keylen, ivlen, len; + unsigned char key[16], iv[4], out[20]; + } tests[] = { + +{ + 16, 4, 20, + + /* key */ + { 't', 'e', 's', 't', ' ', 'k', 'e', 'y', + ' ', '1', '2', '8', 'b', 'i', 't', 's' }, + + /* IV */ + { 0x00, 0x00, 0x00, 0x0 }, + + /* expected output */ + { 0x43, 0x50, 0x0c, 0xcf, 0x89, 0x91, 0x9f, 0x1d, + 0xaa, 0x37, 0x74, 0x95, 0xf4, 0xb4, 0x58, 0xc2, + 0x40, 0x37, 0x8b, 0xbb } +} + +}; + prng_state prng; + unsigned char dst[20]; + int err, x; + + for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) { + if ((err = sober128_start(&prng)) != CRYPT_OK) { + return err; + } + if ((err = sober128_add_entropy(tests[x].key, tests[x].keylen, &prng)) != CRYPT_OK) { + return err; + } + /* add IV */ + if ((err = sober128_add_entropy(tests[x].iv, tests[x].ivlen, &prng)) != CRYPT_OK) { + return err; + } + + /* ready up */ + if ((err = sober128_ready(&prng)) != CRYPT_OK) { + return err; + } + memset(dst, 0, tests[x].len); + if (sober128_read(dst, tests[x].len, &prng) != (unsigned long)tests[x].len) { + return CRYPT_ERROR_READPRNG; + } + sober128_done(&prng); + if (memcmp(dst, tests[x].out, tests[x].len)) { +#if 0 + printf("\n\nSOBER128 failed, I got:\n"); + for (y = 0; y < tests[x].len; y++) printf("%02x ", dst[y]); + printf("\n"); +#endif + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; +#endif +}; + +#endif + diff --git a/sober128tab.c b/sober128tab.c new file mode 100644 index 0000000..49b0bb3 --- /dev/null +++ b/sober128tab.c @@ -0,0 +1,154 @@ +/* $ID$ */ +/* @(#)TuringMultab.h 1.3 (QUALCOMM) 02/09/03 */ +/* Multiplication table for Turing using 0xD02B4367 */ +static const ulong32 Multab[256] = { + 0x00000000, 0xD02B4367, 0xED5686CE, 0x3D7DC5A9, + 0x97AC41D1, 0x478702B6, 0x7AFAC71F, 0xAAD18478, + 0x631582EF, 0xB33EC188, 0x8E430421, 0x5E684746, + 0xF4B9C33E, 0x24928059, 0x19EF45F0, 0xC9C40697, + 0xC62A4993, 0x16010AF4, 0x2B7CCF5D, 0xFB578C3A, + 0x51860842, 0x81AD4B25, 0xBCD08E8C, 0x6CFBCDEB, + 0xA53FCB7C, 0x7514881B, 0x48694DB2, 0x98420ED5, + 0x32938AAD, 0xE2B8C9CA, 0xDFC50C63, 0x0FEE4F04, + 0xC154926B, 0x117FD10C, 0x2C0214A5, 0xFC2957C2, + 0x56F8D3BA, 0x86D390DD, 0xBBAE5574, 0x6B851613, + 0xA2411084, 0x726A53E3, 0x4F17964A, 0x9F3CD52D, + 0x35ED5155, 0xE5C61232, 0xD8BBD79B, 0x089094FC, + 0x077EDBF8, 0xD755989F, 0xEA285D36, 0x3A031E51, + 0x90D29A29, 0x40F9D94E, 0x7D841CE7, 0xADAF5F80, + 0x646B5917, 0xB4401A70, 0x893DDFD9, 0x59169CBE, + 0xF3C718C6, 0x23EC5BA1, 0x1E919E08, 0xCEBADD6F, + 0xCFA869D6, 0x1F832AB1, 0x22FEEF18, 0xF2D5AC7F, + 0x58042807, 0x882F6B60, 0xB552AEC9, 0x6579EDAE, + 0xACBDEB39, 0x7C96A85E, 0x41EB6DF7, 0x91C02E90, + 0x3B11AAE8, 0xEB3AE98F, 0xD6472C26, 0x066C6F41, + 0x09822045, 0xD9A96322, 0xE4D4A68B, 0x34FFE5EC, + 0x9E2E6194, 0x4E0522F3, 0x7378E75A, 0xA353A43D, + 0x6A97A2AA, 0xBABCE1CD, 0x87C12464, 0x57EA6703, + 0xFD3BE37B, 0x2D10A01C, 0x106D65B5, 0xC04626D2, + 0x0EFCFBBD, 0xDED7B8DA, 0xE3AA7D73, 0x33813E14, + 0x9950BA6C, 0x497BF90B, 0x74063CA2, 0xA42D7FC5, + 0x6DE97952, 0xBDC23A35, 0x80BFFF9C, 0x5094BCFB, + 0xFA453883, 0x2A6E7BE4, 0x1713BE4D, 0xC738FD2A, + 0xC8D6B22E, 0x18FDF149, 0x258034E0, 0xF5AB7787, + 0x5F7AF3FF, 0x8F51B098, 0xB22C7531, 0x62073656, + 0xABC330C1, 0x7BE873A6, 0x4695B60F, 0x96BEF568, + 0x3C6F7110, 0xEC443277, 0xD139F7DE, 0x0112B4B9, + 0xD31DD2E1, 0x03369186, 0x3E4B542F, 0xEE601748, + 0x44B19330, 0x949AD057, 0xA9E715FE, 0x79CC5699, + 0xB008500E, 0x60231369, 0x5D5ED6C0, 0x8D7595A7, + 0x27A411DF, 0xF78F52B8, 0xCAF29711, 0x1AD9D476, + 0x15379B72, 0xC51CD815, 0xF8611DBC, 0x284A5EDB, + 0x829BDAA3, 0x52B099C4, 0x6FCD5C6D, 0xBFE61F0A, + 0x7622199D, 0xA6095AFA, 0x9B749F53, 0x4B5FDC34, + 0xE18E584C, 0x31A51B2B, 0x0CD8DE82, 0xDCF39DE5, + 0x1249408A, 0xC26203ED, 0xFF1FC644, 0x2F348523, + 0x85E5015B, 0x55CE423C, 0x68B38795, 0xB898C4F2, + 0x715CC265, 0xA1778102, 0x9C0A44AB, 0x4C2107CC, + 0xE6F083B4, 0x36DBC0D3, 0x0BA6057A, 0xDB8D461D, + 0xD4630919, 0x04484A7E, 0x39358FD7, 0xE91ECCB0, + 0x43CF48C8, 0x93E40BAF, 0xAE99CE06, 0x7EB28D61, + 0xB7768BF6, 0x675DC891, 0x5A200D38, 0x8A0B4E5F, + 0x20DACA27, 0xF0F18940, 0xCD8C4CE9, 0x1DA70F8E, + 0x1CB5BB37, 0xCC9EF850, 0xF1E33DF9, 0x21C87E9E, + 0x8B19FAE6, 0x5B32B981, 0x664F7C28, 0xB6643F4F, + 0x7FA039D8, 0xAF8B7ABF, 0x92F6BF16, 0x42DDFC71, + 0xE80C7809, 0x38273B6E, 0x055AFEC7, 0xD571BDA0, + 0xDA9FF2A4, 0x0AB4B1C3, 0x37C9746A, 0xE7E2370D, + 0x4D33B375, 0x9D18F012, 0xA06535BB, 0x704E76DC, + 0xB98A704B, 0x69A1332C, 0x54DCF685, 0x84F7B5E2, + 0x2E26319A, 0xFE0D72FD, 0xC370B754, 0x135BF433, + 0xDDE1295C, 0x0DCA6A3B, 0x30B7AF92, 0xE09CECF5, + 0x4A4D688D, 0x9A662BEA, 0xA71BEE43, 0x7730AD24, + 0xBEF4ABB3, 0x6EDFE8D4, 0x53A22D7D, 0x83896E1A, + 0x2958EA62, 0xF973A905, 0xC40E6CAC, 0x14252FCB, + 0x1BCB60CF, 0xCBE023A8, 0xF69DE601, 0x26B6A566, + 0x8C67211E, 0x5C4C6279, 0x6131A7D0, 0xB11AE4B7, + 0x78DEE220, 0xA8F5A147, 0x958864EE, 0x45A32789, + 0xEF72A3F1, 0x3F59E096, 0x0224253F, 0xD20F6658, +}; + +/* $ID$ */ +/* Sbox for SOBER-128 */ +/* + * This is really the combination of two SBoxes; the least significant + * 24 bits comes from: + * 8->32 Sbox generated by Millan et. al. at Queensland University of + * Technology. See: E. Dawson, W. Millan, L. Burnett, G. Carter, + * "On the Design of 8*32 S-boxes". Unpublished report, by the + * Information Systems Research Centre, + * Queensland University of Technology, 1999. + * + * The most significant 8 bits are the Skipjack "F table", which can be + * found at http://csrc.nist.gov/CryptoToolkit/skipjack/skipjack.pdf . + * In this optimised table, though, the intent is to XOR the word from + * the table selected by the high byte with the input word. Thus, the + * high byte is actually the Skipjack F-table entry XORED with its + * table index. + */ +static const ulong32 Sbox[256] = { + 0xa3aa1887, 0xd65e435c, 0x0b65c042, 0x800e6ef4, + 0xfc57ee20, 0x4d84fed3, 0xf066c502, 0xf354e8ae, + 0xbb2ee9d9, 0x281f38d4, 0x1f829b5d, 0x735cdf3c, + 0x95864249, 0xbc2e3963, 0xa1f4429f, 0xf6432c35, + 0xf7f40325, 0x3cc0dd70, 0x5f973ded, 0x9902dc5e, + 0xda175b42, 0x590012bf, 0xdc94d78c, 0x39aab26b, + 0x4ac11b9a, 0x8c168146, 0xc3ea8ec5, 0x058ac28f, + 0x52ed5c0f, 0x25b4101c, 0x5a2db082, 0x370929e1, + 0x2a1843de, 0xfe8299fc, 0x202fbc4b, 0x833915dd, + 0x33a803fa, 0xd446b2de, 0x46233342, 0x4fcee7c3, + 0x3ad607ef, 0x9e97ebab, 0x507f859b, 0xe81f2e2f, + 0xc55b71da, 0xd7e2269a, 0x1339c3d1, 0x7ca56b36, + 0xa6c9def2, 0xb5c9fc5f, 0x5927b3a3, 0x89a56ddf, + 0xc625b510, 0x560f85a7, 0xace82e71, 0x2ecb8816, + 0x44951e2a, 0x97f5f6af, 0xdfcbc2b3, 0xce4ff55d, + 0xcb6b6214, 0x2b0b83e3, 0x549ea6f5, 0x9de041af, + 0x792f1f17, 0xf73b99ee, 0x39a65ec0, 0x4c7016c6, + 0x857709a4, 0xd6326e01, 0xc7b280d9, 0x5cfb1418, + 0xa6aff227, 0xfd548203, 0x506b9d96, 0xa117a8c0, + 0x9cd5bf6e, 0xdcee7888, 0x61fcfe64, 0xf7a193cd, + 0x050d0184, 0xe8ae4930, 0x88014f36, 0xd6a87088, + 0x6bad6c2a, 0x1422c678, 0xe9204de7, 0xb7c2e759, + 0x0200248e, 0x013b446b, 0xda0d9fc2, 0x0414a895, + 0x3a6cc3a1, 0x56fef170, 0x86c19155, 0xcf7b8a66, + 0x551b5e69, 0xb4a8623e, 0xa2bdfa35, 0xc4f068cc, + 0x573a6acd, 0x6355e936, 0x03602db9, 0x0edf13c1, + 0x2d0bb16d, 0x6980b83c, 0xfeb23763, 0x3dd8a911, + 0x01b6bc13, 0xf55579d7, 0xf55c2fa8, 0x19f4196e, + 0xe7db5476, 0x8d64a866, 0xc06e16ad, 0xb17fc515, + 0xc46feb3c, 0x8bc8a306, 0xad6799d9, 0x571a9133, + 0x992466dd, 0x92eb5dcd, 0xac118f50, 0x9fafb226, + 0xa1b9cef3, 0x3ab36189, 0x347a19b1, 0x62c73084, + 0xc27ded5c, 0x6c8bc58f, 0x1cdde421, 0xed1e47fb, + 0xcdcc715e, 0xb9c0ff99, 0x4b122f0f, 0xc4d25184, + 0xaf7a5e6c, 0x5bbf18bc, 0x8dd7c6e0, 0x5fb7e420, + 0x521f523f, 0x4ad9b8a2, 0xe9da1a6b, 0x97888c02, + 0x19d1e354, 0x5aba7d79, 0xa2cc7753, 0x8c2d9655, + 0x19829da1, 0x531590a7, 0x19c1c149, 0x3d537f1c, + 0x50779b69, 0xed71f2b7, 0x463c58fa, 0x52dc4418, + 0xc18c8c76, 0xc120d9f0, 0xafa80d4d, 0x3b74c473, + 0xd09410e9, 0x290e4211, 0xc3c8082b, 0x8f6b334a, + 0x3bf68ed2, 0xa843cc1b, 0x8d3c0ff3, 0x20e564a0, + 0xf8f55a4f, 0x2b40f8e7, 0xfea7f15f, 0xcf00fe21, + 0x8a6d37d6, 0xd0d506f1, 0xade00973, 0xefbbde36, + 0x84670fa8, 0xfa31ab9e, 0xaedab618, 0xc01f52f5, + 0x6558eb4f, 0x71b9e343, 0x4b8d77dd, 0x8cb93da6, + 0x740fd52d, 0x425412f8, 0xc5a63360, 0x10e53ad0, + 0x5a700f1c, 0x8324ed0b, 0xe53dc1ec, 0x1a366795, + 0x6d549d15, 0xc5ce46d7, 0xe17abe76, 0x5f48e0a0, + 0xd0f07c02, 0x941249b7, 0xe49ed6ba, 0x37a47f78, + 0xe1cfffbd, 0xb007ca84, 0xbb65f4da, 0xb59f35da, + 0x33d2aa44, 0x417452ac, 0xc0d674a7, 0x2d61a46a, + 0xdc63152a, 0x3e12b7aa, 0x6e615927, 0xa14fb118, + 0xa151758d, 0xba81687b, 0xe152f0b3, 0x764254ed, + 0x34c77271, 0x0a31acab, 0x54f94aec, 0xb9e994cd, + 0x574d9e81, 0x5b623730, 0xce8a21e8, 0x37917f0b, + 0xe8a9b5d6, 0x9697adf8, 0xf3d30431, 0x5dcac921, + 0x76b35d46, 0xaa430a36, 0xc2194022, 0x22bca65e, + 0xdaec70ba, 0xdfaea8cc, 0x777bae8b, 0x242924d5, + 0x1f098a5a, 0x4b396b81, 0x55de2522, 0x435c1cb8, + 0xaeb8fe1d, 0x9db3c697, 0x5b164f83, 0xe0c16376, + 0xa319224c, 0xd0203b35, 0x433ac0fe, 0x1466a19a, + 0x45f0b24f, 0x51fda998, 0xc0d52d71, 0xfa0896a8, + 0xf9e6053f, 0xa4b0d300, 0xd499cbcc, 0xb95e3d40, +}; diff --git a/sprng.c b/sprng.c index f1de461..090bd01 100644 --- a/sprng.c +++ b/sprng.c @@ -19,15 +19,15 @@ const struct _prng_descriptor sprng_desc = { - "sprng", + "sprng", 0, &sprng_start, &sprng_add_entropy, &sprng_ready, &sprng_read, &sprng_done, &sprng_export, - &sprng_import - + &sprng_import, + &sprng_test }; int sprng_start(prng_state *prng) @@ -51,9 +51,9 @@ unsigned long sprng_read(unsigned char *buf, unsigned long len, prng_state *prng return rng_get_bytes(buf, len, NULL); } -void sprng_done(prng_state *prng) +int sprng_done(prng_state *prng) { - _ARGCHK(prng != NULL); + return CRYPT_OK; } int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng) @@ -69,6 +69,11 @@ int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) return CRYPT_OK; } +int sprng_test(void) +{ + return CRYPT_OK; +} + #endif diff --git a/yarrow.c b/yarrow.c index 7354d47..5b48bee 100644 --- a/yarrow.c +++ b/yarrow.c @@ -15,14 +15,15 @@ const struct _prng_descriptor yarrow_desc = { - "yarrow", + "yarrow", 64, &yarrow_start, &yarrow_add_entropy, &yarrow_ready, &yarrow_read, &yarrow_done, &yarrow_export, - &yarrow_import + &yarrow_import, + &yarrow_test }; int yarrow_start(prng_state *prng) @@ -183,10 +184,12 @@ unsigned long yarrow_read(unsigned char *buf, unsigned long len, prng_state *prn return len; } -void yarrow_done(prng_state *prng) +int yarrow_done(prng_state *prng) { _ARGCHK(prng != NULL); /* call cipher done when we invent one ;-) */ + + return CRYPT_OK; } int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng) @@ -222,10 +225,32 @@ int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng if ((err = yarrow_start(prng)) != CRYPT_OK) { return err; } - if ((err = yarrow_add_entropy(in, 64, &prng)) != CRYPT_OK) { + return yarrow_add_entropy(in, 64, prng); +} + +int yarrow_test(void) +{ +#ifndef LTC_TEST + return CRYPT_NOP; +#else + int err; + prng_state prng; + + if ((err = yarrow_start(&prng)) != CRYPT_OK) { return err; } - return yarrow_ready(&prng); + + /* now let's test the hash/cipher that was chosen */ + if ((err = cipher_descriptor[prng.yarrow.cipher].test()) != CRYPT_OK) { + return err; + } + if ((err = hash_descriptor[prng.yarrow.hash].test()) != CRYPT_OK) { + return err; + } + + yarrow_done(&prng); + return CRYPT_OK; +#endif } #endif