IPCrypt2 Ruby Implementation
A Ruby implementation of the IPCrypt specification for encrypting and obfuscating IP addresses, as defined in the IPCrypt IETF draft.
This gem provides privacy-preserving methods for storing, logging, and analyzing IP addresses while maintaining the ability to decrypt them when necessary.
Features
-
Three encryption modes:
-
ipcrypt-deterministic
: Deterministic encryption using AES-128 (same input always produces same output) -
ipcrypt-nd
: Non-deterministic encryption using KIASU-BC with 8-byte tweak -
ipcrypt-ndx
: Non-deterministic encryption using AES-XTS with 16-byte tweak
-
- Full IPv4 and IPv6 support with automatic conversion to unified 16-byte format
- Secure implementations using OpenSSL for cryptographic operations
- Comprehensive test suite with official test vectors from the specification
- Ruby 2.6+ compatibility
Installation
Add this line to your application's Gemfile:
gem 'ipcrypt2'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install ipcrypt2
Usage
Deterministic Encryption (ipcrypt-deterministic)
require 'ipcrypt/deterministic'
# 16-byte key
key = "0123456789abcdeffedcba9876543210".scan(/../).map { |x| x.hex }.pack("C*")
# Encrypt an IP address
ip = "192.0.2.1"
encrypted_ip = IPCrypt::Deterministic.encrypt(ip, key)
decrypted_ip = IPCrypt::Deterministic.decrypt(encrypted_ip, key)
Non-Deterministic Encryption with KIASU-BC (ipcrypt-nd)
require 'ipcrypt/nd'
# 16-byte key
key = "0123456789abcdeffedcba9876543210".scan(/../).map { |x| x.hex }.pack("C*")
# Encrypt an IP address (with optional tweak)
ip = "192.0.2.1"
encrypted_data = IPCrypt::ND.encrypt(ip, key) # Random tweak
# or
tweak = "08e0c289bff23b7c".scan(/../).map { |x| x.hex }.pack("C*")
encrypted_data = IPCrypt::ND.encrypt(ip, key, tweak) # Specific tweak
# Decrypt
decrypted_ip = IPCrypt::ND.decrypt(encrypted_data, key)
Non-Deterministic Encryption with AES-XTS (ipcrypt-ndx)
require 'ipcrypt/ndx'
# 32-byte key
key = "0123456789abcdeffedcba98765432101032547698badcfeefcdab8967452301".scan(/../).map { |x| x.hex }.pack("C*")
# Encrypt an IP address
ip = "192.0.2.1"
encrypted_data = IPCrypt::NDX.encrypt(ip, key)
# Decrypt
decrypted_ip = IPCrypt::NDX.decrypt(encrypted_data, key)
Framework Integration
Ruby on Rails
1. Basic Setup
Add to your Gemfile
:
gem 'ipcrypt2'
Create an initializer config/initializers/ipcrypt.rb
:
# Store your key securely using Rails credentials
Rails.application.config.ipcrypt_key = Rails.application.credentials.ipcrypt_key
# Or use environment variables
Rails.application.config.ipcrypt_key = ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*")
2. ActiveRecord Model Integration
class User < ApplicationRecord
# Store encrypted IP addresses in the database
def ip_address=(ip)
key = Rails.application.config.ipcrypt_key
self.encrypted_ip = IPCrypt::Deterministic.encrypt(ip, key)
end
def ip_address
return nil unless encrypted_ip.present?
key = Rails.application.config.ipcrypt_key
IPCrypt::Deterministic.decrypt(encrypted_ip, key)
end
end
3. Request Logging
Create a concern for controllers:
# app/controllers/concerns/ip_encryption.rb
module IpEncryption
extend ActiveSupport::Concern
included do
before_action :store_encrypted_ip
end
private
def store_encrypted_ip
return unless request.remote_ip.present?
key = Rails.application.config.ipcrypt_key
encrypted_ip = IPCrypt::Deterministic.encrypt(request.remote_ip, key)
# Store in session, database, or logs
session[:encrypted_ip] = encrypted_ip
end
end
# Use in controllers
class ApplicationController < ActionController::Base
include IpEncryption
end
4. Custom Logger
# config/application.rb
class EncryptedIpLogger < ActiveSupport::Logger
def add_ip(severity, ip_address, progname = nil)
key = Rails.application.config.ipcrypt_key
encrypted_ip = IPCrypt::Deterministic.encrypt(ip_address, key)
add(severity, "IP: #{encrypted_ip}", progname)
end
end
# Usage
Rails.logger.add_ip(Logger::INFO, request.remote_ip)
Sinatra
require 'sinatra'
require 'ipcrypt/deterministic'
configure do
set :ipcrypt_key, ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*")
end
helpers do
def encrypt_ip(ip)
IPCrypt::Deterministic.encrypt(ip, settings.ipcrypt_key)
end
def decrypt_ip(encrypted_ip)
IPCrypt::Deterministic.decrypt(encrypted_ip, settings.ipcrypt_key)
end
end
before do
@encrypted_client_ip = encrypt_ip(request.ip)
end
get '/' do
"Your encrypted IP: #{@encrypted_client_ip}"
end
Rack Middleware
Create middleware for any Rack-based application:
# lib/rack/ip_encryptor.rb
module Rack
class IpEncryptor
def initialize(app, key)
@app = app
@key = key
end
def call(env)
# Encrypt the client IP
if env['REMOTE_ADDR']
env['rack.encrypted_ip'] = IPCrypt::Deterministic.encrypt(env['REMOTE_ADDR'], @key)
end
@app.call(env)
end
end
end
# Usage in config.ru
require 'ipcrypt/deterministic'
key = ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*")
use Rack::IpEncryptor, key
run YourApp
Hanami
# config/environment.rb
require 'ipcrypt/deterministic'
Hanami.configure do
# Store key in settings
settings.ipcrypt_key = ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*")
end
# app/actions/application.rb
module YourApp
module Actions
class Application < Hanami::Action
before :encrypt_client_ip
private
def encrypt_client_ip
return unless request.ip
key = Hanami.app.settings.ipcrypt_key
@encrypted_ip = IPCrypt::Deterministic.encrypt(request.ip, key)
end
end
end
end
Grape API
require 'grape'
require 'ipcrypt/deterministic'
class API < Grape::API
helpers do
def ipcrypt_key
@key ||= ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*")
end
def encrypted_client_ip
IPCrypt::Deterministic.encrypt(request.ip, ipcrypt_key)
end
end
before do
# Log encrypted IP for each request
logger.info "Request from: #{encrypted_client_ip}"
end
get '/my-ip' do
{ encrypted_ip: encrypted_client_ip }
end
end
Best Practices
-
Key Management: Never hardcode encryption keys. Use environment variables or secure credential stores like Rails credentials.
-
Choose the Right Mode:
- Use
deterministic
for logs and analytics where you need to correlate multiple requests from the same IP - Use
nd
orndx
for storage where each encryption should be unique
- Use
-
Performance: For high-traffic applications, consider caching the key parsing:
class IpEncryptor def self.key @key ||= ENV['IPCRYPT_KEY'].scan(/../).map { |x| x.hex }.pack("C*") end end
-
Database Storage: Store encrypted IPs as binary or base64-encoded strings:
# Migration add_column :users, :encrypted_ip, :binary # Model def ip_address=(ip) self.encrypted_ip = IPCrypt::Deterministic.encrypt(ip, key) end
Development
After checking out the repo, run bundle install
to install dependencies.
Running Tests
# Run all tests
bundle exec rake test
# Run tests with verbose output
bundle exec rake test_verbose
# Run RuboCop linting
bundle exec rubocop
# Run both tests and linting (default task)
bundle exec rake
Building the Gem
# Build the gem
gem build ipcrypt2.gemspec
# Install locally for testing
gem install ./ipcrypt2-*.gem