File Coverage

blib/lib/Net/Address/IP/Cipher.pm
Criterion Covered Total %
statement 90 139 64.7
branch 8 16 50.0
condition 1 3 33.3
subroutine 13 16 81.2
pod 3 3 100.0
total 115 177 64.9


line stmt bran cond sub pod time code
1             package Net::Address::IP::Cipher 0.4;
2              
3 3     3   156272 use 5.012;
  3         28  
4 3     3   15 use strict;
  3         5  
  3         66  
5 3     3   13 use warnings FATAL => 'all';
  3         6  
  3         126  
6              
7 3     3   16 use Carp;
  3         6  
  3         216  
8              
9 3     3   1787 use Net::IP qw(:PROC);
  3         132505  
  3         1092  
10 3     3   1362 use Crypt::KeyDerivation qw(pbkdf2);
  3         17354  
  3         153  
11 3     3   1190 use Crypt::Cipher::AES;
  3         2404  
  3         3203  
12              
13             =head1 NAME
14              
15             Net::Address::IP::Cipher - IPv6 / IPv4 address encryption to a valid
16             address, for pseudo anonymization.
17              
18             =head1 VERSION
19              
20             Version 0.4
21              
22             =head1 SYNOPSIS
23              
24             Net::Address::IP::Cipher encrypts and decrypts IPv6 and IPv4 addresses
25             to another valid IPv6/v4 address, using a secret key, in a way that's
26             impossible to guess the original IP without the key.
27              
28             use Net::Address::IP::Cipher;
29              
30             my $ipcipher = Net::Address::IP::Cipher->new(
31             password => 'super secret'
32             );
33             my $enc = $ipcipher->enc('::1');
34             print $enc; # 3a3e:7137:6e36:5ecd:4d31:e516:cf47:ec1b
35              
36             It's intended use is to pseudo-anonymize IPs from logs, packet captures,
37             and other analysis. By this way you benefit of having still valid IP
38             addresses and be able to group streams of several messagess, but without
39             revealing the source.
40              
41             This module implements in native perl language the "ipcipher"
42             specification from:
43             L
44              
45              
46             =head1 PREREQUISITES
47              
48             This module requires L for v6/v4 handling and L for
49             all crypto stuff (L, L).
50              
51             =head1 METHODS
52              
53             =head2 new
54              
55             Creates a new Net::Address::IP::Cipher object. You must indicate
56             the secret key for encryption/decryption:
57              
58             my $ipcipher = Net::Address::IP::Cipher->new(password => 'super secret');
59              
60             The key should be declared in either one of two formats:
61              
62             password => 'super secret'
63              
64             for any string used as a password, or
65              
66             barekey => 'bb8dcd7be9a6f43b3304c640d7d7103c'
67              
68             for an hexadecimal representation of a 128-bit key.
69              
70             If you provide both, just the 'password' format will be used.
71              
72             =cut
73              
74             sub new {
75 2     2 1 2147 my $this = shift;
76 2         10 my %params = @_;
77              
78 2   33     16 my $class = ref($this) || $this;
79              
80 2         4 my $key;
81              
82 2 100       10 if ($params{'password'}) {
    50          
83 1         85677 $key = pbkdf2($params{'password'}, 'ipcipheripcipher', 50000, 'SHA1', 16);
84             }
85             elsif ($params{'barekey'}) {
86 1         12 $key = pack 'H*', $params{'barekey'};
87 1 50       6 croak("If you provide a 'barekey' it should be in hexadecimal format, and its lenght should be 128 bits") unless length($key) == 16;
88             }
89              
90 2         13 my $self = {};
91 2         7 bless $self, $class;
92              
93 2         18 $self->{'PRIVKEY'} = $key;
94 2 50       106 $self->{'CIPHER'} = Crypt::Cipher::AES->new($key)
95             or die "Can't create AES cipher, please check your key!";
96              
97 2         18 return $self;
98             }
99              
100             # PRIVATE FUNCTIONS
101              
102             sub _xor4 {
103 24     24   38 my $ps = shift;
104 24         31 my $pk = shift;
105              
106 24         32 my @out;
107 24         46 foreach my $i (0..3) {
108 96         158 push @out, ($ps->[$i] ^ $pk->[$i]) & 0xff;
109             }
110              
111 24         53 return @out;
112             }
113              
114             sub _rotl {
115 108     108   163 my ($b, $r) = @_;
116              
117 108         185 return (($b << $r) & 0xff) | ($b >> (8 - $r));
118             }
119              
120             sub _permute_fwd {
121 18     18   24 my $b = shift;
122              
123 18         27 $b->[0] += $b->[1];
124 18         25 $b->[2] += $b->[3];
125 18         24 $b->[0] &= 0xff;
126 18         24 $b->[2] &= 0xff;
127 18         34 $b->[1] = &_rotl($b->[1], 2);
128 18         33 $b->[3] = &_rotl($b->[3], 5);
129 18         27 $b->[1] ^= $b->[0];
130 18         39 $b->[3] ^= $b->[2];
131 18         29 $b->[0] = &_rotl($b->[0], 4);
132 18         27 $b->[0] += $b->[3];
133 18         26 $b->[2] += $b->[1];
134 18         23 $b->[0] &= 0xff;
135 18         34 $b->[2] &= 0xff;
136 18         34 $b->[1] = &_rotl($b->[1], 3);
137 18         29 $b->[3] = &_rotl($b->[3], 7);
138 18         27 $b->[1] ^= $b->[2];
139 18         24 $b->[3] ^= $b->[0];
140 18         28 $b->[2] = &_rotl($b->[2], 4);
141              
142 18         41 return $b;
143             }
144              
145             sub _permute_bwd {
146 0     0   0 my $b = shift;
147              
148 0         0 $b->[2] = &_rotl($b->[2], 4);
149 0         0 $b->[1] ^= $b->[2];
150 0         0 $b->[3] ^= $b->[0];
151 0         0 $b->[1] = &_rotl($b->[1], 5);
152 0         0 $b->[3] = &_rotl($b->[3], 1);
153 0         0 $b->[0] -= $b->[3];
154 0         0 $b->[2] -= $b->[1];
155 0         0 $b->[0] &= 0xff;
156 0         0 $b->[2] &= 0xff;
157 0         0 $b->[0] = &_rotl($b->[0], 4);
158 0         0 $b->[1] ^= $b->[0];
159 0         0 $b->[3] ^= $b->[2];
160 0         0 $b->[1] = &_rotl($b->[1], 6);
161 0         0 $b->[3] = &_rotl($b->[3], 3);
162 0         0 $b->[0] -= $b->[1];
163 0         0 $b->[2] -= $b->[3];
164 0         0 $b->[0] &= 0xff;
165 0         0 $b->[2] &= 0xff;
166              
167 0         0 return $b;
168             }
169              
170              
171             sub _encrypt {
172 6     6   14 my ($key, $ip) = @_;
173              
174 6         45 my @key = map {unpack('C', $_) } split //, $key;
  96         172  
175 6         27 my @state = split /\./, $ip;
176              
177 6         20 my @pedazo = @key[0..3];
178 6         20 @state = &_xor4(\@state, \@pedazo);
179 6         11 @state = @{&_permute_fwd(\@state)};
  6         19  
180 6         18 @pedazo = @key[4..7];
181 6         27 @state = &_xor4(\@state, \@pedazo);
182 6         13 @state = @{&_permute_fwd(\@state)};
  6         12  
183 6         14 @pedazo = @key[8..11];
184 6         13 @state = &_xor4(\@state, \@pedazo);
185 6         11 @state = @{&_permute_fwd(\@state)};
  6         10  
186 6         17 @pedazo = @key[12..15];
187 6         11 @state = &_xor4(\@state, \@pedazo);
188              
189 6         43 return join '.', @state;
190             }
191              
192             sub _decrypt {
193 0     0   0 my ($key, $ip) = @_;
194              
195 0         0 my @key = map {unpack('C', $_) } split //, $key;
  0         0  
196 0         0 my @state = split /\./, $ip;
197              
198 0         0 my @pedazo = @key[12..15];
199 0         0 @state = &_xor4(\@state, \@pedazo);
200 0         0 @state = @{&_permute_bwd(\@state)};
  0         0  
201 0         0 @pedazo = @key[8..11];
202 0         0 @state = &_xor4(\@state, \@pedazo);
203 0         0 @state = @{&_permute_bwd(\@state)};
  0         0  
204 0         0 @pedazo = @key[4..7];
205 0         0 @state = &_xor4(\@state, \@pedazo);
206 0         0 @state = @{&_permute_bwd(\@state)};
  0         0  
207 0         0 @pedazo = @key[0..3];
208 0         0 @state = &_xor4(\@state, \@pedazo);
209              
210 0         0 return join '.', @state;
211             }
212              
213              
214             =head2 enc
215              
216             Receive an IPv6 or IPv4 string address, in any valid format
217             for Net::IP, and returns the encrypted version as string.
218              
219             my $enc = $ipcipher->enc('::1');
220             print $enc; # b733:fb7:c957:82fc:3d67:e7c3:a667:28da
221              
222             =cut
223              
224             sub enc {
225 12     12 1 3050 my $self = shift;
226 12         46 my $ipin = shift;
227              
228 12         18 my $out;
229              
230 12 50       57 my $ipvx = new Net::IP($ipin) or croak 'IP not valid: ' . (Net::IP::Error());
231              
232 12 100       59890 if ($ipvx->version == 6) {
233 6         45 my $ciphertext = $self->{'CIPHER'}->encrypt(pack('B*', $ipvx->binip));
234              
235 6         98 my $enc = new Net::IP(ip_bintoip(unpack('B*', $ciphertext), 6));
236 6         9231 $out = $enc->short;
237             }
238             else {
239 6         45 $out = &_encrypt($self->{'PRIVKEY'}, $ipin);
240             }
241              
242 12         545 return $out;
243             }
244              
245             =head2 dec
246              
247             Receive and IPv6 or IPv4 string address in its encrypted version,
248             and returns the decrypted IP string.
249              
250             my $dec = $ipcipher->dec('b733:fb7:c957:82fc:3d67:e7c3:a667:28da');
251             print $dec; # ::1
252              
253             =cut
254              
255             sub dec {
256 0     0 1   my $self = shift;
257 0           my $ipin = shift;
258              
259 0           my $out;
260              
261 0 0         my $ipvx = new Net::IP($ipin) or croak 'IP not valid: ' . (Net::IP::Error());
262              
263 0 0         if ($ipvx->version == 6) {
264 0           my $plain = $self->{'CIPHER'}->decrypt(pack('B*', $ipvx->binip));
265 0           my $dec = new Net::IP(ip_bintoip(unpack('B*', $plain), 6));
266 0           $out = $dec->short;
267             }
268             else {
269 0           $out = &_decrypt($self->{'PRIVKEY'}, $ipin);
270             }
271              
272 0           return $out;
273             }
274              
275             =head1 AUTHOR
276              
277             Hugo Salgado, C<< >>
278              
279             =head1 BUGS
280              
281             Please report any bugs or feature requests to C, or through
282             the web interface at L. I will be notified, and then you'll
283             automatically be notified of progress on your bug as I make changes.
284              
285              
286              
287              
288             =head1 SUPPORT
289              
290             You can find documentation for this module with the perldoc command.
291              
292             perldoc Net::Address::IP::Cipher
293              
294              
295             You can also look for information at:
296              
297             =over 4
298              
299             =item * RT: CPAN's request tracker (report bugs here)
300              
301             L
302              
303             =item * AnnoCPAN: Annotated CPAN documentation
304              
305             L
306              
307             =item * CPAN Ratings
308              
309             L
310              
311             =item * Search CPAN
312              
313             L
314              
315             =back
316              
317             =head1 REPOSITORY
318              
319             L
320              
321             =head1 ACKNOWLEDGEMENTS
322              
323             The v4 version is based on the original ipcrypt python version from
324             Jean-Philippe Aumasson:
325             L
326              
327             =head1 LICENSE AND COPYRIGHT
328              
329             Copyright 2018 Hugo Salgado.
330              
331             This program is free software; you can redistribute it and/or modify it
332             under the terms of the the Artistic License (2.0). You may obtain a
333             copy of the full license at:
334              
335             L
336              
337             Any use, modification, and distribution of the Standard or Modified
338             Versions is governed by this Artistic License. By using, modifying or
339             distributing the Package, you accept this license. Do not use, modify,
340             or distribute the Package, if you do not accept this license.
341              
342             If your Modified Version has been derived from a Modified Version made
343             by someone other than you, you are nevertheless required to ensure that
344             your Modified Version complies with the requirements of this license.
345              
346             This license does not grant you the right to use any trademark, service
347             mark, tradename, or logo of the Copyright Holder.
348              
349             This license includes the non-exclusive, worldwide, free-of-charge
350             patent license to make, have made, use, offer to sell, sell, import and
351             otherwise transfer the Package with respect to any patent claims
352             licensable by the Copyright Holder that are necessarily infringed by the
353             Package. If you institute patent litigation (including a cross-claim or
354             counterclaim) against any party alleging that the Package constitutes
355             direct or contributory patent infringement, then this Artistic License
356             to you shall terminate on the date that such litigation is filed.
357              
358             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
359             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
360             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
361             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
362             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
363             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
364             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
365             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
366              
367              
368             =cut
369              
370             1; # End of Net::Address::IP::Cipher