逃出生天:不同威胁下的钱包恢复策略

Simon 写字的地方
2024-10-30 04:49
发布于 Mirror

前言

今年看到很多关于「抢劫加密货币」的新闻:

针对于加密货币实施的犯罪不仅存在于比特世界,在原子世界也变得越来越频繁。今天读到《精通比特币》第三版「种子和恢复码」部分,结合自己之前经历过的钱包安全事件,脑海中出现很多经典的犯罪场景,针对于这些场景,我们可以采用哪些技术手段来保证自己的资产安全呢?

💡 在此之前,你需要知道的:

  • 种子技术是一种在加密货币钱包中用于生成私钥和公钥的技术。通过生成一个随机数作为种子,再利用该种子计算出多个私钥和公钥对,从而实现钱包的生成和恢复。

  • 恢复码是一组用于恢复钱包的助记词,是种子的可读形式。常见的恢复码由12到24个单词组成(如 BIP39),这是通过将种子编码为单词序列得到的。用户可以手动记录这组单词,用于在钱包丢失或损坏时恢复钱包。

假设场景

场景 1:劫持威胁

情景描述

加入你遇到了劫匪,劫匪要求你必须交出比特币钱包的恢复码,你不想暴露自己的全部资产,但又要交出一些钱来保命。

困点

不能让劫匪知道你到底有多少钱。

解决方案

使用 BIP39 和 SLIP39 的合理否认机制。在 BIP39 和 SLIP39 恢复码方案中,用户可以设置一个「主恢复码」以及可选的口令。不同的口令会生成不同的密钥树,因此 Alice 可以设置一个包含少量资金的备用口令,用于应对被迫交出恢复码的情况。这样,歹徒即便拿到恢复码和备用口令,也只能获取少量资产。

def generate_seed(mnemonic_phrase, passphrase=""):
    """
    Generate seed from mnemonic phrase and optional passphrase using PBKDF2.
    """
    # BIP39 defines the salt as "mnemonic" + passphrase
    salt = "mnemonic" + passphrase
    # Generate the seed using PBKDF2-HMAC-SHA512 with 2048 iterations
    seed = hashlib.pbkdf2_hmac("sha512", mnemonic_phrase.encode(), salt.encode(), 2048)
    return seed.hex()

# 初始化 BIP39 助记词生成器
mnemo = Mnemonic("english")

# 生成助记词
mnemonic_phrase = mnemo.generate(strength=128)  # 12 个助记词
print(f"助记词: {mnemonic_phrase}")

# 使用不同的口令生成种子
passphrase_1 = "main_passphrase"
passphrase_2 = "secondary_passphrase"

# 生成不同的种子
seed1 = generate_seed(mnemonic_phrase, passphrase_1)
seed2 = generate_seed(mnemonic_phrase, passphrase_2)

print(f"种子 (口令1): {seed1}")
print(f"种子 (口令2): {seed2}")

# 验证不同口令生成的种子是否相同
if seed1 != seed2:
    print("不同口令生成了不同的种子。")
else:
    print("不同口令生成了相同的种子(请检查代码或口令设置)。")

打印示例

助记词: dose grid token call output jungle spot monster ozone spread bunker canal
种子 (口令1): 5a7a35f7a6e08d7a7efb93f9120d94bf7ffb3a7f82e84e2334fa6310a7f216b20b6fc536...
种子 (口令2): 9e40e3d60ff15a54d37dabc8474825c92a5b830d842fbdb77e5fbe5a6b35b7123c973c6...

场景 2:跨国旅行遭遇检查

情景描述

你全球旅行到了一个对加密货币不友好的国家。入境检查时,当地安全人员要求他上交钱包的恢复码和私钥,但你并不想这么做。

困点

携带的私钥无法完整恢复钱包

解决方案

使用 SLIP39(分布式恢复码),可以将种子恢复码分成多个分片,存放在不同位置。比如我们可以设置 2 个分片,自身携带一个,要求比如两个分片在一起时才可以恢复钱包。

# 初始化 BIP39 助记词生成器
mnemo = Mnemonic("english")

# 生成助记词作为种子
mnemonic_phrase = mnemo.generate(strength=128)  # 生成 12 个助记词
print(f"原始助记词: {mnemonic_phrase}")

# 将助记词分成两个分片,设定 2 个分片才能恢复
mnemonics = generate_mnemonics(master_secret=mnemonic_phrase.encode(), group_threshold=1, groups=[(2, 2)])

print("生成的分片:")
for idx, part in enumerate(mnemonics, start=1):
    print(f"分片 {idx}: {part}")

# 恢复过程,使用两个分片组合恢复原始种子
recovered_phrase = combine_mnemonics(mnemonics)
print(f"恢复的助记词: {recovered_phrase.decode()}")

# 验证恢复的助记词是否和原始助记词一致
if recovered_phrase.decode() == mnemonic_phrase:
    print("成功恢复原始助记词!")
else:
    print("恢复失败,助记词不匹配。")

打印示例

原始助记词: stable whisper section invite risk embrace stand educate snack banner mirror result
生成的分片:
分片 1: armed strong acquire jungle plug mutual letter mother door use field mistake
分片 2: quick trip modify island food reduce length renew warm fortune green square
恢复的助记词: stable whisper section invite risk embrace stand educate snack banner mirror result

💡 原始助记词并不是分片 1 和分片 2 的简单集成。Shamir’s Secret Sharing (SSSS) 算法背后的原理并不是将原始助记词简单地拆分成多个部分,而是通过数学方法将原始数据(种子或助记词)转换成多个唯一且独立的分片。每个分片都包含一部分恢复原始数据所需的信息,但单独一个分片本身不能直接恢复原始助记词。因此:

  • 每个分片都包含部分信息,但单独无法还原原始助记词;

  • 多个分片联合才能进行插值重建,进而恢复原始种子。

场景 3:监守自盗

情景描述

你和几个朋友一起开了一家公司,希望共同管理 BTC 资产。但不希望任何人单独获得恢复码。

困点

任何一个恢复码都无法单独获得所有资产。

解决方案

SLIP39 或 Codex32 的分布式密钥分片。可以将种子恢复码分成多个分片(如 8 个),并将不同的分片分给朋友,同时设定恢复门限为 5 个分片。这样,即使有部分分片丢失或泄露,攻击者仍无法获得完整的恢复码。

💡 Codex32 是一种新的、仍在开发中的恢复码方案,目前尚未有正式的 Python 库或标准实现,但我们可以根据分布式密钥分片的原理,模拟 Codex32 的工作方式。Codex32 类似于 Shamir 的秘密共享方案,但它设计为可以通过物理方式(如纸质打印和手动验证)来生成和验证密钥分片。具体逻辑可以参考 SLIP39 代码片段。

当然,目前很多 EVM 链或图灵完备的公链大多数会采用多签钱包或 AA 钱包的方式来解决此类问题。但由此可能会引申出一个新的问题 - 如果双方各掌握 50% 的权限,那么当意见出现分歧时,很容易陷入僵局。

场景 4:设备丢失或被盗

情景描述

你的手机被盗,而且手机中存放着恢复码的照片。

困点

恢复码照片泄露导致资金被盗。

解决方案

**Aezeed(口令身份验证)。**Aezeed 的口令验证可以防止用户输入错误密码,而攻击者即便拥有恢复码,若不知道正确口令,依然无法生成有效的密钥。

使用 Aezeed 方案,你的恢复码是加密的,并且包含口令身份验证。在恢复钱包时,如果输入错误的密码,会立即显示验证错误,不会生成错误的密钥树。

技术特点

从上述描述中,我们可能感觉 AezeedBIP39SLIP39 在功能上有一定相似性,特别是在使用口令生成不同密钥树方面,但 BIP39 和 SLIP39 的设计旨在生成单一恢复码或分片恢复码,通过可选口令来生成不同的密钥树,但缺少验证机制。输入错误口令时,BIP39 和 SLIP39 仍会生成有效密钥树,可能导致用户误解和丢失资金。但 Aezeed 会通过校验,告知用户输入的是错误的口令。

不仅如此 Aezeed 相比 BIP39 和 SLIP39 还有一些独特的特性,包括其设计目的是为 闪电网络提供更好的支持,并且包含更多的安全特性,如口令验证钱包生日信息,以及多层版本控制。这些特性使得 Aezeed 更适合复杂应用场景,比如高频、实时的交易需求。

由于目前 Aezeed 还没有正式的库实现,但我们可以模拟其通过口令生成不同种子的流程,同时添加口令验证,以确保在用户输入错误口令时返回错误信息。

def generate_seed_with_aezeed(mnemonic_phrase, passphrase=""):
    """
    Generate an Aezeed-style seed from mnemonic phrase and optional passphrase.
    Adds basic password validation by checking a derived hash.
    """
    # Aezeed 风格的种子生成函数
    salt = "mnemonic" + passphrase
    seed = hashlib.pbkdf2_hmac("sha512", mnemonic_phrase.encode(), salt.encode(), 2048)
    # 验证哈希值的前 8 字节作为基本校验
    checksum = seed[:8]
    return seed.hex(), checksum

def verify_passphrase(mnemonic_phrase, passphrase, expected_checksum):
    """
    Verify if the provided passphrase generates the expected checksum.
    """
    _, checksum = generate_seed_with_aezeed(mnemonic_phrase, passphrase)
    return checksum == expected_checksum

# 初始化 BIP39 助记词生成器
mnemo = Mnemonic("english")

# 生成助记词作为种子
mnemonic_phrase = mnemo.generate(strength=128)  # 生成 12 个助记词
print(f"助记词: {mnemonic_phrase}")

# 使用主口令生成种子
main_passphrase = "correct_password"
seed, checksum = generate_seed_with_aezeed(mnemonic_phrase, main_passphrase)
print(f"主种子: {seed}")
print(f"校验码: {checksum.hex()}")

# 测试其他口令是否会产生不同种子,并进行验证
test_passphrase = "wrong_password"
is_valid = verify_passphrase(mnemonic_phrase, test_passphrase, checksum)

print(f"口令验证结果(错误口令): {'有效' if is_valid else '无效'}")

# 验证正确口令
is_valid = verify_passphrase(mnemonic_phrase, main_passphrase, checksum)
print(f"口令验证结果(正确口令): {'有效' if is_valid else '无效'}")

打印示例

助记词: stable whisper section invite risk embrace stand educate snack banner mirror result
主种子: 5a7a35f7a6e08d7a7efb93f9120d94bf7ffb3a7f82e84e2334fa6310a7f216b20b6fc536...
校验码: 5a7a35f7a6e0
口令验证结果(错误口令): 无效
口令验证结果(正确口令): 有效

场景 5:网络黑客攻击

情景描述

你担心黑客攻击威胁到恢复码的安全,因此希望找到一种完全离线的存储方法。

困点

离线存储且可分片(防止单一丢失)

解决方案

Codex32 的手动离线分布式存储。Codex32 恢复码方案允许用户通过打印说明书、剪刀等物理方式生成分片恢复码,避免电子设备参与。Eve 可以将恢复码分片离线打印并分散存放,确保恢复码不会受到网络攻击威胁。

总结

0
粉丝
0
获赞
11
精选
数据来源区块链,不构成投资建议!
网站只展示作者的精选文章
2022 Tagge. With ❤️ from Lambda