앞선 포스트에서 니모닉에 대해서 다뤘다. 이제 그 니모닉으로 어떻게 지갑 주소들이 파생되는지 알아보자.
계층 결정적 지갑, HD Wallet (Hierarchical Deterministic Wallet)
내 지갑을 watch-only 지갑에 연결하고 해외 거래소로부터 송금받을때 아래와 같은 주소를 복사해서 전달받았다.

QR 코드 위에 보면, m/84'/0'/0'/0/0 이라는게 있다.
이는 BIP-32에서 제안된 계층적 결정적 지갑의 파생경로 형태이다.
니모닉으로부터 수많은 공개키를 계층적으로 파생하는 형태다.
이를 해석해보면 다음과 같다. 아래는 순서대로 파생된다.
- m: master key로 마스터 개인키
- 84': 84번째 강화 자식키로, BIP-0084을 따르는 주소임을 명시 (미리 약속된 index)
- 0': 84번째 강화 자식키의 0 번째 손자 강화키. 해당 depth는 코인 종류를 결정함. 0번이 비트코인
- 0': 증손자 강화키. 계정에 해당됨
- 0: 고손자 일반키. 잔돈주소 여부다
- 0: 현손자 일반키. 진짜 주소 index. 32비트로, 총 42억개의 경로가 생긴다.

요약하면 위와 같고, 각각 뭔지와 원리에 대해 하나씩 알아보자.
파생 원리 요약
일단 bip-0039를 통해 니모닉을 생성한다. 니모닉에 대해 궁금하면 아래 포스팅에 자세히 적어두었다.
https://3min-bitcoin.tistory.com/4
니모닉(mnemonic)
셀프 커스터디를 할 때 니모닉 12 or 24자리를 얻어서 잘 보관해두었을 것이다.니모닉이란 무엇인지에 대해 알아보자비트코인 주소를 재사용하지 마라비트코인에서 개인키는 단순한 256bit 숫자다
3min-bitcoin.tistory.com
과정을 간략히 요약하면 아래와 같다.
- 니모닉 -> PBKDF2 함수 -> seed
- seed -> HMAC-SHA512 -> 마스터 확장 개인키 m
- 부모 확장 개인키로부터 부모 확장 공개키, 강화 자식 확장 개인키, 일반 자식 확장 개인키 파생 (연쇄적)
- 부모 확장 공개키로부터 일반 자식 공개키 파생 (연쇄적)
seed 생성, passphrase(패스프레이즈, 패프)
니모닉으로 시드를 만들 때는 아래 그림과 같은 과정을 거친다.

우선 12 ~ 24자리 니모닉을 공백을 기준으로 순서대로 나열한다. ex): bitcoin apple best ... ... ...
그리고 솔트를 뒤에 붙힌다. 솔트란, 해시함수 뒤에 추가하는 문자열로, 눈사태 효과에 의해 아예 다른 결과가 나온다.
솔트의 기본값은 "mnemonic"이다.
패스프레이즈는 시드를 생성할 때 솔트에 추가되는 문구로, 니모닉과 별개의 추가 비밀번호 문구다.
salt = mnemonic || passphrase 형태다. string concat 이라고 보면 된다.
패스프레이즈는 대소문자, 띄어쓰기 절대 틀리면 안된다. 따라서 초심자는 설정에 유의해야 한다.
니모닉을 나열한 문구를 m, 솔트를 k라고 한다면 Seed = PBKDF2(k, m) 는 이와 같이 생성된다.
PBKDF2에서 sha512를 사용했으므로 시드(seed phrase)는 총 512bit이 된다.
PBKDF2는 그림에서 나온것 처럼 HMAC-SHA512 함수를 2048번 반복하는 함수다. 자세한건 아래에 추가로 정리해두었다.
마스터 확장 개인키
"Bitcoin seed" 라는 문자열과 시드를 HMAC-SHA512 함수에 넣으면 512bit의 마스터 확장 개인키가 나온다.
master private key = HMAC-SHA512("Bitcoin seed", Seed)
이게 앞서 말했던 파생경로의 맨 앞부분, 마스터 개인키 m 이다.

상위 256bit(32bytes)는 마스터 개인키, 하위 256bit는 마스터 체인코드가 된다.
체인코드란, 부모키로부터 확장 자식키를 파생할 때 hmac에 사용되는 키 값이다.
부모키에서 자식키 파생원리
계층구조이므로, 마스터 확장 개인키가 최초 부모키가 되어, 자식키들이 하나씩 파생된다.
처음 파생된 자식키가 다시 부모키가 되어 또 자식키를 파생하는 연쇄구조이다.
우선 부모 확장 공개키를 만드는 법을 알아보자. 이거는 개인키 -> 공개키 원리와 아예 동일하다.
부모 확장 공개키는 부모 개인키와 부모 체인코드를 각각 타원곡선 연산(secp256k1)한 결과다.
"개인키 -> secp256k1 -> 공개키" 파생되는 구조를 개인키와 체인코드에 동일하게 256bit씩 적용하여 붙이면 부모확장 공개키가 된다.
부모키로부터 자식키를 파생한하는 원리를 알아보자. 자식은 크게 2종류가 있다.
- 강화 키(Hardened Key): 오직 부모의 확장 개인키로만 파생이 가능한 키, 따옴표를 뒤에 붙혀서 강화키임을 표시한다.
- 일반 키(Normal Key): 부모의 확장 개인키 또는 공개키로 파생이 가능한 키. 확장 공개키만 있어도 일반키는 파생이 가능하다.
💡 watch-only 지갑은 부모 확장 공개키로부터 파생을 시킨 일반 자식키들을 나열하기에, 개인키 없이 임포트가 가능하다. 💡
강화 자식키 파생
(HMAC에 대해서 잘 모른다면, 하단에 작성해둔 HMAC부터 읽어보는 것을 추천한다)
먼저 강화 자식키 파생하는 방법부터 알아보자.
키(체인코드)와 메세지(개인키)를 HMAC-SHA512를 돌리면 새로운 512bit 값이 나온다. 자세한 식은 다음과 같다.
키 조정값 || 자식 체인코드 = HMAC(부모 체인코드, 0x00 || 부모 개인키 || 인덱스)
나온 값 중 상위 256bit는 키 조정값(key tweak)이라고 한다. 그리고 하위 256bit는 자식의 체인코드가 된다.
키 조정값은 부모 개인키와 정수 모듈러 덧셈 연산을 하여 강화 자식 캐인키를 만든다. (타원곡선 덧셈 아님)
강화 자식 개인키 = 키 조정값 + 부모 개인키
강화 자식 개인키에 자식 체인코드를 이어붙이면 512bit짜리 확장 개인키가 된다.
강화 자식 확장 개인키 = 강화 자식 개인키 || 자식 체인코드
인덱스는 총 32bit 값으로 부모로부터 파생되는 자식키의 번호를 나타낸다. 32비트이므로 약 42억개 까지 지정 가능하다.
인덱스 최상위 비트가 1이면 강화 자식키, 최상위 비트가 0이면 일반 자식키를 나타낸다.
고로 1비트씩 빼서 2^31, 21억개의 강화 자식키, 21억개의 일반 자식키가 파생된다.
index0) key tweak || chain_code(자식) = HMAC(chain_code(부모), 0x00 || private_key || 0x80000000)
index1) key tweak || chain_code(자식) = HMAC(chain_code(부모), 0x00 || private_key || 0x80000001)
index2) key tweak || chain_code(자식) = HMAC(chain_code(부모), 0x00 || private_key || 0x80000002)
일반 자식키 파생
일반 자식키는 다음과 같이 생성된다.
키 조정값 || 자식 체인코드 = HMAC(부모 체인코드, 부모 공개키 || 인덱스)
일반 자식 개인키 = 키 조정값 + 부모 개인키
일반 자식 확장 개인키 = 일반 자식 개인키 || 자식 체인코드
index0) key tweak || chain_code(자식) = HMAC(chain_code(부모), public_key || 0x00000000)
index1) key tweak || chain_code(자식) = HMAC(chain_code(부모), public_key || 0x00000001)
index2) key tweak || chain_code(자식) = HMAC(chain_code(부모), public_key || 0x00000002)
일반 자식 공개키
일반 자식 공개키는 2가지 방법으로 파생할 수 있다.
기본적으로 일반 자식 개인키를 타원곡선 연산(secp256k1)을 돌리면 된다.
또 다른 방법으로는 부모 확장 공개키만을 이용하는 방법이다.
개인키 없이 어떻게 가능하냐면, 타원곡선 연산의 분배 법칙이 성립하기 때문이다.
복잡하지만, 어떻게 유도 되는지 하나씩 보자.
- 일반 자식 공개키
- = 일반 자식 개인키 * G (secp256k1 타원곡선 스칼라곱)
- = (key tweak + 부모 개인키) * G (분배 법칙)
- = (key tweak * G) + (부모 개인키 * G)
- = (key tweak * G) + 부모 공개키
- = 키 조정값의 공개키 + 부모 공개키
즉 최종 식을 보면, 키 조정값과, 부모 공개키만 있으면 된다.
심지어 키 조정값도 부모 공개키로 유도해내므로, 부모 공개키만 있으면 된다.
워치온리 지갑에서 수많은 내 공개키 주소들을 불러낼 수 있는건, 확장 공개키를 통해 자식 공개키들을 파생해내기 때문이다.
m/84'/0'/0' 을 임포트 하면, 즉 account 단위의 확장 공개키를 워치 온리 앱에 입력하면 주소가 불러와지는 원리이다.
정리
m/84'/0'/0'/0/0 가 어떻게 파생되는지 알아봤다.
마스터 개인키로부터 5번의 자식키를 연쇄적으로 파생했음을 알 수 있다.
그리고 니모닉 하나로 최종 가장 leaf level에는 총 42억개의 공개키 주소가 파생되고
그 중 21억개는 강화 자식키, 나머지 21억개는 일반 자식키이다.
HMAC-SHA512
MAC은 Message Authentication Code로, 메세지 인증 코드다. 데이터 무결성과 인증을 보장하는 짧은 코드다.
HMAC은 Hash-based Message Authentication Code로, 해시함수를 사용하는 MAC이다.
MAC은 무결성을 보장하고, 인증도 되어야 한다.
무결성이란, 메세지가 중간에 변조되지 않았는지 보장되어야 하는 성질을 뜻한다.
그리고, SHA512 해시함수를 사용하면 무결성은 보장이된다.
예를 들어 "Send 1 BTC to 손흥민" 이라는 문장을 SHA512 해시함수를 돌린다고 해보자.
SHA512 -> "111000... ...101010" (512bit) 와 같이 특정 512bit 문자열 해시값이 나온다.
이를, 수신자한테 <메세지, 해시 값> 세트로 보내면, 수신자는 메세지를 다시 SHA512 해시함수를 돌려서 해시값이 변조되지 않았는지 체크할 수 있다.
그러면 인증은 어떻게 보장되는가?
만약 중간에 박지성 팬이 메세지를 탈취해서, "Send 1 BTC to 박지성" 으로 변조한 후 SHA512를 돌리면 아예 다른 해시값이 나올것이다. 그리고, <변조된 메세지, 변조된 해시 값> 을 수신자에게 보내면, 무결성은 통과하지만, 보내는 사람이 바뀌었는지에 대한 인증은 실패한다.
이를 방지하고, 인증을 추가하기 위해 HMAC함수는 해시함수에 비밀키를 추가 인풋값으로 받는 해시 함수다.
메세지를 m 이라고 하고, 비밀 키를 k 라고 하면 함수는 다음과 같다.
HMAC-SHA512(k, m) = SHA512(k || SHA512(k || m))
즉, HMAC-SHA512는 비밀키와 함께 SHA512 함수를 두 번 돌리는 함수다.
인증이 보장되려면, 송신자, 수신자 모두 양측 모두 비밀키 k 를 알고있어야 한다. 그래야 올바른 HMAC 재현에 성공하기 때문이다.
공격자가 중간에 메세지를 탈취해도 비밀키 k를 모르면 속일 수 없기 때문이다.
BIP-32에서 HMAC을 사용하는 이유는, 대칭키 형태의 인증/보안을 위해 사용하는 것은 아니다.
BIP-32 프로세스가 아닌, 외부에서 생성된 임의의 SHA-512 해시 결과과 충돌할 가능성을 차단하고,
오로지 BIP-32 마스터 키로부터 만들어졌음을 보장하기 위해서 비밀키가 들어가는 HMAC을 사용한다.
PBKDF2 (Password-Based Key Derivation Function 2)
표준 키 파생함수로 (KDF, Key Derivation Function) RFC 8018에 정의되어있고, 비밀번호를 안전한 키로 바꾸는 기술이다.
DerivedKey = PBKDF2(PRF, Password, Salt, Iterations, dkLen)
인터페이스는 위와 같다.
- PRF: 함수로, HMAC-SHA512가 들어가는자리
- Password: 메세지. 니모닉이 들어갈 자리
- Salt: 패스프레이즈 들어갈 자리
- Iterations: PRF를 몇 번 반복할지. bip-0039에서는 2048번
- dkLen: 최종 키 길이. bip-0039에서는 512bit, 64바이트 그대로 사용
즉, 시드를 구할 때 PBKDF2는 HMAC-SHA512 2048번 반복하는 녀석이다
이렇게 하는 이점은
- 브루트 포스 공격 속도 대폭 늦춤
- Salt 처리 방식, 즉 인터페이스가 표준화 되어 있어서 구현, 호환성이 좋음.
참고문헌
Why is the root seed of an HD wallet undergo an additional HMAC-SHA512 hash to generate the master private key and master chain
From what I understand, the wallet seed is created by inputting into PBKDF2 (using HMAC-SHA512 of which 2048 rounds are applied) a bunch of mnemonic code words and an optional salt (defaulted to "
bitcoin.stackexchange.com
https://learnmeabitcoin.com/technical/keys/hd-wallets/
HD Wallets | Hierarchical Deterministic Wallets
HD Wallets Hierarchical Deterministic Wallets BIP 32: Hierarchical Deterministic Wallets A hierarchical deterministic wallet (or "HD Wallet") is a wallet that generates all of its keys and addresses from a single source. Hierarchical – The keys and addre
learnmeabitcoin.com