Skip to content

jedisct1/ipcrypt-kotlin

Repository files navigation

IPCrypt Kotlin Implementation

A complete, idiomatic Kotlin implementation of the IPCrypt specification for IP address encryption and obfuscation.

Overview

This library implements three variants of IP address encryption as defined in draft-denis-ipcrypt:

  • ipcrypt-deterministic: Deterministic, format-preserving encryption using AES-128
  • ipcrypt-nd: Non-deterministic encryption using KIASU-BC with 8-byte tweaks
  • ipcrypt-ndx: Non-deterministic encryption using AES-XTS with 16-byte tweaks

Features

  • Full specification compliance - Passes all official test vectors
  • 🎯 Type-safe API - Leverages Kotlin's type system for safety
  • 🔧 DSL builder pattern - Fluent configuration API
  • 🚀 Extension functions - Convenient utilities for common operations
  • 📦 Zero dependencies - Uses only Java standard library
  • 🧪 Comprehensive testing - Full test coverage with JUnit 5
  • 🔒 Thread-safe - All implementations are thread-safe

Installation

Gradle (Kotlin DSL)

dependencies {
    implementation("org.ipcrypt:ipcrypt-kotlin:1.0.0")
}

Gradle (Groovy)

dependencies {
    implementation 'org.ipcrypt:ipcrypt-kotlin:1.0.0'
}

Maven

<dependency>
    <groupId>org.ipcrypt</groupId>
    <artifactId>ipcrypt-kotlin</artifactId>
    <version>1.0.0</version>
</dependency>

Quick Start

import org.ipcrypt.*

fun main() {
    // Create encryptor using DSL
    val (crypto, key) = ipCrypt {
        randomKey()
        nd()  // Non-deterministic mode
    }
    
    // Encrypt IP addresses
    val encrypted = crypto.encryptNonDeterministic("192.168.1.1")
    println("Encrypted: ${encrypted.toHexString()}")
    
    // Decrypt back
    val original = crypto.decryptNonDeterministic(encrypted)
    println("Original: $original")
}

Usage Examples

Deterministic Encryption

Use for consistent anonymization where the same IP always produces the same encrypted result:

// Using hex key (recommended)
val key = "0123456789abcdeffedcba9876543210".hexToByteArray()
val crypto = IpCrypt.deterministic(key)

val ip = "192.168.1.1"
val encrypted = crypto.encryptDeterministic(ip)
// encrypted is an IPv6-formatted string like "bde9:6789:d353:824c:..."

val decrypted = crypto.decryptDeterministic(encrypted)
assert(decrypted == ip)

// Or generate a random key
val randomKey = IpCrypt.generateKey(IpCrypt.Mode.DETERMINISTIC)
val crypto2 = IpCrypt.deterministic(randomKey)

Non-Deterministic Encryption (ND)

Use for preventing correlation attacks with 8-byte tweaks:

val crypto = IpCrypt.nd(IpCrypt.generateKey(IpCrypt.Mode.ND))

val ip = "10.0.0.1"
val encrypted1 = crypto.encryptNonDeterministic(ip)
val encrypted2 = crypto.encryptNonDeterministic(ip)
// Different outputs each time

assert(!encrypted1.contentEquals(encrypted2))
assert(crypto.decryptNonDeterministic(encrypted1) == ip)
assert(crypto.decryptNonDeterministic(encrypted2) == ip)

Extended Non-Deterministic (NDX)

Highest collision resistance with 16-byte tweaks:

val crypto = IpCrypt.ndx(IpCrypt.generateKey(IpCrypt.Mode.NDX))

val ipv6 = "2001:db8::1"
val encrypted = crypto.encryptNonDeterministic(ipv6)
// Returns 32-byte ByteArray (16-byte tweak + 16-byte ciphertext)

val decrypted = crypto.decryptNonDeterministic(encrypted)
assert(decrypted == ipv6)

DSL Builder Pattern

Fluent API for configuration:

// With random key
val (crypto, key) = ipCrypt {
    randomKey()
    nd()
}

// With hex key
val (crypto2, _) = ipCrypt {
    key("0123456789abcdeffedcba9876543210")
    deterministic()
}

// With byte array key
val (crypto3, _) = ipCrypt {
    key(myKeyBytes)
    ndx()
}

Extension Functions

Convenient utilities for common operations:

// Check if string is valid IP
"192.168.1.1".isIPv4        // true
"2001:db8::1".isIPv6        // true  
"not.an.ip".isIPAddress     // false

// Hex conversions (supports various formats)
val bytes1 = "deadbeef".hexToByteArray()
val bytes2 = "DE:AD:BE:EF".hexToByteArray()  // With colons
val bytes3 = "DE AD BE EF".hexToByteArray()  // With spaces
val hex = bytes1.toHexString()  // Returns lowercase hex

Batch Processing

Process multiple IPs efficiently:

val crypto = IpCrypt.deterministic(key)

// Process a list of IPs
val ips = listOf("192.168.1.1", "10.0.0.1", "172.16.0.1")
val encrypted = ips.map { crypto.encryptDeterministic(it) }

// Decrypt all
val decrypted = encrypted.map { crypto.decryptDeterministic(it) }

// Anonymize log entries
val logs = listOf(
    "User 192.168.1.100 logged in",
    "Request from 192.168.1.101"
)
val ipPattern = """(\d{1,3}\.){3}\d{1,3}""".toRegex()
val anonymized = logs.map { log ->
    ipPattern.replace(log) { match ->
        crypto.encryptDeterministic(match.value)
    }
}

API Reference

IpCrypt Class

Main encryption/decryption interface:

class IpCrypt {
    // Factory methods
    companion object {
        fun deterministic(key: ByteArray): IpCrypt
        fun nd(key: ByteArray): IpCrypt
        fun ndx(key: ByteArray): IpCrypt
        fun withMode(key: ByteArray, mode: Mode): IpCrypt
        fun generateKey(mode: Mode): ByteArray
    }
    
    // Generic methods
    fun encrypt(ipAddress: String, tweak: ByteArray? = null): Any
    fun decrypt(encryptedData: Any): String
    
    // Type-safe methods
    fun encryptDeterministic(ipAddress: String): String
    fun decryptDeterministic(encryptedIp: String): String
    fun encryptNonDeterministic(ipAddress: String, tweak: ByteArray? = null): ByteArray
    fun decryptNonDeterministic(encryptedData: ByteArray): String
}

Key Sizes

  • DETERMINISTIC: 16 bytes (128 bits)
  • ND: 16 bytes (128 bits)
  • NDX: 32 bytes (256 bits)

Output Formats

  • DETERMINISTIC: IPv6-formatted string
  • ND: 24-byte ByteArray (8-byte tweak + 16-byte ciphertext)
  • NDX: 32-byte ByteArray (16-byte tweak + 16-byte ciphertext)

Security Considerations

Key Management

  • Generate keys using a cryptographically secure random number generator
  • Store keys securely using appropriate key management systems
  • Rotate keys regularly based on usage volume
  • Never hardcode keys in source code

Usage Limits

  • Deterministic: No usage limits but reveals patterns
  • ND: ~4 billion operations per key before collision concerns
  • NDX: ~18 quintillion operations per key before collision concerns

Real-World Example

Log anonymization while preserving structure:

data class LogEntry(val timestamp: Long, val ip: String, val action: String)

fun anonymizeLogs(logs: List<LogEntry>, key: ByteArray): List<LogEntry> {
    val crypto = IpCrypt.deterministic(key)
    
    return logs.map { entry ->
        entry.copy(
            ip = try {
                crypto.encryptDeterministic(entry.ip)
            } catch (e: Exception) {
                entry.ip // Keep original if not a valid IP
            }
        )
    }
}

// Usage
val key = IpCrypt.generateKey(IpCrypt.Mode.DETERMINISTIC)
val anonymizedLogs = anonymizeLogs(originalLogs, key)

Running Tests

# Run all tests
./gradlew test

# Run with coverage
./gradlew test jacocoTestReport

# Run specific test class
./gradlew test --tests IpCryptTest

# Verify against specification vectors
kotlin -cp build/classes org.ipcrypt.VerifySpecKt

Building

# Clean build
./gradlew clean build

# Create JAR
./gradlew jar

# Generate documentation
./gradlew dokkaHtml

Code Quality

Linting and Formatting

The project uses ktlint for code formatting and detekt for static analysis:

# Check formatting
./gradlew ktlintCheck
./lint.sh check

# Auto-format code
./gradlew ktlintFormat
./lint.sh format

# Run static analysis
./gradlew detekt

# Generate detailed reports
./gradlew detektGenerateBaseline
./lint.sh report

Pre-commit Hook

Install the pre-commit hook to ensure code quality:

./lint.sh install-hook

This will automatically check formatting before each commit.

Specification Compliance

This implementation fully complies with the IPCrypt specification and passes all official test vectors:

  • ✅ ipcrypt-deterministic (AES-128 ECB)
  • ✅ ipcrypt-nd (KIASU-BC with 8-byte tweak)
  • ✅ ipcrypt-ndx (AES-XTS with 16-byte tweak)

Compatibility

  • Requires Java 8 or higher
  • Kotlin 1.9.20 or higher
  • No external dependencies beyond JDK

About

A Kotlin implementation of the IPCrypt IP encryption/obfuscation methods.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published