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