PageRenderTime 60ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/awalletextractor.py

https://github.com/jwal/jwallib-retired
Python | 127 lines | 104 code | 11 blank | 12 comment | 20 complexity | bd803f76ceac112fe23b0196ec5208c9 MD5 | raw file
  1. #!/usr/bin/env python
  2. """\
  3. %prog [options] IN_FILE OUT_FILE
  4. """
  5. try:
  6. from Crypto.Hash import SHA256, SHA
  7. from Crypto.Cipher import AES, Blowfish, DES3
  8. except ImportError, e:
  9. raise ImportError("Try 'sudo apt-get install python-crypto': %s" % (e,))
  10. import optparse
  11. import os
  12. import sys
  13. import getpass
  14. ALGORITHMS = (
  15. (AES, (
  16. 256,
  17. 192,
  18. 128,
  19. )),
  20. (Blowfish, (
  21. 256,
  22. 192,
  23. )),
  24. # (DES3, (
  25. # 112,
  26. # 168,
  27. # )),
  28. )
  29. CIPHER_MODES = (
  30. # "CBC",
  31. # "CFB",
  32. # "OFB",
  33. "ECB",
  34. )
  35. SHA_REPEATS = tuple(
  36. list(range(15))
  37. + list(range(95, 105))
  38. + list(range(995, 1005))
  39. + list(range(9995, 10005))
  40. )
  41. digesters = (
  42. "digest",
  43. "hexdigest",
  44. )
  45. testers = (
  46. (0, None),
  47. (512, None),
  48. (0, 512 + 32),
  49. (512, 512 + 32),
  50. (512, 512 + (len("TRUE") * 8)),
  51. (0, 512 + (len("TRUE") * 8)),
  52. )
  53. MAGIC_MARKER = "TRUE"
  54. def awallet_extract(crypt_data, password):
  55. salt = crypt_data[:512 // 8]
  56. print "decrypting..."
  57. for seek, stop in testers:
  58. print " seek=%r stop=%r" % (seek, stop)
  59. assert (seek // 8) * 8 == seek, seek
  60. if stop is None:
  61. ciphertext = crypt_data[seek:]
  62. else:
  63. assert (stop // 8) * 8 == stop, stop
  64. ciphertext = crypt_data[seek:stop]
  65. # assert salt + ciphertext == crypt_data
  66. for initial_key in [salt + password, password + salt]:
  67. print " initial_key=%r" % (SHA.new(initial_key).hexdigest(),)
  68. for initial_digester in digesters:
  69. print " initial_digester=%r" % (initial_digester,)
  70. for digester in digesters:
  71. print " digester=%r" % (digester,)
  72. digest = getattr(SHA256.new(initial_key),
  73. initial_digester)()
  74. for repeat_count in SHA_REPEATS:
  75. print " repeat_count=%r" % (repeat_count,)
  76. for i in range(repeat_count):
  77. digest = SHA256.new(digest).digest()
  78. full_key = digest
  79. for algorithm, keysizes in ALGORITHMS:
  80. print " algorithm=%r" % (
  81. algorithm.__name__,)
  82. for keysize in keysizes:
  83. print " keysize=%r" % (keysize,)
  84. assert len(full_key) * 8 >= keysize, \
  85. (len(full_key) * 8, keysize)
  86. assert (keysize // 8) * 8 == keysize, keysize
  87. key = full_key[:keysize // 8]
  88. for cipher_mode in CIPHER_MODES:
  89. print " cipher_mode=%r" % (
  90. cipher_mode,)
  91. mode_param = getattr(
  92. algorithm, "MODE_" + cipher_mode)
  93. cipher = algorithm.new(key, mode_param)
  94. plaintext = cipher.decrypt(crypt_data)
  95. if MAGIC_MARKER in plaintext:
  96. print "@@@@@ got it?"
  97. return plaintext
  98. raise Exception("Failed to decrypt, was the password wrong?")
  99. def main(argv):
  100. parser = optparse.OptionParser(__doc__)
  101. options, args = parser.parse_args(argv)
  102. if len(args) == 0:
  103. parser.error("Missing: IN_FILE")
  104. in_path = os.path.abspath(args.pop(0))
  105. if len(args) == 0:
  106. parser.error("Missing: OUT_FILE")
  107. out_path = os.path.abspath(args.pop(0))
  108. if len(args) > 0:
  109. parser.error("Unexpected: %r" % (args,))
  110. password = getpass.getpass()
  111. with open(in_path, "rb") as in_fh:
  112. data = awallet_extract(in_fh.read(), password)
  113. with open(out_path, "wb") as out_fh:
  114. out_fh.write(data)
  115. if __name__ == "__main__":
  116. sys.exit(main(sys.argv[1:]))