package DeWeave::Crypto;
use Moose;
use namespace::autoclean;
use MooseX::Types::Moose qw(HashRef Str);
use MooseX::Types::Structured qw(Tuple);
use JSON::Any;
use Try::Tiny;
use Digest::SHA ();
use MIME::Base32 'RFC';
use Crypt::CBC;
use MIME::Base64 ();
use Data::Dump 'pp';
has storage => (
isa => 'DeWeave::Storage',
required => 1,
is => 'ro',
);
has sync_key => (
isa => Str,
required => 1,
is => 'ro',
);
sub _byte_sync_key {
my ($self) = @_;
my $key = $self->sync_key;
$key =~ y{89}{LO};
$key =~ s{-}{}g;
my $byte_key = MIME::Base32::decode(uc($key));
return substr($byte_key,0,16);
}
has _hmac_input => (
isa => Str,
default => 'Sync-AES_256_CBC-HMAC256',
is => 'ro',
);
has _encryption_key => (
isa => Str,
lazy_build => 1,
is => 'ro',
);
sub _build__encryption_key {
my ($self) = @_;
my $secret = $self->_hmac_input
. $self->storage->username . "\x01";
return Digest::SHA::hmac_sha256($secret, $self->_byte_sync_key);
}
has _hmac_key => (
isa => Str,
lazy_build => 1,
is => 'ro',
);
sub _build__hmac_key {
my ($self) = @_;
my $secret = $self->_encryption_key . $self->_hmac_input
. $self->storage->username . "\x02";
return Digest::SHA::hmac_sha256($secret, $self->_byte_sync_key);
}
has _keys => (
isa => HashRef[Tuple[Str,Str]],
init_arg => undef,
lazy_build => 1,
is => 'ro',
traits => ['Hash'],
handles => {
_has_collection_keys => 'exists',
_get_collection_keys => 'get',
},
);
sub _build__keys {
my ($self) = @_;
my $j = JSON::Any->new;
my $keys_raw = $self->storage->get_item('storage/crypto/keys');
my $keys_struct = $j->decode($keys_raw);
my $payload = $j->decode($keys_struct->{payload});
my $struct = $j->decode($self->decrypt({
%$payload,
key => $self->_encryption_key,
}));
my $keys = {
default => $struct->{default},
%{$struct->{collections}},
};
return $keys;
}
sub keys_for_collection {
my ($self,$collection) = @_;
my $key;
if ($self->_has_collection_keys($collection)) {
$key = $self->_get_collection_keys($collection);
}
else {
$key = $self->_get_collection_keys('default');
}
return [ map { MIME::Base64::decode($_) } @$key ];
}
sub decrypt {
my ($self,$args) = @_;
my $iv = MIME::Base64::decode($args->{IV});
my $hmac = $args->{hmac};
my $ct = MIME::Base64::decode($args->{ciphertext});
my $key = $args->{key} || $self->keys_for_collection('default')->[0];
my $cipher = Crypt::CBC->new(
-key => $key,
-cipher => 'Crypt::OpenSSL::AES',
-iv => $iv,
-header => 'none',
-literal_key => 1,
);
my $pt = $cipher->decrypt($ct);
return $pt;
}
1;