# IP::Anonymous - Perl port of Crypto-PAn to provide anonymous IP addresses # # Copyright (c) 2005 John Kristoff . All rights # reserved. This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. package IP::Anonymous; $VERSION = '0.04'; require 5.001; # base extensions and modules use strict; # force us to code defensively use warnings; # do not let laziness go unnoticed use vars qw($VERSION); # package scope global vars use Carp; # make errors the caller's problem use Socket; # for convenient dotted quad handling routines # additional modules use Crypt::Rijndael; # the Rijndael crypto magic # module scope vars my $m_pad; # 2nd 128 bits of user key used for secret padding my $ecb; # electronic codebook mode (default) object my $first4bytes_pad; # 1st 4 bytes of secret pad in "network" order # initialize sub new { my $package = shift; my @key = @_; # expected key is 32 8-bit unsigned ints my $m_key; # 1st 128 bits of user key used for cipher # 1st 128 bits of key used as secret key for Rijndael cipher $m_key = pack("C16", @key[0..15]); # 2nd 128 bits of key used for secret padding $m_pad = pack("C16", @key[16..31]); # init the key $ecb = new Crypt::Rijndael $m_key; # encrypt the 128-bit secret pad $m_pad = $ecb->encrypt($m_pad); # first four bytes of secret pad in "network" (big-endian) order $first4bytes_pad = unpack("N", $m_pad); return bless({}, $package); } # generate a bit using the Rijndael cipher for each of the 32 bit address # msb bits taken from original address, remaining bits are from m_pad sub anonymize { my $package = shift; my $address = shift; # expect dotted quad string my $first4bytes_input; # used to handle the byte ordering problem my @rin_input = (); # psuedorandom Rijndael cipher used each round my $rin_output; # encrypted byte result at each round my $result = 0; # initialize psuedorandom one time pad # rudimentary check for correctly formatted dotted quad if($address !~ /^\d{1,3}(?:\.\d{1,3}){3}$/) { croak("ERROR [".__LINE__."]: invalid IP address format\n"); return; } # convert dotted quad to long in "network" (big-endian) order $address = unpack("N", pack("C4", split /\./, $address)); # Rijndael cipher used to obtain each of the 32 anonymized bits @rin_input = unpack("C16", $m_pad); # init input with encrypted pad $rin_input[0] = ($first4bytes_pad >> 24); # mask off excess bits for 64-bit systems in left shift $rin_input[1] = (($first4bytes_pad << 8 & 0xffffffff) >> 24); $rin_input[2] = (($first4bytes_pad << 16 & 0xffffffff) >> 24); $rin_input[3] = (($first4bytes_pad << 24 & 0xffffffff) >> 24); # get an 8-bit byte $rin_output = unpack("C", $ecb->encrypt(pack("C16", @rin_input))); # only the first bit of rin_output is used for each round $result |= ($rin_output >> 7) << 31; # loop through remaining 31 bits for (my $position=1; $position <= 31; $position++) { $first4bytes_input = (($address >> (32-$position)) << (32-$position)) | # mask off excess bits for 64-bit systems (($first4bytes_pad << $position & 0xffffffff) >> $position); $rin_input[0] = ($first4bytes_input >> 24); # mask off excess bits for 64-bit systems in left shift $rin_input[1] = (($first4bytes_input << 8 & 0xffffffff) >> 24); $rin_input[2] = (($first4bytes_input << 16 & 0xffffffff) >> 24); $rin_input[3] = (($first4bytes_input << 24 & 0xffffffff) >> 24); # get an 8-bit byte $rin_output = unpack("C", $ecb->encrypt(pack("C16", @rin_input))); # only the first bit of rin_output is used for each round $result |= ($rin_output >> 7) << (31-$position); } # return anonymized, prefix-preserved address as a dotted quad string return inet_ntoa(pack("N", $result ^ $address)) } 1; __END__ =head1 NAME IP::Anonymous - Perl port of Crypto-PAn to provide anonymous IP addresses =head1 SYNOPSIS use IP::Anonymous; @key = (0..31); my $object = new IP::Anonymous(@key); print $object->anonymize("192.0.2.0")."\n"; =head1 DESCRIPTION This is a Perl port of Crypto-PAn. Crypto-PAn is a cryptography-based sanitization tool for network trace or log data. The tool has the following properties: =over 4 =item * One-to-one The mapping from original IP addresses to anonymized IP addresses is one-to-one. =item * Prefix-preserving The IP address anonymization is prefix-preserving. That is, if two original IP addresses share a k-bit prefix, their anonymized mappings will also share a k-bit prefix. =item * Consistent across traces Multiple traces can be sanitized in a consistent way, over time and across locations, even though the traces might be sanitized separately at different time and/or at different locations. =item * Cryptography-based To sanitize traces, trace owners provide a secret key. Anonymization consistency across multiple traces is achieved by the use of the same key. The construction of IP::Anonymous preserves the secrecy of the key and the (pseudo)randomness of the mapping from an original IP address to its anonymized counterpart. =back This Perl port of Crypto-PAn uses similar logic to that found in Crypto-PAn 1.0, but most importantly maintains consistency in the process so that regardless of implementation, using the same key in each will give consistent results. =head1 REQUIRES Crypt::Rijndael - XS-based implementation of the Advanced Encryption Standard (AES) algorithm Rijndael by Joan Daemen and Vincent Rijmen. =head1 USAGE =over 4 =item $object = new IP::Anonymous(@key) Initializes the electronic codebook object with a 32 8-bit decimal array. This array, consisting of 32 decimals between 0 and 255 inclusive, is the user defined private key for this anonymization session. This 256 bit key should be kept private. The key can be used across sessions to maintain consistent mappings between the original and the anonymized IP addresses. =item $object->anonymize($address) Called with a dotted quad IP address string (e.g. 192.0.2.0). Returns an anonymized version of that IP address as a dotted quad string. =back =head1 BUGS The Crypt::Rijndael module as of version 0.05 contains at least one fatal flaw for users of 64-bit systems. rijndael.h specifies a 32 bit integer as an unsigned long. This works on 32-bit systems, but not 64-bit systems. This is easily circumvented by changing the definition for UINT32 from a unsigned long to an unsigned int for platforms the author has tested on. The Crypt::Rijndael module on CPAN tested with IP::Anonymous has as it's package version number 0.05, but in the Rijndael.pm module file itself, VERSION is set to 0.04. IP::Anonymous specifies that at least 0.04 of Crypt::Rijndael is required, but the original 0.04 version has not been tested. It is presumed to work, but you should use the module whose package version number is 0.05 or later if possible. IP::Anonymous only provides support for IPv4 addresses. =head1 AUTHOR Original Crypto-PAn C++ implementation was done by Jinliang Fan. The Perl module port is by John Kristoff. Thanks to Stephen Gill for initial testing and suggesting changes in the beginning stages of the module implementation process. =head1 COPYRIGHT This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO This module is based on the original Crypto-PAn project tool designed and implemented in C++ by Jinliang Fan. The Crypto-PAn project web page is located at: =over 4 http://www.cc.gatech.edu/computing/Telecomm/cryptopan/ =back =head1 SECURITY Even though this module uses strong cryptography to anonymize IP addresses there may still be a number of avenues of attack that can be successful in discovering underlying information. For a good description of this problem see the paper I by Mark Allman, et al., which can be found at: =over 4 http://www.icir.org/enterprise-tracing/papers.html =back =head1 AVAILABILITY IP::Anonymous is available on the Comprehensive Perl Archive Network (CPAN) and also off the author's homepage (as of this writing) at: =over 4 http://aharp.ittns.northwestern.edu/software/ =back The module author intends to maintain software signatures (PGP and/or message digest hash) on his pages to help you verify that what you have gotten elsewhere is what he expected you to have. =cut