比特幣入門 ② - 錢包

LMessi發表於2023-01-09

比特幣入門 ② - 錢包

錢包

概述

  • 定義

    • 廣義上講,錢包是一個應用程式,為使用者提供互動介面。錢包控制使用者訪問許可權,管理私鑰和地址,跟蹤餘額以及建立和簽名交易。
    • 狹義上講,從程式設計師的角度來看,“錢包”是指用於儲存和管理使用者私鑰的資料結構,也是本章所指的錢包含義。
  • 錢包型別

    • 第一種型別是非確定性錢包(nondeterministic wallet),其中每個私鑰都是從隨機數獨立生成的,金鑰彼此之間無關聯。

      • 必須備份所有私鑰。

      • 如果為了減少備份而複用地址,這會將多個交易與此地址關聯在一起,這樣會增加隱私洩露風險。

      • 除了簡單的測試之外,不推薦非確定性錢包。

    • 第二種型別是確定性錢包(deterministic wallet),其中所有的私鑰都是從一個主私鑰派生出來,這個主私鑰即為種子(seed)。該型別錢包中所有私鑰都相互關聯,如果有原始種子,則可以再次生成全部私鑰。

      • 1 型確定性錢包

      • 分層確定性錢包

        • 優點一:樹狀結構可以用來表達額外的組織含義。

        • 優點二:可以讓使用者去建立一系列公鑰而無須知曉相對應的私鑰。(見子公鑰的派生)

註記詞

  • 助記詞編碼是表示確定性錢包的種子的英語單詞序列。
  • 不同於“腦錢包”,腦錢包由使用者選擇的單片語成,而助記詞是由錢包隨機建立並展現給使用者的。
  • 建立註記詞過程

  1. 建立一個 128~256 位的隨機數(熵)。
  2. 提取隨機數的 SHA256 雜湊值的前 x 位(x 等於隨機數位數除以 32)作為校驗碼。
  3. 將校驗碼新增到隨機序列的末尾。
  4. 將序列按 11 位進行劃分。
  5. 將每 11 位的值對映到一個含 2048($2^{11}$)個單詞的預定義字典中。
  6. 生成的有順序的單片語就是助記詞。

  • 從註記詞生成種子

    • 助記詞表示長度為 128~256 位的熵(隨機數)。透過使用金鑰延伸函式 PBKDF2,熵被用於派生出更長的(512 位)種子。將所得的種子用於構建確定性錢包並派生金鑰。

  1. PBKDF2 金鑰延伸函式的第一個引數是從步驟 6 生成的助記詞。
  2. PBKDF2 金鑰延伸函式的第二個引數是鹽。由固定的字串“mnemonic”與可選的使用者輸入的密碼字串連線組成。
  3. PBKDF2 將助記詞和鹽作為引數,呼叫 2048 次 HMAC-SHA512 雜湊演算法,生成一個 512 位的值作為其延伸的最終輸出。這個 512 位的值就是種子。

建立主私鑰和主鏈碼

  • 根種子(還有可選密碼)輸入到 HMAC-SHA512 演算法中就可以得到一個可用來創造主私鑰(m)和主鏈碼(c)的雜湊值。
  • 主私鑰(m)可以生成相對應的主公鑰(M)。
  • 主鏈碼(c)用於從父私鑰創造子私鑰的那個函式中引入隨機數(熵)。

子金鑰的派生

分層確定性錢包使用 CKD(child key derivation,子金鑰派生)函式從父金鑰派生出子金鑰。

  • 子金鑰派生函式是基於單向雜湊函式的,這個函式結合了:

    • 一個父私鑰或者父公鑰(ECDSA 未壓縮金鑰)
    • 一個 256bit 的鏈碼,鏈碼是用來給這個確定性過程引入隨機資料的,以便知道索引編號和子私鑰也不足以派生其他子私鑰。
    • 一個 32bit 的索引碼,用於按順序生成相應的子私鑰和子鏈編碼。
  • 因為衍生方程是單向方程,所以子金鑰不能被用來發現他們的母金鑰。子金鑰也不能用來發現他們的相同層級的姊妹金鑰。

  • 沒有子鏈碼的話,子金鑰也不能用來衍生出任何孫金鑰。你需要同時有子金鑰以及對應的鏈碼才能建立一個新的分支來衍生出孫金鑰。

  • 父私鑰-> 子私鑰-> 子公鑰

  • 父公鑰-> 子公鑰

擴充套件金鑰

將金鑰以及鏈碼這兩個重要的部分組合在一起,稱為擴充套件金鑰(extended key)。術語“擴充套件金鑰”也被認為是“可擴充套件的金鑰”,因為這種金鑰可以用來派生子金鑰。擴充套件金鑰可以簡單地表示為將金鑰與鏈碼串聯成的序列並儲存。

  • 兩種型別的擴充套件金鑰:

    • 私鑰以及鏈碼組成擴充套件私鑰,字首為 xprv,它可用來派生子私鑰(子私鑰可以用來派生子公鑰)。
    • 公鑰以及鏈碼組成擴充套件公鑰,字首為 xpub,它可以用來派生子公鑰
  • 擴充套件公鑰及其應用:

    • 部署擴充套件公鑰可以創造出無限數量的公鑰以及比特幣地址,但是不能花費傳送到這個地址裡的任何比特幣。與此同時,在另一種更安全的伺服器上,擴充套件私鑰可以衍生出對應的私鑰,簽署交易支付花費。
    • 應用一:安裝擴充套件公鑰在電商的 web 伺服器上。web 伺服器可以使用公鑰衍生函式去給每一筆交易(比如客戶的購物車)創造一個新的比特幣地址。伺服器沒有私鑰,也就避免了被盜的風險。
    • 應用二:冷儲存或者硬體錢包。在這種情況下,擴充套件私鑰可以儲存在紙錢包或者硬體裝置中,與此同時擴充套件公鑰可以線上儲存。使用者可以任意建立“收款”地址,而私鑰可以安全地在離線狀態下儲存。為了使用資金,使用者可以用擴充套件私鑰在比特幣錢包中進行離線簽名或者透過硬體錢包裝置簽名交易。
  • 存在的安全隱患

    如果知道某個子私鑰,有可能造成父私鑰洩露,推導如下:

    1. 兩種子金鑰的派生方式的輸入引數均為父公鑰,父鏈碼,父索引號,導致 512bits 輸出一致,鏈碼(右 256bits)也一致。
    2. 因為擴充套件公鑰包含鏈碼以及可生成後代鏈碼,可得左 256bits。
    3. 根據子私鑰 = 左 256bits + 父私鑰,如果相應子私鑰洩露,則可逆推得到父私鑰。
    4. 繼續逆推最終可得到已知最早的擴充套件公鑰所對應的私鑰。

子私鑰強派生

這個強派生函式使用了父私鑰去推導子私鑰。這導致 512bits 輸出以及鏈碼(右 256bits)與派生子公鑰方式生成的不一致。因此無法獲得相應的左 256bits,也就無法倒推出父私鑰。

  • 注意:這樣也會導致兩種方式生成的子公鑰也不相同。如果後續使用擴充套件公鑰的方式作為收款地址,後續派生子私鑰的方式需要為正常派生,但就算後續發生私鑰洩露,最多隻會影響到這一分支。

因此強派生也因此被用於金鑰樹中擴充套件公鑰的上一層以創造“間隙/防火牆”。

為了避免主金鑰洩露,主金鑰所衍生的第一層級的子金鑰總是透過強化衍生得來。

索引碼、金鑰識別符、路徑

  • 索引碼

    • 常規派生索引碼在 0 和 $2^{31}-1$(0x0 到 0x7FFFFFFF)之間。
    • 強派生索引碼在 $2{31}$和$2{32}-1$(0x80000000 到 0xFFFFFFFF)之間。
    • 為了讓索引碼更容易被閱讀和顯示,強派生的索引號碼也是從 0 開始展示的,但是右上角有一個小撇號。第一個常規子私鑰因此被表述為 0,但是第一個強化子私鑰(索引號為 0x80000000)就被表示為 0'。
  • HD 錢包金鑰識別符(路徑)

    • 每個級別之間用斜槓(/)字元來表示。
    • 由主私鑰派生出的私鑰起始以“m”打頭。由主公鑰派生的公鑰起始以“M”打頭。
    HD path 金鑰描述
    m/0 主私鑰(m)衍生的第一個子私鑰(0)
    m/0/0 第一個子私鑰 (m/0)衍生的第一個子私鑰(0)
    m/0'/0 第一個強化子私鑰 (m/0')衍生的第一個常規子私鑰(0)
    m/1/0 第二個子私鑰 (m/1)衍生的第一個子私鑰(0)
    M/23/17/0/0 第 24 個子公鑰(M/23)衍生的第 18 個子公鑰(M/23/17)的第一個子公鑰(M/23/17/0)的第一個子公鑰(0)
  • HD 錢包樹狀結構的導航

    BIP-44 指定了包含 5 個預定義樹狀層級的結構:

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

    • 第 1 層的 purpose 總是被設定為 44'。
    • 第 2 層的“coin_type”特指幣種,允許多貨幣 HD 錢包中的貨幣在第二個層級下有自己的子樹結構。
    • 第 3 層是“account”,這允許使用者建立多個獨立的子賬號,便於財務統計或者部門管理。
    • 第 4 層是“change”。每一個 HD 錢包在這一層都有兩個子樹,一個用來建立收款地址,另外一個用來建立找零地址。注意無論先前的層級是否使用強派生,這一層級使用的都是常規派生。這是為了允許這一層級的樹可以透過使用擴充套件公鑰的方式生成下一級的公鑰。
    • 被 HD 錢包派生的可用地址是第 4 層級的子級,就是第 5 層級的“address_index”。
    HD path Key described
    M/44'/0'/0'/0/2 比特幣主賬戶的第三個收款公鑰
    M/44'/0'/3'/1/14 比特幣第四個賬戶的第十五個找零收款公鑰
    m/44'/2'/0'/0/1 Litecoin 主賬戶中的第二個私鑰,用於簽名交易

相關文章