Chinese Password Generation with ExkPasswd
Mix.install([
{:exk_passwd, "~> 0.1.0"}
])
Introduction
This notebook demonstrates how to generate memorable Chinese passwords that output as ASCII Pinyin for universal keyboard compatibility.
The Problem
Most password systems worldwide (including in China) require ASCII-only passwords, but:
- Chinese users want memorable passwords in their native language
- Typing Chinese characters requires specific input methods
- International travel means accessing accounts on non-Chinese keyboards
The Solution
- Dictionary: Chinese words (memorable for Chinese speakers)
- Transform: Pinyin romanization (ASCII output, 500+ characters supported)
- Result: Typeable on any keyboard, memorable in Chinese
Features:
- Covers 500+ most frequent Chinese characters (Jun Da frequency list)
-
IME-compatible ü/v mapping (
lv,nvbutju,xu,yu) -
Helper functions:
contains_hanzi?/1,hanzi?/1
Setup: Load Chinese Dictionary
# Common Chinese words for passwords
chinese_words = [
"中国", # China
"世界", # World
"你好", # Hello
"朋友", # Friend
"爱情", # Love
"家人", # Family
"快乐", # Happy
"美好", # Beautiful
"春天", # Spring
"夏天", # Summer
"秋天", # Autumn
"冬天", # Winter
"太阳", # Sun
"月亮", # Moon
"星星", # Stars
"山水", # Mountains and water
"花朵", # Flowers
"树木", # Trees
"和平", # Peace
"希望" # Hope
]
# Load into ExkPasswd
ExkPasswd.Dictionary.load_custom(:chinese, chinese_words)
IO.puts("✓ Loaded #{length(chinese_words)} Chinese words")
Configure Password Generation
config = ExkPasswd.Config.new!(
# Use Chinese dictionary
dictionary: :chinese,
# Chinese words are typically 2 characters
word_length: 2..2,
# Override English default (4-10) to allow shorter words
word_length_bounds: 1..10,
# Use 3 words for good memorability and security
num_words: 3,
# ASCII separator (compatible everywhere)
separator: "-",
# Add digits for additional entropy
digits: {2, 2},
# No padding (keep it clean)
padding: %{char: "", before: 0, after: 0, to_length: 0},
# No case transforms (Chinese has no uppercase/lowercase)
case_transform: :none,
# Apply Pinyin transform for ASCII output
meta: %{
transforms: [%ExkPasswd.Transform.Pinyin{}]
}
)
IO.puts("✓ Configuration created")
Generate Passwords
IO.puts("\n=== Generated Passwords ===\n")
for i <- 1..10 do
password = ExkPasswd.generate(config)
IO.puts("#{i}. #{password}")
end
How It Works
The generation process:
graph LR
A[Chinese Words] --> B[Random Selection]
B --> C[中国-世界-你好]
C --> D[Pinyin Transform]
D --> E[zhongguo-shijie-nihao]
E --> F[Add Digits]
F --> G[45-zhongguo-shijie-nihao-89]
Internal representation: 中国-世界-你好
Output: 45-zhongguo-shijie-nihao-89
✅ Memorable: Chinese speaker remembers the meaning ✅ Compatible: Works on any keyboard, any system ✅ Secure: Cryptographically random word selection
Security Analysis
sample_password = ExkPasswd.generate(config)
IO.puts("\n=== Security Analysis ===")
IO.puts("Sample password: #{sample_password}")
IO.puts("")
IO.puts("Dictionary size: #{length(chinese_words)} words")
IO.puts("Entropy per word: #{Float.round(:math.log2(length(chinese_words)), 2)} bits")
IO.puts("Total word entropy: #{Float.round(:math.log2(length(chinese_words)) * 3, 2)} bits")
IO.puts("Additional entropy: Digits (2 before + 2 after) = ~13.3 bits")
IO.puts("Total entropy: ~#{Float.round(:math.log2(length(chinese_words)) * 3 + 13.3, 1)} bits")
IO.puts("")
IO.puts("Status: Good security for most accounts")
IO.puts("Recommendation: Add more words to dictionary for higher security")
Test Pinyin Transform
Test the Pinyin transform directly:
alias ExkPasswd.Transform.Pinyin
transform = %Pinyin{}
test_words = [
{"中国", "zhongguo"},
{"世界", "shijie"},
{"你好", "nihao"},
{"朋友", "pengyou"},
{"春天", "chuntian"},
{"女人", "nvren"}, # ü after n → v
{"旅行", "lvxing"}, # ü after l → v
{"学习", "xuexi"}, # ü after x → u
{"音乐", "yinyue"} # ü after y → u
]
IO.puts("\n=== Pinyin Transform Test ===\n")
for {chinese, expected} <- test_words do
result = ExkPasswd.Transform.apply(transform, chinese, nil)
match = if result == expected, do: "✓", else: "✗"
IO.puts("#{match} #{chinese} → #{result} (expected: #{expected})")
end
Hanzi Detection Helpers
The Pinyin module provides helper functions for detecting Chinese characters:
alias ExkPasswd.Transform.Pinyin
IO.puts("\n=== Hanzi Detection ===\n")
# Check if text contains Chinese characters
IO.puts("contains_hanzi?(\"Hello世界\") = #{Pinyin.contains_hanzi?("Hello世界")}")
IO.puts("contains_hanzi?(\"Hello World\") = #{Pinyin.contains_hanzi?("Hello World")}")
# Check if a single character is Hanzi
IO.puts("\nhanzi?(\"中\") = #{Pinyin.hanzi?("中")}")
IO.puts("hanzi?(\"A\") = #{Pinyin.hanzi?("A")}")
IO.puts("hanzi?(\"あ\") = #{Pinyin.hanzi?("あ")} # Hiragana")
# Character coverage
map_size = map_size(Pinyin.pinyin_map())
IO.puts("\nPinyin map contains #{map_size} characters")
IO.puts("Covers top 500 most frequent Chinese characters")
Customization Examples
More Words for Higher Security
config_secure = ExkPasswd.Config.new!(
dictionary: :chinese,
word_length: 2..2,
word_length_bounds: 1..10,
num_words: 5, # More words = more entropy
separator: "-",
digits: {3, 3}, # More digits
padding: %{char: "", before: 0, after: 0, to_length: 0},
case_transform: :none,
meta: %{transforms: [%ExkPasswd.Transform.Pinyin{}]}
)
IO.puts("High security password:")
IO.puts(ExkPasswd.generate(config_secure))
Shorter for Website Limits
config_short = ExkPasswd.Config.new!(
dictionary: :chinese,
word_length: 2..2,
word_length_bounds: 1..10,
num_words: 2, # Fewer words
separator: "", # No separator
digits: {2, 2},
padding: %{char: "", before: 0, after: 0, to_length: 0},
case_transform: :none,
meta: %{transforms: [%ExkPasswd.Transform.Pinyin{}]}
)
IO.puts("Short password:")
IO.puts(ExkPasswd.generate(config_short))
Real-World Usage
Example: Chinese User Traveling
Scenario: Li Wei is traveling to the US and needs to access Alipay on a hotel computer.
Without ExkPasswd:
-
Password:
中国世界2023! - Problem: Hotel computer has no Chinese input method ❌
With ExkPasswd:
-
Password:
45-zhongguo-shijie-nihao-89 - Memorized as: “中国世界你好” (China World Hello)
- Types on QWERTY: Easy! ✅
Key Takeaways
- Most systems require ASCII - Even Chinese banks use ASCII-only passwords
- Memorability + Compatibility - Chinese words transformed to Pinyin
- Cryptographically secure - Random selection, not predictable
- Works everywhere - Any keyboard, any system, any country
Next Steps
- Add more Chinese words to your dictionary for higher entropy
-
Try Japanese with Romaji: See
notebooks/i18n_japanese.livemd - Build custom transforms for other languages
- Integrate into your password manager workflow