=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</SECURITY> 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<Cipher::Stream> and 
its parent module, L<Cipher>; for example, the methods used for actual 
cryptography are there.  See those modules for details.

The C<new> 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</SECURITY> 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<i>, C<j> and C<state>.  To understand how to interpret them, see the 
Wikipedia article on RC4, I<Applied Cryptography> 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<generate_keystream> 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<rand> 
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<Cipher>, L<Cipher::Stream>

Bruce Schneier. I<Applied Cryptography, Second Edition>. 1995, published by 
John Wiley & Sons, Inc.

=head1 COPYRIGHT

Copyright (C) 2005 Brent Royal-Gordon <brent@brentdax.com>.

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 ];
        }
    }
}
