关注早先特币钱包开发的人,对确定性钱包一定不陌生。 老式比特币钱包会根据需要随机生成新比特币地址及私钥;确定性钱包的话,由单个种子利用特定算法生成全部数据。
考虑一下这个情形:说有一天Bob的硬盘原地爆炸了,然后钱包也找不回来了。 但是,Bob有备份钱包种子,那么就能用种子重新创建跟以前一模一样的钱包,包括里边的地址跟私钥。
然后,开发者们就雀跃了,再然后大大小小的钱包不是已经实现了确定性钱包,就是走在实现的路上。确定性钱包的本事不止于此。 还有两大其他关键属性:
主公钥(Master Public Key)
主公钥由钱包的主私钥(种子或者种子周边)生成,公钥本身能生成比特币钱包里的所有地址,但不能生成一般私钥。 所以确定性钱包中的余额,对于拥有主公钥的人,是能看不能花。
层级结构(Hierarchy)
层级结构指的是,主私钥生成的私钥,本身也是主私钥,因此充当一个确定性钱包。可视为独立的确定性钱包。
确定性钱包工作原理
早先的确定性钱有两大类型:Electrum钱包和BIP32钱包;各自使用的算法极类似,因此都有主公钥属性;但是BIP32钱包有层级结构,Electrum没有,不排除以后会有。
主公钥挺好玩,我们先来研究这厮。这个概念可行,源自比特币公钥跟一般整数一样能相加相减。
插播:
1. 比特币公钥跟比特币地址多少有点区别,或者确切的说,二者关系紧密。
2. 公钥不能相乘。
那么,在两个不同层面进行的算术运算其实是相同的:生成私钥,是整数算术运算;生成公钥,是公钥算术运算。
确定性钱包使用的算法如下。
计算i号私钥(假设i=5),先用带变量i和主公钥的函数(其实是哈希)计算“偏移”参数,然后将主私钥和偏移值相加。
计算i号公钥,同上方法先计算出偏移值,将偏移值转换成公钥,后将主公钥与偏移值公钥相加。
下面举了几个Electrum钱包的例子,用的是我自己的pybitcointools库。
首先,用种子生成主公、私钥:
> seed =random_electrum_seed()'afc3eef71d96c468ca52b437c385a621'>
mprivkey =electrum_stretch(seed)'5df10c922a1c7888b5c3a5a7106e72576f09c17f0993f4f2ce0ef5ccdb2f53c5'>
mpubkey ='04'+electrum_mpk(mprivkey)'04fd6d91db1bdfc231116fd7d44c61a02e032b38b90aad419ecf75acf501eebdc7a0b7818e16a97a6c87693c723106e6fe17d4da743fae991139b05e6d1fe5c8a8'
然后,生成0号私钥:
> offset = dbl_sha256('0:0:'+mpubkey[2:].decode('hex'))'429251ad9607fd39040072d23f53d54fc3b1ea526310fa45ca2a2df0629e2db2'>
priv0 = add_privkeys(mprivkey,offset)'a0835e3fc02475c1b9c418794fc247a732bbabd16ca4ef38983923bd3dcd8177'
然后,公钥:
> pub0 = add_pubkeys(mpubkey,privkey_to_pubkey(offset))'04d96f3a8ebb0de48a98a5d77003c1d3ed5a36aae3eb20ee138a233e0c644becf77263c6e56cca6cfe064eb87582dcf92e9066d567ae0ffa67b0a5a4fda2bd7d68'>
addr =
pubkey_to_address(pub0)'14EkQ9qsKxWKiBJm5f7mT7ozSKKZbQoZGS'
然后,成功了:
> privkey_to_pubkey(priv0)'04d96f3a8ebb0de48a98a5d77003c1d3ed5a36aae3eb20ee138a233e0c644becf77263c6e56cca6cfe064eb87582dcf92e9066d567ae0ffa67b0a5a4fda2bd7d68'
这里想说什么呢:只需要自己保管好主私钥(还有“种子”),即便主公钥存放在不安全的地方,或者直接给别人了,都okay的。
层级结构
让我们声情并茂的解释下层级结构:
> w = bip32_master_key('qweqweqweqweqwe')'xprv9s21ZrQH143K2KhRQVuMqhz798mvW89J1aJMWEKNfZzv3BPAgBc4TH59K8ZcLt8RrNJvbUzA72A92Grm3MorG2FnFaoZ7B8SDTYXgUBALoi'>
w0 = bip32_ckd(w,0)'xprv9uyTuGongdyZAMxZ2euUBbpsAdtE2nxFBmcQn89UT4ZyzrMg5TXD7azCnsnpH9Q7yrYgG7nVakE6BTxJUarLrDA28VxS3ZWDsgYWZUxtNiH'>
w000 = bip32_ckd(bip32_ckd(bip32_ckd(w,0),0),0)'xprv9zL8JVf2Us8VKFYoi3A8F3LSFuHnxNhAdVyrWuECgcbW13WcSrAZt9QxbbMvrFZsvUtrktVpNJ5iN1JhgUYDZoeXC5qtGfLuLw3reVWSRir'
层级结构的主要用处是在非扁平结构的组织中。 比方说:公司财务官控制BIP0032钱包的根私钥, 再给各个部门配个 “子种子(child seed)”,然后各个部门用自己的种子运行本部门的钱包。财务官就厉害了,手里拿着把通开公司各种金库的万能钥匙,但各个部门手里只有开自己部门金库的钥匙。
上面说过BIP32和Electrum都有主公钥,但BIP32的更强:
> wp = bip32_privtopub(w)'xpub661MyMwAqRbcEomtWXSNCqvqhAcQuas9NoDxJcizDuXtuyiKDivK15PdAPVkPwVXT9rFbjAnE9P3sLh6xnDawXF1uUXrruH1UvALHF89qdP'>
wp000 = bip32_ckd(bip32_ckd(bip32_ckd(wp,0),0),0)'xpub6DKUi1BvKEgnXjdGp4h8cBHAow8HMqR1ziuTKHdpEx8UsqqkzPUpRwjSStDzFc5ALo93wyYXmRr9rvXWcrfw5MPFKCET11KFg3kNrESyWZ6'>
wp000_2 = bip32_privtopub(w000)'xpub6DKUi1BvKEgnXjdGp4h8cBHAow8HMqR1ziuTKHdpEx8UsqqkzPUpRwjSStDzFc5ALo93wyYXmRr9rvXWcrfw5MPFKCET11KFg3kNrESyWZ6'
那么,可以把BIP32主私钥看成是一个顶端,可以向下延伸(并恢复)出无限子私钥。BIP32主公钥的(延伸的)道理一样,但是只能用于恢复公钥及地址。
也存在问题
总结一下上面的内容:
1. 母密钥到子密钥是条单行道。
2. 给出主公钥,不会威胁到资金安全。
再就是公司财务官万能钥匙的例子也集中反映了人们或者开发者对BIP0032钱包开发的种种期许。
但是,对层级结构钱包的描述是有问题的。
解释一下:子密钥与母密钥、主公钥与主私钥之间似乎是单行道,然后就会有人信口开河说子密钥随便丢,母密钥表示okay的,不然就是主公钥随便丢,主私钥表示okay的。考虑一下,两个事件同时发生的概率是多少?
若这种貌似的小概率事件发生,两行pybitcointools 代码,你(确切的说是公司领导们)就懵了。
用Electrum钱包声情并茂的解释一下, 下面是之前创建的主公钥和子私钥:
> mpubkey'04fd6d91db1bdfc231116fd7d44c61a02e032b38b90aad419ecf75acf501eebdc7a0b7818e16a97a6c87693c723106e6fe17d4da743fae991139b05e6d1fe5c8a8'> priv0'a0835e3fc02475c1b9c418794fc247a732bbabd16ca4ef38983923bd3dcd8177'
如上所示,第一个私钥可以用这个公式计算出:
mprivkey + calc_offset(mpubkey,index)
然后这么干:
> offset = dbl_sha256('0:0:'+mpubkey[2:].decode('hex'))'429251ad9607fd39040072d23f53d54fc3b1ea526310fa45ca2a2df0629e2db2'>
mprivkey_2 = subtract_privkeys(priv0,offset)'5df10c922a1c7888b5c3a5a7106e72576f09c17f0993f4f2ce0ef5ccdb2f53c5'
然后就拿到主私钥惹。再然后,就是诸如掏空钱包地址这种事了。VB还很贴心的在pybitcointools 中包含了一个命令,帮人帮到底的简化了掏空这波操作:
> crack_electrum_wallet(mpubkey,priv0,0)'5df10c922a1c7888b5c3a5a7106e72576f09c17f0993f4f2ce0ef5ccdb2f53c5'
这一点上,BIP32也躲闪不及:
> wp = bip32_privtopub(w)> w0 = bip32_ckd(w,0)> crack_bip32_privkey(wp,w0)'xprv9s21ZrQH143K2KhRQVuMqhz798mvW89J1aJMWEKNfZzv3BPAgBc4TH59K8ZcLt8RrNJvbUzA72A92Grm3MorG2FnFaoZ7B8SDTYXgUBALoi'
公平起见,这个漏洞不是所谓的史诗级漏洞,很多开发者都知道的。
还是举公司的例子:
说各子私钥交给公司各部门,主公钥交给财务官,这波操作可谓彻底瓦解确定性钱包的安全保障。结合上面这波声情并茂的代码解释,某部门负责人和某会计串通卷走公司全部资金看上去是迟早的事儿。
那么,这个漏洞可以被修复吗?答案似乎是否定的。
公钥只能做加减运算。 实现主公钥确定性钱包,只能通过上面提到的 “偏移”机制。
意思就是,层级结构跟主公钥不要混一起用,望周知。
Deterministic Wallets, Their Advantages and their Understated Flawsbitcoinmagazine.com
网友评论