def buildCorruptWallet(self, walletPath): crpWlt = PyBtcWallet() crpWlt.createNewWallet(walletPath, securePassphrase='testing', doRegisterWithBDM=False) #not registering with the BDM, have to fill the wallet address pool manually crpWlt.fillAddressPool(100) #grab the last computed address lastaddr = crpWlt.addrMap[crpWlt.lastComputedChainAddr160] #corrupt the pubkey PubKey = hex_to_binary( '0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455' ) lastaddr.binPublicKey65 = SecureBinaryData(PubKey) crpWlt.addrMap[crpWlt.lastComputedChainAddr160] = lastaddr crpWlt.fillAddressPool(200) #insert a gap and inconsistent encryption newAddr = PyBtcAddress() newAddr.chaincode = lastaddr.chaincode newAddr.chainIndex = 250 PrivKey = hex_to_binary( 'e3b0c44298fc1c149afbf4c8996fb92427ae41e5978fe51ca495991b7852b855') newAddr.binPrivKey32_Plain = SecureBinaryData(PrivKey) newAddr.binPublicKey65 = CryptoECDSA().ComputePublicKey( \ newAddr.binPrivKey32_Plain) newAddr.addrStr20 = newAddr.binPublicKey65.getHash160() newAddr.isInitialized = True crpWlt.addrMap[newAddr.addrStr20] = newAddr crpWlt.lastComputedChainAddr160 = newAddr.addrStr20 crpWlt.fillAddressPool(250) lastAddr = crpWlt.addrMap[crpWlt.lastComputedChainAddr160] PrivKey = hex_to_binary( 'e3b0c44298fc1c149afbf4c8996fb92427ae41e5978fe51ca495991b00000000') lastAddr.binPrivKey32_Plain = SecureBinaryData(PrivKey) lastAddr.binPublicKey65 = CryptoECDSA().ComputePublicKey( \ lastAddr.binPrivKey32_Plain) lastAddr.keyChanged = True crpWlt.kdfKey = crpWlt.kdf.DeriveKey(SecureBinaryData('testing')) lastAddr.lock(secureKdfOutput=crpWlt.kdfKey) lastAddr.useEncryption = True crpWlt.fillAddressPool(350) #TODO: corrupt a private key #break an address entry at binary level return crpWlt.uniqueIDB58
def extractSignedDataFromVersionsDotTxt(wholeFile, doVerify=True): """ This method returns a pair: a dictionary to lookup link by OS, and a formatted string that is sorted by OS, and re-formatted list that will hash the same regardless of original format or ordering """ msgBegin = wholeFile.find('# -----BEGIN-SIGNED-DATA-') msgBegin = wholeFile.find('\n', msgBegin + 1) + 1 msgEnd = wholeFile.find('# -----SIGNATURE---------') sigBegin = wholeFile.find('\n', msgEnd + 1) + 3 sigEnd = wholeFile.find('# -----END-SIGNED-DATA---') MSGRAW = wholeFile[msgBegin:msgEnd] SIGHEX = wholeFile[sigBegin:sigEnd].strip() if -1 in [msgBegin, msgEnd, sigBegin, sigEnd]: LOGERROR('No signed data block found') return '' if doVerify: Pub = SecureBinaryData(hex_to_binary(ARMORY_INFO_SIGN_PUBLICKEY)) Msg = SecureBinaryData(MSGRAW) Sig = SecureBinaryData(hex_to_binary(SIGHEX)) isVerified = CryptoECDSA().VerifyData(Msg, Sig, Pub) if not isVerified: LOGERROR('Signed data block failed verification!') return '' else: LOGINFO('Signature on signed data block is GOOD!') return MSGRAW
def verifyZipSignature(outerZipFilePath): result = MODULE_ZIP_STATUS.Invalid try: dataToSign = None signature = None outerZipFile = ZipFile(outerZipFilePath) # look for a zip file in the name list. # There should only be 2 files in this zip: # The inner zip file and the sig file if len(outerZipFile.namelist()) == 3: dataToSign = sha256( sha256(outerZipFile.read(INNER_ZIP_FILENAME)) + sha256(outerZipFile.read(PROPERTIES_FILENAME))) signature = outerZipFile.read(SIGNATURE_FILENAME) if dataToSign and signature: """ Signature file contains multiple lines, of the form "key=value\n" The last line is the hex-encoded signature, which is over the source code + everything in the sig file up to the last line. The key-value lines may contain properties such as signature validity times/expiration, contact info of author, etc. """ dataToSignSBD = SecureBinaryData(dataToSign) sigSBD = SecureBinaryData(hex_to_binary(signature.strip())) publicKeySBD = SecureBinaryData( hex_to_binary(ARMORY_INFO_SIGN_PUBLICKEY)) result = MODULE_ZIP_STATUS.Valid if CryptoECDSA().VerifyData(dataToSignSBD, sigSBD, publicKeySBD) else \ MODULE_ZIP_STATUS.Unsigned except: # if anything goes wrong an invalid zip file indicator will get returned pass return result
def makePubKey(self, byte): """ The input byte will be repeated 32 times, then treated as the x-value of a compressed pubkey. Uncompress it to get a real pubkey. We do this so that we have a valid public key for our tests, in which the validity of our 65-byte keys are checked """ sbd33 = SecureBinaryData('\x02' + byte*32) return CryptoECDSA().UncompressPoint(sbd33).toBinStr()
def signZipFile(zipFilePath, propertiesDictionary=None): if propertiesDictionary: # Create an empty properties file pass # if it's a string treat it like a file name and open it # else if ti's a dictionary save it to a file and use that. # Read the contents of the Zip File and the properties file zipFileData = None propertiesFileData = None dataToSign = sha256(sha256(zipFileData) + sha256(propertiesFileData)) dataToSignSBD = SecureBinaryData(dataToSign) # get the privKeySBD privKeySBD = None signature = CryptoECDSA().SignData(dataToSignSBD, privKeySBD, ENABLE_DETSIGN)
sys.argv.append('--nologging') INIT_VECTOR = '77'*16 TEST_ADDR1_PRIV_KEY_ENCR1 = '500c41607d79c766859e6d9726ef1ea0fdf095922f3324454f6c4c34abcb23a5' TEST_ADDR1_PRIV_KEY_ENCR2 = '7966cf5886494246cc5aaf7f1a4a2777cd6126612e7029d79ef9df47f6d6927d' TEST_ADDR1_PRIV_KEY_ENCR3 = '0db5c1e9a8d1ebc0525bdb534626033b948804a9a34871d67bf58a3df11d6888' TEST_ADDR1_PRIV_KEY_ENCR4 = '5db1314a20ae9fc978477ab3fe16ab17b246d813a541ecdd4143fcf082b19407' TEST_PUB_KEY1 = '046c35e36776e997883ad4269dcc0696b10d68f6864ae73b8ad6ad03e879e43062a0139095ece3bd653b809fa7e8c7d78ffe6fac75a84c8283d8a000890bfc879d' # Create an address to use for all subsequent tests PRIVATE_KEY = SecureBinaryData(hex_to_binary('aa'*32)) PRIVATE_CHECKSUM = PRIVATE_KEY.getHash256()[:4] PUBLIC_KEY = CryptoECDSA().ComputePublicKey(PRIVATE_KEY) ADDRESS_20 = PUBLIC_KEY.getHash160() TEST_BLOCK_NUM = 100 # We pretend that we plugged some passphrases through a KDF FAKE_KDF_OUTPUT1 = SecureBinaryData( hex_to_binary('11'*32) ) FAKE_KDF_OUTPUT2 = SecureBinaryData( hex_to_binary('22'*32) ) class PyBtcAddressTest(TiabTest): # TODO: This test needs more verification of the results. def testEncryptedAddress(self): # test serialization and unserialization of an empty PyBtcAddrss
def testWalletRecovery(self): #run recovery on broken wallet recThread = PyBtcWalletRecovery().RecoverWallet(self.corruptWallet, \ 'testing', RECOVERMODE.Full, \ returnError = 'Dict') recThread.join() brkWltResult = recThread.output self.assertTrue(len(brkWltResult['sequenceGaps'])==1, \ "Sequence Gap Undetected") self.assertTrue(len(brkWltResult['forkedPublicKeyChain'])==3, \ "Address Chain Forks Undetected") self.assertTrue(len(brkWltResult['unmatchedPair'])==100, \ "Unmatched Priv/Pub Key Undetected") self.assertTrue(len(brkWltResult['misc'])==50, \ "Wallet Encryption Inconsistency Undetected") self.assertTrue(len(brkWltResult['importedErr'])==50, \ "Unexpected Errors Found") self.assertTrue(brkWltResult['nErrors']==204, \ "Unexpected Errors Found") #check obfuscated keys yield the valid key #grab root key badWlt = PyBtcWallet() badWlt.readWalletFile(self.corruptWallet, False, False) rootAddr = badWlt.addrMap['ROOT'] SecurePassphrase = SecureBinaryData('testing') secureKdfOutput = badWlt.kdf.DeriveKey(SecurePassphrase) #HMAC Q rootAddr.unlock(secureKdfOutput) Q = rootAddr.binPrivKey32_Plain.toBinStr() nonce = 0 while 1: hmacQ = HMAC256(Q, 'LogMult%d' % nonce) if binary_to_int(hmacQ, BIGENDIAN) < SECP256K1_ORDER: hmacQ = SecureBinaryData(hmacQ) break nonce = nonce + 1 #Bad Private Keys import operator badKeys = [ addrObj for addrObj in (sorted(badWlt.addrMap.values(), key=operator.attrgetter('chainIndex'))) ] #run through obdsPrivKey for i in range(0, len(brkWltResult['privMult'])): obfsPKey = SecureBinaryData( hex_to_binary(brkWltResult['privMult'][i])) pKey = CryptoECDSA().ECMultiplyScalars(obfsPKey.toBinStr(), \ hmacQ.toBinStr()) try: badKeys[i + 201].unlock(secureKdfOutput) except: continue self.assertTrue(binary_to_hex(pKey) == \ badKeys[i+201].binPrivKey32_Plain.toHexStr(), \ 'key mult error') #run recovery on recovered wallet recThread = PyBtcWalletRecovery().RecoverWallet( \ 'armory_%s_RECOVERED.wallet' % self.wltID, \ 'testing', RECOVERMODE.Full, \ returnError = 'Dict') recThread.join() rcvWltResult = recThread.output self.assertTrue(len(rcvWltResult['importedErr'])==50, \ "Unexpected Errors Found") self.assertTrue(rcvWltResult['nErrors']==50, \ "Unexpected Errors Found") self.assertTrue(len(rcvWltResult['negativeImports'])==99, \ "Missing neg Imports") # Running tests with "python <module name>" will NOT work for any Armory tests # You must run tests with "python -m unittest <module name>" or run all tests with "python -m unittest discover" # if __name__ == "__main__": # unittest.main()