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.5;
2              
3 3     3   202023 use 5.012;
  3         29  
4 3     3   17 use strict;
  3         6  
  3         72  
5 3     3   16 use warnings FATAL => 'all';
  3         5  
  3         127  
6              
7 3     3   17 use Carp;
  3         5  
  3         216  
8              
9 3     3   2242 use Net::IP qw(:PROC);
  3         183348  
  3         1165  
10 3     3   1720 use Crypt::KeyDerivation qw(pbkdf2);
  3         23203  
  3         189  
11 3     3   1413 use Crypt::Cipher::AES;
  3         3098  
  3         4402  
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.5
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 1829 my $this = shift;
76 2         8 my %params = @_;
77              
78 2   33     16 my $class = ref($this) || $this;
79              
80 2         5 my $key;
81              
82 2 100       10 if ($params{'password'}) {
    50          
83 1         116063 $key = pbkdf2($params{'password'}, 'ipcipheripcipher', 50000, 'SHA1', 16);
84             }
85             elsif ($params{'barekey'}) {
86 1         10 $key = pack 'H*', $params{'barekey'};
87 1 50       4 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         19 my $self = {};
91 2         7 bless $self, $class;
92              
93 2         25 $self->{'PRIVKEY'} = $key;
94 2 50       131 $self->{'CIPHER'} = Crypt::Cipher::AES->new($key)
95             or die "Can't create AES cipher, please check your key!";
96              
97 2         20 return $self;
98             }
99              
100             # PRIVATE FUNCTIONS
101              
102             sub _xor4 {
103 24     24   37 my $ps = shift;
104 24         34 my $pk = shift;
105              
106 24         35 my @out;
107 24         50 foreach my $i (0..3) {
108 96         174 push @out, ($ps->[$i] ^ $pk->[$i]) & 0xff;
109             }
110              
111 24         59 return @out;
112             }
113              
114             sub _rotl {
115 108     108   175 my ($b, $r) = @_;
116              
117 108         183 return (($b << $r) & 0xff) | ($b >> (8 - $r));
118             }
119              
120             sub _permute_fwd {
121 18     18   40 my $b = shift;
122              
123 18         30 $b->[0] += $b->[1];
124 18         29 $b->[2] += $b->[3];
125 18         27 $b->[0] &= 0xff;
126 18         29 $b->[2] &= 0xff;
127 18         33 $b->[1] = &_rotl($b->[1], 2);
128 18         34 $b->[3] = &_rotl($b->[3], 5);
129 18         32 $b->[1] ^= $b->[0];
130 18         28 $b->[3] ^= $b->[2];
131 18         32 $b->[0] = &_rotl($b->[0], 4);
132 18         28 $b->[0] += $b->[3];
133 18         30 $b->[2] += $b->[1];
134 18         25 $b->[0] &= 0xff;
135 18         27 $b->[2] &= 0xff;
136 18         33 $b->[1] = &_rotl($b->[1], 3);
137 18         32 $b->[3] = &_rotl($b->[3], 7);
138 18         28 $b->[1] ^= $b->[2];
139 18         27 $b->[3] ^= $b->[0];
140 18         31 $b->[2] = &_rotl($b->[2], 4);
141              
142 18         45 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   17 my ($key, $ip) = @_;
173              
174 6         38 my @key = map {unpack('C', $_) } split //, $key;
  96         167  
175 6         29 my @state = split /\./, $ip;
176              
177 6         17 my @pedazo = @key[0..3];
178 6         22 @state = &_xor4(\@state, \@pedazo);
179 6         11 @state = @{&_permute_fwd(\@state)};
  6         19  
180 6         17 @pedazo = @key[4..7];
181 6         15 @state = &_xor4(\@state, \@pedazo);
182 6         12 @state = @{&_permute_fwd(\@state)};
  6         13  
183 6         17 @pedazo = @key[8..11];
184 6         25 @state = &_xor4(\@state, \@pedazo);
185 6         11 @state = @{&_permute_fwd(\@state)};
  6         13  
186 6         15 @pedazo = @key[12..15];
187 6         14 @state = &_xor4(\@state, \@pedazo);
188              
189 6         39 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 3732 my $self = shift;
226 12         39 my $ipin = shift;
227              
228 12         24 my $out;
229              
230 12 50       58 my $ipvx = new Net::IP($ipin) or croak 'IP not valid: ' . (Net::IP::Error());
231              
232 12 100       77911 if ($ipvx->version == 6) {
233 6         48 my $ciphertext = $self->{'CIPHER'}->encrypt(pack('B*', $ipvx->binip));
234              
235 6         116 my $enc = new Net::IP(ip_bintoip(unpack('B*', $ciphertext), 6));
236 6         10899 $out = $enc->short;
237             }
238             else {
239 6         56 $out = &_encrypt($self->{'PRIVKEY'}, $ipin);
240             }
241              
242 12         664 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 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 * Github's request tracker (report bugs here)
300              
301             L
302              
303             =item * CPAN Ratings
304              
305             L
306              
307             =item * Search CPAN
308              
309             L
310              
311             =back
312              
313             =head1 REPOSITORY
314              
315             L
316              
317             =head1 ACKNOWLEDGEMENTS
318              
319             The v4 version is based on the original ipcrypt python version from
320             Jean-Philippe Aumasson:
321             L
322              
323             =head1 LICENSE AND COPYRIGHT
324              
325             Copyright 2020 Hugo Salgado.
326              
327             This program is free software; you can redistribute it and/or modify it
328             under the terms of the the Artistic License (2.0). You may obtain a
329             copy of the full license at:
330              
331             L
332              
333             Any use, modification, and distribution of the Standard or Modified
334             Versions is governed by this Artistic License. By using, modifying or
335             distributing the Package, you accept this license. Do not use, modify,
336             or distribute the Package, if you do not accept this license.
337              
338             If your Modified Version has been derived from a Modified Version made
339             by someone other than you, you are nevertheless required to ensure that
340             your Modified Version complies with the requirements of this license.
341              
342             This license does not grant you the right to use any trademark, service
343             mark, tradename, or logo of the Copyright Holder.
344              
345             This license includes the non-exclusive, worldwide, free-of-charge
346             patent license to make, have made, use, offer to sell, sell, import and
347             otherwise transfer the Package with respect to any patent claims
348             licensable by the Copyright Holder that are necessarily infringed by the
349             Package. If you institute patent litigation (including a cross-claim or
350             counterclaim) against any party alleging that the Package constitutes
351             direct or contributory patent infringement, then this Artistic License
352             to you shall terminate on the date that such litigation is filed.
353              
354             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
355             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
356             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
357             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
358             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
359             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
360             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
361             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
362              
363              
364             =cut
365              
366             1; # End of Net::Address::IP::Cipher