File Coverage

blib/lib/PGP/Finger/Key.pm
Criterion Covered Total %
statement 18 92 19.5
branch 0 22 0.0
condition n/a
subroutine 6 15 40.0
pod 0 4 0.0
total 24 133 18.0


line stmt bran cond sub pod time code
1             package PGP::Finger::Key;
2              
3 1     1   4 use Moose;
  1         1  
  1         7  
4              
5             # ABSTRACT: class for holding and parsing pgp keys
6             our $VERSION = '1.1'; # VERSION
7              
8 1     1   3095 use Digest::SHA qw(sha256_hex sha224_hex);
  1         2  
  1         56  
9 1     1   421 use Digest::CRC qw(crcopenpgparmor_base64);
  1         1470  
  1         52  
10 1     1   369 use MIME::Base64;
  1         433  
  1         63  
11              
12             use overload
13 0     0   0 q{""} => sub { $_[0]->armored },
14 1     1   6 fallback => 1;
  1         1  
  1         8  
15              
16              
17             has 'data' => ( is => 'ro' );
18              
19             has 'attributes' => ( is => 'ro', isa => 'HashRef[Str]', lazy => 1,
20             traits => [ 'Hash' ],
21             default => sub { {} },
22             handles => {
23             set_attr => 'set',
24             get_attr => 'get',
25             has_attr => 'exists',
26             has_attrs => 'count',
27             },
28             );
29              
30             has '_version' => ( is => 'ro', isa => 'Str', lazy => 1,
31             default => sub {
32             my $version;
33             {
34 1     1   104 no strict 'vars'; # is only declared in build
  1         0  
  1         748  
35             $version = defined $VERSION ? $VERSION : 'head';
36             }
37             return 'pgpfinger ('.$version.')';
38             },
39             );
40              
41             sub merge_key {
42 0     0 0   my ( $self, @keys ) = @_;
43              
44 0           foreach my $key ( @keys ) {
45 0           $self->merge_attributes( $key->attributes );
46             }
47 0           return;
48             }
49              
50             sub merge_attributes {
51 0     0 0   my ( $self, $attr ) = @_;
52              
53 0           foreach my $name ( keys %$attr ) {
54 0 0         if( ! $self->has_attr( $name ) ) {
55 0           $self->set_attr( $name => $attr->{$name} );
56             } else {
57 0           $self->set_attr( $name => $self->get_attr($name).', '.$attr->{$name} );
58             }
59             }
60 0           return;
61             }
62              
63             sub new_armored {
64 0     0 0   my ( $class, %options ) = @_;
65 0           my $armored = $options{'data'};
66 0 0         if( ! defined $armored ) {
67 0           die('new_armored called without data');
68             }
69            
70 0           my $b64 = '';
71 0           my @lines = split( /\r?\n/, $armored );
72 0           my $line;
73 0           while( $line = shift @lines ) {
74 0 0         if( $line =~ /^\s*$/ ) { next; }
  0            
75 0 0         if( $line =~ /^-----BEGIN /) { last; } # here we start
  0            
76 0           die('data before BEGIN line in PEM input');
77             }
78 0 0         if( ! @lines ) {
79 0           die('end of PEM before -----BEGIN line has been found');
80             }
81 0           while( $line = shift @lines ) { # get headers if present
82 0 0         if( $line =~ /:/ ) { next; }
  0            
83 0 0         if( $line =~ /^\s*$/ ) { next; }
  0            
84 0           last;
85             }
86 0 0         if( ! @lines ) {
87 0           die('end of PEM before -----END line has been found');
88             }
89 0           $b64 .= $line;
90 0           while( $line = shift @lines ) {
91 0 0         if( $line =~ /^-----END /) { last; } # END
  0            
92 0           $b64 .= $line;
93             }
94 0 0         if( $b64 eq '') {
95 0           die('failed parsing PEM encoded key');
96             }
97 0           $options{'data'} = decode_base64( $b64 );
98              
99 0           return $class->new( %options );
100             }
101              
102             has 'fingerprint' => ( is => 'ro', isa => 'Str', lazy_build => 1);
103              
104             sub _build_fingerprint {
105 0     0     my $self = shift;
106 0           return sha256_hex( $self->data );
107             }
108              
109             has 'armored' => ( is => 'ro', isa => 'Str', lazy_build => 1);
110              
111             sub _build_armored {
112 0     0     my $self = shift;
113 0           my $armored = '';
114 0 0         if( $self->has_attrs ) {
115 0           $armored .= join( "\n",
116             map {
117 0           '# '.$_.': '.$self->get_attr($_)
118 0           } keys %{$self->attributes} );
119 0           $armored .= "\n";
120             }
121              
122 0           my $data = encode_base64( $self->data, '' );
123 0           $data =~ s!(.{1,64})!$1\n!g;
124              
125 0           my $crc = crcopenpgparmor_base64( $self->data );
126              
127 0           $armored .= "-----BEGIN PGP PUBLIC KEY BLOCK-----\n";
128 0           $armored .= "Version: ".$self->_version."\n\n";
129 0           $armored .= $data;
130 0           $armored .= '='.$crc;
131 0           $armored .= "-----END PGP PUBLIC KEY BLOCK-----\n";
132 0           return $armored;
133             }
134              
135             has 'mail' => ( is => 'ro', isa => 'Str', required => 1 );
136              
137             has 'local' => ( is => 'ro', isa => 'Str', lazy => 1,
138             default => sub {
139             my $self = shift;
140             my ($local) = split('@', $self->mail, 2);
141             return( $local );
142             },
143             );
144              
145             has 'domain' => ( is => 'ro', isa => 'Str', lazy => 1,
146             default => sub {
147             my $self = shift;
148             my (undef, $domain) = split('@', $self->mail, 2);
149             return( $domain );
150             },
151             );
152              
153             has 'dns_record_name' => ( is => 'ro', isa => 'Str', lazy => 1,
154             default => sub {
155             my $self = shift;
156             return sha224_hex($self->local).'._openpgpkey.'.$self->domain.'.';
157             },
158             );
159              
160             has 'dns_record_generic' => ( is => 'ro', isa => 'Str', lazy_build => 1);
161              
162             sub _build_dns_record_generic {
163 0     0     my $self = shift;
164 0           my $name = $self->dns_record_name;
165 0           my $data = unpack( "H*", $self->data );
166 0           my $num_octets = length( $self->data );
167              
168 0           return join(' ', $name, 'IN', 'TYPE65280', '\#', $num_octets, $data )."\n";
169             }
170              
171             has 'dns_record_rfc' => ( is => 'ro', isa => 'Str', lazy_build => 1);
172              
173             sub _build_dns_record_rfc {
174 0     0     my $self = shift;
175 0           my $name = $self->dns_record_name;
176 0           my $num_octets = 0;
177 0           my $data = encode_base64( $self->data, '');
178              
179 0           return join(' ', $name, 'IN', 'OPENPGPKEY ', $data )."\n";
180             }
181              
182             sub clone {
183 0     0 0   my $self = shift;
184 0           my $class = ref( $self );
185 0           return $class->new(
186             mail => $self->mail,
187             data => $self->data,
188             attributes => $self->attributes,
189             );
190             }
191              
192             1;
193              
194             __END__
195              
196             =pod
197              
198             =encoding UTF-8
199              
200             =head1 NAME
201              
202             PGP::Finger::Key - class for holding and parsing pgp keys
203              
204             =head1 VERSION
205              
206             version 1.1
207              
208             =head1 AUTHOR
209              
210             Markus Benning <ich@markusbenning.de>
211              
212             =head1 COPYRIGHT AND LICENSE
213              
214             This software is Copyright (c) 2015 by Markus Benning.
215              
216             This is free software, licensed under:
217              
218             The GNU General Public License, Version 2 or later
219              
220             =cut