=head1 NAME Cipher::Arcfour - Arcfour (RC4-compatible) stream cipher =head1 SYNOPSIS $ciphertext = Cipher::Arcfour.encipher($plaintext, :key($key)); &cipher := Cipher::Arcfour.encipherer(:key($key)); print cipher($_) for =$IN; print cipher(); my $cipher = Cipher::Arcfour.new(:key($key)); @output = gather { take $cipher.cipher($_) for @input }; =head1 DESCRIPTION Arcfour is a very simple (less than fifteen readable lines) but surprisingly secure stream cipher. It is believed to be compatible with RSA Security's RC4(tm) cipher, in that keys should produce the same keystream and thus text should be enciphered and deciphered the same way. RC4 and Arcfour are used in standards such as SSH, SSL and TLS, WEP, and WPA, as well as many non-standard cryptosystems. Arcfour operates on whole bytes, XORing elements of a constantly-transforming state table with each byte of plaintext or ciphertext. (It doesn't distinguish between enciphering and deciphering operations.) Although it is secure when used properly, using it incorrectly can lead to serious insecurity; see the L section for details. (In particular, note that the serious insecurities plaguing WEP were caused by improper use of RC4.) =head2 Interface The interface for Cipher::Arcfour is largely defined by L and its parent module, L; for example, the methods used for actual cryptography are there. See those modules for details. The C constructor takes only one option, C<:key>, containing the key. Arcfour keys can be any random number between 40 and 128 bits long, rounded to the nearest byte (but see L for important caveats). Cipher::Arcfour also includes several attributes which might be interesting to people interested in the algorithm itself. The attributes in question are C, C and C. To understand how to interpret them, see the Wikipedia article on RC4, I by Bruce Schneier, or most cryptography resources written in the last ten years or so. =head1 SECURITY Although Arcfour is perfectly secure when used properly, it has several known problems and weaknesses. =over 4 =item Initial portions of Arcfour's keystreams are weak. Basically, the first few bytes of data aren't encrypted well. If your application allows, skip over a kilobyte or so by calling C with a number of bytes and throwing the return value away. Encryptions after this point will be using stronger portions of the keystream. (Just remember to do it at both ends!) =item Encrypting with the exact same key twice can compromise both encryptions. If two different data streams are enciphered with the same key, the encryption can be removed by XORing them together; you might not be able to derive the plaintext from the result, but any cryptographer worth the name can. To avoid this, make sure you combine the key with a nonce or initialization vector, a one-time random number. (It will need to be sent to the other end along with the ciphertext, but it's safe to send it in the clear.) =item Arcfour is susceptible to related-key attacks. This means that you should strive to use only truly random keys; Perl's C function is not good enough. (You should be doing this anyway, though.) It also means that the nonce or initialization vector should be hashed with the key, not merely appended to it; appending can create precisely the sort of weak keying that can be exploited by a cryptoanalyst. Use 128 bits of a good cryptographic hashing/authentication algorithm like HMAC-SHA-256 for this. =back 4 =head1 SEE ALSO L, L Bruce Schneier. I. 1995, published by John Wiley & Sons, Inc. =head1 COPYRIGHT Copyright (C) 2005 Brent Royal-Gordon . This code is free software, and may be used, distributed and/or modified under the same terms as Perl itself. =cut use Cipher::Stream; class Cipher::Arcfour is Cipher::Stream; has Byte @.state; has Int $.i; has Int $.j; submethod BUILD($key?) { my @key; if $key.isa(Array) { @key = @$key } else { @key = map &ord, $key.split } .zeroize(); # Initialize state table my $j = 0; for 0..255 -> $i { ($j += @.state[$i] + @key[$i % @key.elems]) %= 256; @.state[$i, $j] = @.state[$j, $i]; } } method zeroize() { @.state[$_] = $_ for 0..255; $.i = $.j = 0; } method generate_keystream(Int $n) returns Array { return gather { for 1..$n { ++$.i; $.i %= 256; ($.j += @.state[$.i]) %= 256; @.state[$.i, $.j] = @.state[$.j, $.i]; take @.state[ (@.state[$.i] + @.state[$.j]) % 256 ]; } } }