File Coverage

lib/Noise/SymmetricState.pm
Criterion Covered Total %
statement 23 23 100.0
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 31 31 100.0


line stmt bran cond sub pod time code
1 3     3   36 use v5.42.0;
  3         15  
2 3     3   14 use feature 'class';
  3         6  
  3         343  
3 3     3   15 no warnings 'experimental::class';
  3         4  
  3         246  
4             #
5             class Noise::SymmetricState v0.0.1 {
6 3     3   1510 use Noise::CipherState;
  3         8  
  3         141  
7 3     3   1759 use Digest::SHA qw(sha256 sha512);
  3         10195  
  3         315  
8 3     3   1730 use Crypt::Digest::BLAKE2b_512;
  3         5845  
  3         170  
9 3     3   1575 use Crypt::Digest::BLAKE2s_256;
  3         2011  
  3         637  
10 3     3   1418 use Crypt::Mac::HMAC qw[hmac];
  3         3532  
  3         3769  
11             #
12             field $h : reader;
13             field $ck : reader;
14             field $hash : param //= 'SHA256';
15             field $cipher : param;
16             field $cipher_state : reader = Noise::CipherState->new( cipher => $cipher );
17             #
18             method initialize_symmetric ($proto_name) {
19             my $hash_len = $self->_hash_len();
20             if ( length($proto_name) <= $hash_len ) {
21             $h = $proto_name . ( "\0" x ( $hash_len - length($proto_name) ) );
22             }
23             else {
24             $h = $self->_hash($proto_name);
25             }
26             $ck = $h;
27             $cipher_state->set_key(undef);
28             }
29              
30             method mix_key ($ikm) {
31             my ( $next_ck, $temp_k ) = $self->_hkdf( $ck, $ikm );
32             $ck = $next_ck;
33             $cipher_state->set_key( substr( $temp_k, 0, 32 ) );
34             }
35             method mix_hash ($data) { $h = $self->_hash( $h . $data ) }
36              
37             method mix_key_and_hash ($ikm) {
38             my ( $next_ck, $temp_h, $temp_k ) = $self->_hkdf_3( $ck, $ikm );
39             $ck = $next_ck;
40             $self->mix_hash($temp_h);
41             $cipher_state->set_key( substr( $temp_k, 0, 32 ) );
42             }
43              
44             method encrypt_and_hash ($plaintext) {
45             my $ciphertext = $cipher_state->encrypt_with_ad( $h, $plaintext );
46             $self->mix_hash($ciphertext);
47             return $ciphertext;
48             }
49              
50             method decrypt_and_hash ($ciphertext) {
51             my $plaintext = $cipher_state->decrypt_with_ad( $h, $ciphertext );
52             $self->mix_hash($ciphertext);
53             return $plaintext;
54             }
55              
56             method split () {
57             my ( $out1, $out2 ) = $self->_hkdf( $ck, '' );
58             my $c1 = Noise::CipherState->new( cipher => $cipher );
59             $c1->set_key( substr( $out1, 0, 32 ) );
60             my $c2 = Noise::CipherState->new( cipher => $cipher );
61             $c2->set_key( substr( $out2, 0, 32 ) );
62             return ( $c1, $c2 );
63             }
64              
65             method _hash_len () {
66             return 64 if $hash eq 'SHA512' || $hash eq 'BLAKE2b';
67             return 32 if $hash eq 'SHA256' || $hash eq 'BLAKE2s';
68             die 'Unknown hash: ' . $hash;
69             }
70              
71             method _hash ($data) {
72             return sha256($data) if $hash eq 'SHA256';
73             return sha512($data) if $hash eq 'SHA512';
74             return Crypt::Digest::BLAKE2s_256::blake2s_256($data) if $hash eq 'BLAKE2s';
75             return Crypt::Digest::BLAKE2b_512::blake2b_512($data) if $hash eq 'BLAKE2b';
76             die 'Unknown hash: ' . $hash;
77             }
78              
79             method _hmac ( $key, $data ) {
80             my $h_name = $hash;
81             $h_name = 'BLAKE2s_256' if $hash eq 'BLAKE2s';
82             $h_name = 'BLAKE2b_512' if $hash eq 'BLAKE2b';
83             hmac( $h_name, $key, $data );
84             }
85              
86             method _hkdf ( $salt, $ikm ) {
87             my $prk = $self->_hmac( $salt, $ikm );
88             my $out1 = $self->_hmac( $prk, "\x01" );
89             my $out2 = $self->_hmac( $prk, $out1 . "\x02" );
90             return ( $out1, $out2 );
91             }
92              
93             method _hkdf_3 ( $salt, $ikm ) {
94             my $prk = $self->_hmac( $salt, $ikm );
95             my $out1 = $self->_hmac( $prk, "\x01" );
96             my $out2 = $self->_hmac( $prk, $out1 . "\x02" );
97             my $out3 = $self->_hmac( $prk, $out2 . "\x03" );
98             return ( $out1, $out2, $out3 );
99             }
100             };
101             #
102             1;