idobf is a composable library for obfuscating integer IDs into
human-readable, tamper-evident strings. It is designed for scenarios where you
want to expose internal sequential IDs (e.g., database primary keys) to end
users without revealing ordering or cardinality.
- Composable pipeline — mix and match permutations, checksums, and encoders
- Tamper detection — keyed or non-keyed checksums detect forged IDs
- Namespaced obfuscation — same ID in different namespaces produces different output
- Human-readable output — configurable alphabets (Base32, Base58, Base62, etc.)
- Zero external dependencies — pure Go standard library
- Fast — optimized power-of-two encode/decode paths
go get github.com/binadel/idobfpackage main
import (
"fmt"
"github.com/binadel/idobf"
"github.com/binadel/idobf/checksums"
"github.com/binadel/idobf/encoders"
"github.com/binadel/idobf/permutations"
)
func main() {
affine, _ := permutations.NewAffine(64, 0x9e3779b97f4a7c15, 0x85ebca6b)
xor, _ := permutations.NewXor(64, 0xcafebabe)
rotate, _ := permutations.NewRotate(64, 17)
cs, _ := checksums.NewSplitMix64(16)
enc, _ := encoders.NewAlphabetEncoder(
encoders.AlphabetBase32Crockford,
64+cs.ChecksumLength(), // 80 bits
)
pipe := idobf.NewPipeline(
[]idobf.Permutation{affine, xor, rotate},
cs,
enc,
)
obf, _ := pipe.Obfuscate(42, 0) // namespace = 0
fmt.Println(string(obf)) // e.g. "6F8TPBV6F8TP"
id, err := pipe.Restore(obf, 0) // → 42, nil
fmt.Println(id, err)
}| Interface | Methods | Description |
|---|---|---|
Permutation |
Forward(uint64) uint64 |
Bijection over a uint64 domain. Must be invertible. |
Reverse(uint64) uint64 |
||
Checksum |
CalculateChecksum(uint64) uint64 |
Integrity check for an ID value. |
ChecksumLength() int |
Number of bits the checksum occupies. | |
Encoder |
Encode([2]uint64) ([]byte, error) |
128-bit block ↔ byte representation. |
Decode([]byte) ([2]uint64, error) |
||
Obfuscator |
Obfuscate(uint64, uint64) ([]byte, error) |
Top-level interface. |
Restore([]byte, uint64) (uint64, error) |
A Pipeline wires permutations, an optional checksum, and an encoder into an
Obfuscator. Use the builder for readable construction:
pipe, err := idobf.NewPipelineBuilder().
WithPermutations(affine, xor).
WithChecksum(cs).
WithEncoder(enc).
Build()The pipeline XORs the ID with a namespace, applies permutations, computes a
checksum, then encodes the 128-bit block {checksum, permuted_value}.
On restore, it decodes the block, verifies the checksum (returning
checksums.ErrChecksum on mismatch), reverses permutations, and XORs the
namespace back.
| Type | Constructor | Domain | Notes |
|---|---|---|---|
| Affine | NewAffine(bits, a, b) |
2^bits (2–64) |
f(v) = (a·v + b) mod 2^n. a must be odd. |
| Xor | NewXor(bits, key) |
2^bits (2–64) |
f(v) = v ⊕ key. Self-inverse. |
| Rotate | NewRotate(bits, shift) |
2^bits (2–64) |
Circular bit rotation. shift < bits. |
| Feistel | NewFeistel(bits, keys, fn) |
2^bits (2–64, even) |
Feistel network with configurable round function. |
Feistel round functions:
| Function | Description |
|---|---|
DefaultFeistelRound(right, key) |
Multiply-xor-shift avalanche (MurmurHash3-style) |
FastFeistelRound(right, key) |
Lightweight: rotation + golden ratio multiply |
StrongFeistelRound(right, key) |
Full 32-bit avalanche with xor-shift cascade |
| Type | Constructor | Keyed | Speed |
|---|---|---|---|
| SplitMix64 | NewSplitMix64(length) |
No | ~0.3 ns |
| SipHash | NewSipHash(key, length) |
Yes (128-bit key) | ~35 ns |
length is the bit size of the checksum (1–64). A longer checksum provides
better tamper detection at the cost of reduced data capacity.
NewAlphabetEncoder(alphabet, bitSize) creates an encoder. The total bit size
should be 64 + checksumLength (or 64 with no checksum).
Predefined alphabets:
| Name | Base | Characters |
|---|---|---|
AlphabetBase2 |
2 | 01 |
AlphabetBase16 |
16 | 0123456789abcdef |
AlphabetBase32Std |
32 | abcdefghijklmnopqrstuvwxyz234567 |
AlphabetBase32Hex |
32 | 0123456789abcdefghijklmnopqrstuv |
AlphabetBase32Crockford |
32 | 0123456789abcdefghjkmnpqrstvwxyz |
AlphabetBase36 |
36 | 0123456789abcdefghijklmnopqrstuvwxyz |
AlphabetBase58 |
58 | Bitcoin-style (no 0OIl) |
AlphabetBase62 |
62 | 0-9a-zA-Z |
AlphabetBase64 |
64 | RFC 4648 |
AlphabetBase64URL |
64 | URL-safe -_ instead of +/ |
The namespace value is XORed with the ID before permutation. This means the same ID in different namespaces produces completely different output:
a, _ := pipe.Obfuscate(42, 0) // "6F8TPB..."
b, _ := pipe.Obfuscate(42, 1001) // "X2K9MZ..."go test ./... -count=1Benchmarks:
go test ./... -bench=. -benchmemMIT — see LICENSE.