File Coverage

blib/lib/Crypt/OOEnigma/Rotor.pm
Criterion Covered Total %
statement 59 60 98.3
branch 8 12 66.6
condition 1 3 33.3
subroutine 10 10 100.0
pod 0 6 0.0
total 78 91 85.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             #
3             # Rotor.pm:
4             #
5             # Copyright (c) 2002 Ambriel Consulting
6             # sjb Sun Mar 17 20:43:56 GMT 2002
7             #
8              
9             package Crypt::OOEnigma::Rotor ;
10             $VERSION="0.3";
11              
12             =head1 NAME
13              
14             Crypt::OOEnigma::Rotor - The Rotor object for use in Crypt::OOEnigma
15              
16             =head1 SYNOPSIS
17              
18             my $rotor = Crypt::OOEnigma::Rotor->new();
19            
20             # OR
21             my $subHash ={ # the substitution code for the cipher for all A..Z
22             A => "Z",
23             B => "G",
24             # etc
25             };
26             my $freq = 2 ; # The number of letters enciphered per rotation
27             my $start_position = 20 ; # modulo 26
28             my $rotor = Crypt::OOEnigma::Rotor->new(cipher => $subHash,
29             freq => $freq
30             start_position => $pos);
31              
32              
33             # for internal use by Enigma machines:
34             $rotor->init(); # returns the Rotor to its initial state
35             my $op = $rotor->encode($some_letter); # encode a letter in the forward direction
36             $op = $rotor->revencode($some_letter); # encode a letter in the reverse direction
37             $rotor->next(); # prepare for the next encoding, rotating as required
38             $rotor->rotate(); # rotate to the next position
39              
40             =head1 DESCRIPTION
41              
42             This is the Rotor for use in Crypt::OOEnigmas. Use it when you want to
43             create your own Enigmas with specific properties.
44              
45             =head1 NOTES
46              
47             None
48              
49             =head1 BUGS and CAVEATS
50              
51             =head2 Enigma is weak!
52              
53             Cryptographers talk of the strength of a cryptographic algorithm in term of
54             whether it is computationally feasible to break it. It is, of course,
55             computationally feasible to break an Enigma cipher so don't use it for anything
56             serious!
57              
58             =head1 HISTORY
59              
60             This package was created in spring 2002 as an exercise in OO Perl and preparing
61             modules properly for CPAN. More importantly, the Enigma is interesting.
62              
63             CPAN already has a Crypt::Enigma which is not object oriented and implements
64             only one Enigma (whereas you can create any Enigma-like machine with these
65             objects). Hence the package name Crypt::OOEnigma
66              
67             =head1 SEE ALSO
68              
69             The Pleasures of Counting, T W Korner, CUP 1996. A great book for anyone with
70             the slightest interest in mathematics:
71             ISBN 0 521 56087 X hardback
72             ISBN 0 521 56823 4 paperback
73              
74             The Enigmas:
75             Crypt::OOEnigma::Military
76             Crypt::OOEnigma::Commercial
77              
78             The components:
79             Crypt::OOEnigma::Rotor
80             Crypt::OOEnigma::Reflector
81             Crypt::OOEnigma::Plugboard
82              
83             =head1 AUTHOR
84              
85             S J Baker, Ambriel Consulting, http://ambrielconsulting.com
86              
87             =head1 COPYRIGHT
88              
89             This package is licenced under the same terms as Perl itself.
90              
91             =cut
92              
93 9     9   196159 use Storable ;
  9         47339  
  9         648  
94 9     9   83 use Carp ;
  9         18  
  9         2171  
95              
96             # This rotor's substitution, identity by default
97             my @alpha = (A..Z);
98             my $subst = {};
99             foreach my $val (@alpha){
100             $subst->{$val} = $val ;
101             }
102              
103             my %fields = (
104             cipher => $subst,
105             current_cipher => {},
106             inverse_cipher => {},
107             start_position => 0,
108             freq => 1,
109             use_count => 0
110             );
111              
112             # use Autoloading for accessors
113 9     9   10092 use subs qw(cipher current_cipher inverse_cipher start_position freq use_count);
  9         233  
  9         42  
114              
115             sub new {
116 37     37 0 519 my $invocant = shift ;
117 37   33     170 my $class = ref($invocant) || $invocant ;
118 37         218 my $self = { %fields, @_ } ;
119 37         107 bless $self, $class ;
120 37         87 $self->init(); # rotate the rotor to the correct position
121 37         141 return $self ;
122             }
123              
124             sub init {
125 654     654 0 53721 my $self = shift ;
126             # the current_cipher is based on the initial wiring of the rotor
127 654         2623 $self->current_cipher(Storable::dclone($self->cipher()));
128             # Set the rotor to the correct position
129 654         5996 $self->rotate($self->start_position());
130             # and set the appropriate reverse cipher
131 654         972 my %inverse = reverse( %{$self->current_cipher()} );
  654         2066  
132 654         3331 $self->inverse_cipher(\%inverse);
133             # reset the use count
134 654         3828 $self->use_count(0);
135             }
136              
137             sub encode {
138 17521     17521 0 1605576 my $self = shift ;
139 17521         24105 my $source = shift;
140 17521 50       66578 croak("Give me uppercase letters only") unless( $source =~ /[A-Z]/);
141 17521         63932 my $result = $self->current_cipher()->{$source};
142 17521         101164 $self->use_count($self->use_count() + 1);
143 17521         65816 return $result;
144             }
145              
146             # "reverse encode"
147             sub revencode {
148 17484     17484 0 74219 my $self = shift ;
149 17484         24461 my $source = shift;
150 17484         54877 return $self->inverse_cipher()->{$source};
151             }
152              
153             sub next{
154 15306     15306 0 63374 my $self = shift ;
155             # rotate if required
156 15306 100       42736 if( ($self->use_count() % $self->freq()) == 0 ){
157 9078         16791 $self->rotate(1);
158             }
159             }
160              
161             sub rotate {
162             # TODO consider efficiency
163 9813     9813 0 63549 my $self = shift ;
164 9813         9636 my $places = shift;
165 9813         57854 my @alpha = (A..Z);
166            
167             # get the old substitution and rotate it
168 9813         11619 my @sub = ();
169 9813         12951 foreach my $key (@alpha){
170 255138         755405 push @sub, $self->current_cipher()->{$key};
171             }
172 9813         26886 for(my $i = 0 ; $i < $places ; ++$i){
173 12955         39532 unshift @sub, (pop @sub);
174             }
175              
176             # create a new substitution hash from the new substitution
177 9813         13823 my $newSub = {};
178 9813         14204 foreach my $key (@alpha){
179 255138         399416 $newSub->{$key} = shift @sub ;
180             }
181              
182             # set up the new ciphers
183 9813         33762 $self->current_cipher($newSub);
184 9813         50966 my %inverse = reverse( %{$self->current_cipher()} );
  9813         33626  
185 9813         50725 $self->inverse_cipher(\%inverse);
186              
187 9813         88981 return ;
188             }
189              
190             sub AUTOLOAD {
191 389513     389513   459427 my $self = shift;
192             # only access instance methods not class methods
193 389513 50       711367 croak "$self is not an object" unless(ref($self));
194 389513         475236 my $name = our $AUTOLOAD;
195 389513 50       828976 return if($name =~ /::DESTROY/ );
196 389513         1117230 $name =~ s/.*://; # strip fully-qualified portion
197 389513 50       861238 unless (exists $self->{$name} ) {
198 0         0 croak "Can't access `$name' field in object of class $self";
199             }
200 389513 100       628525 if (@_) {
201 39110         81585 return $self->{$name} = shift;
202             } else {
203 350403         1287184 return $self->{$name};
204             }
205             }
206              
207             1;