-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparse-keyring-file.rb
113 lines (98 loc) · 2.62 KB
/
parse-keyring-file.rb
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
#!/usr/bin/env ruby
# Try to parse the binary keyring
# Purpose: provide a simple CLI client such like pwsafe
# for gnome-keyring without and gtk/dbus dependencies
# encoding: utf-8
f = File.open("/home/feitel/.gnome2/keyrings/login.keyring", "r:binary").read
class Parser
attr_reader :data, :pos
def initialize string
@data = string
@pos = 0
end
def string bytes
byte bytes
end
def unsigned bytes
a = byte(bytes)
y = 0
a.each_byte do |x| y = (y<<8 | x) end
return y
end
def lstring
l = unsigned32
return string(l)
end
def unsigned32 count=1
unsigned(count * 4)
end
def byte bytes=1
# FIXME not every char has only one byte
r = @data[@pos,bytes]
@pos += bytes
return r
end
def time
return Time.at(unsigned32(2))
end
def hash
byte(16)
end
end
require "pp"
@r = Hash.new
def g value, name
@r[name] = value
pp "#{name} -- #{value}"
value
end
p = Parser.new(f)
g(p.string(16), :intro) == "GnomeKeyring\n\r\0\n" or raise "incorrect intro"
g(p.byte(2), :version)
g(p.byte, :crypto)
g(p.byte, :hash)
g(p.lstring, :keying_name)
g(p.time, :ctime)
g(p.time, :mtime)
g(p.unsigned32, :flags)
g(p.unsigned32, :lock_timeout)
hash_iterations = g(p.unsigned32, :hash_iterations)
salt = g(p.byte(8), :salt)
g(p.unsigned(4*4), :reserved) == 0 or raise "reserved not zero"
num_items = g(p.unsigned32, :num_items)
num_items.times do |x|
g(p.unsigned32, "item#{x}_id".to_sym)
# TODO type?
g(p.unsigned32, "item#{x}_type".to_sym)
attr_num = g(p.unsigned32, "item#{x}_num_attributes".to_sym)
attr_num.times do |y|
g(p.lstring , "item#{x}_attribute#{x}_name".to_sym)
attr_type = g(p.unsigned32, "item#{x}_attribute#{x}_type".to_sym)
case attr_type
# TODO correct??
when 0: g(p.lstring, "item#{x}_attribute#{x}_hash".to_sym)
when 1: g(p.unsigned32, "item#{x}_attribute#{x}_hash".to_sym)
else raise "unknown type #{attr_type}"
end
end
end
num_encrypted_bytes = g(p.unsigned32 , :num_encrypted_bytes)
encrypted_bytes = g(p.byte(num_encrypted_bytes) , :encrytped_hash)
# FIXME not every char has only one byte
raise "invalid length" unless p.pos == p.data.size
password = ""
# TODO all following doesn't work
# I have to ask on Mailing-List for more informations
require 'openssl'
require 'digest/sha1'
c = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
c.decrypt
k = password + salt
hash_iterations.times do
k = Digest::SHA256.hexdigest(k)
end
c.key = k[0..31]
c.iv = k[32..63]
d = c.update(encrypted_bytes)
d << c.final
puts "decrypted: #{d}\n"