main.lua

changeset 0
89e39cd5a7cd
equal deleted inserted replaced
-1:000000000000 0:89e39cd5a7cd
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");

mercurial