1+ package main
2+
3+ import (
4+ "crypto/rand"
5+ "encoding/hex"
6+ "fmt"
7+ "net"
8+ "strings"
9+
10+ "github.com/jedisct1/dlog"
11+ ipcrypt "github.com/jedisct1/go-ipcrypt"
12+ )
13+
14+ // IPCryptConfig holds the configuration for IP address encryption
15+ type IPCryptConfig struct {
16+ Key []byte
17+ Algorithm string
18+ Tweak []byte // For non-deterministic modes
19+ }
20+
21+ // NewIPCryptConfig creates a new IPCryptConfig from configuration values
22+ // Returns nil when encryption is disabled (algorithm is "none" or empty)
23+ func NewIPCryptConfig (keyHex string , algorithm string ) (* IPCryptConfig , error ) {
24+ // Default to "none" if empty
25+ if algorithm == "" {
26+ algorithm = "none"
27+ }
28+
29+ // Return nil for "none" algorithm - encryption disabled
30+ if algorithm == "none" {
31+ return nil , nil
32+ }
33+
34+ if keyHex == "" {
35+ return nil , fmt .Errorf ("IP encryption algorithm is set to %s but no key provided" , algorithm )
36+ }
37+
38+ key , err := hex .DecodeString (keyHex )
39+ if err != nil {
40+ return nil , fmt .Errorf ("invalid IP encryption key (must be hex): %w" , err )
41+ }
42+
43+ config := & IPCryptConfig {
44+ Key : key ,
45+ Algorithm : algorithm ,
46+ }
47+
48+ // Validate key length and prepare config based on algorithm
49+ switch strings .ToLower (algorithm ) {
50+ case "ipcrypt-deterministic" :
51+ // Deterministic IPCrypt uses 16-byte keys
52+ if len (key ) != 16 {
53+ return nil , fmt .Errorf ("ipcrypt-deterministic requires a 16-byte (32 hex chars) key, got %d bytes" , len (key ))
54+ }
55+
56+ case "ipcrypt-nd" :
57+ // Non-deterministic with 8-byte tweak
58+ if len (key ) != 16 {
59+ return nil , fmt .Errorf ("ipcrypt-nd requires a 16-byte (32 hex chars) key, got %d bytes" , len (key ))
60+ }
61+ config .Tweak = make ([]byte , 8 )
62+
63+ case "ipcrypt-ndx" :
64+ // Extended non-deterministic with 16-byte tweak
65+ if len (key ) != 32 {
66+ return nil , fmt .Errorf ("ipcrypt-ndx requires a 32-byte (64 hex chars) key, got %d bytes" , len (key ))
67+ }
68+ config .Tweak = make ([]byte , 16 )
69+
70+ default :
71+ return nil , fmt .Errorf ("unsupported IP encryption algorithm: %s (must be 'ipcrypt-deterministic', 'ipcrypt-nd', 'ipcrypt-ndx', or 'none')" , algorithm )
72+ }
73+
74+ return config , nil
75+ }
76+
77+ // EncryptIP encrypts an IP address using the configured encryption
78+ func (config * IPCryptConfig ) EncryptIP (ip net.IP ) (string , error ) {
79+ if config == nil {
80+ return ip .String (), nil
81+ }
82+
83+ switch config .Algorithm {
84+ case "ipcrypt-deterministic" :
85+ // Deterministic encryption
86+ encrypted , err := ipcrypt .EncryptIP (config .Key , ip )
87+ if err != nil {
88+ return "" , fmt .Errorf ("failed to encrypt IP: %w" , err )
89+ }
90+ return encrypted .String (), nil
91+
92+ case "ipcrypt-nd" :
93+ // Non-deterministic: generate random tweak for this encryption
94+ if _ , err := rand .Read (config .Tweak ); err != nil {
95+ return "" , fmt .Errorf ("failed to generate random tweak: %w" , err )
96+ }
97+ encrypted , err := ipcrypt .EncryptIPNonDeterministic (ip .String (), config .Key , config .Tweak )
98+ if err != nil {
99+ return "" , fmt .Errorf ("failed to encrypt IP (nd): %w" , err )
100+ }
101+ // Return as hex string for non-deterministic modes since they return bytes
102+ return hex .EncodeToString (encrypted ), nil
103+
104+ case "ipcrypt-ndx" :
105+ // Extended non-deterministic: generate random tweak
106+ if _ , err := rand .Read (config .Tweak ); err != nil {
107+ return "" , fmt .Errorf ("failed to generate random tweak: %w" , err )
108+ }
109+ encrypted , err := ipcrypt .EncryptIPNonDeterministicX (ip .String (), config .Key , config .Tweak )
110+ if err != nil {
111+ return "" , fmt .Errorf ("failed to encrypt IP (ndx): %w" , err )
112+ }
113+ // Return as hex string for non-deterministic modes
114+ return hex .EncodeToString (encrypted ), nil
115+
116+ default :
117+ return "" , fmt .Errorf ("unsupported algorithm: %s" , config .Algorithm )
118+ }
119+ }
120+
121+ // EncryptIPString encrypts an IP address string
122+ func (config * IPCryptConfig ) EncryptIPString (ipStr string ) string {
123+ if config == nil || ipStr == "" {
124+ return ipStr
125+ }
126+
127+ ip := net .ParseIP (ipStr )
128+ if ip == nil {
129+ // If it's not a valid IP, return as-is
130+ return ipStr
131+ }
132+
133+ encrypted , err := config .EncryptIP (ip )
134+ if err != nil {
135+ dlog .Warnf ("Failed to encrypt IP %s: %v" , ipStr , err )
136+ return ipStr
137+ }
138+
139+ return encrypted
140+ }
141+
142+ // DecryptIP decrypts an encrypted IP address
143+ func (config * IPCryptConfig ) DecryptIP (encryptedStr string ) (string , error ) {
144+ if config == nil {
145+ return encryptedStr , nil
146+ }
147+
148+ switch config .Algorithm {
149+ case "ipcrypt-deterministic" :
150+ // Parse as IP for deterministic mode
151+ ip := net .ParseIP (encryptedStr )
152+ if ip == nil {
153+ return "" , fmt .Errorf ("invalid encrypted IP address: %s" , encryptedStr )
154+ }
155+ decrypted , err := ipcrypt .DecryptIP (config .Key , ip )
156+ if err != nil {
157+ return "" , fmt .Errorf ("failed to decrypt IP: %w" , err )
158+ }
159+ return decrypted .String (), nil
160+
161+ case "ipcrypt-nd" :
162+ // Decode from hex for non-deterministic mode
163+ encrypted , err := hex .DecodeString (encryptedStr )
164+ if err != nil {
165+ return "" , fmt .Errorf ("failed to decode encrypted IP: %w" , err )
166+ }
167+ decrypted , err := ipcrypt .DecryptIPNonDeterministic (encrypted , config .Key )
168+ if err != nil {
169+ return "" , fmt .Errorf ("failed to decrypt IP (nd): %w" , err )
170+ }
171+ return decrypted , nil
172+
173+ case "ipcrypt-ndx" :
174+ // Decode from hex for extended non-deterministic mode
175+ encrypted , err := hex .DecodeString (encryptedStr )
176+ if err != nil {
177+ return "" , fmt .Errorf ("failed to decode encrypted IP: %w" , err )
178+ }
179+ decrypted , err := ipcrypt .DecryptIPNonDeterministicX (encrypted , config .Key )
180+ if err != nil {
181+ return "" , fmt .Errorf ("failed to decrypt IP (ndx): %w" , err )
182+ }
183+ return decrypted , nil
184+
185+ default :
186+ return "" , fmt .Errorf ("unsupported algorithm: %s" , config .Algorithm )
187+ }
188+ }
0 commit comments