File-KDBX
view release on metacpan or search on metacpan
lib/File/KDBX/Safe.pm view on Meta::CPAN
else {
throw 'Safe strings must be a hashref or stringref', type => ref $string;
}
push @{$self->{items}}, $item;
$self->{index}{refaddr($string)} = $item;
$self->{counter} += length($item->{val});
}
return $self;
}
sub lock_protected { shift->add_protected(@_) }
sub add_protected {
my $self = shift;
my $filter = is_coderef($_[0]) ? shift : undef;
my @strings = map { is_arrayref($_) ? @$_ : $_ } @_;
@strings or throw 'Must provide strings to lock';
for my $string (@strings) {
my $item = {str => $string, off => $self->{counter}};
$item->{filter} = $filter if defined $filter;
if (is_scalarref($string)) {
next if !defined $$string;
$item->{val} = $$string;
erase $string;
}
elsif (is_hashref($string)) {
next if !defined $string->{value};
$item->{val} = $string->{value};
erase \$string->{value};
}
else {
throw 'Safe strings must be a hashref or stringref', type => ref $string;
}
push @{$self->{items}}, $item;
$self->{index}{refaddr($string)} = $item;
$self->{counter} += length($item->{val});
}
return $self;
}
sub unlock {
my $self = shift;
my $cipher = $self->cipher;
$cipher->finish;
$self->{counter} = 0;
for my $item (@{$self->{items}}) {
my $string = $item->{str};
my $cleanup = erase_scoped \$item->{val};
my $str_ref;
if (is_scalarref($string)) {
$$string = $cipher->crypt(\$item->{val});
if (my $encoding = $item->{enc}) {
my $decoded = decode($encoding, $string->{value});
erase $string;
$$string = $decoded;
}
$str_ref = $string;
}
elsif (is_hashref($string)) {
$string->{value} = $cipher->crypt(\$item->{val});
if (my $encoding = $item->{enc}) {
my $decoded = decode($encoding, $string->{value});
erase \$string->{value};
$string->{value} = $decoded;
}
$str_ref = \$string->{value};
}
else {
die 'Unexpected';
}
if (my $filter = $item->{filter}) {
my $filtered = $filter->($$str_ref);
erase $str_ref;
$$str_ref = $filtered;
}
}
return $self->clear;
}
sub peek {
my $self = shift;
my $string = shift;
my $item = $self->{index}{refaddr($string)} // return;
my $cipher = $self->cipher->dup(offset => $item->{off});
my $value = $cipher->crypt(\$item->{val});
if (my $encoding = $item->{enc}) {
my $decoded = decode($encoding, $value);
erase $value;
return $decoded;
}
return $value;
}
sub cipher {
my $self = shift;
$self->{cipher} //= do {
require File::KDBX::Cipher;
File::KDBX::Cipher->new(stream_id => STREAM_ID_CHACHA20, key => random_bytes(64));
};
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
File::KDBX::Safe - Keep strings encrypted while in memory
=head1 VERSION
version 0.906
=head1 SYNOPSIS
use File::KDBX::Safe;
$safe = File::KDBX::Safe->new;
my $msg = 'Secret text';
$safe->add(\$msg);
# $msg is now undef, the original message no longer in RAM
my $obj = { value => 'Also secret' };
$safe->add($obj);
# $obj is now { value => undef }
say $safe->peek($msg); # Secret text
$safe->unlock;
say $msg; # Secret text
say $obj->{value}; # Also secret
=head1 DESCRIPTION
This module provides memory protection functionality. It keeps strings encrypted in memory and decrypts them
as-needed. Encryption and decryption is done using a L<File::KDBX::Cipher::Stream>.
A safe can protect one or more (possibly many) strings. When a string is added to a safe, it gets added to an
internal list so it will be decrypted when the entire safe is unlocked.
=head1 ATTRIBUTES
=head2 cipher
( run in 0.759 second using v1.01-cache-2.11-cpan-39bf76dae61 )