File Coverage

blib/lib/File/KeePass/KDBX/Tie/Header.pm
Criterion Covered Total %
statement 26 26 100.0
branch n/a
condition n/a
subroutine 10 10 100.0
pod 0 2 0.0
total 36 38 94.7


line stmt bran cond sub pod time code
1             package File::KeePass::KDBX::Tie::Header;
2             # ABSTRACT: Database headers
3              
4 2     2   968 use warnings;
  2         4  
  2         55  
5 2     2   10 use strict;
  2         2  
  2         51  
6              
7 2     2   9 use File::KDBX::Constants qw(:magic :version :cipher :random_stream);
  2         3  
  2         349  
8 2     2   12 use File::KDBX::Util qw(snakify);
  2         11  
  2         97  
9 2     2   14 use File::KeePass::KDBX;
  2         3  
  2         36  
10 2     2   9 use Time::Piece;
  2         3  
  2         27  
11 2     2   159 use namespace::clean;
  2         18  
  2         25  
12              
13 2     2   766 use parent 'File::KeePass::KDBX::Tie::Hash';
  2         4  
  2         8  
14              
15             our $VERSION = '0.902'; # VERSION
16              
17             my @META_FIELDS = qw(
18             Generator
19             HeaderHash
20             DatabaseName
21             DatabaseNameChanged
22             DatabaseDescription
23             DatabaseDescriptionChanged
24             DefaultUserName
25             DefaultUserNameChanged
26             MaintenanceHistoryDays
27             Color
28             MasterKeyChanged
29             MasterKeyChangeRec
30             MasterKeyChangeForce
31             RecycleBinUUID
32             RecycleBinChanged
33             EntryTemplatesGroup
34             EntryTemplatesGroupChanged
35             LastSelectedGroup
36             LastTopVisibleGroup
37             HistoryMaxItems
38             HistoryMaxSize
39             SettingsChanged
40             RecycleBinEnabled
41             );
42             # MemoryProtection - flattened in KeePass
43             # Binaries - distributed to entries in KeePass
44             # CustomIcons - handled by File::KeePass::KDBX::Tie::CustomIcons
45             # CustomData - handled by File::KeePass::KDBX::Tie::CustomData
46              
47             my %GET = (
48             sig1 => sub { $_[0]->sig1 },
49             sig2 => sub { $_[0]->sig2 },
50             ver => sub { $_[0]->version },
51             version => sub { $_[0]->sig2 == KDBX_SIG2_1 ? 1 : 2 },
52             comment => sub { $_[0]->comment },
53             enc_iv => sub { $_[0]->encryption_iv },
54             enc_type => sub {
55             my %enc_type = (
56             CIPHER_UUID_AES128() => 'rijndael',
57             CIPHER_UUID_AES256() => 'rijndael',
58             CIPHER_UUID_CHACHA20() => 'chacha20',
59             CIPHER_UUID_SALSA20() => 'salsa20',
60             CIPHER_UUID_SERPENT() => 'serpent',
61             CIPHER_UUID_TWOFISH() => 'twofish',
62             );
63             $enc_type{$_[0]->cipher_id || ''} || 'rijndael';
64             },
65             flags => sub {
66             my $cipher_id = $_[0]->cipher_id || '';
67             $cipher_id eq CIPHER_UUID_AES128 || $cipher_id eq CIPHER_UUID_AES256
68             ? 2
69             : $cipher_id eq CIPHER_UUID_TWOFISH
70             ? 8
71             : undef;
72             },
73             checksum => sub { undef },
74             n_entries => sub { $_[0]->entries->size },
75             n_groups => sub { $_[0]->groups->size - ($_[0]->_has_implicit_root ? 1 : 0) },
76             header_size => sub { undef }, # not available from KDBX
77             raw => sub { undef }, # not available from KDBX
78             rounds => sub { $_[0]->transform_rounds },
79             seed_key => sub { $_[0]->transform_seed },
80             seed_rand => sub { $_[0]->master_seed },
81             cipher => sub {
82             my %cipher = (
83             CIPHER_UUID_AES128() => 'aes',
84             CIPHER_UUID_AES256() => 'aes',
85             CIPHER_UUID_CHACHA20() => 'chacha20',
86             CIPHER_UUID_SALSA20() => 'salsa20',
87             CIPHER_UUID_SERPENT() => 'serpent',
88             CIPHER_UUID_TWOFISH() => 'twofish',
89             );
90             $cipher{$_[0]->cipher_id || ''} || 'aes';
91             },
92             compression => sub { $_[0]->compression_flags },
93             protected_stream => sub {
94             my %protected_stream = (
95             STREAM_ID_RC4_VARIANT() => 'rc4variant',
96             STREAM_ID_SALSA20() => 'salsa20',
97             STREAM_ID_CHACHA20() => 'chacha20',
98             );
99             $protected_stream{$_[0]->inner_random_stream_id} || 'unknown',
100             },
101             protected_stream_key => sub { $_[0]->inner_random_stream_key },
102             start_bytes => sub { $_[0]->stream_start_bytes },
103             0 => sub { "\r\n\r\n" },
104             # META
105             protect_notes => sub { $_[0]->protect_notes ? 1 : 0 },
106             protect_password => sub { $_[0]->protect_password ? 1 : 0 },
107             protect_title => sub { $_[0]->protect_title ? 1 : 0 },
108             protect_url => sub { $_[0]->protect_url ? 1 : 0 },
109             protect_username => sub { $_[0]->protect_username ? 1 : 0 },
110             recycle_bin_enabled => sub { $_[0]->recycle_bin_enabled ? 1 : 0 },
111             custom_data => sub { $_[-1]->_tie({}, 'CustomData', $_[0]) },
112             custom_icons => sub { $_[-1]->_tie({}, 'CustomIcons', $_[0]) },
113             );
114             for my $meta_key (@META_FIELDS) {
115             my $key = snakify($meta_key);
116             next if $GET{$key};
117             if ($key =~ /_changed$/) {
118             $GET{$key} = sub { File::KeePass::KDBX::_decode_datetime($_[0]->meta->{$key}) };
119             }
120             else {
121             $GET{$key} = sub { $_[0]->meta->{$key} };
122             }
123             }
124              
125             my %SET = (
126             sig1 => sub { $_[0]->sig1($_) },
127             sig2 => sub { $_[0]->sig2($_) },
128             ver => sub { $_[0]->version($_) },
129             version => sub { $_[0]->sig2($_ == 1 ? KDBX_SIG2_1 : $_ == 2 ? KDBX_SIG2_2 : undef) },
130             comment => sub { $_[0]->comment($_) },
131             enc_iv => sub { $_[0]->encryption_iv($_) },
132             enc_type => sub {
133             my %enc_type = (
134             chacha20 => CIPHER_UUID_CHACHA20,
135             rijndael => CIPHER_UUID_AES256,
136             salsa20 => CIPHER_UUID_SALSA20,
137             serpent => CIPHER_UUID_SERPENT,
138             twofish => CIPHER_UUID_TWOFISH,
139             );
140             $_[0]->cipher_id($enc_type{$_} || CIPHER_UUID_AES256);
141             },
142             flags => sub {
143             my %cipher = (
144             2 => CIPHER_UUID_AES256,
145             8 => CIPHER_UUID_TWOFISH,
146             );
147             $_[0]->cipher_id($cipher{$_}) if $cipher{$_};
148             },
149             checksum => sub { }, # readonly
150             n_entries => sub { }, # readonly
151             n_groups => sub { }, # readonly
152             header_size => sub { }, # not available
153             raw => sub { }, # not available
154             rounds => sub { $_[0]->transform_rounds($_) },
155             seed_key => sub { $_[0]->transform_seed($_) },
156             seed_rand => sub { $_[0]->master_seed($_) },
157             cipher => sub {
158             my %cipher = (
159             aes => CIPHER_UUID_AES256,
160             chacha20 => CIPHER_UUID_CHACHA20,
161             salsa20 => CIPHER_UUID_SALSA20,
162             serpent => CIPHER_UUID_SERPENT,
163             twofish => CIPHER_UUID_TWOFISH,
164             );
165             $_[0]->cipher_id($cipher{$_} || CIPHER_UUID_AES256);
166             },
167             compression => sub { $_[0]->compression_flags($_) },
168             protected_stream => sub {
169             my %protected_stream = (
170             rc4variant => STREAM_ID_RC4_VARIANT,
171             salsa20 => STREAM_ID_SALSA20,
172             chacha20 => STREAM_ID_CHACHA20,
173             );
174             my $default_id = $_[0]->version < KDBX_VERSION_4_0 ? STREAM_ID_SALSA20 : STREAM_ID_CHACHA20;
175             my $id = $protected_stream{$_} || $default_id;
176             $_[0]->inner_random_stream_id($id);
177             },
178             protected_stream_key => sub { $_[0]->inner_random_stream_key($_) },
179             start_bytes => sub { $_[0]->stream_start_bytes($_) },
180             0 => sub { }, # readonly
181             # META
182             protect_notes => sub { $_[0]->protect_notes($_) },
183             protect_password => sub { $_[0]->protect_password($_) },
184             protect_title => sub { $_[0]->protect_title($_) },
185             protect_url => sub { $_[0]->protect_url($_) },
186             protect_username => sub { $_[0]->protect_username($_) },
187             recycle_bin_enabled => sub { $_[0]->recycle_bin_enabled($_) },
188             custom_data => sub { }, # TODO - Replace all custom data
189             custom_icons => sub { }, # TODO - Replace all icons
190             );
191             for my $meta_key (@META_FIELDS) {
192             my $key = snakify($meta_key);
193             next if $SET{$key};
194             if ($key =~ /_changed$/) {
195             $SET{$key} = sub { $_[0]->meta->{$key} = File::KeePass::KDBX::_encode_datetime($_) };
196             }
197             else {
198             $SET{$key} = sub { $_[0]->meta->{$key} = $_ };
199             }
200             }
201              
202 630     630 0 1549 sub getters { \%GET }
203 17     17 0 71 sub setters { \%SET }
204              
205             1;
206              
207             __END__