File Coverage

blib/lib/Crypt/RS14_PP.pm
Criterion Covered Total %
statement 59 68 86.7
branch 6 12 50.0
condition n/a
subroutine 14 17 82.3
pod 4 4 100.0
total 83 101 82.1


line stmt bran cond sub pod time code
1             #!perl -w
2            
3             =begin PerlDox
4            
5             =head1 NAME
6            
7             =cut
8            
9             package Crypt::RS14_PP; #< A pure Perl implementation of RS14, aka "Spritz", encryption algorithm.
10             our $VERSION = '0.02'; #<
11            
12             =head1 SYNOPSIS
13            
14             use Crypt::RS14_PP;
15            
16             my $key = '16 to 64 bytes of key';
17             my $rs14 = Crypt::RS14_PP->new($key);
18             my $ctext = $rs14->encrypt('This is my plain text.');
19             $rs14->set_key($key);
20             my $ptext = $rs14->encrypt($ctext); # or decrypt as both do the same
21             print "$ptext\n"; # prints 'This is my plain text.'
22            
23             =head1 DESCRIPTION
24            
25             RS14, aka "Spritz", is an encryption algorithm, proposed by Ron Rivist
26             and Jacob Schuldt, as a replacement for RC4, created by Ron Rivist.
27             RS14, like RC4, is a stream algorithm. It takes the basic concepts behind
28             RC4, enhancing and updating them for greater security.
29            
30             Being pure Perl, this module is really just a testing tool. An XS or
31             Inline::C implementation will provide far better performance.
32            
33             I While this module's API is a superset of the Crypt:: API, the RS14
34             algorithm is not intended for use with Crypt::CBC or similar. By its
35             nature, it already operates in OFB (Output Feedback) mode.
36            
37             I Only the encrypt/decrypt capabilities of RS14 are implemented.
38            
39             I In this module, encrypt/decrypt use bitwise exclusive-or (C<^>) to
40             encipher/decipher the input, as this is commonly used in stream ciphers.
41             As a consequence, encrypt and decrypt are the same. Other operations are
42             possible. This not specified in the algorithm specification.
43            
44             I To encrypt "wide characters", such as Unicode, the character stream
45             B be encoded into a byte stream before encrypting. (For Unicode, use
46             UTF-8 encoding.) Whatever encoding is used, security is enhanced by excluding
47             any byte order marks.
48            
49             =cut
50            
51 2     2   31562 use warnings;
  2         6  
  2         89  
52 2     2   12 use strict;
  2         4  
  2         208  
53            
54             # only load Carp if needed
55             sub _carp
56             {
57 3     3   27 require Carp;
58 3         748 Carp::carp(@_);
59             }
60             sub _croak
61             {
62 0     0   0 require Carp;
63 0         0 Carp::croak(@_);
64             }
65            
66             # Tried C but causes bitwise ops to treat numbers as signed (see Perl documentation)
67            
68             ## @internal
69            
70             =head2 Constants
71            
72             =cut
73            
74             use constant {
75 2         378 N => 256, #< Number of elements in S-Box.
76             # @note This implementation is byte oriented, so N == 256
77             # @note This implementation assumes N is a power of 2. If not,
78             # update of w will need enhancement to ensure gcd(N,w) == 1,
79             # i.e., N and w must be relatively prime.
80 2     2   12 };
  2         13  
81            
82             use constant {
83 2         1827 A => N + 0, #< index of a (number of nibbles absorbed) in instance array
84             I => N + 1, #< index of i (an internal state index) in instance array
85             J => N + 2, #< index of j (an internal state index) in instance array
86             K => N + 3, #< index of k (an internal state index) in instance array
87             W => N + 4, #< index of w (an internal state index) in instance array
88             Z => N + 5, #< index of z (output state index) in instance array
89             M => N - 1, #< mask for modulo-N operations
90 2     2   17 };
  2         4  
91            
92             ## @endinternal
93            
94             =head2 Class Methods
95            
96             =cut
97            
98             ## Creates a RS14 object and optionally sets the cryptographic key.
99             sub new
100             {
101 3     3 1 568 my ($class,
102             $key #< @param - Key (optional - used for compatability with other Crypt:: modules)
103             ) = @_;
104 3         7 my $self = bless [];
105 3 50       16 $self->set_key($key) if defined $key;
106 3         19 return $self;
107             }
108            
109             =head2 Instance Methods
110            
111             =cut
112            
113             ## Sets the cryptographic key.
114             sub set_key
115             {
116 3     3 1 7 my ($S,
117             $key #< @param - Key - 16 to N/4 bytes of key
118             ) = @_;
119 3 50       7 if (defined $key)
120             {
121 3 50       17 _carp('key too short') if (length($key) < 16);
122 3 50       15 _croak('key too long') if (length($key) > (N / 4));
123 3         23 my @bytes = unpack('C*', $key);
124 3         14 $S->_init(); # only initialize if key is going to be used
125 3         8 $S->_absorb($key);
126 3         9 $S->_shuffle();
127             }
128             }
129            
130             ## Encrypt (or decrypt) the given data bytes. (This function is
131             # identical to C.)
132             # @note Because this is a stream cipher, C.
133             # To encrypt (or decrypt) 2 messages with same key, you must C
134             # before each message. Also, to encrypt and decrypt with same key, you must
135             # C between encrypting and decrypting (or use 2 objects).
136             sub encrypt
137             {
138 0     0 1 0 my $S = $_[0];
139 0         0 my @bytes = unpack('C*', $_[1]); #< @param $string Byte string to encrypt or decrypt
140            
141 0 0       0 _croak('No key set') unless ($$S[A]); # this test assumes key limited to N/4 bytes (and other assumptions)
142 0         0 @bytes = map { ($_ ^ $S->_cipher()) } @bytes;
  0         0  
143 0         0 return pack('C*', @bytes);
144             }
145            
146             ## Decrypt (or encrypt) the given data bytes. (This function is an
147             # alias to C.)
148             sub decrypt
149             {
150             ## @par See L.
151 0     0 1 0 goto &encrypt;
152             }
153            
154             ## @internal
155            
156             ## Update the S-Box state. Update the state with values that
157             # are a complex function of the current values.
158             sub _update
159             {
160 4632     4632   3626 my $S = $_[0];
161 4632         5126 my ($i, $j, $k, $w) = \@$S[I .. W];
162 4632         4485 $$i = ($$i + $$w) & M;
163 4632         4794 $$j = ($$k + $$S[($$j + $$S[$$i]) & M]) & M;
164 4632         4256 $$k = ($$i + $$k + $$S[$$j]) & M;
165 4632         9140 @$S[$$j, $$i] = @$S[$$i, $$j];
166             }
167            
168             ## Produce next byte of the cipher stream. The output is a
169             # complex function of the state and itself. This is a form
170             # of OFB mode (Output Feedback).
171             sub _cipher
172             {
173 24     24   1449 my $S = $_[0];
174 24         58 $S->_update();
175 24         35 my ($i, $j, $k, $w, $z) = \@$S[I .. Z];
176 24         84 $$z = ($$S[($$j + $$S[($$i + $$S[($$z + $$k) & M] & M)]) & M]) & M;
177             }
178            
179             ## Thoroughly mix the S-Box. Repeatedly call _update to provide
180             # very complex new values to the state.
181             sub _whip
182             {
183 9     9   15 my $S = $_[0];
184 9         20 $S->_update() for (0 .. ((N * 2) - 1));
185 9         21 $$S[W] += 2; ## @note If N not a power of 2, a complex update is
186             # required to keep w relatively prime to N
187             }
188            
189             ## More mixing - this step is irreversible. It intentionally looses
190             # information about the current state. Specifically, it maps 2**(N/2)
191             # states to 1. This makes it harder to reverse engineer the key.
192             sub _crush
193             {
194 6     6   10 my $S = $_[0];
195 6         14 for my $v (0 .. (int(N / 2) - 1))
196             {
197 768 100       1243 if ($$S[$v] > $$S[(N - 1) - $v])
198             {
199 365         513 @$S[$v, (N - 1) - $v] = @$S[(N - 1) - $v, $v];
200             }
201             }
202             }
203            
204             ## The mix master
205             sub _shuffle
206             {
207 3     3   10 $_[0]->_whip();
208 3         9 $_[0]->_crush();
209 3         7 $_[0]->_whip();
210 3         10 $_[0]->_crush();
211 3         9 $_[0]->_whip();
212 3         16 $_[0]->[A] = 0;
213             }
214            
215             ## Bring in key data
216             # @note Byte oriented implementation
217             # @note Assumes key limited to N/2 nibbles (N/4 bytes). Otherwise
218             # must check if a >= (N/2) to trigger a _shuffle.
219             sub _absorb
220             {
221 3     3   5 my $S = $_[0];
222 3         5 my $a = \$$S[A];
223 3         12 for (split '', $_[1]) #< @param $string Key string (bytes) to absorb
224             {
225 14         13 my $t = ord($_);
226 14         25 for my $x ((0x0f & $t), ((0xf0 & $t) >> 4))
227             {
228             # (see note) $S->_shuffle() if ($$a >= int(N / 2));
229 28         41 @$S[int(N / 2) + $x, $$a] = @$S[$$a, int(N / 2) + $x];
230 28         40 $$a++;
231             }
232             }
233             }
234            
235             ## Initialize the S-Box and state variables
236             sub _init
237             {
238 3     3   5 my $S = $_[0];
239             # a i j k w z
240 3         26 @$S[A .. Z] = (0, 0, 0, 0, 1, 0);
241 3         265 $$S[$_] = $_ for (0 .. (N - 1));
242             }
243            
244             ## @endinternal
245            
246             1;
247            
248             __DATA__