|
1 #!/usr/bin/env lua5.3 |
|
2 |
|
3 local ciphers = require "openssl.cipher"; |
|
4 local kdf = require "openssl.kdf"; |
|
5 local zlib = require "zlib"; |
|
6 local sqlite = require "lsqlite3"; |
|
7 |
|
8 local dbuffer = require "util.dbuffer"; |
|
9 |
|
10 local file_password = assert(arg[2], "no password specified"); |
|
11 |
|
12 local input_filename = assert(arg[1], "no ceb file specified"); |
|
13 local db_filename = input_filename:gsub("%.ceb$", "")..".sqlite3"; |
|
14 |
|
15 local function read_header(f) |
|
16 local function read_int() |
|
17 return (">i4"):unpack(f:read(4)); |
|
18 end |
|
19 local function read_short() |
|
20 return (">i2"):unpack(f:read(2)); |
|
21 end |
|
22 local function read_long() |
|
23 return (">i8"):unpack(f:read(8)); |
|
24 end |
|
25 local function read_string() |
|
26 local n = read_short(); |
|
27 return f:read(n); |
|
28 end |
|
29 |
|
30 return { |
|
31 version = read_int(); |
|
32 app_id = read_string(); |
|
33 jid = read_string(); |
|
34 timestamp = math.floor(read_long()/1000); |
|
35 iv = f:read(12); |
|
36 salt = f:read(16); |
|
37 }; |
|
38 end |
|
39 |
|
40 local f = io.open(input_filename); |
|
41 |
|
42 local header = read_header(f); |
|
43 |
|
44 print("version", header.version); |
|
45 print("app", header.app_id); |
|
46 print("jid", header.jid); |
|
47 print("timestamp", os.date("%c", header.timestamp)); |
|
48 |
|
49 |
|
50 local function generate_key(password, salt) |
|
51 return kdf.derive({ |
|
52 type = "PBKDF2"; |
|
53 md = "sha1"; |
|
54 pass = password; |
|
55 salt = salt; |
|
56 iter = 1024; |
|
57 outlen = 128/8; |
|
58 }); |
|
59 end |
|
60 |
|
61 print("k", #(generate_key(file_password, header.salt))); |
|
62 |
|
63 local decryption_key = generate_key(file_password, header.salt); |
|
64 |
|
65 local cipher = ciphers.new("AES-128-GCM"):decrypt(decryption_key, header.iv); |
|
66 |
|
67 local decompress = zlib.inflate(); |
|
68 |
|
69 local db = sqlite.open(db_filename); |
|
70 do |
|
71 local db_tables = { |
|
72 [[create table accounts (uuid text primary key, username text, server text, password text, display_name text, status number, status_message text, rosterversion text, options number, avatar text, keys text, hostname text, port number, resource text)]]; |
|
73 [[create table conversations (uuid text, accountUuid text, name text, contactUuid text, contactJid text, created number, status number, mode number, attributes text)]]; |
|
74 [[create table messages (uuid text, conversationUuid text, timeSent number, counterpart text, trueCounterpart text, body text, encryption number, status number, type number, relativeFilePath text, serverMsgId text, axolotl_fingerprint text, carbon number, edited number, read number, oob number, errorMsg text, readByMarkers text, markable number, remoteMsgId text, deleted number, bodyLanguage text)]]; |
|
75 [[create table prekeys (account text, id text, key text)]]; |
|
76 [[create table signed_prekeys (account text, id text, key text)]]; |
|
77 [[create table sessions (account text, name text, device_id text, key text)]]; |
|
78 [[create table identities (account text, name text, ownkey text, fingerprint text, certificate text, trust number, active number, last_activation number, key text)]]; |
|
79 }; |
|
80 |
|
81 for _, query in ipairs(db_tables) do |
|
82 db:exec(query); |
|
83 end |
|
84 end |
|
85 |
|
86 local buffer = dbuffer.new(1024*1024, 128); |
|
87 |
|
88 repeat |
|
89 local enc_data = f:read(4096); |
|
90 if not enc_data then break; end |
|
91 |
|
92 local gz_data = assert(cipher:update(enc_data)); |
|
93 |
|
94 local status, data, eof = pcall(decompress, gz_data); |
|
95 if not status then |
|
96 print("EE: Failed to decompress: "..tostring(data)); |
|
97 return 1; |
|
98 end |
|
99 |
|
100 buffer:write(data); |
|
101 |
|
102 local line = buffer:read_until("\n"); |
|
103 local query_buffer; |
|
104 while line do |
|
105 local balanced_quotes = select(2, line:gsub("'", "%0")) % 2 == 0; |
|
106 |
|
107 if query_buffer then |
|
108 table.insert(query_buffer, line); |
|
109 if not balanced_quotes then |
|
110 db:exec(table.concat(query_buffer, "\n")); |
|
111 query_buffer = nil; |
|
112 end |
|
113 else |
|
114 if balanced_quotes then |
|
115 db:exec(line); |
|
116 else |
|
117 query_buffer = { line }; |
|
118 end |
|
119 end |
|
120 |
|
121 line = buffer:read_until("\n"); |
|
122 end |
|
123 until eof |
|
124 |
|
125 print("Done"); |