-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathkwallet2john.py
133 lines (116 loc) · 4.33 KB
/
kwallet2john.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python
# This software is Copyright (c) 2014, Sanju Kholia <sanju.kholia at gmail.com>
# and it is hereby released to the general public under the following terms:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.
#
# "kde-runtime/kwalletd/backend/kwalletbackend.cc" file is authoritative.
#
# Use gdb -p `pidof kwalletd5` and "break gcry_kdf_derive" to debug this code.
import sys
import os
import struct
from binascii import hexlify
KWMAGIC = "KWALLET\n\r\0\r\n"
KWMAGIC_LEN = 12
KWALLET_VERSION_MAJOR = 0
KWALLET_VERSION_MINOR = 0
KWALLET_CIPHER_BLOWFISH_ECB = 0 # this was the old KWALLET_CIPHER_BLOWFISH_CBC
KWALLET_CIPHER_3DES_CBC = 1
KWALLET_CIPHER_GPG = 2
KWALLET_CIPHER_BLOWFISH_CBC = 3
KWALLET_HASH_SHA1 = 0
KWALLET_HASH_MD5 = 1 # unsupported (even upstream)
KWALLET_HASH_PBKDF2_SHA512 = 2 # used when using kwallet with pam or since 4.13 version
N = 128
PBKDF2_SHA512_KEYSIZE = 56
PBKDF2_SHA512_SALTSIZE = 56
PBKDF2_SHA512_ITERATIONS = 50000
def process_file(filename):
offset = 0
new_version = False # PBKDF2-HMAC-SHA512 if True
kwallet_minor_version = -1
try:
fd = open(filename, "rb")
except IOError:
e = sys.exc_info()[1]
sys.stderr.write("%s\n" % str(e))
return
# TOCTOU but who cares, right? ;)
size = os.stat(filename).st_size
buf = fd.read(KWMAGIC_LEN)
if buf != KWMAGIC:
sys.stderr.write("%s : Not a KDE KWallet file!\n" % filename)
return
offset += KWMAGIC_LEN
buf = bytearray(fd.read(4))
offset += 4
# First byte is major version, second byte is minor version
if buf[0] != KWALLET_VERSION_MAJOR:
sys.stderr.write("%s : Unknown major version!\n" % filename)
return
# 0 has been the MINOR version until 4.13, from that point we use it to
# upgrade the hash
#
# See runtime/kwalletd/backend/backendpersisthandler.cpp for details
if buf[1] != 0: # Old KWALLET_VERSION_MINOR
if buf[1] != 1: # New KWALLET_VERSION_MINOR
sys.stderr.write("%s : Unknown minor version!\n" % filename)
return
new_version = True
kwallet_minor_version = buf[1]
if buf[2] != KWALLET_CIPHER_BLOWFISH_ECB and buf[2] != KWALLET_CIPHER_BLOWFISH_CBC:
sys.stderr.write("%s : Unsupported cipher <%d>\n" % (filename, buf[2]))
return
if buf[3] != KWALLET_HASH_SHA1 and buf[3] != KWALLET_HASH_PBKDF2_SHA512:
sys.stderr.write("%s : Unsupported hash <%d>\n" % (filename, buf[3]))
return
# Read in the hashes
buf = fd.read(4)
n = struct.unpack("> I", buf)[0]
if n > 0xffff:
sys.stderr.write("%s : sanity check failed!\n" % filename)
sys.exit(6)
offset += 4
for i in range(0, n):
buf = fd.read(16)
offset += 16
buf = fd.read(4) # read 4 bytes more
fsz = struct.unpack("> I", buf)[0]
offset += 4
for j in range(0, fsz):
fd.read(16)
offset += 16
# Read in the rest of the file
encrypted_size = size - offset
encrypted = fd.read(encrypted_size)
encrypted_size = len(encrypted)
if encrypted_size % 8 != 0:
sys.stderr.write("%s : invalid file structure!\n", filename)
sys.exit(7)
if new_version:
# read salt
salt_filename = os.path.splitext(filename)[0] + ".salt"
try:
salt = open(salt_filename).read()
except:
sys.stderr.write("%s : unable to read salt from %s\n" % (filename, salt_filename))
sys.exit(8)
salt_len = len(salt)
iterations = PBKDF2_SHA512_ITERATIONS # is this fixed?
sys.stdout.write("%s:$kwallet$%ld$%s$%d$%d$%s$%s" %
(os.path.basename(filename), encrypted_size,
hexlify(encrypted), kwallet_minor_version, salt_len,
salt.encode("hex"), iterations))
sys.stdout.write(":::::%s\n" % filename)
else:
sys.stdout.write("%s:$kwallet$%ld$%s" % (os.path.basename(filename), encrypted_size, hexlify(encrypted)))
sys.stdout.write(":::::%s\n" % filename)
fd.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write("Usage: %s <.kwl file(s)>\n" % sys.argv[0])
sys.exit(-1)
for i in range(1, len(sys.argv)):
process_file(sys.argv[i])