]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/Cipher/Stream.pm
Add recursive transactions
[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 Scalar::Util qw(blessed);
11 use Module::Load;
12 use namespace::clean;
13
14 use parent 'File::KDBX::Cipher';
15
16 our $VERSION = '999.999'; # VERSION
17
18 sub init {
19 my $self = shift;
20 my %args = @_;
21
22 if (my $uuid = $args{uuid}) {
23 if ($uuid eq CIPHER_UUID_CHACHA20 && length($args{iv}) == 16) {
24 # extract the counter
25 my $buf = substr($self->{iv}, 0, 4, '');
26 $self->{counter} = unpack('L<', $buf);
27 }
28 elsif ($uuid eq CIPHER_UUID_SALSA20) {
29 # only need eight bytes...
30 $self->{iv} = substr($args{iv}, 8);
31 }
32 }
33 elsif (my $id = $args{stream_id}) {
34 my $key_ref = ref $args{key} ? $args{key} : \$args{key};
35 if ($id == STREAM_ID_CHACHA20) {
36 ($self->{key}, $self->{iv}) = unpack('a32 a12', digest_data('SHA512', $$key_ref));
37 }
38 elsif ($id == STREAM_ID_SALSA20) {
39 ($self->{key}, $self->{iv}) = (digest_data('SHA256', $$key_ref), STREAM_SALSA20_IV);
40 }
41 }
42
43 return $self;
44 }
45
46 =method crypt
47
48 $ciphertext = $cipher->crypt($plaintext);
49 $plaintext = $cipher->crypt($ciphertext);
50
51 Encrypt or decrypt some data. These ciphers are symmetric, so encryption and decryption are the same
52 operation. This method is an alias for both L<File::KDBX::Cipher/encrypt> and L<File::KDBX::Cipher/decrypt>.
53
54 =cut
55
56 sub crypt {
57 my $self = shift;
58 my $stream = $self->_stream;
59 return join('', map { $stream->crypt(ref $_ ? $$_ : $_) } grep { defined } @_);
60 }
61
62 =method keystream
63
64 $stream = $cipher->keystream;
65
66 Access the keystream.
67
68 =cut
69
70 sub keystream {
71 my $self = shift;
72 return $self->_stream->keystream(@_);
73 }
74
75 =method dup
76
77 $cipher_copy = $cipher->dup(%attributes);
78
79 Get a copy of an existing cipher with the counter reset, optionally applying new attributes.
80
81 =cut
82
83 sub dup {
84 my $self = shift;
85 my $class = blessed($self);
86
87 my $dup = bless {%$self, @_}, $class;
88 delete $dup->{stream};
89 return $dup;
90 }
91
92 sub _stream {
93 my $self = shift;
94
95 $self->{stream} //= do {
96 my $s = eval {
97 my $pkg = 'Crypt::Stream::'.$self->algorithm;
98 my $counter = $self->counter;
99 my $pos = 0;
100 if (defined (my $offset = $self->offset)) {
101 $counter = int($offset / 64);
102 $pos = $offset % 64;
103 }
104 my $s = $pkg->new($self->key, $self->iv, $counter);
105 # seek to correct position within block
106 $s->keystream($pos) if $pos;
107 $s;
108 };
109 if (my $err = $@) {
110 throw 'Failed to initialize stream cipher library',
111 error => $err,
112 algorithm => $self->algorithm,
113 key_length => length($self->key),
114 iv_length => length($self->iv),
115 iv => unpack('H*', $self->iv),
116 key => unpack('H*', $self->key);
117 }
118 $s;
119 };
120 }
121
122 sub encrypt { goto &crypt }
123 sub decrypt { goto &crypt }
124
125 sub finish { delete $_[0]->{stream}; '' }
126
127 =attr algorithm
128
129 $algorithm = $cipher->algorithm;
130
131 Get the stream cipher algorithm. Can be one of C<Salsa20> and C<ChaCha>.
132
133 =attr counter
134
135 $counter = $cipher->counter;
136
137 Get the initial counter / block count into the keystream.
138
139 =attr offset
140
141 $offset = $cipher->offset;
142
143 Get the initial byte offset into the keystream. This has precedence over L</counter> if both are set.
144
145 =cut
146
147 sub algorithm { $_[0]->{algorithm} or throw 'Stream cipher algorithm is not set' }
148 sub counter { $_[0]->{counter} // 0 }
149 sub offset { $_[0]->{offset} }
150 sub key_size { { Salsa20 => 32, ChaCha => 32 }->{$_[0]->{algorithm} || ''} // 0 }
151 sub iv_size { { Salsa20 => 8, ChaCha => 12 }->{$_[0]->{algorithm} || ''} // -1 }
152 sub block_size { 1 }
153
154 1;
155 __END__
156
157 =head1 SYNOPSIS
158
159 use File::KDBX::Cipher::Stream;
160
161 my $cipher = File::KDBX::Cipher::Stream->new(algorithm => $algorithm, key => $key, iv => $iv);
162
163 =head1 DESCRIPTION
164
165 A subclass of L<File::KDBX::Cipher> for encrypting and decrypting data using a stream cipher.
166
167 =cut
This page took 0.040971 seconds and 4 git commands to generate.