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.6;
2              
3 3     3   195206 use 5.012;
  3         29  
4 3     3   15 use strict;
  3         5  
  3         70  
5 3     3   14 use warnings FATAL => 'all';
  3         7  
  3         115  
6              
7 3     3   14 use Carp;
  3         6  
  3         208  
8              
9 3     3   2160 use Net::IP qw(:PROC);
  3         180745  
  3         1146  
10 3     3   1624 use Crypt::KeyDerivation qw(pbkdf2);
  3         8925  
  3         181  
11 3     3   1267 use Crypt::Cipher::AES;
  3         2761  
  3         4244  
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.6
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 1824 my $this = shift;
76 2         9 my %params = @_;
77              
78 2   33     15 my $class = ref($this) || $this;
79              
80 2         4 my $key;
81              
82 2 100       10 if ($params{'password'}) {
    50          
83 1         116292 $key = pbkdf2($params{'password'}, 'ipcipheripcipher', 50000, 'SHA1', 16);
84             }
85             elsif ($params{'barekey'}) {
86 1         11 $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         18 my $self = {};
91 2         10 bless $self, $class;
92              
93 2         23 $self->{'PRIVKEY'} = $key;
94 2 50       134 $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   39 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         178 push @out, ($ps->[$i] ^ $pk->[$i]) & 0xff;
109             }
110              
111 24         52 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   26 my $b = shift;
122              
123 18         29 $b->[0] += $b->[1];
124 18         28 $b->[2] += $b->[3];
125 18         28 $b->[0] &= 0xff;
126 18         23 $b->[2] &= 0xff;
127 18         48 $b->[1] = &_rotl($b->[1], 2);
128 18         30 $b->[3] = &_rotl($b->[3], 5);
129 18         28 $b->[1] ^= $b->[0];
130 18         29 $b->[3] ^= $b->[2];
131 18         26 $b->[0] = &_rotl($b->[0], 4);
132 18         28 $b->[0] += $b->[3];
133 18         24 $b->[2] += $b->[1];
134 18         27 $b->[0] &= 0xff;
135 18         26 $b->[2] &= 0xff;
136 18         35 $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         30 $b->[2] = &_rotl($b->[2], 4);
141              
142 18         51 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   15 my ($key, $ip) = @_;
173              
174 6         34 my @key = map {unpack('C', $_) } split //, $key;
  96         175  
175 6         28 my @state = split /\./, $ip;
176              
177 6         17 my @pedazo = @key[0..3];
178 6         16 @state = &_xor4(\@state, \@pedazo);
179 6         13 @state = @{&_permute_fwd(\@state)};
  6         15  
180 6         15 @pedazo = @key[4..7];
181 6         16 @state = &_xor4(\@state, \@pedazo);
182 6         11 @state = @{&_permute_fwd(\@state)};
  6         13  
183 6         14 @pedazo = @key[8..11];
184 6         13 @state = &_xor4(\@state, \@pedazo);
185 6         12 @state = @{&_permute_fwd(\@state)};
  6         10  
186 6         15 @pedazo = @key[12..15];
187 6         13 @state = &_xor4(\@state, \@pedazo);
188              
189 6         34 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 3586 my $self = shift;
226 12         65 my $ipin = shift;
227              
228 12         22 my $out;
229              
230 12 50       109 my $ipvx = new Net::IP($ipin) or croak 'IP not valid: ' . (Net::IP::Error());
231              
232 12 100       79791 if ($ipvx->version == 6) {
233 6         52 my $ciphertext = $self->{'CIPHER'}->encrypt(pack('B*', $ipvx->binip));
234              
235 6         132 my $enc = new Net::IP(ip_bintoip(unpack('B*', $ciphertext), 6));
236 6         10930 $out = $enc->short;
237             }
238             else {
239 6         46 $out = &_encrypt($self->{'PRIVKEY'}, $ipin);
240             }
241              
242 12         667 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 * MetaCPAN
304              
305             L
306              
307             =back
308              
309             =head1 REPOSITORY
310              
311             L
312              
313             =head1 ACKNOWLEDGEMENTS
314              
315             The v4 version is based on the original ipcrypt python version from
316             Jean-Philippe Aumasson:
317             L
318              
319             =head1 LICENSE AND COPYRIGHT
320              
321             Copyright 2023 Hugo Salgado.
322              
323             This program is free software; you can redistribute it and/or modify it
324             under the terms of the the Artistic License (2.0). You may obtain a
325             copy of the full license at:
326              
327             L
328              
329             Any use, modification, and distribution of the Standard or Modified
330             Versions is governed by this Artistic License. By using, modifying or
331             distributing the Package, you accept this license. Do not use, modify,
332             or distribute the Package, if you do not accept this license.
333              
334             If your Modified Version has been derived from a Modified Version made
335             by someone other than you, you are nevertheless required to ensure that
336             your Modified Version complies with the requirements of this license.
337              
338             This license does not grant you the right to use any trademark, service
339             mark, tradename, or logo of the Copyright Holder.
340              
341             This license includes the non-exclusive, worldwide, free-of-charge
342             patent license to make, have made, use, offer to sell, sell, import and
343             otherwise transfer the Package with respect to any patent claims
344             licensable by the Copyright Holder that are necessarily infringed by the
345             Package. If you institute patent litigation (including a cross-claim or
346             counterclaim) against any party alleging that the Package constitutes
347             direct or contributory patent infringement, then this Artistic License
348             to you shall terminate on the date that such litigation is filed.
349              
350             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
351             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
352             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
353             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
354             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
355             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
356             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
357             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
358              
359              
360             =cut
361              
362             1; # End of Net::Address::IP::Cipher