File Coverage

lib/Text/Cipher/KeywordAlphabet.pm
Criterion Covered Total %
statement 39 39 100.0
branch 11 12 91.6
condition 3 3 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 63 64 98.4


line stmt bran cond sub pod time code
1             ###############################################################################
2             # Purpose : Substitution cipher based on a keyword alphabet
3             # Author : John Alden
4             # Created : Jan 2005
5             # CVS : $Id: KeywordAlphabet.pm,v 1.5 2005/03/20 20:02:11 aldenj20 Exp $
6             ###############################################################################
7              
8             package Text::Cipher::KeywordAlphabet;
9              
10 1     1   888 use strict;
  1         2  
  1         35  
11 1     1   771 use Text::Cipher;
  1         2760  
  1         37  
12 1     1   22 use Carp;
  1         2  
  1         72  
13 1     1   6 use vars qw($VERSION $AUTOLOAD);
  1         2  
  1         775  
14             $VERSION = sprintf "%d.%03d", (q$Revision: 1.5 $ =~ /: (\d+)\.(\d+)/);
15              
16             sub new {
17 7     7 1 11089 my ($class, $keywords, $offset) = @_;
18 7 100       203 croak("offset must be an integer") unless($offset =~ /^\-?\d*$/); #Integer or blank
19 6         8 my %seen;
20 6 100       26 my $alphabet = join("", map {uc} grep {/^[a-z]$/i && !$seen{$_}++} (split //, $keywords), 'a'..'z');
  156         231  
  186         1031  
21 6         39 $alphabet = _rotate_alphabet($alphabet, $offset);
22 6         33 my $self = {
23 6         862 cipher => new Text::Cipher(join("", 'A'..'Z', 'a'..'z'), join("", $alphabet, map {lc} $alphabet)),
24 6         29 decipher => new Text::Cipher(join("", $alphabet, map {lc} $alphabet), join("", 'A'..'Z', 'a'..'z')),
25             alphabet => $alphabet
26             };
27 6         574 return bless($self, $class);
28             }
29              
30             sub alphabet {
31 6     6 1 2029 my ($self) = shift;
32 6         49 return $self->{alphabet};
33             }
34              
35             sub AUTOLOAD {
36 2     2   1483 my ($self) = shift;
37            
38             # DESTROY messages should never be propagated
39 2 50       8 return if $AUTOLOAD =~ /::DESTROY$/;
40              
41             # Remove the package name
42 2         4 my $package = __PACKAGE__;
43 2         33 $AUTOLOAD =~ s/^${package}:://;
44              
45             # Pass on to either the enciphering or deciphering object
46 2 100       7 if($AUTOLOAD =~ /^decipher/) {
47 1         3 $AUTOLOAD =~ s/^decipher/encipher/;
48 1         5 $self->{decipher}->$AUTOLOAD(@_);
49             } else {
50 1         7 $self->{cipher}->$AUTOLOAD(@_);
51             }
52             }
53              
54             # Based on routine in Text::Shift
55             sub _rotate_alphabet {
56             # Get parameters
57 6     6   10 my($string,$mag) = @_;
58 6         10 my $strlng = length($string);
59            
60             # Handle outliers
61 6 100 100     41 $mag %= $strlng if(abs($mag) > $strlng or $mag < 0);
62              
63             # Return rotated string
64 6 100       17 return $string if($mag == 0);
65 4         12 $string .= substr($string,0,$mag, "");
66 4         8 return $string;
67             }
68              
69             1;
70              
71             =head1 NAME
72              
73             Text::Cipher::KeywordAlphabet - Substitution cipher based on a keyword alphabet
74              
75             =head1 SYNOPSIS
76              
77             #Create a keyword alphabet with a left shift of 5
78             $cipher = new Text::Cipher::KeywordAlphabet("the quick brown fox", -5);
79              
80             #Fetch the generated alphabet
81             $keyword_alphabet = $cipher->alphabet();
82              
83             #Encipher a string
84             $ciphered = $cipher->encipher($message);
85              
86             #Decipher an enciphered message
87             $message = $cipher->decipher($ciphered);
88              
89             #Some convenience methods
90             $cipher->encipher_scalar(\$some_scalar);
91             $cipher->decipher_scalar(\$some_scalar);
92             @ciphered = $cipher->encipher_list(@list);
93             @list = $cipher->decipher_list(@ciphered);
94             $cipher->encipher_array(\@some_array);
95             $cipher->decipher_array(\@some_array);
96              
97             #Other uses
98             $null_cipher = new Text::Cipher::KeywordAlphabet(); #no-op cipher
99             $rot13_cipher = new Text::Cipher::KeywordAlphabet(undef, 13); #Caesar cipher
100              
101             =head1 DESCRIPTION
102              
103             This module generates a monoalphabetic substitution cipher from a set of words, resulting in what's sometimes referred to as a "keyword (generated) alphabet".
104             Here's a good definition, plagiarised from an anonymous source:
105              
106             "A keyword alphabet is formed by taking a word or phrase, deleting the second and subsequent occurrence of each letter and then writing the remaining letters of the alphabet in order.
107             Encipherment is achieved by replacing each plaintext letter by the letter that appears N letters later in the (cyclic) keyword alphabet."
108              
109             The keyword alphabet is case-insensitive - both uppercase and lowercase characters will be transformed with the same mapping.
110             The offset (N in the definition above) can be a positive or negative integer.
111              
112             L is an introductory tutorial on how substitution ciphers can be broken.
113             L contains a full worked example.
114             L provides an online substitution cipher breaker.
115              
116             At the risk of stating the obvious, since substitution ciphers are easy to break, it's advisable not to use them for protecting important data.
117             Look at some of the more heavy-duty ciphers in the Crypt:: namespace which plug into Crypt::CBC if you want to protect data.
118              
119             =head1 METHODS
120              
121             =over 4
122              
123             =item $obj = new Text::Cipher::KeywordAlphabet($keyword_phrase, $offset)
124              
125             Create a new keyword alphabet
126              
127             =item $keyword_alphabet = $obj->alphabet();
128              
129             Return the keyword alphabet created by the constructor
130              
131             =item $ciphered = $obj->encipher($message)
132              
133             Enciphers a string using the keyword alphabet
134              
135             =item $message = $obj->decipher($ciphered)
136              
137             Reverse of encipher()
138              
139             =item $obj->encipher_scalar(\$some_scalar);
140              
141             By-reference equivalent of encipher()
142              
143             =item $obj->decipher_scalar(\$some_scalar);
144              
145             By-reference equivalent of decipher()
146              
147             =item @ciphered = $obj->encipher_list(@list);
148              
149             Convenience method provided by Text::Cipher
150              
151             =item @list = $obj->decipher_list(@ciphered);
152              
153             Reverse of encipher_list().
154              
155             =item $obj->encipher_array(\@some_array);
156              
157             Convenience method provided by Text::Cipher
158              
159             =item $obj->decipher_array(\@some_array);
160              
161             Reverse of encipher_array().
162              
163             =back
164              
165             =head1 VERSION
166              
167             See $Text::Cipher::KeywordAlphabet::VERSION.
168             Last edit: $Revision: 1.5 $ on $Date: 2005/03/20 20:02:11 $
169              
170             =head1 BUGS
171              
172             None known. This module has not been used heavily in production so it's not impossible a bug may have slipped through the unit tests.
173             Bug reports are welcome, particularly with patches & test cases.
174              
175             =head1 AUTHOR
176              
177             John Alden
178              
179             =head1 SEE ALSO
180              
181             =over 4
182              
183             =item Text::Cipher and Regexp::Tr
184              
185             Useful building blocks for substitution ciphers
186              
187             =item Text::Shift and Crypt::Rot13
188              
189             Caesar (aka shift or rot-N) ciphers (see L)
190              
191             =item Crypt::Caesar
192              
193             Crack Caesar ciphers using letter frequency (see L)
194              
195             =item Crypt::Vigenere
196              
197             Vigenere polyalphabetic cipher (see L)
198              
199             =item Crypt::Enigma and Crypt::OOEnigma
200              
201             Implementations of Enigma ciphers (see L)
202              
203             =back
204              
205             =head1 COPYRIGHT AND LICENSE
206              
207             Copyright 2005 by John Alden
208              
209             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
210              
211             =cut