]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Key.pm
c7bb2b33053916bb3e1c805e3f68a3affa3a620e
[chaz/p5-File-KDBX] / lib / File / KDBX / Key.pm
1 package File::KDBX::Key;
2 # ABSTRACT: A credential that can protect a KDBX file
3
4 use warnings;
5 use strict;
6
7 use Devel::GlobalDestruction;
8 use File::KDBX::Error;
9 use File::KDBX::Safe;
10 use File::KDBX::Util qw(erase);
11 use Hash::Util::FieldHash qw(fieldhashes);
12 use Module::Load;
13 use Ref::Util qw(is_arrayref is_coderef is_hashref is_ref is_scalarref);
14 use Scalar::Util qw(blessed openhandle);
15 use namespace::clean;
16
17 our $VERSION = '0.800'; # VERSION
18
19 fieldhashes \my %SAFE;
20
21
22 sub new {
23 my $class = shift;
24 my %args = @_ % 2 == 1 ? (primitive => shift, @_) : @_;
25
26 my $primitive = $args{primitive};
27 delete $args{primitive} if !$args{keep_primitive};
28 return $primitive->hide if blessed $primitive && $primitive->isa($class);
29
30 my $self = bless \%args, $class;
31 return $self->init($primitive) if defined $primitive;
32 return $self;
33 }
34
35 sub DESTROY {
36 local ($., $@, $!, $^E, $?);
37 !in_global_destruction and do { $_[0]->_clear_raw_key; eval { erase \$_[0]->{primitive} } }
38 }
39
40
41 sub init {
42 my $self = shift;
43 my $primitive = shift // throw 'Missing key primitive';
44
45 my $pkg;
46
47 if (is_arrayref($primitive)) {
48 $pkg = __PACKAGE__.'::Composite';
49 }
50 elsif (is_scalarref($primitive) || openhandle($primitive)) {
51 $pkg = __PACKAGE__.'::File';
52 }
53 elsif (is_coderef($primitive)) {
54 $pkg = __PACKAGE__.'::ChallengeResponse';
55 }
56 elsif (!is_ref($primitive)) {
57 $pkg = __PACKAGE__.'::Password';
58 }
59 elsif (is_hashref($primitive) && defined $primitive->{composite}) {
60 $pkg = __PACKAGE__.'::Composite';
61 $primitive = $primitive->{composite};
62 }
63 elsif (is_hashref($primitive) && defined $primitive->{password}) {
64 $pkg = __PACKAGE__.'::Password';
65 $primitive = $primitive->{password};
66 }
67 elsif (is_hashref($primitive) && defined $primitive->{file}) {
68 $pkg = __PACKAGE__.'::File';
69 $primitive = $primitive->{file};
70 }
71 elsif (is_hashref($primitive) && defined $primitive->{responder}) {
72 $pkg = __PACKAGE__.'::ChallengeResponse';
73 $primitive = $primitive->{responder};
74 }
75 else {
76 throw 'Invalid key primitive', primitive => $primitive;
77 }
78
79 load $pkg;
80 bless $self, $pkg;
81 return $self->init($primitive);
82 }
83
84
85 sub reload { $_[0] }
86
87
88 sub raw_key {
89 my $self = shift;
90 return $self->{raw_key} if !$self->is_hidden;
91 return $self->_safe->peek(\$self->{raw_key});
92 }
93
94 sub _set_raw_key {
95 my $self = shift;
96 $self->_clear_raw_key;
97 $self->{raw_key} = shift; # after clear
98 $self->_new_safe->add(\$self->{raw_key}); # auto-hide
99 }
100
101 sub _clear_raw_key {
102 my $self = shift;
103 my $safe = $self->_safe;
104 $safe->clear if $safe;
105 erase \$self->{raw_key};
106 }
107
108
109 sub hide {
110 my $self = shift;
111 $self->_new_safe->add(\$self->{raw_key}) if defined $self->{raw_key};
112 return $self;
113 }
114
115
116 sub show {
117 my $self = shift;
118 my $safe = $self->_safe;
119 $safe->unlock if $safe;
120 return $self;
121 }
122
123
124 sub is_hidden { !!$SAFE{$_[0]} }
125
126 sub _safe { $SAFE{$_[0]} }
127 sub _new_safe { $SAFE{$_[0]} = File::KDBX::Safe->new }
128
129 1;
130
131 __END__
132
133 =pod
134
135 =encoding UTF-8
136
137 =head1 NAME
138
139 File::KDBX::Key - A credential that can protect a KDBX file
140
141 =head1 VERSION
142
143 version 0.800
144
145 =head1 DESCRIPTION
146
147 A master key is one or more credentials that can protect a KDBX database. When you encrypt a database with
148 a master key, you will need the master key to decrypt it. B<Keep your master key safe!> If someone gains
149 access to your master key, they can open your database. If you forget or lose any part of your master key, all
150 data in the database is lost.
151
152 There are several different types of keys, each implemented as a subclass:
153
154 =over 4
155
156 =item *
157
158 L<File::KDBX::Key::Password> - Password or passphrase, knowledge of a string of characters
159
160 =item *
161
162 L<File::KDBX::Key::File> - Possession of a file ("key file") with a secret.
163
164 =item *
165
166 L<File::KDBX::Key::ChallengeResponse> - Possession of a device that responds correctly when challenged
167
168 =item *
169
170 L<File::KDBX::Key::YubiKey> - Possession of a YubiKey hardware device (a type of challenge-response)
171
172 =item *
173
174 L<File::KDBX::Key::Composite> - One or more keys combined as one
175
176 =back
177
178 A good master key is produced from a high amount of "entropy" (unpredictability). The more entropy the better.
179 Combining multiple keys into a B<Composite> key combines the entropy of each individual key. For example, if
180 you have a weak password and you combine it with other keys, the composite key is stronger than the weak
181 password key by itself. (Of course it's much better to not have any weak components of your master key.)
182
183 B<COMPATIBILITY NOTE:> Most KeePass implementations are limited in the types and numbers of keys they support.
184 B<Password> keys are pretty much universally supported. B<File> keys are pretty well-supported. Many do not
185 support challenge-response keys. If you are concerned about compatibility, you should stick with one of these
186 configurations:
187
188 =over 4
189
190 =item *
191
192 One password
193
194 =item *
195
196 One key file
197
198 =item *
199
200 One password and one key file
201
202 =back
203
204 =head1 METHODS
205
206 =head2 new
207
208 $key = File::KDBX::Key->new({ password => $password });
209 $key = File::KDBX::Key->new($password);
210
211 $key = File::KDBX::Key->new({ file => $filepath });
212 $key = File::KDBX::Key->new(\$file);
213 $key = File::KDBX::Key->new(\*FILE);
214
215 $key = File::KDBX::Key->new({ composite => [...] });
216 $key = File::KDBX::Key->new([...]); # composite key
217
218 $key = File::KDBX::Key->new({ responder => \&responder });
219 $key = File::KDBX::Key->new(\&responder); # challenge-response key
220
221 Construct a new key.
222
223 The primitive used to construct the key is not saved but is immediately converted to a raw encryption key (see
224 L</raw_key>).
225
226 A L<File::KDBX::Key::Composite> is somewhat special in that it does retain a reference to its component keys,
227 and its raw key is calculated from its components on demand.
228
229 =head2 init
230
231 $key = $key->init($primitive);
232
233 Initialize a L<File::KDBX::Key> with a new primitive. Returns itself to allow method chaining.
234
235 =head2 reload
236
237 $key = $key->reload;
238
239 Reload a key by re-reading the key source and recalculating the raw key. Returns itself to allow method
240 chaining.
241
242 =head2 raw_key
243
244 $raw_key = $key->raw_key;
245 $raw_key = $key->raw_key($challenge);
246
247 Get the raw encryption key. This is calculated based on the primitive(s). The C<$challenge> argument is for
248 challenge-response type keys and is ignored by other types.
249
250 B<NOTE:> The raw key is sensitive information and so is memory-protected while not being accessed. If you
251 access it, you should memzero or L<File::KDBX::Util/erase> it when you're done.
252
253 =head2 hide
254
255 $key = $key->hide;
256
257 Put the raw key in L<File::KDBX/"Memory Protection">. Does nothing if the raw key is already in memory
258 protection. Returns itself to allow method chaining.
259
260 =head2 show
261
262 $key = $key->show;
263
264 Bring the raw key out of memory protection. Does nothing if the raw key is already out of memory protection.
265 Returns itself to allow method chaining.
266
267 =head2 is_hidden
268
269 $bool = $key->is_hidden;
270
271 Get whether or not the key's raw secret is currently in memory protection.
272
273 =head1 BUGS
274
275 Please report any bugs or feature requests on the bugtracker website
276 L<https://github.com/chazmcgarvey/File-KDBX/issues>
277
278 When submitting a bug or request, please include a test-file or a
279 patch to an existing test-file that illustrates the bug or desired
280 feature.
281
282 =head1 AUTHOR
283
284 Charles McGarvey <ccm@cpan.org>
285
286 =head1 COPYRIGHT AND LICENSE
287
288 This software is copyright (c) 2022 by Charles McGarvey.
289
290 This is free software; you can redistribute it and/or modify it under
291 the same terms as the Perl 5 programming language system itself.
292
293 =cut
This page took 0.046788 seconds and 3 git commands to generate.