File Coverage

blib/lib/App/MatrixTool/Command/notary.pm
Criterion Covered Total %
statement 21 65 32.3
branch 0 12 0.0
condition n/a
subroutine 7 13 53.8
pod 0 3 0.0
total 28 93 30.1


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2015-2016 -- leonerd@leonerd.org.uk
5              
6             package App::MatrixTool::Command::notary;
7              
8 1     1   727 use strict;
  1         2  
  1         26  
9 1     1   4 use warnings;
  1         2  
  1         24  
10 1     1   3 use base qw( App::MatrixTool );
  1         2  
  1         77  
11              
12             our $VERSION = '0.08';
13              
14 1     1   4 use MIME::Base64 qw( decode_base64 );
  1         1  
  1         48  
15 1     1   512 use Protocol::Matrix qw( verify_json_signature );
  1         16377  
  1         64  
16              
17 1     1   6 use constant DESCRIPTION => "Fetch a server's signing key via another server";
  1         2  
  1         62  
18 1     1   5 use constant ARGUMENTS => ( "server_name", "via" );
  1         2  
  1         598  
19              
20             =head1 NAME
21              
22             matrixtool notary - Fetch a server's signing key via another server
23              
24             =head1 SYNOPSIS
25              
26             $ matrixtool notary my-server.org matrix.org
27              
28             =head1 DESCRIPTION
29              
30             This command uses the notary federation API to fetch the keys from a Matrix
31             homeserver (the "target") by querying via another server (the "notary"). This
32             helps you test whether a given server can see the one you are testing.
33              
34             =head1 OPTIONS
35              
36             There are no additional options for this command.
37              
38             =cut
39              
40             sub get_key_notary_v2
41             {
42 0     0 0   my $self = shift;
43 0           my ( $server_name, $via ) = @_;
44              
45             $self->http_client->request_json(
46             method => "GET",
47             server => $via,
48             path => "/_matrix/key/v2/query/$server_name/*",
49              
50             on_ready => sub {
51 0     0     my ( $conn ) = @_;
52 0           my $socket = $conn->read_handle;
53              
54 0           $self->output_info( "Connected to " . $self->format_addr( $socket->peername ) );
55 0           Future->done;
56             },
57             )->then( sub {
58 0     0     my ( $body ) = @_;
59              
60             # Find our result.
61 0           foreach ( @{ $body->{server_keys} } ) {
  0            
62 0 0         return Future->done( $_ ) if $_->{server_name} eq $server_name;
63             }
64              
65 0           $self->output_fail( "Could not find a key result for '$server_name'");
66 0           Future->fail( "Failed" );
67 0           });
68             }
69              
70             sub output_check_failure
71             {
72 0     0 0   my $self = shift;
73             # TODO: option to make this fatal or non-fatal
74 0           $self->output_fail( @_ );
75             }
76              
77             sub run
78             {
79 0     0 0   my $self = shift;
80 0           my ( $server_name, $via ) = @_;
81              
82             $self->get_key_notary_v2( $server_name, $via )->then( sub {
83 0     0     my ( $result ) = @_;
84              
85 0           $self->output( "Keys from $result->{server_name} via notary $via" );
86 0           $self->output();
87              
88 0           my $store = $self->server_key_store;
89              
90 0           foreach my $key_id ( keys %{ $result->{verify_keys} } ) {
  0            
91 0           my $key = decode_base64 $result->{verify_keys}{$key_id}{key};
92              
93 0           $self->output( "Key id $key_id" );
94 0           $self->output( " " . $self->format_binary( $key ) );
95              
96 0           my $ok;
97 0           foreach my $signing_server ( keys %{ $result->{signatures} } ) {
  0            
98 0           foreach my $signing_key_id ( keys %{ $result->{signatures}{$signing_server} } ) {
  0            
99 0           my $signing_key = $store->get( server => $signing_server, id => $key_id );
100 0 0         next unless defined $signing_key;
101              
102 0           my $verified = eval { verify_json_signature( $result,
  0            
103             public_key => $signing_key,
104             origin => $signing_server,
105             key_id => $signing_key_id,
106 0           ); 1 };
107              
108 0 0         $verified or
109             $self->output_check_failure( "Signature verification failed for server_name=$signing_server key_id=$signing_key_id" );
110              
111 0 0         $verified and $ok++,
112             $self->output_ok( "Verified using server_name=$signing_server key_id=$signing_key_id" );
113             }
114             }
115              
116 0           my $cached = $store->get( server => $server_name, id => $key_id );
117 0 0         if( !defined $cached ) {
    0          
118             # ignore but don't store
119             }
120             elsif( $cached eq $key ) {
121 0           $self->output_info( "Matches cached key" );
122             }
123             else {
124 0           $self->output_warn( "Does not match cached key " . $self->format_binary( $cached ) );
125             }
126             }
127              
128 0           Future->done;
129 0           });
130             }
131              
132             =head1 EXAMPLES
133              
134             For example, once you believe your server is working correctly according to
135             C you can query the F server to see if that
136             can fetch the same keys:
137              
138             $ matrixtool notary example.com matrix.org
139             [INFO] Connected to 83.166.64.33:8448
140             Keys from example.com via notary matrix.org
141              
142             Key id ed25519:auto
143             base64::aBcDeFgHiJ...
144             [OK] Verified using server_name=matrix.org key_id=ed25519:auto
145             [OK] Verified using server_name=example.com key_id=ed25519:auto
146             [INFO] Matches cached key
147              
148             =cut
149              
150             =head1 AUTHOR
151              
152             Paul Evans
153              
154             =cut
155              
156             0x55AA;