[마스터링 이더리움] 5장: 지갑

728x90

 

넓은 의미에서 지갑은 이더리움의 주요 사용자 인터페이스를 제공하는 소프트웨어 애플리케이션이다.

지갑은

  • 사용자 돈에 대한 접근을 통제하고,
  • 키와 주소를 관리하며,
  • 잔액을 추적하고,
  • 트랜잭션 생성과 서명을 제어한다.

개발자의 시각으로 좁혀보면,

지갑은 사용자의 키를 보관하고 관리하기 위해 사용되는 시스템으로, 모든 지갑은 키 관리 구성요소를 갖고 있다.

그 자체로 전부인 지갑도 있고, 좀 더 넓은 범주에서 이더리움 기반 탈중앙화 애플리케이션의 인터페이스인 브러우저의 일부이거나, 댑(DApp)의 한 부분인 지갑도 있다.

 

 

지갑 기술의 개요

지갑을 설계할 때 중요한 고려사항 하나는 편의성과 프라이버시 사이에 균형을 맞추는 것이다.

  • 가장 편리한 이더리움 지갑은 하나의 개인키와 주소를 가지고 이를 재사용해서 모든 것을 처리하는 지갑이다. 하지만 이것은 프라이버스 보안에 대해 매우 위험하다.
  • 반대로 모든 프랜잭션에 새로운 키를 사용하는 것이 프라이버시를 위해 가장 좋지만 관리하기가 몹시 어렵다.

이 사이에서 적절한 균형을 달성하기가 쉽지 않은데, 이것이 바로 좋은 지갑을 설계하는 것이 다른 무엇보다 중요한 이유다.

 

 

이더리움에 관한 일반적인 오해 중 하나는 이더리움 지갑이 이더 혹은 토큰을 보유한다는 것이다.

 

하지만 매우 엄격하게 말하면 지갑은 단지 키만 보유한다.

이더 혹은 다른 토큰은 이더리움 블록체인에 기록된다.

 

사용자는 지갑에 있는 키로 트랜잭션을 서명함으로써 네트워크에서 토큰을 제어한다.

이러한 맥락에서 이더리움 지갑은 키체인(keychain)이다.

 

이 개념보다 더 중요한 것은 기존 은행의 중앙화된 시스템을 다루는 것에서부터 블록체인 플랫폼의 탈중앙화된 시스템으로 사고방식을 바꾸는 것이다.

💡 중앙화된 시스템탈중앙화된 시스템

✅ 중앙화된 시스템: 은행과 나만이 나의 계좌에 있는 돈을 볼 수 있고 트랜잭션을 위해 자금을 옮기고 싶다고 은행만 납득시키면 되는 시스템

 탈중앙화된 시스템: 모든 사람이 계좌의 이더 잔액을 볼 수 있고 계좌 주인을 알지 못하지만 소유자는 트랜잭션 진행을 위해 자금을 옮기고 싶어 한다는 것을 모든 사람에게 납득시켜야 하는 시스템

 

실제로 블록체인의 이런 탈중앙화된 시스템으로 인해 우리는 지갑 없이도 계좌의 잔액을 확인하는 독립적인 방법이 있다.

예를 들면, 익스플로러(이더스캔).

  • 정리
    • 이더리움 지갑은 키를 포함하고 이더나 토큰은 포함하지 않는다. 지갑은 개인키와 공개키 쌍을 포함하는 키체인과 같은 것이다. 사용자는 개인키로 트랜잭션에 서명함으로써 이더가 자신의 소유임을 증명한다. 이더는 블록체인에 저장된다.

 

그렇다면 지갑의 두 가지 형태에 대해서 알아보자.

지갑의 주요한 두 가지 형태

지갑이 포함하는 키가 서로 관련이 있느냐 없느냐에 따라 구분

  1. 비결정적 지갑
    • 각기 다른 무작위 수로부터 각가의 키를 무작위적으로 추출하는 비결정적 지갑(nondeterministic wallet)
    • 이런 형태의 지갑을 ‘그냥 열쇠뭉치(Just a Bunch Of Keys)’라는 뜻이 JBOK 지갑이라고 부른다.
  2. 결정적 지갑
    • 모든 키가 시드(seed)라고 하는 단일 마스터 키로부터 파생된 결정적 지갑(deterministic wallet)이다.
    • 모든 키는 서로 관련이 있고 원래의 시드를 갖고 있다면 다시 키를 파생시킬 수 있음
    • 결정적 지갑에는 여러 가지 키 파생 방식이 있는데, 가장 많이 사용하는 파생 방식은 ‘HD 지갑(뒤에서 다룰 것)’에서 설명하는 트리 같은 구조를 사용한다.

여기서 말하는 seed는 지갑의 뉴모닉 코드 단어(mnemonic code words)라고 한다.

 

누군가가 여러분의 뉴모닉 코드 단어를 손에 넣으면 지갑을 재생성하여 여러분의 이더와 스마트 컨트랙트에 접근할 수 있다.

따라서 조심히 보관하길 바란다.

 

 

 

비결정적(무작위) 지갑

사전 판매용으로 생성된 첫 번째 이더리움 지갑은 각 지갑 파일에 무작위로 추출된 단일 개인키를 저장했다.

하지만 이런 예전 스타일 지갑은 여러 측면에서 불편해 현재는 결정적 지갑으로 대체되고 있다.

 

비결정적 지갑은 이더리움 주소의 제사용을 피하는 것이 좋은 지침으로 간주되어 매 트랜잭션마다 새로운 주소(새로운 개인키)를 사용한다.

 

이렇게 하려면 정기적으로 키 목록을 증가시켜야 하는데, 이는 정기적인 백업이 필요하다는 뜻이다.

만일 지갑을 백업하기 전에 데이터를 잃어버리면 자금과 스마트 컨트랙트에 접근하지 못하게 된다.

 

‘타입 0’으로 볼 수 있는 지갑으로 때마다 모두 새로운 주소를 위한 새로운 지갑 파일을 만들기 때문에 다루기 어렵다.

그럼에도 불구하고 많은 이더리움 클라이언트(게스 포함)는 보안 강화를 위해 암호문으로 암호화된 단일(무작위로 생성된) 개인키가 들어있는 JSON 인코딩 파일인 키저장소(keystore) 파일을 사용한다.

 

아래가 JSON 파일의 내용이다.

{
	"address": "001d3f1ef827552ae1114027bd3ecf1f086ba0f9", 
	"crypto": {
		"cipher": "aes-128-ctr", 
		"ciphertext":
			"233a9f4d236ed0c13394b504b6da5df02587c8bf1ad8946f6f2b58f055507ece", 
		"cipherparams": {
			"iv": "d10c6ec5bae81b6cb9144de81037fa15" 
		},
		"kdf": "scrypt", 
		"kdfparams": {
			"dklen": 32, 
			"n": 262144, 
			"p": 1, 
			"r": 8, 
			"salt":
				"99d37a47c7c9429c66976f643f386a61b78b97f3246adca89abe4245d2788407" 
		},
	"mac": "594c8df1c8ee0ded8255a50caf07e8c12061fd859f4b7c76ab704b17c957e842",
	"id": "4fcb2ba4-ccdb-424f-89d5-26cce304bf9c", 
	"version": 3
}

키저장소 형식은 무차별, 사전 및 레인보우 테이블 공격을 대비해 암호 확정 알고리즘으로 알려진 키 파생 함수(Key derivation function, KDF)를 사용한다.

간단히 말하면, 개인키는 암호문에 의해 직접 암호화되지 않고, 암호문은 반복적으로 해싱됨으로써 강화된다.

  • 키저장소 JSON에서 파라미터 crypto.kdfparams.n을 통해 해시 함수가 몇 라운드를 반복했는 지 알 수 있다. (여기서는 262144번)
  • 이 소리인 즉슨, 공격자가 암호문을 무차별적으로 생성하려면 암호화를 시도할 때마다 262144번의 해시 라운드를 적용해야 한다. 암호문에 대한 공격 불가능

자바스크립트 라이브러리 keythereum과 같이 키저장소 형식을 읽고 쓸 수 있는 여러 소프트웨어 라이브러리가 존재한다.

 

비결정적 지갑은 간단한 테스트 외에는 사용을 권장하지 않는다. 매우 불편하기 때문이다.

대신 백업을 위해 뉴모직 시드가 있는 산업계 표준 HD(Hierarchical Deterministic) 지갑을 사용해라.

 

 

 

 

결정적(시드) 지갑

“ 결정적 지갑 = 시드 지갑 “ 으로, 단일 마스터 키 또는 ‘단일 시드’로부터 파생된 개인키를 포함하고 있는 지갑을 의미한다.

💡 시드
 ▶️ 시드는 개인키를 만들기 위해 인덱스 번호나 ‘체인 코드’ 같은 데이터와 결합된 무작위로 추출된 번호다.

< 시드의 활용 >
1️⃣ 지갑 내보내기
2️⃣ 지갑 가져오기
3️⃣ 다른 지갑 간에 모든 키를 쉽게 이관 가능

 

  • 결정적 지갑에서 시드는 모든 파생된 키를 복구할 수 있음.
    이와 같은 구조에 따른 장단점 존재 :
    • 장점:
      • 보안 노력을 단일 데이터에 집중할 수 있다는 점.
      • 생성 시점에 단일 백업으로 지갑에 있는 모든 자금과 컨트랙트를 안전하게 보호할 수 있다는 점.
    • 단점:
      • 즉, 시드 털리면 다 털림.
      • 시드만 있으면 전체 지갑에 접근이 가능하기 때문에 시드의 보안이 매우 중요함.

 

 

 

HP 지갑 (BIP-32/BIP-44)

결정적 지갑은 단일 시드로부터 아주 많은 키를 쉽게 추출하기 위해 개발되었다.

현재 가장 개선된 결정적 지갑은 비트코인의 BIP-32 표준으로 정의된 HD 지갑이다.

💡 HD 지갑 (HD = Hierarchical Deterministic)
  • 트리 구조로 파생된 키들을 가지고 있음
  • 이러한 구조는 부모 키가 자식 키의 시퀀스를 파생할 수 있고, 각각의 자식은 다시 또 손자 키의 시퀀시를 파생할 수 있음.
HD 지갑: 단일 시드로부터 생성된 키 트리 구조

 

HD 지갑은 결정적 지갑에 비해 몇 가지 장점을 지닌다.

HD 지갑의 장점

  • 트리 구조에서의 분기(브랜치, branch)에 각각 다른 역할을 분배할 수 있음.
    • 예시 1) 하위 키의 특정 분기는 입급을 받는 데 사용, 다른 분기는 송금 후 잔액을 받는 데 사용.
    • 예시 2) 부서, 자회사, 특정 기능 혹은 회계 범주로 다른 분기를 할당하여 기업 환경 설정과 같은 구조적인 의미를 표현하는 데도 사용
  • 개인키에 접속하지 않고 사용자가 공개키 시퀀시를 만들 수 있음
    • HD 지갑은 보안상 안전하지 않은 서버, 보기 전용, 수신 전용의 용도로 사용할 수 있는데, 이 때 이 지갑에는 자금을 움직이는 개인키가 들어 있지 않게 만들 수 있음.

 

 

 

시드와 니모닉 코드 (BIP-39)

안전한 백업 및 검색을 위해 개인키를 인코딩하는 데는 다양한 방법이 있다.

  • 뉴모닉 (mnemonic)
    • 현재 많이 사용하고 있는 방법인 단어 시퀀스를 사용하는 것으로, 올바른 순서로 단어 시퀀스가 입력되면 고유한 개인키를 다시 만들 수 있다. (전형적인 메타마스크 뉴모닉 문구)
    • BIP-39에 의해 표준화됨.
    • 이더리움은 이 표준을 사용해서 백업 및 복구를 위한 호환이 가능한 뉴모닉으로 시드 가져오기 혹은 내보내기를 할 수 있다.
  • 왜 이러한 접근을 선호하는 가?
    • 실용적인 측면에서, 16진수 시퀀스는 기록할 때 오류가 발생할 확률이 높다.
    • 반대로 알려진 단어 목록은 단어들을 사용할 때 중복성이 커서 다루기가 매우 쉽다.
    • 만약 ‘inzect’라고 우연히 기록된 게 있다면, 지갑을 복구해야 할 때 ‘inzect’는 유효한 단어가 아니므로 ‘insect’를 사용해야 한다고 빠르게 결정할 수 있다.
    • 따라서 시드 문구는 꼭 오프라인으로 종이에 보관하는 것이 좋다.

 

 

 

지갑의 모범 사례

암호화폐 지갑 기술이 발전함에 따라 광범위하게 상호운용할 수 있고, 사용하기 쉽고, 안전하고, 유연한 지갑을 만들기 위한 산업 표준이 등장했다.

이러한 표준을 통해 지갑은 단일 뉴모닉에서 여러 개의 서로 다른 암호화폐에 대한 키를 파생시킬 수 있다.

이러한 일반적인 표준은 다음과 같다:

  • BIP-39 기반 니모닉 코드 단어
  • BIP-39 기반 HD 지갑
  • BIP-39 기반 다목적 HD 지갑 구조
  • BIP-39 기반 복수화폐 및 복수계정 지갑

이 표준은 소프트웨어 및 하드웨어 지갑에 광범위하게 채택되어 모든 지갑의 상호운용이 가능하게 되었다.

 

이러한 표준을 지원하는

  • 소프트웨어 지갑의 가장 유명한 예로는 메타마스크가 있다.
  • 하드 웨어 지갑의 가장 유명한 예로는 레저(Ledger)가 있다.

따라서 만약에 이더리움 지갑을 구현하려면, BIP-32, BIP-39, BIP-43, BIP-44 표준을 따라 백업을 위해 뉴모닉 코드로 인코딩된 시드를 사용하여 HD 지갑을 구축해야 한다.

 

 

 

 

니모닉 코드 단어 (BIP-39)

니코닉 코드 단어는 결정적 지갑을 파생하기 위해 시드로 사용되는 난수를 인코딩하는 단어 시퀸스다.

단어 시퀀스는 시드를 다시 만들어내고, 이 시드로부터 지갑과 모든 파생된 키들을 재생성할 수 있다.

 

뉴모닉 단어로 결정적 지갑을 구현한 지갑 어플리케이션은 지갑을 처음 만들 때 12-24개의 단어 시퀀스를 보여줄 것이다. (메타마스크 recovery phrase/mnemonic)

자세한 게 궁금하다면 이 포스트 참조 :  [블록체인/기본 개념] - 메타마스크(MetaMask) 입문(1)

 

단어 시퀀서는 지갑의 백업으로, 동일하거나 호환 가능한 지갑 어플리케이션에서 모든 키를 복구하고 다시 생성하는 데 사용할 수 있다.

💡 브레인월렛(brainwallet) vs 뉴모닉 단어
브레인월렛: 사용자가 고른 단어로 구성 → 단어를 암기해야함
뉴모닉 단어: 지갑이 무작위로 생성해서 사용자에게 보여줌 → 조금 더 안전

 

BIP-39에 뉴모닉 코드가 정의되어 있는데 이는 트레저(Trezor) 하드웨어 지갑을 지원하는 회사가 제안했으며,

이는 뉴모닉 코드 표준의 한 가지일 뿐이다. (현재는 사실상 업계 표준 느낌)

 

BIP-39 이전에 사용한 다른 단어 세트인 다른 표준도 있다. (일렉트릭 시드)

BIP-39는 이더리움을 지우너하는 복수화폐 지갑을 생상하는 데 사용 가능하나, 일렉트릭 시드는 불가능하다.

 

니모닉 단어 생성

니모닉 단어는 BIP-39에서 정의한 표준화된 절차에 따라 지갑에서 자동으로 생성된다.

지갑은 엔트로피의 원천에서 시작해서 체크섬을 추가하고 단어 목록에 엔트로피를 매핑한다.

 

절차는 다음과 같다:

1. 128-256비트의 무작위 암호화 시퀀스 S를 생성한다. 이 때, 생성된 엔트로피가 몇 비트인지에 따라 체크섬의 길이와 뉴모닉 길이가 달라진다.

2. S를 SHA-256으로 해싱한 값을 32비트로 나눈 길이를 체크섬으로 생성한다.

3. 무작위 시퀀스 S의 끝에 체크섬을 추가한다.

4. 시퀀스와 체크섬을 연결한 것을 11비트 단위로 나눈다.

5. 각각의 11비트 값을 사전에 정의된 2048단어 사전과 매핑한다.

6. 단어의 시퀀스로부터 순서를 유지하면서 니모닉 코드를 생성한다.

(추가 절차가 남았다. 아래에서 이어진다. )

 

니모닉에서 시드까지

니모닉 단어는 128-256비트 길이의 엔트로피를 표현한다. 엔트로피는 키 스트레칭 함수 PBKDF2를 사용하여 더 긴(512비트) 시드를 파생하는 데 사용되며, 생성된 시드는 결정론적 지갑을 구축하고 키를 파생하는 데 사용된다.

 

키 스트레칭 함수에는 아래와 같은 두 가지 파라미터가 있다 :

  • 니모닉(mnemonic)
  • 솔트(salt)
    • 무차별 대입 공격을 가능하게 하는 조회 테이블(lookup table) 생성을 어렵게 하는 목적
    • 추가적인 보안 요소 역할을 하는 암호문 추가를 사용할 수 있게 함 (선택적 암호문)

위 6단계 절차에서 이어서 뉴모닉이 어떻게 시드를 생성하는 지를 나머지 절차를 보면,

  1. PBKDF2 키 스트레칭 함수의 첫 번째 파라미터는 6단계에서 생성된 뉴모닉이다.
  2. PBKDF2 키 스트레칭 함수의 두 번째 파라미터는 솔트다. 솔트는 문자열 상수 “mnemonic”과 선택적으로 사용자가 지정한 암호문을 합친다.
  3. PBKDF2는 최종 출력으로 512비트 값을 만드는 HMAC-SHA512 알고리즘으로, 2048 해시 라운드를 사용하여 뉴모닉과 솔트 파라미터를 확장하며, 이 결과로 나온 512비트 값이 시드다.

아래는 엔트로피와 암호문에 따른 뉴모닉 코드와 시드의 예시다.

 

 

BIP-39 선택적 암호문

BIP-39 표준은 시드의 파생에 선택적 암호문을 사용할 수 있다.

BIP-39에서 잘못된 암호문은 없다. 모든 암호문은 유효하며 각각 다른 시드를 만들어내고, 가능한 초기화되진 않은 많은 지갑을 형성한다.

 

선택적 암호문은 두 가지 중요한 특징을 지닌다.

  • 뉴모닉 자체만으로는 의미가 없도록 만들어서, 뉴모닉 백업이 도난으로부터 보호될 수 있도록 하는 2차 팩터로 기능한다.
  • 공격자의 협박 때문에 암호문을 가르쳐 줘야 할 경우는 진짜 암호문 대신 그럴 듯한 가짜 암호문을 제공한다. 그러면 대부분의 자금을 담고 있는 ‘진짜’ 지갑 대신 적은 양의 자금이 있는 지갑으로 공격자의 주의를 돌릴 수 있다.

하지만 암호문의 사용은 위험한 점도 있다.

  • 만약 지갑의 주인이 의식을 잃었거나 사망했고 암호문을 알고 있는 사람이 없다면, 시드는 쓸모없어지고 지갑에 저장된 모든 자금은 영원히 잃게 된다.
  • 반대로, 소유자가 암호문을 시드와 동일한 위치에 백업한다면 2차 팩터를 사용하는 의미가 없어진다.

 

니모닉 코드로 작업하기

BIP-39는 여러 가지 프로그래밍 언어의 라이브러리로 구현되어 있다.

python-mnemonic : 파이썬으로 BIP-39를 제안한 사토시랩 팀의 표준참조 구현

ConsenSys/eth-lightwallet : 노드와 브라우저용 경량 JS 이더리움 지갑 (BIP-39 포함)

npm/bip39 : 비트코인 BIP-39의 자바스크립트구현 - 결정적 키 생성용 뉴모닉 코드

 

이 외에도 테스트와 실습에 아주 유용한 독립적인 웸 페이지로 구현된 BIP-39 생성기가 있다.

뉴모닉 코드 변환기는 뉴모닉, 시드, 확정된 개인키를 생성한다.

또한 오프라인/온라인 접속이 가능하다.

뉴모닉 코드 변환기 URL - https://iancoleman.io/bip39/

 

 

 

시드로 HD 지갑 생성하기

HD 지갑은 128, 256 또는 512 비트 임의의 숫자인 단일 루트 시드(root seed)로 만든다. 일반적으로 앞 절에서 상세하게 설명했던 뉴모닉이 시드를 생성한다.

HD 지갑의 모든 키는 루트 시드에서 결정적으로 파생되었으며, 모든 호환 HD 지갑에서 그 시도로부터 전체 HD 지갑을 재생성할 수 있다.

 

즉, 루트 시드를 파생시킨 뉴모닉을 전송하는 것만으로도 수천 혹은 수백만 개의 키가 포함된 HD 지갑의 내보내기, 백업, 복원, 가져오기를 쉽게 만든다.

 

 

 

 

HD 지갑(BIP-32)과 경로(BIP-43/44)

대부분의 HD 지갑은 결정적 키 생성을 위한 사실상 표준인 BIP-32 표준을 따른다.

 

상호운용이 가능한 수십 가지의 BIP-32 구현은 많은 소프트웨어 라이브러리에서 제공하고 있다.

이들은 대부분 비트코인 지갑용으로 설계되었다.

 

비트코인은 이더리움과 다른 방법으로 주소를 구현하지만, BIP-32 호환 가능 지갑과 동일한 키 파생 구현을 공유한다.

 

이더리움을 위해 설계된 지갑(https://github.com/ConsenSys/eth-lightwallet)을 사용하거나,

이더리움 주소 인코딩 라이브러리를 추가하여 비트코인에서 채택한 것을 사용해라.

 

BIP-32로 테스트하고 실습하기에 유용한 독립형 웹페이지 : BIP-32 생성기 (http://bip32.org/)

(https가 아니므로 안전하지 않고 오직 테스트용으로만 쓰는 것을 강력 권고)

 

확장된 공개키와 개인키

BIP-32 용어로 말하자면, 키는 ‘확장’될 수 있다.

적절한 수학적 연산을 사용하여 확장된 ‘부모(parent)’ 키는 ‘자식(child)’ 키를 파생시킬 수 있게 되고, 앞서 설명한 키와 주소의 계층 구조를 만들 수 있게 된다.

트리 계층 구조의 어느 곳에서든 부모 키를 선택할 수 있다.

 

키를 확장하는 것은 키 자체를 가져와서 특수 체인 코드를 추가하는 것이다. 체인 코드는 자식 키를 생성하기 위해 각 키와 혼합된 256비트 이진 문자열이다.

 

만약 키가 개인키면 , 접두어가 xprv로 구분되는 확장된 개인키가 된다.

xprv9s21ZrQH143K2JF8RafpqtKiTbsbaxEeUaMnNHsm5o6wCW3z8ySyH4UxFVSfZ8n7ESu7fgir8i…

만약 키가 공개키면, 접두어가 xpub로 구분되는 확장된 공개키가 된다.

xpub661MyMwAqRbcEnKbXcCqD2GT1di5zQxVqoHPAgHNe8dv5JP8gWmDproS6kFHJnLZd23tWevhdn...

HD 지갑의 매우 유용한 특징은 개인키가 없는 부모 공개키에서 자식 공개키를 파생할 수 있는 능력이다.

자식 공개키를 파생하기 위한 방법은 두 가지가 있다 :

  • 자식 개인키로부터 직접 파생
  • 부모 공개키로부터 직접 파생

따라서 확장된 공개키는 HD 지갑 구조의 해당 분기(branch)에서 모든 공개키(오직 공개키만 가능)를 파생하는 데 사용될 수 있다.

 

 

여기서 서버 또는 어플리케이션에는 확정된 공개키의 사본이 있지만 개인키는 없기 때문에 이러한 방법은 매우 안전한 공개키 전용 배포를 만드는 데 사용할 수 있다.

이러한 배포는 무한한 수의 공개키와 이더리움 주소를 생성할 수 있지만, 그 주소에 있는 돈은 쓸 수 없다.

다른 보안 서버에서 확장된 개인키는 트랜잭션에 서명하고 돈을 사용하기 위해 관련된 모든 개인키를 파생해서 쓸 수 있다.

 

이러한 방법의 어플리케이션 예시 :

  • 전자상거래 어플리케이션을 제공하는 웹 서버에 확장된 공개키를 설치하는 것
    • 웹 서버는 공개키 파생 함수를 사용하여 모든 트랜잭션(예: 고객 쇼핑 카트)에 대한 새로움 이더리움 주소를 만들 수 있는 반면, 도난에 취약한 개인키는 가지고 있을 필요가 없다.
  • 콜드 스토리지 또는 하드웨어 지갑
    • 확장된 개인키는 하드웨어 지갑에 저장할 수 있으며, 확장된 공개키는 온라인으로 보관할 수 있다. 사용자는 수신 주소를 자유롭게 만들 수 있으며, 개인키는 오프라인으로 안전하게 저장된다.
    • 자금을 사용하기 위해 사용자는 오프라인 서명 이더리움 클라이언트에서 확장된 개인키를 사용하거나 하드웨어 지갑 장치에서 트랜잭션을 서명할 수 있다.

 

강화된 자식 키의 파생

확장된 공개키 또는 xpub으로부터 공개키의 분기를 파생하는 능력은 매우 유용하지만 잠재적인 위험이 있다.

 

xpub에 대한 접근이 자식 개인키에 대한 접근을 제공하진 않는다. 그러나 xpub이 체인코드(부모 공개키에서 자식 공개키를 파생하는 데 사용)를 포함하므로, 만약 하위 개인키가 유출된 경우, 해당 개인키가 체인코드와 함께 다른 모든 자식 개인키를 파생하기 위해 사용될 수도 있다.

 

즉, 유출된 하나의 자식 개인키와 부모 체인 코드는 모든 자식의 개인키를 노출시킬 수 있고, 부모 체인코드와 함께 자식 개인키를 사용하여 부모 개인키를 추론할 수 있다.

 

이러한 위험에 대응하기 위해 HD 지갑은 강화 파생(hardened derivation)이라고 하는 대체 가능 파생 함수를 사용한다.

💡 강화 파생 함수

이 파생 함수는 부모 공개키와 자식 체인 코드 간의 관계를 끊는다.

자식 체인 코드를 파생하기 위해 부모 공개키 대신에 부모의 개인키를 사용한다.

체인 코드를 이용하여 부모/자식 시퀀스에 ‘방화벽’을 생성하며,
이 때 체인 코드는 부모 또는 형제 개인키를 유출하는 데 사용할 수 없다.

 

즉, 유출된 체인 코드의 위험에 노출되지 않고 편리하게  xpub 을 이용해 공개키의 분기를 파생하기 위해서는

일반적인 부모가 아닌 강화된 부모로 공개키 분기를 파생해야 한다.

마스터 키의 유출을 방지하기 위해서는 항상 강화 파생으로 파생된 마스터 키의 1단계 자식 사용을 강력히 추천한다.

 

일반 및 강화 파생을 위한 인덱스 번호

하나의 부모 키에서 여러 개의 자식 키를 파생할 수 있다.

이를 관리하기 위해 인덱스 번호를 사용한다.

▶️ 각 인덱스 번호는 특수한 자식 파생 함수를 사용해 부모 키와 결합될 때 각각 다른 자식 키를 만들어낸다.

 

 

BIP-32 부모-자식 파생 함수에 사용하는 인덱스 번호는 32비트 정수다.

💡 인덱스 비교 (강화파생 키 vs 비강화파생 키)

▶️ 강화파생을 통해 파생된 키 : 2^31 ~ 2^32-1 (0x0 ~ 0x7FFFFFFF)
▶️ 비강화(일반) 파생을 통해 파생된 키 : 0 ~ 2^31-1 (0x80000000 ~ 0xFFFFFFFF)

따라서 인덱스가 2^31보다 작으면 자식은 일반이고, 이상이면 자식은 강화된다.

 

강화된 자식 인덱스 번호를 좀 더 편하게 표현하기 위해 사용하는 표현법이 있다.

처음 강화된 자식인   0x80000000  부터 차례로  0'  ,   1'  ,   2'   ,  3'    …으로 맵핑한다.

그냥 숫자가 아니라 {숫자} +   '   (따옴표) 표시를 한다. 즉,   0'  ,   1'  ,   2'   이런식으로 표현하는 것이다.

 

예를 들어보면,    0x80000001  인덱스를 가진 강화자식키는 2번째에 매핑되는   1'  를 표현식으로 갖는다.

▶️ HD 지갑 인덱스에 i’가 표시되면 이는 2^31 + i 를 의미한다.

 

 

HD 지갑 키 식별자 (경로)

HD 지갑의 키는 ‘경로(path)’ 이름 규칙을 사용하여 식별하며, 트리의 각 레벨은 슬래시(  /  ) 문자로 구분한다.

마스터 개인키에서 파생된 개인키는 소문자  m 으로 시작하며,

마스터 공개키에서 파생된 공개키는 대문자  M 으로 시작한다.

 

🔽 아래 예제가 이해를 도울 것이다.

 

키의 ‘조상(ancestry)’은 키를 파생한 마스터 키에 도달할 때까지 오른쪽에서 왼쪽으로 읽는다.

예를 들어, m/x/y의 z번째 자식 키는 식별자 m/x/y/z, m/x의 y번째 자식 키는 m/x/y 로 표현한다.

 

 

HD 지갑 트리 구조 탐색

HD 지갑 트리 구조는 대단히 유연하다. 다르게 말하면, 무한한 복잡성을 허용한다는 뜻이다.

각 부모의 확장 키는 40억 개의 자식(20억 개의 일반 자식과 20억개의 강화된 자식)을 가질 수 있고,

그 자식들은 각각 또 40억 개의 자식들(20억 개의 일반 자식과 20억개의 강화된 자식)을 가질 수 있다.

 

이러한 복잡성 때문에 두 가지 BIP (BIP-43, BIP-44)는 HD 지갑 구조의 표준을 만들어

잠재적인 복잡성을 관리할 수 있는 방법을 제공한다.

 

  • BIP-43
    • 강화된 첫 번째 자식 인덱스를 트리 구조의 목적(purpose)’를 나타내는 특수 식별자를 사용하도록 제안
    • 이 사양(BIP-44)을 기반으로 한 HD 지갑은 트리의 하나의 분기의 하나의 레벨만 사용해야 하며, 여기에 사용된 인덱스는 트리의 나머지 구조와 이름 공간을 식별하여 지갑의 목적을 정의함.
    • 즉,  m/i’ 에서  i 는 목적을 나타내는 인덱스 번호이고, 이 분기만을 사용하는 HD 지갑은  i  로 구문되는 특정한 목적을 나타내기 위한 것이다.
    • 여기서  i 는 BIP에 지정이 되어 있든가, 이 사양을 사용해서 개발하는 본인이 지정하든가 하는 듯하다.
      (예를 들어, 1은 수신용, 2는 송금용 등등) - 내 추측
  • BIP-44
    • BIP-43 사양을 확정하여 ‘목적’ 번호를  44’ 로 설정하여 복수화폐 복수계정 구조를 제안
    • 이 사양(BIP-44)을 따르는 모든 HD 지갑 구조는 단지 하나의 트리 분기  (m/44’/*)  만을 사용한다는 사실에 의해 식별
    • BIP-44는 미리 정의된 다섯 가지 트리 레벨로 구성된 구조를 지정
       m / purpose' / coin_type' / account' / change / address_index 
      • 첫 번째 레벨인  purpose'  는 항상  44’ 로 설정
      • 두 번째 레벨인  coin_type' 은 암호화폐 동전의 종류를 지정하며, 여기서 지정한 통화만이 자체 하위 트리를 갖는 HD 지갑에서 허용된다.
        • 예를 들어, 이더리움은  m/44’/60' , 비트코인은  m/44’/0'  
        • 여러 화폐에 대한 표준 정의는 SLIP0044 를 참고
      • 세 번째 레벨인  account' 는 사용자의 지갑 목적에 따라 세분화할 수 있는 레벨이다.
        • 지갑을 회계 또는 조직 목적을 위한 별도의 논리적 하위 계좌로 세분화할 수 있다.
        • 즉, 만들 지갑 계정의 개수라고 생각하면 편하겠다.
        • 예를 들어,  m/44’/60'/0 ,  m/44’/60'/1 을 통해 HD 지갑에는 2개의 이더리움 ‘계정’을 포함할 수 있다.
        • 각 계정은 자체 하위 트리의 루트다.
      • 네 번째 레벨인  change 에는 단 2개의 하위트리가 있다. 하나는 입금 주소 작성용이고, 다른 하나는 잔액 주소 작성용이다.
        • 이더리움은 비트 코인에 있는 잔액 주소가 필요하지 않으므로 단지 입금 경로만 사용한다.
        • 이 레벨은 이전 레벨과 달리 일반 파생을 사옹한다는 사실을 알아두자.
        • (참고: BIP-44는 원래 비트코인을 위해 제작되었기 때문에 이더리움 세계와 관련이 없는 ‘특이점(quirk)’이 포함되어 있음)
      • 다섯 번째 레벨인  address_index 는 네 번째 레벨의 자식으로서 파생되어 사용 가능한 주소의 Index가 되는 것이다.
        • 예를 들어, 주 메인 계정( 0' )에서 이더리움 지급을 위한( 0 ) 세번째 입금 주소( 2' ) 는  M/44'/60'/0'/0/2  와 같다.
  •  

 

🔽 아래 예시를 통해 정리해보자.