]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Cipher/Stream.pm
2cd4033c21f3cc16d20a6b27d41584ef7275c628
[chaz/p5-File-KDBX] / lib / File / KDBX / Cipher / Stream.pm
1 package File::KDBX::Cipher::Stream;
2 # ABSTRACT: A cipher stream encrypter/decrypter
3
4 use warnings;
5 use strict;
6
7 use Crypt::Digest qw(digest_data);
8 use File::KDBX::Constants qw(:cipher :random_stream);
9 use File::KDBX::Error;
10 use File::KDBX::Util qw(:class);
11 use Scalar::Util qw(blessed);
12 use Module::Load;
13 use namespace::clean;
14
15 extends 'File::KDBX::Cipher';
16
17 our $VERSION = '0.900'; # VERSION
18
19
20 has 'counter', is => 'ro', default => 0;
21 has 'offset', is => 'ro';
22 sub key_size { { Salsa20 => 32, ChaCha => 32 }->{$_[0]->{algorithm} || ''} // 0 }
23 sub iv_size { { Salsa20 => 8, ChaCha => 12 }->{$_[0]->{algorithm} || ''} // -1 }
24 sub block_size { 1 }
25
26 sub init {
27 my $self = shift;
28 my %args = @_;
29
30 if (my $uuid = $args{uuid}) {
31 if ($uuid eq CIPHER_UUID_CHACHA20 && length($args{iv}) == 16) {
32 # extract the counter
33 my $buf = substr($self->{iv}, 0, 4, '');
34 $self->{counter} = unpack('L<', $buf);
35 }
36 elsif ($uuid eq CIPHER_UUID_SALSA20) {
37 # only need eight bytes...
38 $self->{iv} = substr($args{iv}, 8);
39 }
40 }
41 elsif (my $id = $args{stream_id}) {
42 my $key_ref = ref $args{key} ? $args{key} : \$args{key};
43 if ($id == STREAM_ID_CHACHA20) {
44 ($self->{key}, $self->{iv}) = unpack('a32 a12', digest_data('SHA512', $$key_ref));
45 }
46 elsif ($id == STREAM_ID_SALSA20) {
47 ($self->{key}, $self->{iv}) = (digest_data('SHA256', $$key_ref), STREAM_SALSA20_IV);
48 }
49 }
50
51 return $self;
52 }
53
54
55 sub crypt {
56 my $self = shift;
57 my $stream = $self->_stream;
58 return join('', map { $stream->crypt(ref $_ ? $$_ : $_) } grep { defined } @_);
59 }
60
61
62 sub keystream {
63 my $self = shift;
64 return $self->_stream->keystream(@_);
65 }
66
67
68 sub dup {
69 my $self = shift;
70 my $class = blessed($self);
71
72 my $dup = bless {%$self, @_}, $class;
73 delete $dup->{stream};
74 return $dup;
75 }
76
77 sub _stream {
78 my $self = shift;
79
80 $self->{stream} //= do {
81 my $s = eval {
82 my $pkg = 'Crypt::Stream::'.$self->algorithm;
83 my $counter = $self->counter;
84 my $pos = 0;
85 if (defined (my $offset = $self->offset)) {
86 $counter = int($offset / 64);
87 $pos = $offset % 64;
88 }
89 my $s = $pkg->new($self->key, $self->iv, $counter);
90 # seek to correct position within block
91 $s->keystream($pos) if $pos;
92 $s;
93 };
94 if (my $err = $@) {
95 throw 'Failed to initialize stream cipher library',
96 error => $err,
97 algorithm => $self->{algorithm},
98 key_length => length($self->key),
99 iv_length => length($self->iv),
100 iv => unpack('H*', $self->iv),
101 key => unpack('H*', $self->key);
102 }
103 $s;
104 };
105 }
106
107 sub encrypt { goto &crypt }
108 sub decrypt { goto &crypt }
109
110 sub finish { delete $_[0]->{stream}; '' }
111
112 1;
113
114 __END__
115
116 =pod
117
118 =encoding UTF-8
119
120 =head1 NAME
121
122 File::KDBX::Cipher::Stream - A cipher stream encrypter/decrypter
123
124 =head1 VERSION
125
126 version 0.900
127
128 =head1 SYNOPSIS
129
130 use File::KDBX::Cipher::Stream;
131
132 my $cipher = File::KDBX::Cipher::Stream->new(algorithm => $algorithm, key => $key, iv => $iv);
133
134 =head1 DESCRIPTION
135
136 A subclass of L<File::KDBX::Cipher> for encrypting and decrypting data using a stream cipher.
137
138 =head1 ATTRIBUTES
139
140 =head2 counter
141
142 $counter = $cipher->counter;
143
144 Get the initial counter / block count into the keystream.
145
146 =head2 offset
147
148 $offset = $cipher->offset;
149
150 Get the initial byte offset into the keystream. This has precedence over L</counter> if both are set.
151
152 =head1 METHODS
153
154 =head2 crypt
155
156 $ciphertext = $cipher->crypt($plaintext);
157 $plaintext = $cipher->crypt($ciphertext);
158
159 Encrypt or decrypt some data. These ciphers are symmetric, so encryption and decryption are the same
160 operation. This method is an alias for both L<File::KDBX::Cipher/encrypt> and L<File::KDBX::Cipher/decrypt>.
161
162 =head2 keystream
163
164 $stream = $cipher->keystream;
165
166 Access the keystream.
167
168 =head2 dup
169
170 $cipher_copy = $cipher->dup(%attributes);
171
172 Get a copy of an existing cipher with the counter reset, optionally applying new attributes.
173
174 =head1 BUGS
175
176 Please report any bugs or feature requests on the bugtracker website
177 L<https://github.com/chazmcgarvey/File-KDBX/issues>
178
179 When submitting a bug or request, please include a test-file or a
180 patch to an existing test-file that illustrates the bug or desired
181 feature.
182
183 =head1 AUTHOR
184
185 Charles McGarvey <ccm@cpan.org>
186
187 =head1 COPYRIGHT AND LICENSE
188
189 This software is copyright (c) 2022 by Charles McGarvey.
190
191 This is free software; you can redistribute it and/or modify it under
192 the same terms as the Perl 5 programming language system itself.
193
194 =cut
This page took 0.046069 seconds and 3 git commands to generate.