Description
CASCADED STREAM CIPHER
TECHNICAL FIELD
This invention relates generally to data storage and data transmission security. More particularly, the present invention relates to methods of encrypting and decrypting stored and/or transmitted data.
BACKGROUND ART •
Methods of making written text unreadable by those to whom it is not intended are at least 2000 years old. This is accomplished by ciphers. Their objective is to transform the message such that it can be read only with the help of a key. The original message is known as plaintext, the transformed message is known as cipher- text . A new era of cryptography began with the computer age. Software and/or digital hardware can be employed to perform encrypting and decrypting operations very rapidly.
One way to classify encryption methods (ciphers) is into private (symmetric) and public (asymmetric) . In case of a private cipher the message is de- crypted with the same key it was encrypted. In case of public ciphers the encryption key cannot decrypt the message. Therefore it needs not be kept secret and can be made public. Another key, the private key must be used to decrypt the message. Another classification is into block ciphers and stream ciphers. A block cipher transforms blocks of fixed size one block at a time into another block, typically of the same size such that:
C = f(P,K)
where C is the encrypted block, P is the original block, K is the key, and f is the encrypting transformation. Presumably the person who does not know the key K will not be able to recover P from C. An example of such a cipher is DES . (Bruce Schneier: Applied Cryptography,
John Willey & Sons, 1994, pp. 219-241.) A stream cipher generates a very long stream of pseudo-random numbers. The message is encrypted with this stream. If the message consists of a sequence of 8-bit ASCII codes such that each character takes one byte of computer memory then each byte is transformed by one of the pseudo-random numbers, typically by performing exclusive or operation. An example: if
{Pi} = PI, P2, P3,... Pi, ... Pn
is the original message i.e. a sequence of characters (bytes)
{Ri} = Rl, R2, R3,...Ri,...Rn
is the stream of pseudo-random numbers, then
{Ci} = PI xor Rl, P2 xor R2, P3 xor R3, ... Pi xor Ri, ... Pn xor Rn
is the encrypted message. ('xor' signifies the exclusive or operation. )
There are several methods for generating pseudo-random numbers. The three most common are linear congruential generators, linear feedback shift register
(LFSR) generators, and lagged Fibonacci generators (LFG) .
None of these methods alone are suitable for encryption purposes because the cryptanalyst can readily decipher them. In order to be usable for encryption these methods have to be modified. Various combinations of shift reg-
ister generators are most often used to generate stream ciphers .
Encryption methods and devices are applicable in all situations where data are stored or transmitted. With the emergence and growth of Internet there is an ever increasing need to protect information from unauthorized interception. Encryption is an important class of methods for accomplishing computer and network security and for guarding sensitive information.
Prior Art
The most common devices used in stream ciphers are linear feedback shift registers (LFSR) . Their operation is described e.g. in Bruce Schneier: Applied Cryp- tography, John Willey & Sons, 1994, pp. 351-356. The cipher generated by only one such shift register is easy to break. In order to obtain a cryptographically secure stream cipher based on linear feedback shift registers it is necessary to combine several of them. A number of ways to combine several feedback shift registers have been proposed. One example of an encryption method based on combination of LFSRs is described in U.S. Patent 5,703,952 "Method and Apparatus for Generating a Stream Cipher", 1997. The most relevant examples are "Beth- Piper Stop-and-Go Generator", "Gollman Cascade", "Alternating Stop-and-Go Generator", "Bilateral Stop-and-Go Generator". (Bruce Schneier: Applied Cryptography, John Willey & Sons, 1994, pp. 359-361) These combinations share with the current invention the feature that the in- dividual LFSRs are clocked asynchronously.
But there are other ways of generating pseudorandom streams of numbers . Particularly interesting are Lagged Fibonacci Generators (LFG) . They operate on a principle similar to LFSRs, but unlike LFSRs, which gen- erate only one bit a time, they generate an entire word
of length n in one operation, n is typically 16, 32, or 6 . LFGs have some good properties such as very long period. They can be used as random number generators for mathematical computations. Their disadvantage is that they do not satisfy certain statistical tests. In order to do so they have to be modified. (Donald E. Knuth: The Art of Computer Programming, Volume 2, Seminumerical Algorithms, Third Edition, Addison Wesley, 1998, pp. 27-29, 186-188) The advantage of LFGs over LFSRs is that they are faster. Furthermore the trend is towards implementing encryption operations in software and LFGs are much easier to implement that way than LFSRs . Recently attention has been turning to using LFGs as the basis of encryption methods. LFGs also have to be combined; other- wise, the cipher is trivial to break. Methods of doing so have appeared in literature. Examples are in the following articles: U. Blocher, M. Dichtl, "Fish: a fast software stream cipher", Fast Software Encryption, Springer, LNCS, v. 809 pp. 41-44 and Ross Anderson, "On Fibonacci Keystream Generators", Fast Software Encryption, Second International Workshop, Proceedings, pp. 346-52. "Fish" is no longer viable since it has already been broken (Anderson, "On Fibonacci Keystream Generators"). The method proposed by Anderson, called "PIKE", takes 2.75 LFG ticks per one LFG tick i.e. it is 2.75 times slower than the LFG alone. The method proposed in the present invention takes less than 2 ticks per one LFG tick.
Mathematical analysis discovered random number generators that are slow but very secure cryptographi- cally. The quadratic residue random number generator, also known as Blum Blum Shub (BBS) , is believed to be unbreakable. Its main disadvantage is that it is too slow for most practical applications . By the term cryptographically secure we mean difficult to break. In case of the BBS generator it has been proven mathematically
that under certain assumptions the computational effort required to break it is so large that it cannot be practically accomplished. (L. Blum, M. Blum, and M. Shub: "A Simple, Unpredictable Pseudo-Random Number Generator", SIAM Journal on Computing, v. 15, n. 2, 1986, pp. 364- 383) .
In 1995 on the Internet John Kelsey suggested " using a fast stream cipher for short lengths of cipher- text, and then refilling the internal state from a slower cipher. " The idea was elaborated by Peter Kwangjun Suk, who suggested "using the secure Blum-Blum- Shub quadratic residue generator wi th a 512 bi t modulus? We could run a fast 64-bit block cipher in OFB mode, and occasionally flush the shift register and insert 63 bi ts from seven iterations of the BBS generator . . . . For greater security and speed in a hardware implementation, we could run the above in hardware, using 7 BBS generators in parallel (wi th 7 different 512-bi t moduli) and the same 64 bit block algori thm running in OFB mode. " A block cipher in OFB mode (Bruce Schneier: Applied Cryptography, John
Willey & Sons, 1994, p. 162) functions as a cryptographically secure random number generator. Such generators tend to be slower than the inherently stream oriented ones such as LFGs. By re-seeding the shift regis- ter, the aforementioned method periodically replaces the entire internal state of the random number generator. The Blum Blum Shub generator can indeed be used to generate from time to time the keys for any cipher. The proposal above only automates the process. This however is not a true combination of several random number generators, because instead of combining their outputs or controlling synchronization one merely affects the internal state of another.
One disadvantage of this method is that it can- not be applied to LFGs, where the internal state is too
large. Another disadvantage of this method is that it cannot be implemented in software. The reason is that all the seven BBSs and the block cipher have to run in parallel on several physical processors. The seven BBSs will consume most of the computing power. If the execution was interleaved, which would be the case of software implementation, the system would be so slow as to be impractical. In a variant suitable for software implementation the execution of the block cipher would have to be interleaved with the execution of the BBS generator. In order to achieve reasonable speed the BBS would have to be executed infrequently. It means that the flush register could also be re-seeded only infrequently. Therefore the block cipher would have to be sufficiently secure and thereby slow. At the end very little is achieved over a simple OFB implementation without any BBS (when the aforementioned scheme is implemented in software.) Yet another disadvantage is that it is difficult to determine the exact level of security of the block cipher. If a standard block cipher such as DES were used the speed of the system would be mediocre. The frequent re-seeding of the flush register presumably relaxes the security requirements of the block cipher. But it is very difficult practically to determine what that level should be and how to achieve it.
The fastest known encryption algorithm known thus far is SEAL 3.0. It is described in Phillip Rogaway & Don Coppersmith: "A Software Optimized Encryption Algorithm", Journal of Cryptology, vol. 11, num. 4, pp. 273-287, 1998 and in U.S. Patent 5,675,653 "Computer Readable Device Implementing a Software-efficient Pseudorandom Function Encryption" . The internal structure of this method bears no resemblance to the present invention. The method of the present invention is as fast or faster than SEAL 3.0.
Objects and Advantages
The objects of the present invention are: a) to provide a very fast (possibly the fastest thus far) , yet secure, method of encrypting and decrypting data; and b) to provide a method of generating a pseudorandom stream of numbers, which is nearly as fast as the lagged Fibonacci random number generators (LFG) but has better statistical properties.
DISCLOSURE OF INVENTION
The present invention combines several pseudorandom numbers generators (PRNG) in a cascade structure. The PRNTGs in layer 2 run more slowly (typically 64x) than the PRNGs in layer 1. In general The PRNGs in layer n + 1 run more slowly (typically 64x) than the PRNGs in layer n. The pseudo-random stream from layer n + 1 is used to mangle the pseudo-random stream in layer n. The present invention accomplishes said mangling by taking short segments of the stream from layer n + 1 and deriving typically 64 different permutations. Said permutations are XORed with the pseudo-random stream of layer n.
BRIEF DESRIPTION OF THE DRAWINGS
FIG. 1 is a block diagram of the present invention.
FIG. 2 is a detailed view of one of several identical operational units and its context.
BEST MODE OF CARRYING OUT THE INVENTION
FIG. 1 shows a block diagram of the present invention. Random number generators 1, 2, 3 are Lagged Fibonacci Generators (LFG). These are described e.g. in Donald E. Knuth: The Art of Computer Programming, Vol. 2,
Seminumerical Algorithms, Third Edition, Addison Wesley, 1998, pp. 27-29, 186-188. They generate pseudo-random numbers according to the following formula:
X[n.] = (X[n-p] + X[n-q])mod M, p > q > 0 (1)
In the preferred embodiment q = 24, p = 55, Linear Feedback Shift Registers (LFSR) are a special case of LFGs such that the modulus M = 1. The present inven- tion applies to LFSRs as well to LFGs. In fact any sufficiently fast pseudo-random number generators can be used in place of generators 1, 2, 3. Each of them can be of different kind.
A random number generator 4 is slow but crypto- graphically secure. In the preferred embodiment it is implemented by an algorithm that randomly picks numbers from a stream of pseudo-random numbers generated by a yet another LFG according to formulas (2), (2').
s0 = LFG[J® ] (2)
J(i + 1) = J(i) + 516 + randc(i + 1)] , (2')
where s[i] it i-th secure pseudo-random number output by slow but cryptographically secure generator 4, LFG[j] is the j-th pseudo-random number generated by said LFG, randc(i) is the i-th pseudo-random number generated by a congruential pseudo-random number generator, said pseudorandom numbers randc(i) being in the interval <0, 999>. The constant 516 can be chosen arbitrarily but should be at least 500. Pseudo-random numbers output by said slow but cryptographically secure generator 4 are picked at intervals, which range from 516 to 1515, from the pseudorandom stream generated by said LFG. Said congruential random number generator randc ( ) generates pseudo-random numbers according to formulas (3) and (4) .
r(i + 1) = (r(i) * 4096 + 150889) mod 714025 (3) randc(i ) = (1000/ 714025) * r(ϊ) (4)
Formula (3) describes a classical congruential random number generator, which generates pseudo-random numbers in the interval <0, 714024>. (Donald E. Knuth: The Art of Computer Programming, Volume 2, Seminumerical Algorithms, Third Edition, Addison Wesley, 1998, pp. 10-25) . The constants 4096, 150889, 714025 cannot be chosen arbitrarily. Formula (4) describes how the pseudo-random numbers from said classical congruential random number generator are modified to produce pseudorandom numbers in the interval <0, 999>. The right side of formula (4) yields a rational number while the result on the left side of formula (4) is an integer; said rational number is converted to said integer such that the fractional part is truncated.
In an alternative embodiment random number gen- erator 4 is quadratic residue generator. This generator generates a sequence of pseudo-random bits z1, z2, z2, ... z . The i-th pseudo-random bit z± is the least significant bit of x. , where
X "i--.l mod n (5)
n is a Blum integer i.e. a product p * q of two large prime numbers p, q, which are congruent to 3 modulo 4 i.e. p,q = 3 mod 4. The initial integer x_ of the se-
2 quence (5) is s mod n, s is relatively prime to n. This generator is described in literature and its principle, structure, operation or method is not claimed by this patent. (L. Blum, M. Blum, and M. Shub: "A Simple, Unpredictable Pseudo-Random Number Generator", SIAM Journal on
Computing, v. 15, n. 2, 1986, pp. 364-383). When said pseudo-random bits z. are grouped in chunks of 32, each such chunk (or sequence) constitutes a 32-bit pseudorandom number. Said modulus n should have 1024 bits. In order to guarantee that said quadratic residue generator has sufficiently long cycle, p and q should be special primes. Prime p is special if p = 2pl + 1 and pi = 2p2 + 1 where pi and p2 are also prime. [Terry Ritter, The Efficient Generation of Cryptographic Confusion Sequences . Cryptologia . 15(2): 81-139]
Any sufficiently secure random number generator can be used in place of generator 4.
Three operational units 6, 7, 8 each combine their two inputs . In the preferred embodiment said op- erational units 6, 7, 8 are identical, are decomposed on FIG. 2, and described in detail below. However it is not necessary for operational units 6, 7, 8 to be identical.
FIG. 1 shows a cascade of three operational units 6, 7, 8 i.e. the number of steps in the cascade is 3. But any reasonable number of steps can be used. In the preferred embodiment all three steps of the cascade are identical but it is not necessary.
FIG. 2 is exploded step of the cascade 13 from FIG. 1. Dashed box 216 is operational unit 6 of FIG. 1. Input 21 is the same as connector 9 of FIG. 1. Inputs 22 are the same as connector 10 of FIG. 1. Inputs 22 provide 64 streams of pseudo-random numbers from LFGs 23. In the preferred embodiment each pseudo-random number is an
32. integer m the interval <0, 2 -1>. Such integers are usually held in 32-bit registers. But any reasonable number of bits can be used. The 64 LFGs 23 are the same as LFG 1 of FIG. 1. For the sake of simplicity LFGs 23 are depicted as only one block 1 on FIG. 1. Each buffer 24 holds 64 integers from its corresponding LFG 23. Buffer 25 holds 64 integers that have arrived via input
21. The sequence of 64 integers held in buffer 25 will henceforth be referred to as dynamic key. Each permuta- tor 26 will permute said dynamic key. There are 64 per- mutators 26 and each of them will most likely perform a different permutation. The permutation will be set up at the beginning of each encryption session based on the static key (see below) . An adder 27 performs addition n modulo 2 (typically n = 32) . A buffer 28 has the capacity to hold 64 x 64 = 4096 integers.
Operation of the Preferred Embodiment
The basic operation is illustrated on FIG 1. On average generator 2 generates one pseudo-random number once per 64 random numbers of generator 1. Analogically generator 3 generates one pseudo-random number once per 64 random numbers of generator 2, and generator 4 generates one pseudo-random number once per 64 random numbers of generator 3. As a result generator 4 runs approximately 262 000 times more slowly than generator 1. Before encryption can start, the entire system described herein has to be initialized. The state of the apparatus is the content of LFGs 1, 2, 3 and of slow but cryptographically secure random number generator 4, as well as the state of operational units 6, 7, 8. The nuna- ber of possible states is very large. Using a key so large is not necessary. A 128-bit key is more than sufficient to make a brute force attack (trying keys one by one) impossible. The preferred embodiment therefore uses 128-bit key, henceforth referred to as static key. It is an equivalent of four 32-bit integers. But in order to initialize all LFGs 1, 2, 3 several hundred or thousand 32-bit integers are needed. Each component 1, 2, 3, 4, 6, 7, 8 are initialized by its corresponding initializer 11. Each initializer 11 uses the static key held in reg- ister 12.
More detailed operation is shown on FIG. 2. At the beginning of each encryption session an initialization is performed; a static key is fed in initializer 29. In the preferred embodiment the static key has 128 bits but other key lengths, including but not limited to 64 bits, are possible. Said static key is used to set a permutation in each permutator 26. Each permutator 26 will receive 64 integers in a certain order from its input 210. It will permute said sequence of 64 integers and output said 64 integers on its output 211 in a different order. There are 64! different possible permutations for each permutator 26.
(a) Initialization of LFGs 23 Furthermore LFGs 23 (FIG. 2) also need to be initialized. This task is performed by initializer 215. Initializer 215 fills all 64 registers of each LFG 23 with pseudorandom numbers. In the preferred embodiment the initialization is performed with the help of eight identical congruential random number generators, CRNG1, ... CRNG8. Said congruential random number generator is described above. A 128-bit static key is broken into eight 16-bit integers and each congruential random number generator is initialized with one of said 16-bit inte- gers, such that each said congruential random number generator is initialized with different said 16-bit integer. Then CRNG1 fills all the registers of each LFG 23. Then the content of all LFGs 23 is permuted using the algorithm in Table 1 and CRNG2 in place of Random () . Then a stream of pseudo-random numbers generated by CRNG3 is superimposed to the content of LFGs 23 using exclusive or operation. Then LFG4 is used to permute the content of each LFGs 23. The procedure is repeated such that a stream of pseudo-random numbers generated by even nu - bered CRNG is superimposed to the content of LFGs 23 us-
ing exclusive or operation, and the odd numbered CRNG are used to permute the content of LFGs 23.
The following algorithm, expressed in C programming language, is used to permute the contents of said address registers A± : for(i=64-l; i>0; i-){ m = Random!} % i; temp = A[i]; AH = A[m]; -A.[m] = temp;
}
Table 1
This algorithm was first published by Durstenfeld (Bruce Schneier: Applied Cryptography, John Willey & Sons, 1994, p. 374.) In the preferred embodiment slow but secure random number generator identical with 4 is used for the function Random() . But any sufficiently secure generator can be substituted particularly when the speed of initialization is an issue. However each possible value of the static key should result in a different permutation.
In an alternative embodiment Secure Hash Algorithm (SHA) is used to initialize LFGs 23. SHA is a standard hash function. Its description can be found in NIST FIPS PUB 180, "Secure Hash Standard", National Institute of Standards and Technology, U.S. Department of Commerce, DRAFT, Apr. 1993. It produces a 160-bit hash
6 of any message of any length < 2 . It can be used m the following manner to initialize LFGs 23: Each LFG 23 con- sists of a number of 32-bit registers. First a 128-bit static key is written in the first four registers of the first LFG 23. A 160-bit hash function of said four registers is computed with SHA. 128 bit of said 160 bits are selected and written in the next four registers of the first LFG 23. Then another 160-bit hash function of the
first eight registers of the first LFG 23 is computed and the process is repeated until all registers of the first LFG 23 are filled. Then a 160-bit hash function using SHA of the entire first LFG 23 is computed and written in the first four registers of the next LFG 23. The process is repeated until all LFGs 23 are initialized.
In yet another alternative the random number generator in initializer 215 is BBS.
(b) Initialization of the Slow but Secure Random Number Generator 4
Slow but secure random number generator 4 has its own LFG. Said LFG also needs to be initialized. This task is performed by initializer 11. Initializer 11 fills all 64 registers of said LFG with pseudorandom numbers. In the preferred embodiment the initialization is performed with the help of eight identical congruential random number generators, CRNGl, ... CRNG8. Said congruential random number generator is described in section "Initialization of LFGs 23." Said congruential random numbers generators were initialized earlier and there is no need to initialize them again. Then CRNGl fills all the registers of said LFG. Then the content of said LFG is permuted using the algorithm in Table 1 and CRNG2 in place of Random() . Then a stream of pseudo-random numbers generated by CRNG3 is superimposed to the content of said LFG using exclusive or operation. Then LFG4 is used to permute the content of said LFG. The procedure is repeated such that a stream of pseudo-random numbers gener- ated by even numbered CRNG is superimposed to the content of said LFG using exclusive or operation, and the odd numbered CRNG are used to permute the content of said LFG.
In an alternative embodiment when BBS is used as a slow but secure random number generator 4 the proce-
512 dure is as follows, a 128-bit key is multiplied by 2 i.e. it is shifted by 512 bits to the left:
s = static_key * 2 (6)
A square modulo n, where n is the modulus of said BBS is computed
= s mod n (?)
Then χ is computed
2 x. = x0 mod n (8)
If x0 = xχ then s is incremented by one
s = s + 1 (9)
And the whole procedure is repeated until x0 is not equal to x., it means that said BBS is not in a degenerate cycle.
(c) Initialization of Permutators 26
In the preferred embodiment permutator 26 has 64 registers R0, Rχ, ... R63. It receives the dynamic key from buffer 25 in chunks of 64 pseudo-random numbers N0, N-_, ... N-3. Said random numbers are not stored in said registers R0, R1, ... R63 in sequential order but are per- mutated. This may be illustrated by the way of example: N- may be stored in R46, Nχ in R13, N2 in R25 etc. As random numbers N0, Nχ, ... N63 are received one by one they are stored in appropriate register Ri . This is done with the help of 64 address registers A0, A.., ... A63. According to
the example, register A- contains 46, which is the index (address) of register R46; register Aχ contains 13, which is the index (address) of register R13; register A2 contains 25, which is the index (address) of register R25. Said registers A0, A.. , ... A63 are set during initialization by initializer 29. First each register A± is loaded with its own index i i.e. A. := 0, K := 1, A2 := 2, ... A63
:= 63. Then the content of said address registers A0 := 0, Ai := 1, A2 : = 2 , ... A53 := 63 is permuted using the algo- rithm in Table 1."
(d) Runtime operation
Once initialization is completed encryption may begin. 64 pseudo-random integers generated by each LFG 23 are fed to the corresponding buffers 24 via connectors 22. At the same time a dynamic key, consisting of a sequence of 64 integers, is fed to permutators 26. Said sequence of 64 pseudo-random integers i.e. said dynamic key has been generated by an upstream unit of the cas- cade. Each permutator then permutes said sequence of 64 pseudo-random integers. Most likely each permutators 26 will perform a different permutation.
In an alternative embodiment there is only one LFG 23. Instead of 64 LFGs 23 each generating 64 pseudo- random integers, there is only one LFG generating 64 x 64 = 4096 pseudo-random integers. Said 4096 pseudo-random integers are distributed among buffers 24 such that each
buffer 24 receives 64 pseudo-random integers. Any reasonable number of LFGs 23 can be used. Equally any reasonable number of permutators 26 can be used.
In the next stage the content of buffers 24, consisting of 64 pseudo-random integers, is shifted out one by one via connector 212. Also synchronously the permutated content of permutators 26, consisting of 64 integers, is shifted out via connector 211. Each two integers, one from buffer 24 and the other from permutator 26 are combined in adder 27. In the preferred embodiment
_2 adder 27 performs addition modulo 2 but other
32 operations such as subtraction modulo 2 or 'exclusive or' are also possible. The resulting integers are fed into buffer 28 via connector 213. In an alternative embodiment permutators 26 and/or LFGs 23 are themselves permutated from time to time i.e. are assigned to different adders 27. This means that for example the content of the first permutator 26 may be swapped with the content of the 15-th per- mutator, the content of the second permutator may be swapped with the content of the 36-th permutator etc.
When 64 x 64 pseudo-random integers from buffers 24 and corresponding 64 x 64 integers from permutators 26 are processed by adders 27 and fed into buffer 28, said buffer 28 will contain 64 x 64 = 4096 integers. At that point these integers are shifted out one by one via output 214.
In an alternative development the integers from adders 27 are not stored in buffer 28 consecutively but in random order, said random order may change dynamically from time to time .
Then the whole procedure is repeated i.e. 64 pseudo-random integers generated by each LFG 23 are fed to the corresponding buffers 24 via connectors 22. At the same time a dynamic key, consisting of a sequence of
64 pseudo-random integers, is fed to permutators 26 etc. Output 214 can be used as a stream of pseudorandom numbers for mathematical purposes, or it can be used for encryption purposes . When it is used for en- 5. cryption then the sequence of integers of output 214 is combined with a plaintext. In the preferred embodiment the plaintext is represented as a sequence of octets (bytes) . Said octets are concatenated four at a time to form 32-bit integers. Resulting sequence of said 32-bit 0 integers is combined with the sequence of 32-bit integers of output 214. Said combination is accomplished by exclusive or operation. Let {Pi} = Pi, P2 , ..., Pi be a sequence of integers representing a plaintext. Let {Ri} = Rl, R2 , ..., Ri be a sequence of integers from output 5 214. The ciphertext sequence {Ci} = Cl, C2 , ..., Ci is formed as follows:
Ci = Ri xor Pi
0 where 'xor' is bitwise exclusive or operation. In the preferred embodiment 'exclusive or' is used but other op-
32 erations such as addition modulo 2 or subtraction modulo
32
2 are also possible.
The decryption process is a simple inversion of 5 the encryption. During decryption the same stream of pseudo-random numbers is generated as during encryption. The plaintext is obtained as follows:
Pi = Ri xor Ci 0
In order to decrypt a ciphertext the apparatus of the present invention has to be initialized to the same state in which it was prior to encryption.
The methods of encryption and decryption hereby 5 described have been implemented in software using C Ian-
guage. The listing of the program is included with this application as Appendix A.
Appendix A
Copyright (c) Cascade Research, 2000
This file is provided only for research, evaluation, and experimentation. Using this file for commercial or personal purposes or incorporating it in any other product without written permission from Cas- cade Research is prohibited.
// SemiTest.c //
#include <stdio.h> #include <stdlib.h>
#include <s__i_ιg.l_>
#include <t__ne.h>
#include ''ser-iirami.h''
#include "Slo Sec.h" #include "I__.tSem._i"
FILE *Input, *Output; unsigned long binBuffer[BUFFER_SIZE]; unsigned char *pbinBuffer = (unsigned char *)binBuffer;
/* this file contains the key in the form of 4 32-bit unsigned integers */ const char cKeyFilef] = "key.txt"; int main(int argc, char *argvJJ) { size_t nSize; long nTotSize = 0; int i; unsigned long nKey[4]; /* 128 bits */ clock_t start, finish; if (argc != 3) { printf("Wrong number of arguments!\n"); p__ntf("Semitest <input filename> <output f_lename>\n"); exit(l);
}
/**** Initialize RNG ****/ /* open key file */ Input = fopen(cKeyFile,"r"); if (Input == NULL) { fp_intf(stde_r, "Could not open %s\n", cKeyFile); perror("ERRORϋ SemiTest"); return(l); }
/* The key consists of 4 32-bit numbers i.e. 128 bits */ printf("\nHere is the key:\n"); for(i=0; i<4; i++) { fscanf(Input, "%x\n", &nKey|i]); printf("nKey[%d] = %x\n", i, nKeyH);
} printf("\n");
/* close the key file */ fclose(Input); printf("Input file: %s\n", argv[l]); printf(" Output file: %s\n", argv[2]);
/* open input file */ Input = fopen(argv[l],"rb"); if (Input == NULL){ fpr_ntf(stderr, "Could not open %s\n", argvfl]); perror("ERRORϋ SemiTest"); return(l);
}
/* open output file */ Output = fopen(argv[2],"wb"); if (Output == NULL) { fprintf(stderr, "Could not open %s\n", argv[2]); perrorfERRORϋ SemiTestt"); fclose(Input); retum(l); } start = clockø;
/* initialize RNG */ ImtSemi_:amis((unsigned char *)nKey); /* encrypt a file */ while(l) {
/* read buffer */ nSize = fread(binBuffer, 1, BUFFER_SIZE*sizeof(unsigned long), Input); if (fe_ror(Tnput) != 0) { fprintf(stderr, "Could not read from %s\n", argv[l]); perror("ERRORϋ SemiTest"); break;
} if (nSize <= 0) break; /* file finished */
/***# encrypt/ decrypt buffer ****/ CryptXOR(binBuffer); // API /* write buffer */
J-write(binBuffer, 1, nSize, Output); if (ferror(Output) != 0){ fprintf(stderr, "Could not write to %s\n", argv[2]); perror("ERRORϋ SemiTest"); break;
} nTotSize += nSize;
finish = clockO; p_intf("\nFile size = %u KB, duration = %f s\n", nTotSize/1024, (double)(finish-start)/CLOCKS_PER_SEC); fclose(Output);
fclose(Input); retnm(0);
// Semirami.h //
// Fibonacci generator
#define CASCADE_DEPTH 3 // 3 layers, BBS layer not included #define X_SIZE 64 // size of circular buffers
#define N_GENERATORS_IN_A_LAYER 64 #define BUFFER_SIZE (X_SIZE * N_GENERATORS_IN_A_LAYER)
// Function prototypes - APIs void IrtitSemiramis(unsigned char * nlnitVal); unsigned long Random ord(void); unsigned char RandomByteO; void CryptXOR(unsigned long nBufferQ); // Semirami.c //
#include <stdio.h> #include <stdlib.h> //#define NDEBUG /* disable assertions */ #include <assert.h>
#include "InitSem.h" #include "semirami.h" #include "SlowSec.h" //extern FILE *Interm;
/**** FILE SCOPE VARIABLES ****/ static unsigned long __BigBuffer|N SENERATORS JNrfJLAYER*X_SIZE]; // encrypt this static int iResult = N_GENERATORS_IN_A_LAYER*X_SIZE;
// states of all Fibbonacci generators static unsigned long nX[CASCADE_DEPTITj [N_GENERATORS_IN_A_LAYER] [X_SIZE]; static unsigned long* pnX; // pointer, used for speed
/* intermediate layers of random numbers */ static unsigned long nResult[CASCADE_DEPTH-l][N_GENERATORS_IN_A_LAYER*X_SIZE]; static int ilndexesβ] = {0, 0, 0}; static unsigned long nDynamicKey[CASCADE_DEPTH| [N_GENERATORS_IN_A_I_AYER];
/* these tables permutate the dynamic key */ static unsigned long iPermutKey[CASCADE_DEPTH] | _GENERATORS_IN AJ AYER] |X_SIZE]; /* initialization */ unsigned long nCongState[2*NKEY_LENGTH|; /* states of 8 congruential generators */
/* forward declaration */ static void RunOneLayerOfLFGs(int iLayer, unsigned long* pnBuffer);
/**** This routine gets the dynamic key from the upper segment ****/ static void GetDynamicKey(int iLayer) { int i, j; unsigned long* pnDynamicKey = &nDynamicKey [iLayer] [0]; unsigned long* pnResult = &nResult[iLayer] [0];
/* fill an array with random numbers */ if (ilndexes [iLayer] >= N_GENERATORS_IN A_LAYER*X_SIZE) { ilndexes [iLayer] = 0;
/* fill nResultg with random numbers from the LEVEL ABOVE */ RunOneLayerOfLFGs(iLayer+l, nResult[iLayer]);
} /* get new dynamic key */ if (0 > iLayer | | iLayer > 2) printf ("iLayer = %d\n", iLayer); assert(0 <= iLayer && iLayer <= 2);
/* permutate a segment */ for(i=iIndexes[_Layer],j=0; i< ilndexes [iLayer] +X_SIZE; i++,j++) { ρnDynamicKey[j] = pnResult[i]; }
-Indexes [iLayer] = i;
/**** This routine gets the dynamic key from BBS ****/ static void FillUppermost(void) { int i; for(i=0; i<X_SIZE; i++) { nDynamicKey[CASCADE_DEPTH-l]|ϊ] = SlowButSecureO; }
/**** Tliis routine realizes the 192 (i.e. 3*64) Lagged Fibonacci generators
* It fills an array of size X_SIZE with pseudo-random numbers
* Input parameters:
* iLayer <0, 2> - layer (segment) number, 1 is the fastest segment * nBuffer - output array ****/
#define P 24 #define Q 55 static void RunOneLayerOfLFGs( int iLayer , unsigned long nBufferQ)
{ unsigned int i, k, 1, m; /* indexes */ int iBuffer; unsigned long* piPermutKey; unsigned long* pnDynamicKey = nDyna icKey [iLayer]; assert(P < X_SIZE && Q < X_SIZE); iBuffer=0; for(m=0; m<N_GENERATORS_IN_A_LAYER; m++) { i = 0; k = (i-Q) & (X_SIZE-1);
1 = (i-P) & (X_SIZE-1); pnX = &nX[iLayer] [m] [0]; // get a LFGs piPermutKey = &-PermutKey[iLayer][m] [0]; for(; 1<X_SIZE; i++,k++,l++)
{ pnXfi] = (pnXjk] - pnX[l]); // LFG
// add a random number from permutated dynamic key assert(0 <= piPermurKey[m] && piPermutKey[i] < X_SIZE); nBuffer [iBuffer++] = pnX[i] + pnDynamicKey[piPermutKey[i]]; } for(l = °; k<X_SIZE; i++,k++,l-l-+)
{ pnX[i] = (pnX[k] - pnX|l]); // LFG assert(0 <= piPermutKeyfm] && piPermutKey[i] < X_SIZE); nBuffer[iBuffer++] = pnX[i] + pnDynamicKey[piPermutKey[i]]; } for(k = 0; i<X_SIZE; i++,k++,H-+) { pnX[ι| = (pnX[k] - pnX[l]); // LFG assert(0 <= piPermutKey[m] && piPermutKey[i] < X_SIZE); nBuffer [iBuffer++] = pnX[i] + pnDynamicKey[piPermu_Key[i]];
}
assert(m == N_GENERATORS_IN_A_LAYER && iBuffer
■■ N_GENERATORS_IN_A_LAYER*X_SIZE); if (iLayer < CASCADE_DEPTH - 1) { /* iLayer < 2 */
GetDynamicKeyftLayer); /* 0, 1 */
} else { FfflUppermostO; /* 2 */
}
/*##* me JJ.-JJ- ^ js **** /
/* CryptXOR(unsigned long nBufferQ)
* This function xors the array nBuffer with a stream of pseudorandom numbers
* The buffer is 4096 (i.e. 16 kbytes)32-bit words long ****/ void CryptXOR(unsigned long nBuffer[])
{ unsigned int i, k, 1, m; /* indexes */ int iLayer = 0; int iBuffer; unsigned long* piPermutKey;
/* There is one dynamic key for all 64 PRNGs */ unsigned long* pnDynamicKey = nDynamicKey[iLayer]; assert(P < X_SIZE && Q < X_SIZE); iBuffer = 0;
/* execute this loop for each of the 64 PRNGs */ for(m=0; m<N JGENERATORS N A_LAYER; m++) { i = 0; k = (i-Q) & (X_SIZE-1);
1 = (i-P) & (X_SIZE-1); pnX = &nX[iLayer] [m] [0]; // get a LFGs
piPermutKey = &iPer_nutKey[iLayer] [m] [0]; /* The permutation is different for each of the 64 PRNGs */ for(; 1<X_SIZE; H~f-,k++,l++) { pnX[i] = (pnX[k] - pnX[l]); // LFG
// add a random number from permutated dynamic key assert(0 <= piPermutKeyfm] && piPermutKey[i] < X_SIZE); nBuffer [iBuffer++] Λ= pnX[i] + pnDynamicKey[piPermutKey[i]]; } for(l = 0; k<X_SIZE; i++,k++,l++)
{ pnX[i] = (pnXrk] - pnX|l]); // LFG assert(0 <= piPermutKeyH && piPermutKe [i] < X_SIZE); nBuffer [iBuffer++]
Λ= pnX[i] + pnDynamicKey[piPermutKey[i]]; } for(k = 0; i<X_SIZE; i++,k++,H-+) { pnX[ι] = (pnX[k] - pnXp]); // LFG assert(0 <= piPermutKey[m] && piPermutKey[i] < X_SIZE); nBuffer [iBuffer++] = pnX[i] + pnDynamicKey[piPermutKey[i]];
} // for assert(m == N_GENERATORS_IN_A_LAYER && iBuffer == N_GENERATORS_IN _A_LAYER*X_SIZE); if (iLayer < CASCADE_DEPTH - 1) { /* if (iLayer < 3) */
GetDynamicKey(iLayer); /* 0, 1, 2 */
} else {
FillUppermostO; /* 3 */ }
}
/**** This routine returns one random 32-bit word ****/ unsigned long Random ord(void) { if (iResult == N_GENERATORS_IN A_LAYER*X_SIZE)
{
RunOneLayerOfLFGs(0, nBigBuffer); iResult = 0; // reset index } return nBigBuffer[iResult++]; } /**#* Returns evenly distributed random numbers in the interval <0,255> ****/ unsigned char Rando_r_Byte(void)
{ if (iResult == N_GENERATORS_IN_A_LAYER*X_SIZE)
{ RunOneLayerOfLFGs(0, nBigBuffer); iResult = 0; // reset index
} return (unsigned char)(nBigBuffer[iResult++]*256.0/(0xFFFFFFFF+1.0));
/**** void InitSemiramis(unsigned char * nlnitVal) {
* This function intializes the random number generator
* Input parameters:
* nlnitVal - pointer to an array of 16 bytes holding the key ****/ void InitSemiramis(unsigned char * nlnitVal) { int m, n, i; unsigned short * p_16b_nInitVal = (unsigned short *)nInitVal;
/* copy key to RNG state array, use 8 16-bit words */ for (i=0; i<2*NKEY_LENGTH; i++) { nCongState|ι] = p_16b_nInitVal[i]; }
/* initialize LFGs */ InitArray(nCongState, &nX[0] [0] [0], CASCADE_DEPTH*N_GENERATORS_IN_A_LAYER*X_SIZE); /* Initialize the intermediate layers of random numbers */
InitArray(nCongState, &nResult[0][0], (CASCADE_DEPTH- l)*N_GENERATORS_IN_A_LAYER*X_SIZE); for(n=0; n<CASCADE_DEPTH-l; n++) ilndexes [n] = 0; /* initialize dynamic key */
InitArray(nCongState, &nDynamicKey[0] [0], CASCADE_DEPTH*N_GENERATORS_IN_A_LAYER);
/* initialize slow but secure generator */ InitSlow(nCongState);
/* Initialize the permutations */ for(m=0; m<CASCADE_DEPTH; m++){ for(n=0; n<N_GENERATORS_IN_A_LAYER; n++) { for(i=0; i<X_SIZE; i++)iPermutKey [m] [n] [i] - i;
Shuffle(&iPermutKey[m] [n] [0], X_SIZE); } }
// SlowSec.h //
// Function prototypes void InitSlow(unsigned long nCongStatefJ); unsigned long Fibo(void); unsigned char ByteFibo(void); unsigned long SlowButSecure(void);
// slowsec.c //
#include "InitSem.h" #include "SlowSec.h"
#define LFG_SIZE 4096 #define K8 2281
#define L8 1029
// state of the lagged Fibonacci generator static unsigned int iO, kO, 10; // indexes
static unsigned long __X0[LFG_SIZE]; // circular buffer
// IntiaHzes the simpHfied slow but secure random number generator void InitSlow(unsigned long nCongStateQ)
{ int i;
/* initialize LFGs */ InitArray(nCongState, &nX0[0], K8);
// initialize indexes iO = K8; kO = (Ϊ0-K8) & (LFG_SIZE-1);
10 = (Ϊ0-L8) & (LFG_SIZE-1); for(i=0; i<5000; i++) FiboQ;
}
// Returns evenly distributed 32-bit random numbers static unsigned long Fibo(void)
{ unsigned long nResult; nX0[i0] = (nXOjkO] - nX0[10]); nResult = nX0[i0]; kO = (++k0) & (LFG_SIZE-1);
10 = (++10) & (LFG_SIZE-1); iO = (++i0) & (LFG_SIZE-1); return nResult; }
// SimpHfied implementation of a slow but secure random number generator unsigned long SlowButSecure() { int i; int n; n = (int)((1000.0/CMOD)*CongruentialO) + 516; for(i=0; i<n; i++)
{
FiboO; } return FiboQ;
// InitSem.h //
#define CMOD 714025L
#define NKEY_LENGTH 4 /* The 128-bit key consists of 4 32-bit words */
// Function prototypes void Mixl -(unsigned long Arra [j, int nLength); void Shuffle(unsigned long ArrayQ, int nLength); void Clear(void); void InitCong(unsigned long nSeed); unsigned long Congruential(void); void InitArray(unsigned long nCongStateQ, unsigned long Arrayf], int nLength);
// InitSem.c //
#include "InitSem.h" #include "SlowSech"
#define Al 4096L #define A0 150889L /**** Oldfashioned congruential random number generator ****/ unsigned long nRandom; // The state of the congruential generator void InitCong(unsigned long nSeed)
{ nRandom = nSeed % CMOD;
}
// Returns evenly distributed random numbers // in the interval <0,CMOD-1> unsigned long Congruential(void)
{ nRandom = (Al * nRandom + A0) % CMOD; // generate random number return nRandom;
}
// Returns evenly distributed random numbers
// in the interval <0,255> static unsigned char ByteCong(void)
{ return (unsigned char)(CongruentialO*256.0/CMOD);
}
/**** Tliis routine permutates array ArrayfJ of lengtii nLength ****/ void Mixlt(unsigned long Array Q, int nLength) { int i; long int m; long int temp; for(i=nLength-l; i>0; i-){ m = (CongruentialO » 8) % i; temp = Array [i]; Arrayfi] = Array[m]; Array [m] = temp; }
} /* end Mixlt */
/****This routine permutates array Array of length nLength * using a different random number generator ****/ void Shuffle(unsigned long ArrayQ, int nLength) { int i; long int m; long int temp;
for(i=nLength-l; i>0; _--){ m = (SlowButSecureO » 8) % i; temp = Array[i]; Array [i] = Array [m];
Array[m] = temp; }
} /* end IvBxIt */
/**** This function fills an array with random numbers
* Parameters:
* nCongState - an array of 8 RNG states
* Array - the array of 32-bit words to be filled * nLength - the length of the array ****/ void InitArray(unsigned long nCongStateQ, unsigned long ArrayQ, int nLengtii)
{ int m, i; /* indexes */ unsigned short * p_16b_Array = (unsigned short *) Array;
// Clear array for(m=0; m<nLength; m++) { /* use 32-bit words */
Array[m] = 0L; } for(m=0; m<2*NKEY_LENGTH; ) { /* use 8 16-bit words
*/
InitCong(nCongState[m]); for(i=0; i<100; i++) Congruential(); // exercise congruential generator for(i=0; i<2*nLength; i++) p_16b_Array|ϊ] Λ=
(unsigned short)((OxFFFF+1.0)*(double)CongruentialO/CMOD); nCongState[m++] = CongruentialO; /* remeber RNG state */ InitCong(nCongState[m]); for(i=0; i<100; i++) CongruentialO; // exercise congruential generator
MixIt(Array, nLength); nCongState[m++] = CongruentialO; /* remeber RNG state */
} }