Cover photo

助記詞掉了如何找回?以Tezos為例子

揳子

如果你常在區塊鏈的世界衝浪,肯定會有一個抱團取暖的社團分享訊息,每當夥伴分享區塊鏈社會事件,如暴力討債、礦場偷電、名人錢包被盜事件⋯⋯往往是見怪不鮮不能引起你的共鳴,但看到夥伴們錢包被盜的事情,肯定會令大家豎直椅背開始團購冷錢包,或是把電腦、手機的助記詞刪除改用紙質筆記本。

隊友聽我分享完點頭如搗蒜,隨即把所有數位的痕跡都抹除,並將錢包的助記詞抄送在紙質筆記本上,然而隊友不諳區塊鏈的基礎知識,只抄送了11位助記詞!找尋那遺失的1位的過程正是本文要分享的。

確認可能性並使用程式計算遺失的那一位

硬幹程式之前,必須先確認規格,才能衡量這個硬幹的可能性,Tezos如其他熱門的加密貨幣,如:Ether、Bitcoin一樣,使用BIP39的規格設計助記詞,這裡有一些重點幫大家濃縮:

  1. 助記詞其實就是由2048個不同的單字組成,每一個單字對應一個二進制號碼,從0x000000000000x11111111111,對整個表格有興趣請移駕這裡

  2. 首四位不會重複,如:642號expand與643號expect只有三位重複,為的是避免助記詞在抄送的過程中出現筆誤的現象。

  3. 支援多種語言,如:英文、日文、韓文、西班牙文、簡體中文、繁體中文、法文、義大利文、捷克文、葡萄牙文都可以有助記詞,但目前大多數錢包都是使用英文助記詞。

透過以上的推理,只需要從表格中取1個助記詞跟11位已知的助記詞進行排列組合後生成『錢包地址』,再拿這個『錢包地址』跟自己的『錢包地址』進行比對就能知道答案了,於是我就寫了一段Python程式:

from pytezos.crypto.key import Key
from mnemonic import Mnemonic
import numpy as np

incomplete_words = [...]
target_address = "tz1..."
size = len(Mnemonic(LANGUAGE).wordlist)

for position in range(12):
  print("======")
  print(position)
  for index in range(size):

    # pick one from world list vector
    variable = Mnemonic(LANGUAGE).wordlist[index]
    
    # insert above to an incomplete seed phrase array
    input_data = np.insert(incomplete_words, position, variable)

    # as string format
    input_data_str = ' '.join(input_data)
    try:
      sk = Key.from_mnemonic(mnemonic=input_data_str)
      if(sk.public_key_hash() == target_address):
        print(position, index, input_data)
    except Exception as e:      
      pass

但以上方法但始終找不到正確的地址。

後來翻了一下BIP44的規格,發現常用的瀏覽器錢包插件如:MetaMask、Temple Wallet、Kukai Wallet都是HD Wallet的一種,必須了解HD Wallet的設計原理才能進一步解決問題。

HD Wallet跟你想的不一樣

我們熟知的區塊鏈基礎知識中,一組助記詞可以推導出一組『私鑰』,再用『私鑰』推導出一組『公鑰』,而『公鑰』可以推導出『公鑰哈希』和『錢包地址』,以上推導是單向的,無法反向算出源頭。雖然這樣的構想很棒,但遇到組織架構如:企業、去中心化組織就會遇到:

  • 多人管理一個助記詞很難去歸因資產的管理責任

  • 人員調動、組織擴時充管理權限無法更新

在BIP44的規格被提出來之後:

  • 『私鑰』用有者視同一個節點,可以再去派生子節點

  • 派生採用階層式的架構(見下圖),可以擁有無限層

  • 父節點的『私鑰』擁有者透過派生的機制可以推導出所有的子節點,因此可以向下管理所有子節點的資產

  • 反之子節點的擁有者,只能去派生更下一層的節點,且子節點不能推導回父節點

Hierarchical Deterministic Wallets aka HD Wallet
Hierarchical Deterministic Wallets aka HD Wallet

透過以上特性,最大的受益者肯定是組織架構,這讓他們能夠方便管理區塊鏈資產,但同時又保留了區塊鏈加密的特性。

回到問題本身

原先的Python程式碼只要修改以下兩點就好了:

  • 取代Key物件,改用HD Wallet Key物件

  • 使用派生產生『私鑰』,再產生『錢包地址』

from pytezos.crypto.key import Key
from mnemonic import Mnemonic
import numpy as np

incomplete_words = [...]
target_address = "tz1..."
size = len(Mnemonic(LANGUAGE).wordlist)

for position in range(12):
  print("======")
  print(position)
  for index in range(size):

    # pick one from world list vector
    variable = Mnemonic(LANGUAGE).wordlist[index]
    
    # insert above to an incomplete seed phrase array
    input_data = np.insert(incomplete_words, position, variable)

    # as string format
    input_data_str = ' '.join(input_data)
    try:
      hdKey = HDKey.from_mnemonic(mnemonic=input_data_str)
      sk = hdKey.derive("m/44/1729/0/0")
      if(sk.public_key_hash() == target_address):
        print(position, index, input_data)
    except Exception as e:      
      pass

但很可惜以上程式碼不能成功執行,因為pytezos還沒支援HDKey物件,所以要改用C#與netezos,這邊不囉唆直接上C#程式碼。

using System;
using Netezos.Keys;
using System.Collections.Generic;
using System.Linq;

string[] lines = System.IO.File.ReadAllLines(@"english.txt");

namespace ConsoleApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var target_address = "tz1..."

            for (int i = 0; i < 12; i++)
            {
                Console.WriteLine("Current Index: ", i);
                foreach (string line in lines)
                {
                    var words = new List<string> { ... };
                    words.Insert(i, line);
                    var mnemonic = new Mnemonic(words);
                    var key = HDKey.FromMnemonic(mnemonic, "", ECKind.Ed25519);
                    var firstchild = key.Derive("m/44'/1729'/0'/0'");
                    if (firstchild.Address == target_address)
                    {
                        Console.WriteLine("");
                        Console.WriteLine(line);
                        Console.WriteLine(firstchild.Address);
                    }
                }
            }
        }
    }
}

再聊聊組織架構

透過理解BIP44也窺探了組織是怎麼管理區塊鏈資產的,看看Binance在鍊上精美且龐大的資產。

https://tzkt.io/tz1S8MNvuFEUsWgjHvi3AxibRBf388NhT1q2/operations/
https://tzkt.io/tz1S8MNvuFEUsWgjHvi3AxibRBf388NhT1q2/operations/

而且不只一個Binance帳號在鏈上活躍著,到底他們是怎麼管理多個帳號,而安全性又在哪裡?這時候新的問題是,實務上如何讓多人管理鏈上資產呢?一些解決方案提供商提出一些想法,不外乎:

  • 分散式的管理

    1. 操作者用工具進行操作,而不用錢包操作(因為錢包操作者等同擁有錢包的權限)

    2. 權限保管者擁有的是產生『私鑰』的某一環,可以是一段能生成助記詞的工具,且權限保管者看不到工具生成的結果,他的操作等同於蓋章

    3. 工具由開發者開發,且角色和操作者、權限保管者分開

  • 尋找外包,讓方案提供商負責安全上的問題

結尾

今天的情況比較幸運,所以助記詞在3分鐘內使用ThinkPad T14s筆電很快就算出來了。倘若少了2位註記詞,可能就會需要耗費3分鐘的平方,也就是9個小時,此外助記詞若是順序有問題,使用以上暴力破解想找回助記詞幾乎不可行,最後祝大家都能找回助記詞。

Reference