Thu, 07 Apr 2022 18:11:33 +0100
Initial commit
0 | 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"); |