[카테고리:] 미분류

  • 🔍 BIP‑39 mnemonicToSeed 내부 흐름

    1. 입력값 준비
      • Mnemonic: 예) "abandon abandon ... about"
      • Salt: "mnemonic" + passphrase (passphrase가 없다면 빈 문자열)
    2. PBKDF2-HMAC-SHA512 적용
    3. 출력값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 표준)

    1. salt = "mnemonic" + passphrase (NFKD normalization 적용)
    2. 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

    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는 미포함)
    실수시 영향다른 시드 생성 → 완전 다른 지갑 생성