- ์
๋ ฅ๊ฐ ์ค๋น
- Mnemonic: ์)
"abandon abandon ... about"
- Salt:
"mnemonic"
+passphrase
(passphrase๊ฐ ์๋ค๋ฉด ๋น ๋ฌธ์์ด)
- Mnemonic: ์)
- PBKDF2-HMAC-SHA512 ์ ์ฉ
- ์๊ณ ๋ฆฌ์ฆ: PBKDF2
- ํด์ ํจ์: HMAC-SHA512
- ๋ฐ๋ณต ํ์: 2,048ํ
- ์ถ๋ ฅ ๊ธธ์ด: 64๋ฐ์ดํธ (512๋นํธ) github.com+2medium.com+2github.com+2github.com
- ์๊ณ ๋ฆฌ์ฆ: PBKDF2
- ์ถ๋ ฅ๊ฐ โ
seed
- ๋ฐํ๋ 64๋ฐ์ดํธ ๋ฐ์ดํฐ๋ HD ์ง๊ฐ์ ๋ง์คํฐ ์๋๋ก ์ฌ์ฉ๋๋ฉฐ, ์ด ์๋๋ฅผ ํตํด BIP-32 ๊ณ์ธต์ ํค ์ ๋๊ฐ ์งํ๋ฉ๋๋ค.
๐ฆ ์ฝ๋ ์ ๊ตฌํ (trezorโcrypto ๊ธฐ์ค)
C ์ธํฐํ์ด์ค๋ ์ผ๋ฐ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ์ ๋๋ค:
cCopyEditvoid mnemonic_to_seed(const char* mnemonic, const char* passphrase, unsigned char* seed, void* ctx);
- ๋ด๋ถ์ ์ผ๋ก
PBKDF2(HMAC-SHA512, password=mnemonic, salt="mnemonic"+passphrase, iter=2048, dkLen=64)
ํธ์ถ seed
๋ฒํผ์ 64๋ฐ์ดํธ ๊ฒฐ๊ณผ๋ฅผ ์ฑ์์ค๋๋ค.
์ด ํจ์๋ Trust Wallet์ HDWallet.cpp
์์ ๋ค์๊ณผ ๊ฐ์ด ๋ํ๋์ด ์ฌ์ฉ๋ฉ๋๋ค:
cppCopyEditmnemonic_to_seed(mnemonic.c_str(), passphrase.c_str(), seed, nullptr);
โฑ๏ธ ์ฑ๋ฅ ๋ฉํธ๋ฆญ
- 2,048ํ ๋ฐ๋ณต โ ์ฝ 4,096ํ์ SHA-512 ๊ณ์ฐ์ ์ํ
- ์ด ์ฐ์ฐ๋์ ๋ธ๋ฃจํธ ํฌ์ค ๊ณต๊ฒฉ ๋ฐฉ์ด ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋ณดํธ์ ์ถฉ๋ถํ ์ค๊ณ๋์ด ์์ต๋๋ค.
โ ์์ฝ
mnemonicToSeed
= PBKDF2(HMAC-SHA512) ๊ธฐ๋ฐ์ ์์ ํ key stretching- ์
๋ ฅ๊ฐ: ๋๋ชจ๋ ๋ฌธ๊ตฌ +
"mnemonic"
+ (์ ํ์ ) ํจ์คํ๋ ์ด์ฆ - ์ค์ : 2,048ํ ๋ฐ๋ณต โ 64๋ฐ์ดํธ ์๋ ์์ฑ
- ์์ฑ๋ ์๋๋ ์ดํ BIPโ32 ํค ์ ๋์ ์ง๊ฐ ์ ์ฒด์ ์ธ ๋ณด์์ ๊ธฐ๋ฐ์ด ๋ฉ๋๋ค.
๐ 1. ๊ตฌํ ์์น & ํจ์ ์๊ทธ๋์ฒ
ํ์ผ: bip39.c
(trezorโcrypto)
cCopyEditvoid mnemonic_to_seed(const char* mnemonic, const char* passphrase, unsigned char* seed, void* ctx);
์ด ํจ์๋ BIPโ39 ํ์ค์ ๋ฐ๋ผ ๋๋ชจ๋ + ํจ์คํ๋ ์ด์ฆ๋ฅผ ์ ๋ ฅ๋ฐ์ 64๋ฐ์ดํธ(512๋นํธ) ์๋๋ฅผ ์์ฑํฉ๋๋ค stackoverflow.com+13github.com+13medium.com+13cs.utexas.edu+6bips.dev+6github.com+6.
๐ง 2. ๋ด๋ถ ์๊ณ ๋ฆฌ์ฆ ํ๋ฆ (BIP-39 ํ์ค)
salt = "mnemonic" + passphrase
(NFKD normalization ์ ์ฉ)PBKDF2
๋ฅผ ํตํด:- PRF: HMACโSHA512
- iterations: 2048
- dkLen (์ถ๋ ฅ๊ธธ์ด): 64๋ฐ์ดํธ
์ฆ:
cCopyEditPBKDF2-HMAC-SHA512(password=mnemonic, salt="mnemonic"+passphrase, iter=2048, dkLen=64)
โ seed[64]
์ด ๊ณผ์ ์ ๋๋ชจ๋ ๋ฌธ์ฅ์ ์๋๋ก ์์ ํ๊ฒ ํ์ฅ(key stretching) ํฉ๋๋ค crypto.stackexchange.com+8bips.dev+8en.wikipedia.org+8codeandlife.com+4stackoverflow.com+4reddit.com+4en.wikipedia.org+1zh.wikipedia.org+1.
โ๏ธ 3. PBKDF2-๋ธ๋ญ ์ฒ๋ฆฌ ๊ตฌํ ๋ถ์ (pbkdf2.c
)
ํ์ผ: pbkdf2.c
pbkdf2_hmac_sha512
ํจ์๋ PRF HMACโSHA512๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ ๋ธ๋ก(Uโ, Uโ, โฆ Uโ)์ XOR ํ์ ๋ฐ๋ณต ๊ณ์ฐ์ ์ํํฉ๋๋ค (RFC 8018 ๊ท๊ฒฉ) docs.pingidentity.com+15github.com+15codeandlife.com+15stackoverflow.com+3en.wikipedia.org+3cs.utexas.edu+3.- ๋จ์ ๊ตฌ์กฐ ์์:
cCopyEditfor (i = 1; i <= blocks; i++) {
U = HMAC(password, salt || INT(i))
T = U
for (j = 2; j <= iterations; j++) {
U = HMAC(password, U)
T ^= U
}
append T to derived key
}
- iterations = 2,048, dkLen = 64, HMACโSHA512 ๋ธ๋ก ๊ธธ์ด์ ๋ฐ๋ผ ๋ธ๋ก ์๋ 1๊ฐ(Uโ) ๋๋ 2๊ฐ(Uโ,Uโ)๊ฐ ์์ฑ๋ฉ๋๋ค.
๐ 4. ๋ณด์ ๊ณ ๋ ค ์ฌํญ
2048
๋ฒ ๋ฐ๋ณต ์ํ: ๋จ์ HMAC๋ณด๋ค 2K ๋ฐฐ ๋๋ฆฐ ์ฐ์ฐ์ผ๋ก brute-force ๊ณต๊ฒฉ ์ ํญ ๊ฐํ reddit.com+1zh.wikipedia.org+1.- ์ถ๋ ฅ ๊ธธ์ด 512๋นํธ๋ BIPโ32 ๋ง์คํฐ ์๋ ์์ฑ์ ์ ํฉํฉ๋๋ค.
- salt ๊ฐ์ด
"mnemonic"+passphrase"
์ด๋ฏ๋ก, ํจ์คํ๋ ์ด์ฆ ์ฌ์ฉ ์ ๋์ผ ๋๋ชจ๋์ด๋ผ๋ ๋ค๋ฅธ seed ์์ฑ โ ์ถ๊ฐ ๋ณด์ ๊ณ์ธต ์ ๊ณต crypto.stackexchange.com+10bips.dev+10github.com+10.
๐ 5. ์ ์ฒด ์์ฝ ํ ์ด๋ธ
๋จ๊ณ | ๋์ ๋ด์ฉ |
---|---|
1. ์ ๋ ฅ๊ฐ ์ค๋น | mnemonic + passphrase (UTFโ8 NFKD) |
2. salt ์์ฑ | "mnemonic" + passphrase ๋ฌธ์์ด ์กฐํฉ |
3. PBKDF2 ํธ์ถ | PRF = HMAC-SHA512, iter = 2048, dkLen = 64 |
4. seed ๋ฐํ | ์์ฑ๋ 64๋ฐ์ดํธ ์๋์ ์ฑ์์ ธ HDWallet ์ ๊ธฐ๋ฐ์ผ๋ก ์ฌ์ฉ๋จ |
๐งฉ 6. HDWallet.cpp
์์ ํ์ฉ ์์
Trust Wallet์ ์๋์ ๊ฐ์ด ํด๋น ์๋ ์์ฑ ํจ์๋ฅผ ๋ํํ์ฌ ์ฌ์ฉํฉ๋๋ค:
cppCopyEditmnemonic_to_seed(mnemonic.c_str(), passphrase.c_str(), seed, nullptr);
์ดํ ์์ฑ๋ seed
๋ฅผ ๊ธฐ๋ฐ์ผ๋ก BIP-32 ๋ง์คํฐ ํค์ ์ฒด๊ณ์ HD ํ์ ํค ์์ฑ์ผ๋ก ์ด์ด์ง๋ ๊ตฌ์กฐ์
๋๋ค.
๐ก BIP-39์์์ NFKD ์ ๊ทํ ๊ณผ์ (Mnemonic & Passphrase)
BIP-39 ํ์ค์ **๋๋ชจ๋(mnemonic phrase)**๊ณผ ํจ์คํ๋ ์ด์ฆ(passphrase) ๋ชจ๋์ ๋ํด Unicode NFKD (Normalization Form KD) ์ ๊ทํ๋ฅผ ์๊ตฌํฉ๋๋ค. ์ด๋ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ํ๊ธ, ์ผ๋ณธ์ด, ์ด๋ชจ์ง ๋ฑ์ ์๊ฐ์ ์ผ๋ก ๋์ผํ์ง๋ง ์ด์ง์ ์ผ๋ก ๋ค๋ฅธ ๋ฌธ์์ด์ ์ผ๊ด๋๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํจ์ ๋๋ค.
๐ 1. NFKD๋?
NFKD = Normalization Form Compatibility Decomposition
- ํธํ ๋ถํด(KD): visually ๋์ผํ์ง๋ง ์๋ฏธ๊ฐ ํธํ๋๋ ๊ธ์๋ค์ ๊ตฌ์ฑ์์๋ก ๋ถํด.
- ์:
รฉ
(U+00E9) โe
+ฬ
(U+0065 + U+0301)
๐ 2. BIP-39 ์ ๊ทํ ์๊ตฌ ์ฌํญ
โBoth the mnemonic sentence and the passphrase must be encoded in UTF-8 using NFKD normalization.โ
์ฆ:
- ๋๋ชจ๋:
"abandon abandon abandon ... about"
- ํจ์คํ๋ ์ด์ฆ: ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์์ ๋ฌธ์์ด (์:
"my pass"
)
๋ ๋ค NFKD๋ก ์ ๊ทํ๋ ํ UTF-8 ๋ฐ์ดํธ๋ก ๋ณํ๋์ด PBKDF2 ํจ์์ ์ ๋ฌ๋ฉ๋๋ค.
๐ง 3. Trust Wallet / trezor-crypto ์ ์ฉ ์ฌ๋ถ
trezor-crypto์์๋ ๋ช
์์ ์ผ๋ก NFKD
์ ๊ทํ๋ฅผ ์ํํ์ง ์์ต๋๋ค. ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- C์ธ์ด ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ NFKD ์ฒ๋ฆฌ๊ฐ ์์
- ๋์ ์์ ์ ํ๋ฆฌ์ผ์ด์ (์: Trust Wallet App, Python, JS ๋ฑ)์์ ์ฒ๋ฆฌํ๋๋ก ํฉ๋๋ค.
๐ ์์: Trust Wallet์์ Swift ๋๋ Java ์ฑ ์๋จ์์ NFKD ์ ์ฉ ํ mnemonic_to_seed()
๋ก ์ ๋ฌ
๐งช 4. ์์ (Python)
pythonCopyEditimport unicodedata
mnemonic = "๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ๊ฐ" # ์กฐํฉํ ํ๊ธ
normalized = unicodedata.normalize("NFKD", mnemonic)
print([hex(ord(c)) for c in normalized])
๊ฒฐ๊ณผ:
plaintextCopyEdit['0x1100', '0x1161', '0x1100', '0x1161', ...] # ๋ถ๋ฆฌ๋ ์๋ชจ
โ ๏ธ 5. ์ฃผ์์ฌํญ
ํญ๋ชฉ | ์ค๋ช |
---|---|
โ ํ์ ๋จ๊ณ | NFKD๋ ์๋ ์์ฑ์ ํต์ฌ ์ ์ฒ๋ฆฌ๋ก, ์๋ต ์ ์์ ํ ๋ค๋ฅธ ์๋ ์์ฑ |
๐ ๊ตญ์ ํ ๋ฌธ์ | ํ๊ธ, ์ผ๋ณธ์ด, ์๋์ด, ์ ์ผํธ๊ฐ ์๋ ์ ๋ฝ ๋ฌธ์ ๋ฑ์์ ํฐ ์ฐจ์ด ๋ฐ์ |
๐งช ํ ์คํธ ๋๊ตฌ | Python unicodedata.normalize() , Rust unicode-normalization , Java Normalizer ํด๋์ค ๋ฑ ์ฌ์ฉ |
โ ์์ฝ
ํญ๋ชฉ | ๋ด์ฉ |
---|---|
์ ๊ทํ ๋ฐฉ์ | Unicode NFKD (ํธํ ๋ถํด) |
์ ์ฉ ๋์ | ๋๋ชจ๋, ํจ์คํ๋ ์ด์ฆ |
์ธ์ฝ๋ฉ | UTF-8 ๋ฐ์ดํธ๋ก ๋ณํ ํ PBKDF2์ ์ ๋ ฅ |
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌํ | ๋๋ถ๋ถ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ์ฒ๋ฆฌ (Trezor-Crypto๋ ๋ฏธํฌํจ) |
์ค์์ ์ํฅ | ๋ค๋ฅธ ์๋ ์์ฑ โ ์์ ๋ค๋ฅธ ์ง๊ฐ ์์ฑ |
๋ต๊ธ ๋จ๊ธฐ๊ธฐ