]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Key.pm
Remove objects from deleted objects when added
[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 = '999.999'; # VERSION
18
19 fieldhashes \my %SAFE;
20
21 =method new
22
23 $key = File::KDBX::Key->new({ password => $password });
24 $key = File::KDBX::Key->new($password);
25
26 $key = File::KDBX::Key->new({ file => $filepath });
27 $key = File::KDBX::Key->new(\$file);
28 $key = File::KDBX::Key->new(\*FILE);
29
30 $key = File::KDBX::Key->new({ composite => [...] });
31 $key = File::KDBX::Key->new([...]); # composite key
32
33 $key = File::KDBX::Key->new({ responder => \&responder });
34 $key = File::KDBX::Key->new(\&responder); # challenge-response key
35
36 Construct a new key.
37
38 The primitive used to construct the key is not saved but is immediately converted to a raw encryption key (see
39 L</raw_key>).
40
41 A L<File::KDBX::Key::Composite> is somewhat special in that it does retain a reference to its component keys,
42 and its raw key is calculated from its components on demand.
43
44 =cut
45
46 sub new {
47 my $class = shift;
48 my %args = @_ % 2 == 1 ? (primitive => shift, @_) : @_;
49
50 my $primitive = $args{primitive};
51 delete $args{primitive} if !$args{keep_primitive};
52 return $primitive->hide if blessed $primitive && $primitive->isa($class);
53
54 my $self = bless \%args, $class;
55 return $self->init($primitive) if defined $primitive;
56 return $self;
57 }
58
59 sub DESTROY {
60 local ($., $@, $!, $^E, $?);
61 !in_global_destruction and do { $_[0]->_clear_raw_key; eval { erase \$_[0]->{primitive} } }
62 }
63
64 =method init
65
66 $key = $key->init($primitive);
67
68 Initialize a L<File::KDBX::Key> with a new primitive. Returns itself to allow method chaining.
69
70 =cut
71
72 sub init {
73 my $self = shift;
74 my $primitive = shift // throw 'Missing key primitive';
75
76 my $pkg;
77
78 if (is_arrayref($primitive)) {
79 $pkg = __PACKAGE__.'::Composite';
80 }
81 elsif (is_scalarref($primitive) || openhandle($primitive)) {
82 $pkg = __PACKAGE__.'::File';
83 }
84 elsif (is_coderef($primitive)) {
85 $pkg = __PACKAGE__.'::ChallengeResponse';
86 }
87 elsif (!is_ref($primitive)) {
88 $pkg = __PACKAGE__.'::Password';
89 }
90 elsif (is_hashref($primitive) && defined $primitive->{composite}) {
91 $pkg = __PACKAGE__.'::Composite';
92 $primitive = $primitive->{composite};
93 }
94 elsif (is_hashref($primitive) && defined $primitive->{password}) {
95 $pkg = __PACKAGE__.'::Password';
96 $primitive = $primitive->{password};
97 }
98 elsif (is_hashref($primitive) && defined $primitive->{file}) {
99 $pkg = __PACKAGE__.'::File';
100 $primitive = $primitive->{file};
101 }
102 elsif (is_hashref($primitive) && defined $primitive->{responder}) {
103 $pkg = __PACKAGE__.'::ChallengeResponse';
104 $primitive = $primitive->{responder};
105 }
106 else {
107 throw 'Invalid key primitive', primitive => $primitive;
108 }
109
110 load $pkg;
111 bless $self, $pkg;
112 return $self->init($primitive);
113 }
114
115 =method reload
116
117 $key = $key->reload;
118
119 Reload a key by re-reading the key source and recalculating the raw key. Returns itself to allow method
120 chaining.
121
122 =cut
123
124 sub reload { $_[0] }
125
126 =method raw_key
127
128 $raw_key = $key->raw_key;
129 $raw_key = $key->raw_key($challenge);
130
131 Get the raw encryption key. This is calculated based on the primitive(s). The C<$challenge> argument is for
132 challenge-response type keys and is ignored by other types.
133
134 B<NOTE:> The raw key is sensitive information and so is memory-protected while not being accessed. If you
135 access it, you should memzero or L<File::KDBX::Util/erase> it when you're done.
136
137 =cut
138
139 sub raw_key {
140 my $self = shift;
141 return $self->{raw_key} if !$self->is_hidden;
142 return $self->_safe->peek(\$self->{raw_key});
143 }
144
145 sub _set_raw_key {
146 my $self = shift;
147 $self->_clear_raw_key;
148 $self->{raw_key} = shift; # after clear
149 $self->_new_safe->add(\$self->{raw_key}); # auto-hide
150 }
151
152 sub _clear_raw_key {
153 my $self = shift;
154 my $safe = $self->_safe;
155 $safe->clear if $safe;
156 erase \$self->{raw_key};
157 }
158
159 =method hide
160
161 $key = $key->hide;
162
163 Put the raw key in L<File::KDBX/"Memory Protection">. Does nothing if the raw key is already in memory
164 protection. Returns itself to allow method chaining.
165
166 =cut
167
168 sub hide {
169 my $self = shift;
170 $self->_new_safe->add(\$self->{raw_key}) if defined $self->{raw_key};
171 return $self;
172 }
173
174 =method show
175
176 $key = $key->show;
177
178 Bring the raw key out of memory protection. Does nothing if the raw key is already out of memory protection.
179 Returns itself to allow method chaining.
180
181 =cut
182
183 sub show {
184 my $self = shift;
185 my $safe = $self->_safe;
186 $safe->unlock if $safe;
187 return $self;
188 }
189
190 =method is_hidden
191
192 $bool = $key->is_hidden;
193
194 Get whether or not the key's raw secret is currently in memory protection.
195
196 =cut
197
198 sub is_hidden { !!$SAFE{$_[0]} }
199
200 sub _safe { $SAFE{$_[0]} }
201 sub _new_safe { $SAFE{$_[0]} = File::KDBX::Safe->new }
202
203 1;
204 __END__
205
206 =head1 DESCRIPTION
207
208 A master key is one or more credentials that can protect a KDBX database. When you encrypt a database with
209 a master key, you will need the master key to decrypt it. B<Keep your master key safe!> If someone gains
210 access to your master key, they can open your database. If you forget or lose any part of your master key, all
211 data in the database is lost.
212
213 There are several different types of keys, each implemented as a subclass:
214
215 =for :list
216 * L<File::KDBX::Key::Password> - Password or passphrase, knowledge of a string of characters
217 * L<File::KDBX::Key::File> - Possession of a file ("key file") with a secret.
218 * L<File::KDBX::Key::ChallengeResponse> - Possession of a device that responds correctly when challenged
219 * L<File::KDBX::Key::YubiKey> - Possession of a YubiKey hardware device (a type of challenge-response)
220 * L<File::KDBX::Key::Composite> - One or more keys combined as one
221
222 A good master key is produced from a high amount of "entropy" (unpredictability). The more entropy the better.
223 Combining multiple keys into a B<Composite> key combines the entropy of each individual key. For example, if
224 you have a weak password and you combine it with other keys, the composite key is stronger than the weak
225 password key by itself. (Of course it's much better to not have any weak components of your master key.)
226
227 B<COMPATIBILITY NOTE:> Most KeePass implementations are limited in the types and numbers of keys they support.
228 B<Password> keys are pretty much universally supported. B<File> keys are pretty well-supported. Many do not
229 support challenge-response keys. If you are concerned about compatibility, you should stick with one of these
230 configurations:
231
232 =for :list
233 * One password
234 * One key file
235 * One password and one key file
236
237 =cut
This page took 0.049661 seconds and 4 git commands to generate.