File Coverage

blib/lib/Digest/OMAC/Base.pm
Criterion Covered Total %
statement 84 113 74.3
branch 15 26 57.6
condition 5 5 100.0
subroutine 14 18 77.7
pod 0 8 0.0
total 118 170 69.4


line stmt bran cond sub pod time code
1             package Digest::OMAC::Base;
2              
3 17     17   126 use strict;
  17         33  
  17         718  
4             #use warnings;
5 17     17   100 use Carp;
  17         35  
  17         2347  
6 17     17   19870 use MIME::Base64;
  17         17785  
  17         1463  
7              
8 17     17   210 use constant DEBUG => 0;
  17         38  
  17         1745  
9 17     17   95 use constant UNPACK_CAN_GROUP => $] >= 5.008;
  17         32  
  17         39978  
10              
11             sub new {
12 29     29 0 6260 my ( $class, $key, $cipher, @args ) = @_;
13              
14 29 50       113 if ( ref $key ) {
15 0         0 $cipher = $key;
16 0         0 $key = undef;
17             }
18              
19 29   100     187 $cipher ||= 'Crypt::Rijndael';
20              
21 29         110 my $self = bless {
22             cipher => undef,
23             }, $class;
24              
25 29         248 return $self->_init($key, $cipher, @args);
26             }
27              
28             sub add {
29 28     28 0 683 my ( $self, @msg ) = @_;
30 28         75 my $msg = join('', grep { defined } $self->{saved_block}, @msg);
  58         620  
31              
32 28         69 $self->{ix} += length($msg);
33              
34 28         46 my $c = $self->{cipher};
35 28         70 my $blocksize = $c->blocksize;
36              
37 28         5757 my @blocks = UNPACK_CAN_GROUP
38             ? unpack("(a$blocksize)*", $msg)
39             : ( $msg =~ /(.{1,$blocksize})/sg );
40              
41 28 100       288 return unless @blocks;
42              
43 22 100       58 if ( length($blocks[-1]) < $blocksize ) {
44 8         15 $self->{saved_block} = pop @blocks;
45             } else {
46 14         28 $self->{saved_block} = '';
47             }
48              
49 22 100       53 return unless @blocks;
50              
51 21         33 my $Y = $self->{Y}; # Y[i-1]
52 21         28 my $unenc_y;
53              
54 21         38 foreach my $block ( @blocks ) {
55 46         79 $unenc_y = $block ^ $Y;
56 46         197 $Y = $c->encrypt( $unenc_y ); # Y[i] = E( M[1] xor Y[-1] )
57             }
58              
59 21         45 $self->{unenc_Y} = $unenc_y;
60 21         31 $self->{Y} = $Y;
61              
62 21         65 return;
63             }
64              
65             sub digest {
66 26     26 0 265 my $self = shift;
67              
68 26         47 my $c = $self->{cipher};
69 26         62 my $blocksize = $c->blocksize;
70              
71 26         42 my $last_block = $self->{saved_block};
72              
73 26         30 my $X;
74              
75 26 100 100     138 if ( length($last_block) or !$self->{ix} ) {
76 14         325 my $padded = pack("B*", substr( unpack("B*", $last_block) . "1" . ( '0' x ($blocksize * 8) ), 0, $blocksize * 8 ) );
77 14         50 $X = $padded ^ $self->{Y} ^ $self->{Lu2};
78             } else {
79 12         33 $X = $self->{unenc_Y} ^ $self->{Lu};
80             }
81              
82 26         133 $self->reset;
83              
84 26         229 return $c->encrypt( $X );
85             }
86            
87             sub reset {
88 26     26 0 51 my $self = shift;
89 26         69 my $blocksize = $self->{cipher}->blocksize;
90 26         60 $self->{Y} = "\x00" x $blocksize;
91 26         39 $self->{saved_block} = '';
92 26         41 return $self;
93             }
94              
95              
96             sub _init {
97 29     29   65 my ( $self, $key, $cipher ) = @_;
98              
99 29 50       93 if ( ref $cipher ) {
100 0         0 $self->{cipher} = $cipher;
101             } else {
102 29 100       1927 eval "require $cipher; 1;"
103             or croak "Couldn't load $cipher: $@";
104 28         541 $self->{cipher} = $cipher->new($key);
105             }
106              
107 28         83 $self->{saved_block} = '';
108              
109 28         52 my $c = $self->{cipher};
110              
111 28         367 my $blocksize = $c->blocksize;
112              
113 28         135 my $zero = "\x00" x $blocksize;
114              
115 28         63 $self->{Y} = $zero;
116            
117 28         175 my $L = $self->{cipher}->encrypt($zero);
118            
119 28         75 if (DEBUG) { printf STDERR qq{DEBUG >> L=%s\n}, unpack "H*", $L }
120              
121 28         220 $self->{Lu} = $self->_lu( $blocksize, $L );
122              
123 28         579 if (DEBUG) { printf STDERR qq{DEBUG >> Lu=%s\n}, unpack "H*", $self->{Lu}; }
124              
125 28         164 $self->{Lu2} = $self->_lu2( $blocksize, $L, $self->{Lu} ); # for OMAC2 this is actually Lu^-1, not Lu^2, but we still call it Lu2
126              
127 28         495 if (DEBUG) { printf STDERR qq{DEBUG >> Lu2=%s\n}, unpack "H*", $self->{Lu2}; }
128              
129 28         1075 return $self;
130             }
131              
132             sub _lu {
133 42     42   86 my ( $self, $blocksize, $L ) = @_;
134 42         246 $self->_shift_lu( $L, $self->_lu_constant($blocksize) );
135             }
136              
137             sub _shift_lu {
138 42     42   75 my ( $self, $L, $constant ) = @_;
139              
140             # used to do Bit::Vector's shift_left but that's broken
141 42         425 my ( $msb, $tail ) = unpack("a a*", unpack("B*",$L));
142              
143 42         318 my $Lt = pack("B*", $tail . "0");
144              
145 42 100       95 if ( $msb ) {
146 20         630 return $Lt ^ $constant;
147             } else {
148 22         86 return $Lt;
149             }
150             }
151              
152             sub _lu_constant {
153 42     42   74 my ( $self, $blocksize ) = @_;
154              
155 42 50       117 if ( $blocksize == 16 ) { # 128
    0          
156 42         271 return ( ("\x00" x 15) . "\x87" );
157             } elsif ( $blocksize == 8 ) { # 64
158 0         0 return ( ("\x00" x 7 ) . "\x1b" );
159             } else {
160 0         0 die "Blocksize $blocksize is not supported by OMAC";
161             }
162             }
163              
164             sub _lu2 {
165 0     0   0 die "lu2 needs to be defined by subclass";
166             }
167              
168             # support methods
169             sub hexdigest {
170 20     20 0 192 return unpack 'H*', $_[0]->digest;
171             }
172              
173             sub b64digest {
174 0     0 0   my $result = MIME::Base64::encode($_[0]->digest);
175 0           $result =~ s/=+$//;
176 0           return $result;
177             }
178              
179             sub addfile {
180 0     0 0   my $self = shift;
181 0           my $handle = shift;
182 0           my $n;
183 0           my $buff = '';
184              
185 0           while (($n = read $handle, $buff, 4*1024)) {
186 0           $self->add($buff);
187             }
188 0 0         unless (defined $n) {
189 0           croak "read failed: $!";
190             }
191 0           return $self;
192             }
193              
194             sub add_bits {
195 0     0 0   my $self = shift;
196 0           my $bits;
197             my $nbits;
198              
199 0 0         if (scalar @_ == 1) {
200 0           my $arg = shift;
201 0           $bits = pack 'B*', $arg;
202 0           $nbits = length $arg;
203             }
204             else {
205 0           $bits = shift;
206 0           $nbits = shift;
207             }
208 0 0         if (($nbits % 8) != 0) {
209 0           croak 'Number of bits must be multiple of 8 for this algorithm';
210             }
211 0           return $self->add(substr $bits, 0, $nbits/8);
212             }
213              
214             1;
215             __END__
216              
217             =head1 NAME
218              
219             Digest::OMAC::Base - The One-key CBC MAC message authentication code (base
220             class for OMAC hashes)
221              
222             =head1 SYNOPSIS
223              
224             use base qw(Digest::OMAC::Base);
225              
226             =head1 DESCRIPTION
227              
228             This module is used internally by L<Digest::CMAC>/L<Digest::OMAC1> and
229             L<Digest::OMAC2> (which does different shifting than OMAC1 but is otherwise the
230             same).
231