Skip to content

Latest commit

 

History

History
358 lines (273 loc) · 23.5 KB

File metadata and controls

358 lines (273 loc) · 23.5 KB

5장. 지갑

📖 원문: Mastering Bitcoin 2nd Edition - Chapter 05. Wallets

목차


지갑은 다음과 같은 역할을 하는 애플리케이션(응용 프로그램)이다.

  1. 사용자의 자산에 접근하고 제어한다.
  2. 개인키와 주소를 관리한다.
  3. 잔액을 조회한다.
  4. 트랜잭션(거래)을 생성하거나 서명한다.

위의 역할이 일반 사용자 관점의 정의라면, 프로그래머의 관점에서 지갑은 사용자의 키를 저장하고 관리하기 위한 자료 구조(Data Structure)라 할 수 있다.

지갑 기술 개요

  • 지갑에는 비트코인(이하 자산)이 들어있는 것이 아니라 개인키가 들어있을 뿐이다.
  • 자산은 비트코인 네트워크에 기록되며 사용자는 개인키를 통한 트랜잭션 서명을 통해 이를 관리한다.
  • 비트코인 지갑은 개인키와 공개키를 담은 열쇠고리와 같다.
  • 비트코인은 트랜잭션의 출력값(Outputs, vout 또는 txout)을 통해 저장되며, 개인키는 출력값을 검증하는데 쓰인다.
  • 두 종류의 지갑
    • 비결정적 지갑: 독립적인 개인키를 여러개 포함하고 있다.
    • 결정적 지갑: 모든 개인키가 하나의 마스터키로부터 파생된다. 가장 대중적인 것이 계층 결정적 지갑(Hierarchical Deterministic Wallets)이다.

비결정적 지갑(Nondeterministic/Random Wallets)

  • Type-0

  • 최초의 비트코인 코어가 제공하는 지갑에는 무작위로 생성된 100개의 개인키가 제공되고 각 키를 한 번씩만 사용할 수 있었다.

  • 다량의 키를 백업하고, 불러와 쓰는 것이 매우 불편하다.


    그림 1. Type-0: 무작위로 생성된 키 집합

결정적 지갑(Deterministic/Seeded Wallets)

  • Type-1

  • 결정적 지갑은 공통 시드(Seed)로부터 파생된 여러 개의 개인키를 포함하는 지갑이다.

  • 공통 시드는 랜덤으로 추출되는 숫자이며, 공통 시드만 있으면 여러 개의 개인키를 복구할 수 있다.


    그림 2. Type-1: 하나의 시드로부터 파생된 일련의 키들

    💡 시드(Seed)

    난수 생성기(Random Number Generator, 무작위 수를 출력)에 사용되는 입력값을 말한다.

HD 지갑(Hierarchical Deterministic Wallets)

  • Type-2

  • BIP-32 표준에 정의된 가장 발전된 형태의 지갑이다.

  • 하나의 공통 시드로부터 트리 구조로 다량의 키를 만들 수 있는 구조이다.

  • 공통 시드로부터 추출된 마스터 키는 자식키, 손자키를 무제한으로 생성할 수 있다.

  • 사용자는 여러 개의 개인키를 보관할 필요가 없고, 개인키 없이도 여러 개의 공개키를 생성할 수 있다.

  • 현재 가장 널리 사용되는 방식은 BIP-44에 5단계 경로를 갖는 HD 지갑이다.


    그림 3. Type-2: 한 시드에서 생성한 트리 구조의 키들

    💡 트리(Tree)

    계층적 자료를 표현하는데 사용하는 구조. 나무를 거꾸로 세워놓은 것 같은 모양이다. 하나의 노드(루트 노드)로부터 시작되어 부모-자식 관계로 표현되고 부모에서 자식으로의 방향성을 갖는다. 같은 부모를 갖는 노드를 형제 노드(sibling node), 한 노드에서 다른 노드 사이에 이르는 길에 있는 노드들의 순서를 경로(path)라 한다.

시드와 니모닉 코드

  • 니모닉(Mnemonic)이라는 일련의 영어 단어로 시드를 생성하는 표준을 사용하면 HD 지갑을 더욱 쉽게 쓸 수 있다.

  • 니모닉 코드 표준은 BIP-39에 정의되어 있다.

  • 결정적 지갑의 시드 표현 비교

    • 16진수 0C1E24E5917779D297E14D45F14E1A1A

    • 12 단어 니모닉 army van defense carry jealous true garbage claim echo media make crunch

가장 실용적인 지갑의 형태

  • 일반적으로 지갑은 다음 표준을 따른다.

    • BIP-39: 니모닉 코드
    • BIP-32: HD 지갑
    • BIP-43: 다목적의 HD 지갑
    • BIP-44: 다양한 암호화폐(Multicurrency)와 다량의 주소(Multiaccount)를 지원하는 지갑

지갑 기술 상세

BIP-39: 니모닉 코드 단어

  • 결정적 지갑을 만들기 위해 시드로 사용될 난수를 일련의 영단어로 표현한 것을 말한다.
  • 최소 12개에서 24개의 단어로 표현된다.

니모닉 생성하기 (1/2)

  • BIP-39에 정의된 표준화된 프로세스를 사용한다. 아래 그림은 12 단어 니모닉 코드를 생성하는 예제이다.

    • ① 128비트 길이의 엔트로피(랜덤 숫자)
    • ② 엔트로피를 SHA256 해시하여 첫 4비트를 체크섬으로 사용
    • ③ 엔트로피와 체크섬을 붙여 132비트 생성
    • ④ 132비트를 11비트씩 쪼개 12개로 분할
    • ⑤ 미리 정의된 2048(211)개의 단어 사전을 이용하여 각각을 단어로 치환
    • ⑥ 단어 배열의 니모닉 코드 완성


    그림 4. 12자리 니모닉 코드 예제: 엔트로피를 생성하고 니모닉 코드로 변환
  • 엔트로피 길이에 따라 니모닉 단어의 개수가 달라진다.

    엔트로피(bits) 체크섬(bits) 엔트로피+체크섬(bits) 니모닉 단어 길이
    128 4 132 12
    160 5 165 15
    192 6 198 18
    224 7 231 21
    256 8 264 24

    💡 엔트로피(Entropy)

    불확실성. 어떤 과정의 결과가 얼마나 예측불가한지를 나타내는 값 정도로 이해하면 된다. 엔트로피가 높을수록 결과에 대한 확실성이 낮아진다.

    지갑에서 엔트로피란 시드를 만들어내기 위한 특정 길이의 숫자이다. 따라서, 엔트로피 생성 시 암호학적으로 무작위한 알고리즘을 사용하는 것이 매우 중요하다.

니모닉을 시드로 만들기 (2/2)

  • 니모닉으로 표현된 128~256비트 길이의 엔트로피는 키 스트레칭 함수, PBKDF2(Password-Based Key Derivation Function 2)를 통해 512비트로 변환된다.

  • 변환 결과인 512비트가 결정적 지갑에서 키를 도출하는 시드로 사용된다.

  • 키 스트레칭 함수의 입력으로 두 가지 매개변수, 니모닉과 솔트(Salt)가 필요하다.

    • 암호학에서 솔트란 보안을 위해, 단방향 함수에 추가 입력으로 사용되는 임의의 데이터를 말한다.
    • 키 스트레칭 함수(단방향 함수)에서 솔트는 무차별 대입 공격(Brute-force attack)을 어렵게하는 역할을 한다.
    • BIP-39 표준에서 솔트는 또 다른 목적을 가지는데, 바로 시드를 보호하는 "추가 보안" 요소로 패스프레이즈(Passphrase, 암호 또는 암호문구)를 도입할 수 있도록 하는 것이다.
    • 패스프레이즈는 선택적으로 사용할 수 있다.
  • 아래 그림은 니모닉 생성하기 (1/2) 단계 다음의 단계를 나타낸다.

    • ⑦ PBKDF2의 첫번째 파라미터는 앞 단계의 ⑥에서 얻은 니모닉
    • ⑧ 두번째 파라미터, 솔트는 'mnemonic'과 사용자가 정의한 패스프레이즈를 붙인 값
    • ⑨ PBKDF2는 니모닉 코드와 솔트를 입력으로 2048회의 HMAC-SHA512 해시를 수행
  • 이 결과로 얻어진 512비트가 바로 공통 시드이다.



    그림 5. 니모닉으로부터 시드 생성
  • 패스프레이즈의 역할

    • 니모닉 코드가 유출되어도 패스프레이즈로 지갑을 안전하게 보호할 수 있다.
    • 패스프레이즈가 있는 지갑과 없는 지갑을 구분해서 탈취 협박 시 공격자를 속이는데 사용할 수 있다.
    • 하지만, 이를 기억하지 못할 경우 자산은 영구손실 된다.
    • 그렇다고, 니모닉 코드와 함께 보관하면 패스프레이즈의 목적이 무산된다.
  • 엔트로피 길이와 패스프레이즈에 따른 시드 비교

    • 12자 니모닉 코드: 패스프레이즈 지정하지 않는 경우
    엔트로피 (128비트) 0c1e24e5917779d297e14d45f14e1a1a
    니모닉 (12 단어) army van defense carry jealous true garbage claim echo media make crunch
    패스프레이즈 (지정하지 않음)
    시드 (512비트) 5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39 a88b76373733891bfaba16ed27a813ceed498804c0570
    • 12자 니모닉 코드: 패스프레이즈 지정한 경우
    엔트로피 (128비트) 0c1e24e5917779d297e14d45f14e1a1a
    니모닉 (12 단어) army van defense carry jealous true garbage claim echo media make crunch
    패스프레이즈 SuperDuperSecret
    시드 (512비트) 3b5df16df2157104cfdd22830162a5e170c0161653e3afe6c88defeefb0818c793dbb28ab3ab091897d0 715861dc8a18358f80b79d49acf64142ae57037d1d54
    • 24자 니모닉 코드: 패스프레이즈 지정하지 않은 경우
    엔트로피 (256비트) 2041546864449caff939d32d574753fe684d3c947c3346713dd8423e74abcf8c
    니모닉 (24 단어) cake apple borrow silk endorse fitness top denial coil riot stay wolf luggage oxygen faint major edit measure invite love trap field dilemma oblige
    패스프레이즈 (지정하지 않음)
    시드 (512비트) 3269bce2674acbd188d4f120072b13b088a0ecf87c6e4cae41657a0bb78f5315b33b3a04356e53d062e5 5f1e0deaa082df8d487381379df848a6ad7e98798404

    💡 많은 지갑들이 12 단어 이상 니모닉 코드를 지원하지 않는다. 위 표에서 엔트로피의 입력이 달라도 동일하게 512 비트의 시드를 출력하는 것을 알 수 있는데, 보안 관점에서 HD 지갑 생성에 실제로 사용되는 엔트로피의 양은 대략 128비트로, 이는 12 단어에 해당한다. 따라서, 12개 이상의 단어를 제공하면 시드 생성에 불필요한 엔트로피를 추가로 발생시키는 것이 된다. (사용성 측면에서도 짧은 12 단어가 더 좋다.)

시드로 HD 지갑을 생성하는 방법

  • 앞 단계에서 생성한 512비트 시드의 왼쪽 256비트는 마스터 개인키, 오른쪽 256비트는 체인코드가 된다.

  • 마스터 개인키 m은 마스터 공개키 M을 만드는데 사용된다. (M = m * G)

  • 마스터 체인코드는 자식키를 만들때 엔트로피로 사용된다.

  • HD 지갑은 키 유도(CKD, Child Key Derivation) 함수를 통해 부모키로부터 자식키를 유도한다.


    그림 6. 루트 시드(공통 시드)로부터 마스터키와 체인코드 생성

정규 개인키 유도법(Private Child Key Derivation)

  • 자식키를 만들기 위해 HMAC-SHA512에 세 개의 입력이 필요하다.

    • 부모의 공개키
    • 부모의 체인코드: 체인코드는 자식키로 부모키를 추출할 수 없도록 한다.
    • 인덱스: 여러 개의 자식을 만들어 내는데에 활용한다.
      • 인덱스는 총 32비트로 최대 31비트(0 ~ 231-1)로 표현할 수 있는 만큼의 자식을 만들어낸다.
      • 나머지 절반은 어디에 사용되는지 뒤에서 설명한다.
      • 31비트(231)는 2,147,483,647이다.
  • 결과로 얻은 512비트 중 왼쪽 256비트는 부모의 개인키와 더한 후 mod 연산 하여 자식 개인키를 만든다.

  • 오른쪽 256비트는 자식의 체인코드가 되어 손자키를 생성하는데 사용된다.

    💡 mod 연산

    특정 값으로 나누었을 때의 나머지를 구하는 것을 말하고, % 부호를 사용한다.

    💡 개인키 유도법에서 mod 연산의 나누는 값, n은 다음과 같이 매우 큰 소수이다.

    n = 115792089237316195423570985008687907852837564279074904382605163141518161494337

확장키(Extended Key)
  • 여기서 확장키(Extended Key) 개념이 나오는데, 이로부터 자식키를 도출해낼 수 있기 때문에 확장가능한 키(Extensible Key)라 이해할 수도 있다.

  • 확장키는 확장 개인키(Extended Private Key)와 확장 공개키(Extended Public Key)를 통틀어 부르는 말이다.

    • 확장 개인키: 개인키와 체인코드를 붙인 것.
    • 확장 공개키: 공개키와 체인코드를 붙인 것.
  • 확장키는 자식키를 만드는데 중요한 인자로 사용된다.

  • 확장키는 BIP-32 호환 지갑 간에 쉽게 내보내고 가져올 수 있도록 Base58Check 인코딩을 한다.

    • 인코딩 된 확장 개인키는 xprv, 개인키는 xpub 접두어를 붙여 쉽게 알아볼 수 있도록 한다.
  • 자식키 유도 시 단방향 해시함수를 활용하기 때문에 자식들 간에도 같은 부모로부터 파생된 것인지 연결성을 찾을 수 없다.


    그림 7. 부모 개인키를 확장하여 자식 개인키 만들기

정규 공개키 유도법(Public Child Key Derivation)

  • 자식 공개키를 만드는 두 가지 방법

    • 자식 개인키로 연산
    • 확장 공개키로부터 추출
  • 확장 공개키를 사용하면 자식 개인키 없이도 자식 공개키를 만들 수 있다.

  • 이 방법은 개인키를 서버에 저장하지 않아야 하면서도 무수한 주소를 만들어내야 하는 커머스 시스템에서 활용될 수 있다.

  • 정규 공개키 유도법에 의해 자식 공개키를 유도하는 방법은 정규 개인키 유도법에서 자식 개인키를 유도하는 기법과 동일하다. 따라서 하나의 자식 개인키가 유출되면 부모의 개인키를 추론할 수 있는 가능성이 생겨 버린다.

  • 또한 확장 공개키는 체인코드를 포함하고 있기 때문에 하위 개인키가 하나라도 유출된 경우, 모든 하위 개인키를 유도할 수 있게 된다.

  • 심지어 부모의 체인코드와 자식 개인키를 사용하여 부모의 개인키까지 추론할 수 있다.

  • 이처럼 정규 방식으로 생성한 확장 공개키는 공개키 간 부모-자식 관계를 알 수 있게 하여, 프라이버시 위배를 가져올 위험이 있다.


    그림 8. 부모 공개키를 확장하여 자식 공개키 만들기

단절 개인키 유도법(Hardened Private Key Derivation)

  • 단절 개인키 유도법은 정규 확장키 유도법이 내재한 위험을 해결한다.

  • 이 방법은 키 스트레칭 함수(PBKDF2)에 공개키가 아닌 개인키를 입력으로 사용하기 때문에, 개인키를 모르는 한 자식키를 만들 수 없다.

  • 이로써 부모의 공개키와 자식 체인코드와의 관계가 끊어진다. 따라서, 한 부모와 자식키들 간의 관계를 추론해낼 수 없다.

  • 앞서 정규 유도법 설명에서 총 32비트 인덱스 중 31비트를 사용한다고 설명하였는데, 정규 유도법에서 사용하지 않은 나머지 231 ~ 232 - 1가 단절 유도법에 쓰인다.

  • 대시(') 기호를 사용하여 단절 유도법을 사용함을 표기할 수 있다.

  • 단절 공개키 유도법은 불가능하다. 현재 많은 지갑에서 채택되고 있는 BIP-44 표준은 마스터키와 1세대 자식들에 단절 개인키 유도법이 채택된 모범사례라 할 수 있다.


    그림 9. 단절된 자식키 유도: 부모의 공개키 생략

    💡 Hardened: 단절된

    강화된, 단련된, 어렵게된 등의 사전적 의미를 갖는 hardened에 대해 '강화 개인키 유도법' 혹은 '단절 개인키 유도법'으로 혼용해서 사용하는 것을 볼 수 있다. 여기에서는 hardened를 부모 - 자식 간 연결성을 단절시켜 추론을 어렵게 만든다는 의미로 해석하고 "단절"이라는 번역을 채택하였다.

HD 지갑의 키 식별(경로, path)

  • HD 지갑의 트리 구조에서는 여러 개의 주소를 부모-자식 관계로 표현할 수 있다.

  • 특정 주소를 추적할 때에는 트리의 경로(path)로 표기된다.

    • 루트에는 마스터키가 표현된다.
    • 마스터 개인키를 m으로 표현하고, 마스터 공개키는 M으로 표현한다.
    • 그 뒤 인덱스에 따라 자식키가 표현되며 이러한 방식으로 최대 40억 개의 자식 표현이 가능하다.
    HD 경로 설명
    m/0 마스터 개인키의 첫번째 자식 개인키
    m/0/0 m/0의 첫번째 자식 개인키
    m/0'/0 첫번째 단절 자식 개인키(m/0')의 첫번째 정규 자식 개인키
    m/1/0 두번째 자식 개인키(m/1)의 첫번째 자식 개인키
    M/23/17/0/0 마스터 공개키(M)의 24번째 자식(M/23)의 18번째 자식(M/23/17)으로부터 나온 첫번째 자식(M/23/17/0)의 첫번째 자식 공개키
HD 지갑과 BIP-44
  • HD 지갑은 총 40억 개의 자식키를 보유할 수 있기 때문에 정해진 규칙이 없이 남용되면 자산을 탐색하는 데에 많은 시간이 소요될 것이다.

  • 따라서, BIP-44 표준에서는 트리 내부에 있는 경로 규칙을 정규화하고 이를 따르도록 권장한다.

  • BIP-43에서는 최상위 경로를 제외한 첫번째 레벨을 '목적(purpose)'으로 나타내자고 제안하였다.

  • BIP-44 표준은 다양한 체인의 다양한 자산을 관리할 수 있는 HD 지갑의 다목적 구조를 제안하였는데, BIP-43를 확장하여 목적 정보를 44'로 고정하고, 5 레벨로 구조화하였다.

  • BIP-44가 제안한 HD 지갑의 경로: m / purpose' / coin_type' / account' / change / address_index

    • purpose': 44'
    • coin_type': 화폐의 유형. 예) 0' 비트코인 메인넷, 1' 비트코인 테스트넷, 2' 라이트코인
    • account': 용도에 따라 하위 계좌 지정 가능
    • change: 잔돈 여부. 0(false)이나 1(true) 값을 가진다. (원문에서는 외부에 공개되는 receiving 주소를 생성하는 해당 레벨에 대해, external chain이라는 용어를 사용한다.)
    • address_index: 주소의 일련 번호
  • 각 레벨에서 BIP-32에서 나타난 단절 유도 방법(')이 사용되는 것을 확인할 수 있다.

  • BIP-44 적용 예시

    coin account chain address 경로
    Bitcoin 첫번째 external 첫번째 m/44'/0'/0'/0/0
    Bitcoin 첫번째 external 두번째 m/44'/0'/0'/0/1
    Bitcoin 첫번째 change 첫번째 m/44'/0'/0'/1/0
    Bitcoin 첫번째 change 두번째 m/44'/0'/0'/1/1
    Bitcoin 두번째 external 첫번째 m/44'/0'/1'/0/0
    Bitcoin 두번째 external 두번째 m/44'/0'/1'/0/1
    Bitcoin 두번째 change 첫번째 m/44'/0'/1'/1/0
    Bitcoin 두번째 change 두번째 m/44'/0'/1'/1/1

    💡 SLIP-44에는 coin_type'에 대해 선점된 체인들의 고유 번호가 등록되어있다.