IndicCrypt is a C++23 cryptographic library focused on format-preserving encryption (FPE) for Indic-language text.
The project combines:
- Unicode-aware Indic text processing
- Script and language-aware symbol modeling
- Structured encryption domains
- Modern cryptographic primitives
- Arbitrary-precision arithmetic
- FF1 format-preserving encryption
Status: Active development
Current test status: 29/29 tests passing
Language: C++23
Indic languages have linguistic structures that are not well represented by treating text as arbitrary bytes.
IndicCrypt is designed around a structured representation:
Indic Text
│
▼
Unicode / Grapheme Processing
│
▼
Indic Symbols
│
▼
Language / Script Aware Domain
│
▼
FPE Radix Representation
│
▼
FF1 Format-Preserving Encryption
│
▼
Encrypted Symbol Domain
│
▼
Encrypted Indic Text
The long-term goal is to allow applications to protect sensitive Indic text while preserving its structural representation.
IndicCrypt is being developed incrementally through independently tested layers.
- Unicode foundation using ICU4C
- Grapheme processing
- Indic script model
- Indic language model
-
IndicSymbol - Text canonicalization
- Cryptographic byte/key types
- Key lifecycle primitives
- KDF
- HKDF
- AES block primitives
- AES-256-GCM
- FPE alphabet abstraction
- FPE codec
- FPE radix
- FPE domain
- FPE parameters
- Arbitrary-precision FPE numerals
- FPE numeral mathematics
- Numeral encoding
- FF1 parameter block (
P) - FF1
Qblock - FF1 PRF
- FF1 Y generation
- FF1 Y number representation
- FF1 round value arithmetic
- FF1 round function
- 10-round FF1 Feistel construction
- FF1 encryption
- FF1 decryption
- Even-length message handling
- Odd-length message handling
- Message-length preservation
- Radix-domain preservation
The current FF1 implementation is structured as:
Input
│
▼
FpeParameters
│
├── Domain
├── Key
└── Tweak
│
▼
Initial A / B split
│
▼
┌──────────────────────────────┐
│ FF1 Round 0 │
│ │
│ P + Q │
│ ↓ │
│ PRF │
│ ↓ │
│ Y │
│ ↓ │
│ NUM(A) ± Y mod radix^m │
└──────────────────────────────┘
│
▼
Round 1
│
▼
...
│
▼
Round 9
│
▼
Ciphertext
Decryption performs the same construction in reverse order using subtraction.
FF1 operates on radix values instead of producing arbitrary binary ciphertext.
Conceptually:
Plaintext:
[0, 1, 0, 1, 0]
│
▼
FF1
│
▼
Ciphertext:
[1, 0, 1, 1, 0]
The ciphertext remains inside the configured radix domain and preserves the original message length.
This property is important for structured data and text domains.
The implementation explicitly handles odd-length inputs.
Examples currently tested include:
2 digits
3 digits
4 digits
5 digits
6 digits
7 digits
8 digits
9 digits
10 digits
For odd lengths, the FF1 halves alternate in size during the Feistel rounds.
For example, with three digits:
Round 0:
A = 1 digit
B = 2 digits
↓
C = 1 digit
↓
A = previous B
B = C
Round 1:
A = 2 digits
B = 1 digit
↓
C = 2 digits
The implementation preserves the total message length throughout the process.
IndicCrypt
│
├── core/
│ ├── crypto/
│ │ ├── AES
│ │ ├── AES-GCM
│ │ ├── KDF
│ │ ├── HKDF
│ │ └── key lifecycle
│ │
│ ├── text/
│ │ ├── canonicalizer
│ │ ├── grapheme
│ │ ├── language
│ │ ├── script
│ │ └── symbol
│ │
│ └── fpe/
│ ├── alphabet
│ ├── codec
│ ├── domain
│ ├── radix
│ ├── parameters
│ ├── numeral
│ ├── numeral math
│ ├── numeral encoding
│ ├── PRF
│ ├── parameter block
│ ├── Q block
│ ├── Y
│ ├── Y number
│ ├── round value
│ ├── round
│ └── FF1
│
├── include/
│ └── indiccrypt/
│ ├── crypto/
│ ├── text/
│ └── fpe/
│
├── tests/
│ ├── crypto/
│ ├── text/
│ └── fpe/
│
└── research/
├── crypto-design.md
├── search-security.md
└── threat-model.md
IndicCrypt does not treat Indic text simply as a sequence of bytes.
The text layer provides abstractions for:
- Unicode-aware processing
- Grapheme boundaries
- Script information
- Language information
- Canonicalization
- Structured symbols
The primary symbol abstraction is:
indiccrypt::text::IndicSymbolA symbol contains:
Symbol ID
Grapheme
Script
Language
This allows the FPE layer to operate on a controlled symbol domain instead of directly operating on arbitrary UTF-8 byte sequences.
The FPE subsystem separates text representation from mathematical encryption.
IndicSymbol
│
▼
FpeAlphabet
│
▼
FpeCodec
│
▼
FpeRadix
│
▼
FpeDomain
│
▼
FpeParameters
│
▼
Ff1
Defines the symbols belonging to an encryption domain.
FpeAlphabet(
Script script,
std::vector<IndicSymbol> symbols,
std::string version
);The alphabet provides deterministic symbol-to-index mapping.
Converts between:
IndicSymbol[]
and:
FpeRadix::Value[]
This separates text representation from the numerical representation used by FF1.
Represents the numerical domain used by FPE.
Each digit satisfies:
0 <= digit < radix
Combines:
Alphabet
Codec
Radix
into a single encryption domain.
Carries the parameters required by FF1:
Domain
Key
Tweak
Length constraints
FF1 requires arithmetic that can exceed native integer sizes.
IndicCrypt therefore uses:
Boost.Multiprecision::cpp_int
through the Ff1Numeral abstraction.
Supported operations include:
toInteger()
fromInteger()
digits()
radix()
size()
empty()This allows the FPE implementation to operate on radix powers substantially larger than 64-bit integer limits.
The FF1 implementation is split into small components:
Ff1
│
├── Ff1Numeral
├── Ff1NumeralMath
├── Ff1NumeralEncoding
├── Ff1Prf
├── Ff1ParameterBlock
├── Ff1QBlock
├── Ff1YGenerator
├── Ff1YNumber
├── Ff1RoundValue
└── Ff1Round
Each component has its own tests where appropriate.
IndicCrypt uses CTest with CMake/Ninja.
Current result:
29/29 tests passed
100% tests passed
Total Test time ≈ 13 seconds
Run the complete suite:
ctest --test-dir build --output-on-failureRun the FF1 integration test:
ctest --test-dir build -R indiccrypt_ff1_tests -VThe FF1 test suite currently verifies:
- Encryption executes successfully
- Decryption executes successfully
- Encryption is deterministic
- Decryption recovers the original plaintext
- Ciphertext remains inside the radix
- Message length is preserved
- Invalid digits are rejected
- Even-length messages
- Odd-length messages
- Multiple message lengths
IndicCrypt currently requires:
- C++23-compatible compiler
- CMake
- Ninja
- OpenSSL 3
- ICU4C
- Boost
The project is currently developed and tested on macOS.
cmake -S . -B build -G Ninjacmake --build buildctest --test-dir build --output-on-failureUsed for cryptographic primitives and AES-GCM.
Used for Unicode-aware text processing.
Used for arbitrary-precision arithmetic required by the FPE implementation.
IndicCrypt is intentionally being built in independently tested milestones.
Current progression:
Unicode foundation
↓
Indic text model
↓
Cryptographic primitives
↓
FPE domain
↓
Arbitrary-precision numerals
↓
FF1 infrastructure
↓
P block
↓
Q block
↓
PRF
↓
Y generation
↓
FF1 round arithmetic
↓
Complete FF1
↓
Indic text integration
↓
Known-answer vectors
↓
Differential testing
↓
Security review
The major difference from earlier stages is that the FF1 encryption/decryption engine is now implemented and passing its integration tests.
Security is a primary design goal.
The repository contains ongoing security and design documentation:
research/crypto-design.md
research/search-security.md
research/threat-model.md
However:
IndicCrypt is experimental software and should not yet be used to protect production secrets.
Passing 29/29 unit and integration tests does not constitute cryptographic validation.
Before a production release, the project still requires:
- Official FF1 known-answer test vectors
- Differential/interoperability testing
- Extensive boundary testing
- Fuzz testing
- Security review
- Cryptographic review
- API review
- Key-management review
- Performance benchmarking
Indic text is represented through language, script, grapheme, and symbol abstractions.
Text processing, domain encoding, cryptography, and FPE mathematics remain separate layers.
Established cryptographic primitives such as OpenSSL are preferred where appropriate.
Invalid keys, lengths, radix digits, and numerical values are rejected explicitly.
FPE arithmetic should not be artificially constrained by native integer sizes.
Major subsystems are introduced with independent tests.
Passing tests is a milestone, not proof that a cryptographic implementation is ready for production.
- Repository structure
- CMake build system
- ICU4C integration
- Unicode foundation
- Indic text model
- Text canonicalization
- Crypto types
- Key lifecycle
- KDF
- HKDF
- AES block primitive
- AES-256-GCM
- Expanded cryptographic validation
- Alphabet
- Codec
- Radix
- Domain
- Parameters
- Numeral representation
- Numeral mathematics
- Numeral encoding
- FF1 API
- FF1 PRF
- FF1 P block
- FF1 Q block
- Y generation
- Y number representation
- Round value arithmetic
- FF1 round function
- FF1 encryption
- FF1 decryption
- Even-length handling
- Odd-length handling
- Official FF1 known-answer vectors
- Differential/interoperability testing
- Indic alphabet registry
- Language-specific domains
- Script-specific domains
- Grapheme-preserving encryption
- Indic text encryption API
- Indic text decryption API
- End-to-end Indic text tests
- Extensive negative testing
- Fuzz testing
- Differential testing
- Performance benchmarks
- Security review
- API stabilization
- Documentation
- Release candidate
Current milestone: FF1 implementation
Tests: 29 / 29
Build: Passing
Text foundation: Complete
Crypto foundation: Complete
FPE foundation: Complete
FF1 rounds: Complete
FF1 encryption: Complete
FF1 decryption: Complete
Known-answer tests: Pending
Indic text API: Pending
Security review: Pending
Production ready: No
License information will be added as the project approaches its first public release.
This version matches your **actual current milestone**: the FF1 engine is no longer merely “in development”; you have reached **29/29 passing tests**. Your uploaded README's old 25/25 status and “FF1 implementation still being implemented” section are now stale. :contentReference[oaicite:1]{index=1}