class CryptoPAn

Public Class Methods

new(key) click to toggle source
# File lib/cryptopan.rb, line 4
def initialize(key)
  if key.is_a? Array
    material = key.pack("c*")
  else
    material = key
  end

  raise "Key material is not 32 bytes." if material.length != 32

  @key = material[0..15]
  unencrypted_pad = material[16..31]

  @pad = encrypt(unencrypted_pad)
end

Public Instance Methods

anonymise(ip) click to toggle source
# File lib/cryptopan.rb, line 19
def anonymise(ip)
  long_ip = NetAddr.ip_to_i ip

  encrypt_input = @pad.dup

  long_pad = @pad[0].to_i << 24
  long_pad += @pad[1].to_i << 16
  long_pad += @pad[2].to_i << 8
  long_pad += @pad[3].to_i

  result = 0

  32.times do |i|
    first4bytes_input = long_pad

    if i > 0
      first4bytes_input = (long_ip >> (32-i)) << (32-i) | ((long_pad << i) & 0xffffffff) >> i
    end

    encrypt_input[0] = first4bytes_input >> 24
    encrypt_input[1] = (first4bytes_input << 8) >> 24
    encrypt_input[2] = (first4bytes_input << 16) >> 24
    encrypt_input[3] = (first4bytes_input << 24) >> 24

    encrypt_output = encrypt(encrypt_input.pack("c*"))[0]

    result |= (((encrypt_output & 0xff) >> 7) << (31-i))
  end

  value = result ^ long_ip

  output_value = []
  output_value[0] = value >> 24 & 0xff
  output_value[1] = value >> 16 & 0xff
  output_value[2] = value >> 8 & 0xff
  output_value[3] = value & 0xff

  output_value.join(".")
end

Private Instance Methods

encrypt(value) click to toggle source
# File lib/cryptopan.rb, line 61
def encrypt(value)
  cipher = OpenSSL::Cipher.new('AES-128-ECB')
  cipher.padding = 0
  cipher.encrypt

  cipher.key = @key

  (cipher.update(value) + cipher.final).bytes
end