]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Key.pm
add initial WIP
[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 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 Encrypt the raw key for L<File::KDBX/"Memory Protection>. Returns itself to allow method chaining.
160
161 =cut
162
163 sub hide {
164 my $self = shift;
165 $self->_new_safe->add(\$self->{raw_key}) if defined $self->{raw_key};
166 return $self;
167 }
168
169 =method show
170
171 $key = $key->show;
172
173 Decrypt the raw key so it can be accessed. Returns itself to allow method chaining.
174
175 You normally don't need to call this because L</raw_key> calls this implicitly.
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 sub is_hidden { !!$SAFE{refaddr($_[0])} }
187
188 # sub show_scoped {
189 # my $self = shift;
190 # require Scope::Guard;
191 # $self-
192 # return
193 # }
194
195 sub _safe { $SAFE{refaddr($_[0])} }
196 sub _new_safe { $SAFE{refaddr($_[0])} = File::KDBX::Safe->new }
197
198 1;
199 __END__
200
201 =head1 DESCRIPTION
202
203 A master key is one or more credentials that can protect a KDBX database. When you encrypt a database with
204 a master key, you will need the master key to decrypt it. B<Keep your master key safe!> If someone gains
205 access to your master key, they can open your database. If you forget or lose any part of your master key, all
206 data in the database is lost.
207
208 There are several different types of keys, each implemented as a subclass:
209
210 =for :list
211 * L<File::KDBX::Key::Password> - Password or passphrase, knowledge of a string of characters
212 * L<File::KDBX::Key::File> - Possession of a file ("key file") with a secret.
213 * L<File::KDBX::Key::ChallengeResponse> - Possession of a device that responds correctly when challenged
214 * L<File::KDBX::Key::YubiKey> - Possession of a YubiKey hardware device (a type of challenge-response)
215 * L<File::KDBX::Key::Composite> - One or more keys combined as one
216
217 A good master key is produced from a high amount of "entropy" (unpredictability). The more entropy the better.
218 Combining multiple keys into a B<Composite> key combines the entropy of each individual key. For example, if
219 you have a weak password and you combine it with other keys, the composite key is stronger than the weak
220 password key by itself. (Of course it's much better to not have any weak components of your master key.)
221
222 B<COMPATIBILITY NOTE:> Most KeePass implementations are limited in the types and numbers of keys they support.
223 B<Password> keys are pretty much universally supported. B<File> keys are pretty well-supported. Many do not
224 support challenge-response keys. If you are concerned about compatibility, you should stick with one of these
225 configurations:
226
227 =for :list
228 * One password
229 * One key file
230 * One password and one key file
231
232 =cut
This page took 0.044434 seconds and 4 git commands to generate.