Hierarchical Deterministic Wallet

HD Wallet

HD Wallet이란 무엇일까?

HD 지갑(에이치디 지갑)이란 Hierarchical Deterministic Wallet의 약자로서, 하나의 마스터 시드(seed) 키를 사용하여 무수히 많은 주소를 생성할 수 있는 암호화폐 지갑이다. 계층적 결정 지갑이라고도 한다.

“해시넷 위키”

한 마디로 마스터 seed 키를 사용하여 여러 개의 주소를 생성하게 만들어주는 지갑이다.

매번 코인의 트랜잭션이 발생할 때마다 지갑의 주소가 새로 생성되는 지갑을 HD Wallet 이라고 한다.

보통 일반적으로는 지갑 = 1개의 주소라고 생각하는데, 그렇지 않다.

하나의 지갑에 여러 주소를 통합적으로 관리할 수도 있으며, 이렇게 임의로 여러 개의 주소를 그때 그때 추가 생성하기도 한다.

내부적으로 추가되고 생성된 적이 있는 모든 주소는 계속해서 유효하며, HD 지갑 내에서 통합되어 관리된다.

지갑에서 보여지는 잔액은 이 모든 주소에 들어있는 코인 수의 총합이 되며, 수신 가능하게 표시되는 주소는 단지 최근에 생성한 주소를 대표적으로 보여주는 것 뿐이다.

위 그림을 살펴보자. 12 word random Mnemonic에서 Master Private Key가 나온다.

그 다음으로 add Counter라는 작업을 거치는데, 단순하게 생각해서 내가 10개의 주소를 생성하겠다고 한다면 10개까지 작업을 진행하면 된다.

그렇게 되면 generate key단계에서 10개의 private key가 나오고, 당연히 이를 통해 10 개의 주소를 생성할 수 있다.

각기 다른 10명이 이렇게 생성된 각 주소에 입금하더라도, 1개의 지갑에 돈이 모이게 된다.

위험하지 않을까?

그렇지 않다. 오히려 더 안전해진다.

블록체인의 지갑 주소는 이메일과 같다. 단 하나의 주소값을 사용하게 된다면 내가 보내고 받는 모든 트랜잭션 내역을 한 눈에 파악할 수 있게 된다.

HD지갑에서는 하나의 지갑내에 여러개의 주소를 생성하여 알아서 분산하여 보관한다.

따라서 이러한 부분을 어느정도 방지할 수 있게 된다.

물론 파쇄된 문서도 맞추어볼 수 있듯이, 부지런히 블럭체인을 조합해보면 이것이 HD지갑 내부의 여러 주소의 것인가 추론은 해볼 수 있을 것이다.

그러나 당연히 한 눈에 정리되어 보이는 것과는 상당한 수준의 차이가 있다.

자식의 비밀키가 노출되면 부모의 비밀키는 위험하지 않을까?

당연히 괜찮다. 자식의 비밀키로 부모의 비밀키를 역산하는 것은 불가능하다.

구현법

Seed

HD 지갑은 루트 시드(Root Seed) 한 개로부터 많은 키와 주소가 생성된다.

루트 시드는 니모닉 단어 순서(Mnemonic Word Sequence)를 가장 많이 사용한다.

루트 시드를 HMAC-SHA512 알고리즘 함수를 사용하여 해시한 값에서 마스터 개인키(Mater Private Key)마스터 체인코드(Master Chain Code)를 생성한다.

512비트의 해시된 값에서 왼쪽 256비트를 마스터 개인키로 사용하고, 오른쪽 256비트를 체인코드로 사용한다.

그리고 마스터 공개키는 타원곡선 곱셈 함수를 사용하여 마스터 개인키로부터 계산된다.

그리고 다시 시드(A)를 생성해서 이 시드(A)를 가지고 마스터 키를 생성한다.

따라서 루트 노드를 파생시키는 니모닉 혹은 루트 시드만 알고 있다면 수백만 개의 키가 포함된 HD 지갑을 백업, 복원, 가져오기가 가능하다.

왜 루트 시드를 만들어놓고 다시 시드(A)를 만드는걸까?

이 때 DRBG라는 것이 사용된다. 프로그래밍 언어에서는 랜덤 함수를 사용하지만, 이것은 사실 예측이 가능한 가짜 난수이다.

따라서 진짜 난수를 생성하기 위해서는 가짜 난수값을 해싱한 값을 사용해야 한다.

진짜 난수를 생성하는 알고리즘을 DRBG라고 하고, HD 지갑에서 시드키를 생성할 때 사용된다.

시드에서 마스터키가 어떻게 나오는걸까?

계층 결정적 지갑은 자식키 유도 함수(CKD)를 사용하여 부모키로 부터 자식키를 파생한다.

자식키 생성에는 부모키, 체인코드, 인덱스 번호가 사용된다.

자식키를 생성하기 위해 부모키, 체인코드, 인덱스 번호를 결합하여 HMAC-SHA512로 해시한다.

해시된 값(512비트)을 반으로 나눠 개인키(256비트)와 체인코드(256비트)를 생성한다.

이 방법으로 자식키를 계속 생성할 수 있다.

강화된 유도법 (Hardened Derivation)

만약 자식 확장 개인키가 유출되는 경우, 자식 확장 개인키가 포함하고 있는 키와 체인코드를 사용하면 다른 자식의 개인키 전부를 알아 낼 수 있다.

이러한 문제에 대응하기 위해 HD지갑은 강화된 유도(Hardened Derivation) 함수를 사용한다.

강화된 유도 함수는 부모 키와 자식 체인코드 관계를 끊어버려서, 부모키와 자식키 사이에 **방화벽(firewall)**을 만든다.

m/44'/60'/0'/0/0 은 뭘까

위의 알 수 없는 저 문장은 바로 경로이다.

HD 지갑의 각 트리 레벨은 슬래시로 구분된다.

마스터 Private Key에서 파생된 비밀 키는 m이 되고, 마스터 공개 키에서 생성된 공개키는 M이 된다.

이 후 트리 레벨이 깊어질 수록 슬래시가 더 붙으면서 경로가 추가된다.

M / 0 : 마스터 공개키의 첫 번째 자식 공개키

m / 0 : 마스터 비밀키의 첫 번째 자식 비밀키

m / 0 / 1 : 마스터 비밀 키의 첫 번째 자식의 두 번째 자식 비밀키

BIP-44

비트코인의 BIP-44 표준은 복잡한 키 생성에 대한 방법을 제공한다.

BIP-44는 미리 정의된 5개의 레벨의 path로 구성된다.

m / purpose' / coin_type' / account' / change / address_index

경로에 있는 아포스트로피(') 문자는 BIP-32의 강화된 유도(hardened derivation)가 사용됨을 나타낸다.

  • purpose는 44'를 사용한다. BIP-44 규격이 사용됨을 의미한다.

  • coin_type은 가상화폐 코인 유형을 의미한다. 이더리움은 60'을 사용한다. 다른 코인 유형 코드는 SLIP0044 표준 문서에서 찾아 볼 수 있다.

  • account은 회계나 조직 목적을 위해 지갑을 하위 계좌로 세분화할 때 사용한다.

  • change은 이더리움에서 사용하지 않는다.

  • 비트코인에서 수신 주소(receiving address)와 잔액 주소(change address)를 구분하기 위해 사용한다.

  • address_index는 주소 번호를 의미한다. 순차적으로 증가한다.

Javascript 예시는 다음과 같다.

var Bitcore = require('bitcore-lib');

// 마스터 확장 개인키 생성
var xPriKey = new Bitcore.HDPrivateKey();

// 단절된 확장 자식 공개키 생성
var xPubKey = xPriKey.deriveChild("m/44'/60'/0'").hdPublicKey;

// 0번째 자식 공개키 생성
var pubKey = xPubKey.deriveChild("m/0/0").publicKey;
console.log(publicKey)

pubKey가 자식의 공개키 주소, xPriKey가 자식의 비밀키가 된다.

니모닉 코드 단어 BIP-39

니모닉 코드 단어(mnemonic code words)는 순서대로 나열된 영어 단어에서 시드(seed)를 만드는 방법이다.

니모닉 코드는 BIP-39 표준에 정의되어 있다. 니모닉 코드를 사용하면 HD 지갑을 쉽게 복원할 수 있다.

니모닉 코드 단어열은 12 ~ 24 단어로 구성된다.

자바스크립트를 이용해서 니모닉 시드에서 키를 생성해보자.

var Mnemonic = require('bitcore-mnemonic');

// 니모닉 코드 생성
var code = new Mnemonic(Mnemonic.Words.ENGLISH);
console.log(code.toString());

// 니모닉 코드에서 개인키 생성
var xPriKey = code.toHDPrivateKey();
console.log(xPriKey);

니모닉 단어로부터 개인키를 복원할 때는 다음과 같다.

// 복원용 니모닉 단어
var words = "damage clog alert hurt fork purchase iron cotton apple buffalo survey vast";

// 니모닉 단어로 부터 개인키 복원
var xPriKey = Mnemonic(words).toHDPrivateKey();
console.log(xPriKey);

참고로 bip39 라이브러리를 사용하면 한글을 니모닉 단어로 사용 할 수 있다.

만약 이더리움 지갑을 개발한다면 BIP-32, BIP-39, BIP-43, BIP-44 표준에 따라 니모닉 코드를 시드로 사용하는 HD지갑으로 구현해야 한다.

Sources …

Last updated