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); # TODO verify return $pt; } 1;