]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Cipher/Stream.pm
Version 0.905
[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.905'; # 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 load $pkg;
90 my $s = $pkg->new($self->key, $self->iv, $counter);
91 # seek to correct position within block
92 $s->keystream($pos) if $pos;
93 $s;
94 };
95 if (my $err = $@) {
96 throw 'Failed to initialize stream cipher library',
97 error => $err,
98 algorithm => $self->{algorithm},
99 key_length => length($self->key),
100 iv_length => length($self->iv),
101 iv => unpack('H*', $self->iv),
102 key => unpack('H*', $self->key);
103 }
104 $s;
105 };
106 }
107
108 sub encrypt { goto &crypt }
109 sub decrypt { goto &crypt }
110
111 sub finish { delete $_[0]->{stream}; '' }
112
113 1;
114
115 __END__
116
117 =pod
118
119 =encoding UTF-8
120
121 =head1 NAME
122
123 File::KDBX::Cipher::Stream - A cipher stream encrypter/decrypter
124
125 =head1 VERSION
126
127 version 0.905
128
129 =head1 SYNOPSIS
130
131 use File::KDBX::Cipher::Stream;
132
133 my $cipher = File::KDBX::Cipher::Stream->new(algorithm => $algorithm, key => $key, iv => $iv);
134
135 =head1 DESCRIPTION
136
137 A subclass of L<File::KDBX::Cipher> for encrypting and decrypting data using a stream cipher.
138
139 =head1 ATTRIBUTES
140
141 =head2 counter
142
143 $counter = $cipher->counter;
144
145 Get the initial counter / block count into the keystream.
146
147 =head2 offset
148
149 $offset = $cipher->offset;
150
151 Get the initial byte offset into the keystream. This has precedence over L</counter> if both are set.
152
153 =head1 METHODS
154
155 =head2 crypt
156
157 $ciphertext = $cipher->crypt($plaintext);
158 $plaintext = $cipher->crypt($ciphertext);
159
160 Encrypt or decrypt some data. These ciphers are symmetric, so encryption and decryption are the same
161 operation. This method is an alias for both L<File::KDBX::Cipher/encrypt> and L<File::KDBX::Cipher/decrypt>.
162
163 =head2 keystream
164
165 $stream = $cipher->keystream;
166
167 Access the keystream.
168
169 =head2 dup
170
171 $cipher_copy = $cipher->dup(%attributes);
172
173 Get a copy of an existing cipher with the counter reset, optionally applying new attributes.
174
175 =head1 BUGS
176
177 Please report any bugs or feature requests on the bugtracker website
178 L<https://github.com/chazmcgarvey/File-KDBX/issues>
179
180 When submitting a bug or request, please include a test-file or a
181 patch to an existing test-file that illustrates the bug or desired
182 feature.
183
184 =head1 AUTHOR
185
186 Charles McGarvey <ccm@cpan.org>
187
188 =head1 COPYRIGHT AND LICENSE
189
190 This software is copyright (c) 2022 by Charles McGarvey.
191
192 This is free software; you can redistribute it and/or modify it under
193 the same terms as the Perl 5 programming language system itself.
194
195 =cut
This page took 0.042498 seconds and 4 git commands to generate.