hiero-ledger / hiero-ledger/hiero-sdk-cpp
[Intermediate]: Normalize Mnemonic Passphrases to NFKD
- Dominant language
- C++
- Stars
- 42
- Forks
- 108
- Avg merge
- 11h 45m
- Merged PRs (30d)
- 2
Description
## 🧩 Intermediate Friendly
This issue is a good fit for contributors who are already familiar with the Hiero C++ SDK and feel comfortable navigating the codebase.
Intermediate Issues often involve:
- Exploring existing implementations
- Understanding how different components work together
- Making thoughtful changes that follow established patterns
The goal is to support deeper problem-solving while keeping the task clear, focused, and enjoyable to work on.
---
## 🐞 Problem Description
Per the [BIP-39 specification](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki), both the mnemonic phrase and the passphrase should be normalized to **UTF-8 NFKD** (Unicode Normalization Form KD - Compatibility Decomposition) before being used in seed derivation.
**Current implementation in `MnemonicBIP39::toSeed()`:**
```cpp
const std::string mnemonicString = toString();
const std::string saltStr = std::string("mnemonic").append(passphrase);
// passphrase used directly without normalization
```
**Why this matters:**
- The same character can have multiple Unicode representations (e.g., "é" as U+00E9 vs "e" + U+0301 combining accent)
- Without NFKD normalization, different representations produce different seeds
- This causes interoperability issues with other BIP-39 compliant wallets
- Users could lose access to funds if they enter their passphrase on a different system
---
## 💡 Expected Outcome
Add NFKD normalization to both the mnemonic string and passphrase before seed derivation in `MnemonicBIP39::toSeed()`.
```cpp
std::vector MnemonicBIP39::toSeed(std::string_view passphrase) const
{
// Normalize mnemonic and passphrase to NFKD
const std::string normalizedMnemonic = normalizeToNFKD(toString());
const std::string normalizedPassphrase = normalizeToNFKD(std::string(passphrase));
const std::string saltStr = std::string("mnemonic").append(normalizedPassphrase);
// ... rest of implementation
}
```
---
## 🧠 Implementation Notes
**Unicode normalization library options:**
1. **ICU (International Components for Unicode)** - Comprehensive but heavy
- vcpkg: `vcpkg install icu`
- Well-tested, industry standard
2. **utf8proc** - Lightweight alternative
- vcpkg: `vcpkg install utf8proc`
- Smaller footprint, focused on normalization
3. **uni-algo** - Modern C++ header-only option
- vcpkg: `vcpkg install uni-algo`
- C++17 compatible, no runtime dependencies
**Example using utf8proc:**
```cpp
#include
std::string normalizeToNFKD(const std::string& input) {
utf8proc_uint8_t* result;
utf8proc_ssize_t length = utf8proc_map(
reinterpret_cast(input.c_str()),
static_cast(input.length()),
&result,
UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT
);
if (length < 0) {
throw std::runtime_error("NFKD normalization failed");
}
std::string normalized(reinterpret_cast(result), length);
free(result);
return normalized;
}
```
**Files to modify:**
- `vcpkg.json` - Add normalization library dependency
- `src/sdk/main/src/MnemonicBIP39.cc` - Add normalization to `toSeed()`
- Possibly create a utility function in `impl/` for reuse
**Testing:**
- Add test cases with non-ASCII passphrases
- Test with characters that have multiple Unicode representations
- Verify seed output matches other BIP-39 implementations
---
## ✅ Acceptance Criteria
- [ ] Unicode normalization library added as dependency
- [ ] `toSeed()` normalizes both mnemonic and passphrase to NFKD
- [ ] Unit tests added for non-ASCII passphrases
- [ ] Seeds match reference BIP-39 implementations for normalized inputs
- [ ] Documentation updated if needed
---
## 📋 Contribution Guide
- [ ] Comment `/assign` to request the issue
- [ ] Wait for assignment
- [ ] Fork the repository and create a branch
- [ ] Evaluate library options and choose one
- [ ] Implement NFKD normalization
- [ ] Add tests with Unicode edge cases
- [ ] Sign each commit using `-s -S`
- [ ] Push your branch and open a pull request
Read [Workflow Guide](docs/training/workflow.md) for step-by-step workflow guidance.
Read [README.md](README.md) for setup instructions.
**Pull requests cannot be merged without `S` and `s` signed commits.**
See the [Signing Guide](docs/training/signing.md).
---
## 📚 Additional Context or Resources
**References:**
- [BIP-39 Specification](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
- [Unicode NFKD Normalization](https://unicode.org/reports/tr15/)
- [utf8proc library](https://github.com/JuliaStrings/utf8proc)
**Files:**
- `src/sdk/main/src/MnemonicBIP39.cc` - Seed derivation implementation
- `src/sdk/main/include/MnemonicBIP39.h` - Public interface
- `vcpkg.json` - Dependencies
If you have questions, the community is happy to help:
https://discord.com/channels/905194001349627914/1337424839761465364
Contributor guide
Assessment
This issue has not been assessed yet.