summaryrefslogtreecommitdiff
path: root/lib/DeWeave/Crypto.pm
blob: 618e095c1e9f9830a9f69ee28f63f9664b07a4bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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;
 
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};
    return MIME::Base32::decode($key);
}
 
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_payload = $self->storage->get_item('crypto/keys');
    my $struct = $j->decode($keys_payload);
 
    my $keys = {
        default => $struct->{default},
        %{$struct->{collections}},
    };
 
    return $keys;
}
 
sub keys_for_collection {
    my ($self,$collection) = @_;
 
    if ($self->_has_collection_keys($collection)) {
        return $self->_get_collection_keys($collection);
    }
    else {
        return $self->_get_collection_keys('default');
    }
}
 
sub decrypt {
    my ($self,$args) = @_;
 
    my $iv = $args->{IV};
    my $hmac = $args->{hmac};
    my $ct = $args->{ciphertext};
 
    my $cipher = Crypt::CBC->new(
        -key => $self->_encryption_key,
        -cipher => 'Crypt::OpenSSL::AES',
        -iv => $iv,
        -header => 'none',
        -padding => 'null',
        -literal_key => 1,
    );
 
    my $pt = $cipher->decrypt($ct);
    # TODO verify 
    return $pt;
}
 
1;