Class: Keepassx::Header

Inherits:
Object
  • Object
show all
Defined in:
lib/keepassx/header.rb

Constant Summary collapse

ENCRYPTION_FLAGS =
[
  [1, 'SHA2'],
  [2, 'Rijndael'],
  [2, 'AES'],
  [4, 'ArcFour'],
  [8, 'TwoFish'],
].freeze
SIGNATURES =
[0x9AA2D903, 0xB54BFB65].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(header_bytes = nil) ⇒ Header

rubocop:disable Metrics/MethodLength



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
# File 'lib/keepassx/header.rb', line 58

def initialize(header_bytes = nil)
  if header_bytes.nil?
    @signature1    = SIGNATURES[0]
    @signature2    = SIGNATURES[1]
    @flags         = 3 # SHA2 hashing, AES encryption
    @version       = 0x30002
    @master_seed   = SecureRandom.random_bytes(16)
    @encryption_iv = SecureRandom.random_bytes(16)
    @groups_count  = 0
    @entries_count = 0
    @master_seed2  = SecureRandom.random_bytes(32)
    @rounds        = 50_000
  else
    header_bytes   = StringIO.new(header_bytes)
    @signature1    = header_bytes.read(4).unpack('L*').first
    @signature2    = header_bytes.read(4).unpack('L*').first
    @flags         = header_bytes.read(4).unpack('L*').first
    @version       = header_bytes.read(4).unpack('L*').first
    @master_seed   = header_bytes.read(16)
    @encryption_iv = header_bytes.read(16)
    @groups_count  = header_bytes.read(4).unpack('L*').first
    @entries_count = header_bytes.read(4).unpack('L*').first
    @content_hash  = header_bytes.read(32)
    @master_seed2  = header_bytes.read(32)
    @rounds        = header_bytes.read(4).unpack('L*').first
  end
end

Instance Attribute Details

#content_hashObject

Returns the value of attribute content_hash.



54
55
56
# File 'lib/keepassx/header.rb', line 54

def content_hash
  @content_hash
end

#encryption_ivObject (readonly)

Returns the value of attribute encryption_iv.



51
52
53
# File 'lib/keepassx/header.rb', line 51

def encryption_iv
  @encryption_iv
end

#entries_countObject

Returns the value of attribute entries_count.



53
54
55
# File 'lib/keepassx/header.rb', line 53

def entries_count
  @entries_count
end

#groups_countObject

Returns the value of attribute groups_count.



52
53
54
# File 'lib/keepassx/header.rb', line 52

def groups_count
  @groups_count
end

Instance Method Details

#encodeString

Return encoded header

Returns:

  • (String)

    Encoded header representation.



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/keepassx/header.rb', line 129

def encode
  [@signature1].pack('L*')      <<
    [@signature2].pack('L*')    <<
    [@flags].pack('L*')         <<
    [@version].pack('L*')       <<
    @master_seed                <<
    @encryption_iv              <<
    [@groups_count].pack('L*')  <<
    [@entries_count].pack('L*') <<
    @content_hash               <<
    @master_seed2               <<
    [@rounds].pack('L*')
end

#encryption_typeObject



93
94
95
96
97
98
# File 'lib/keepassx/header.rb', line 93

def encryption_type
  ENCRYPTION_FLAGS.each do |(flag_mask, encryption_type)|
    return encryption_type if @flags & flag_mask
  end
  'Unknown'
end

#final_key(master_key, keyfile_data = nil) ⇒ Object

rubocop:disable Metrics/MethodLength



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/keepassx/header.rb', line 102

def final_key(master_key, keyfile_data = nil)
  key = Digest::SHA2.new.update(master_key).digest

  if keyfile_data
    keyfile_hash = extract_keyfile_hash(keyfile_data)
    key = master_key == '' ? keyfile_hash : Digest::SHA2.new.update(key + keyfile_hash).digest
  end

  aes = OpenSSL::Cipher.new('AES-256-ECB')
  aes.encrypt
  aes.key = @master_seed2
  aes.padding = 0

  @rounds.times do
    key = aes.update(key) + aes.final
  end

  key = Digest::SHA2.new.update(key).digest
  key = Digest::SHA2.new.update(@master_seed + key).digest
  key
end

#valid?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/keepassx/header.rb', line 88

def valid?
  @signature1 == SIGNATURES[0] && @signature2 == SIGNATURES[1]
end