In 1.35c, etotheipi figured he could generate the chaincode from a HMAC of the root, which led to the current 2 strings backups (32 bytes of data). I have no recollection of ever using wallets with a chaincode generated from the bad HMAC (I've been actively contributing the Armory since September 2014). I'm guessing there is a narrow window during which users have generated wallets with the bad HMAC?
In my opinion all wallets are generated with the bad HMAC.
This is how armory works:
bin_root_key = 0xed095dd690c8975209cc820cdda5b71c01ba866623c85396f81ff0eda8416960cffd777c
hash256(bin_root_key) = ddbed0169f589ff48ec32cb448dd16f5288a0719bb2454a15036b8575903ffda
bin_chaincode = HMAC256(hash256(bin_rootkey), 'Derive Chaincode from Root Key')
chaincode = int(codecs.encode(bin_chaincode, 'hex'), 16)
print (hex(chaincode))
84444f1c8c83c9523f120fbae08fa47cb602342ce26b03a31d01dcf343e2e13e
def HMAC(key, msg, hashfunc=sha512, hashsz=None):
""" This is intended to be simple, not fast. For speed, use HDWalletCrypto() """
hashsz = len(hashfunc('')) if hashsz==None else hashsz
key = (hashfunc(key) if len(key)>hashsz else key)
if bytes == str: # python2
key = key.ljust(hashsz, '\x00')
okey = ''.join([chr(ord('\x5c')^ord(c)) for c in key])
ikey = ''.join([chr(ord('\x36')^ord(c)) for c in key])
return hashfunc( okey + hashfunc(ikey + msg) )
else: #python3
key = key.ljust(hashsz, b'\x00')
okey = bytes(a ^ c for c, a in zip(b'\x5c'*hashsz, key))
ikey = bytes(a ^ c for a, c in zip(b'\x36'*hashsz, key))
return hashfunc( okey + hashfunc(ikey + msg.encode('utf-8')) )
HMAC256 = lambda key,msg: HMAC(key, msg, sha256, 32)
If instead of 32 I use 64
HMAC256 = lambda key,msg: HMAC(key, msg, sha256,
32) -> HMAC256 = lambda key,msg: HMAC(key, msg, sha256,
64)
I get:
bin_root_key = 0xed095dd690c8975209cc820cdda5b71c01ba866623c85396f81ff0eda8416960cffd777c
hash256(bin_root_key) = ddbed0169f589ff48ec32cb448dd16f5288a0719bb2454a15036b8575903ffda
bin_chaincode = HMAC256(hash256(bin_rootkey), 'Derive Chaincode from Root Key')
chaincode = int(codecs.encode(bin_chaincode, 'hex'), 16)
print (hex(chaincode))
66fb31cdb3015355c3e2d65bf53f55d4dd45da43d1138a10a887d0e3c7bed51b
that is equal to the result of the correct HMAC:
import hmac
import hashlib
k = bytearray([0xdd,0xbe,0xd0,0x16,0x9f,0x58,0x9f,0xf4,0x8e,0xc3,0x2c,0xb4,0x48,0xdd,0x16,0xf5,0x28,0x8a,0x07,0x19,0xbb,0x24,0x54,0xa1,0x50,0x36,0xb8,0x57,0x59,0x03,0xff,0xda]);
x = hmac.new(k, "Derive Chaincode from Root Key", hashlib.sha256);
print x.hexdigest()
66fb31cdb3015355c3e2d65bf53f55d4dd45da43d1138a10a887d0e3c7bed51b
then Armory uses a different HMAC function.
Armory in my dev branch is running off of libbtc instead of cryptopp, and it manages to recover older wallets with the expected data, so I wouldn't be too worried about implementing support for the botched HMAC.
Because you use the same wrong size (32 instead of 64):
https://github.com/goatpig/BitcoinArmory/blob/dev/armoryengine/ArmoryUtils.py#L1941