File Coverage

blib/lib/Net/SSH/Perl/Key.pm
Criterion Covered Total %
statement 61 110 55.4
branch 10 42 23.8
condition 0 9 0.0
subroutine 15 23 65.2
pod 4 7 57.1
total 90 191 47.1


line stmt bran cond sub pod time code
1             # $Id: Key.pm,v 1.20 2008/10/02 20:46:17 turnstep Exp $
2              
3             package Net::SSH::Perl::Key;
4 1     1   4309 use strict;
  1         4  
  1         44  
5 1     1   9 use warnings;
  1         4  
  1         47  
6              
7 1     1   305 use Crypt::Digest::SHA256 qw( sha256 );
  1         3462  
  1         53  
8 1     1   282 use Crypt::Digest::MD5 qw( md5 );
  1         509  
  1         45  
9 1     1   250 use Crypt::Misc qw( encode_b64 decode_b64 );
  1         7205  
  1         96  
10 1     1   362 use Net::SSH::Perl::Buffer;
  1         2  
  1         96  
11              
12             sub new {
13 3     3 1 719 my $class = shift;
14 3 100       14 if ($class eq __PACKAGE__) {
15 1         3 $class .= "::" . shift();
16 1     1   395 eval "use $class;";
  1         7  
  1         30  
  1         58  
17 1 50       8 die "Key class '$class' is unsupported: $@" if $@;
18             }
19 3         12 my $key = bless {}, $class;
20 3         18 $key->init(@_);
21 3         11 $key;
22             }
23              
24 1     1   6 use vars qw( %KEY_TYPES );
  1         2  
  1         367  
25             %KEY_TYPES = (
26             'ssh-dss' => 'DSA',
27             'ssh-rsa' => 'RSA',
28             'ssh-ed25519' => 'Ed25519',
29             'ecdsa-sha2-nistp256' => 'ECDSA256',
30             'ecdsa-sha2-nistp384' => 'ECDSA384',
31             'ecdsa-sha2-nistp521' => 'ECDSA521',
32             );
33              
34             sub new_from_blob {
35 0     0 0 0 my $class = shift;
36 0         0 my($blob) = @_;
37 0         0 my $b = Net::SSH::Perl::Buffer->new( MP => 'SSH1' );
38 0         0 $b->append($blob);
39 0         0 my $ssh_name = $b->get_str;
40 0 0       0 my $type = $KEY_TYPES{$ssh_name} or return;
41 0         0 __PACKAGE__->new($type, @_);
42             }
43              
44             sub extract_public {
45 0     0 1 0 my $class = shift;
46 0         0 my($blob) = pop @_;
47 0 0       0 my $expected_type = @_ ? shift : undef;
48              
49 0         0 my $type;
50             my $options;
51 0         0 foreach my $t (keys %KEY_TYPES) {
52 0 0       0 if ((my $type_offset = index($blob,$t)) >= 0) {
53 0         0 $type = $t;
54 0 0       0 $options = substr($blob,0,$type_offset,'') if $type_offset > 0;
55 0         0 last;
56             }
57             }
58              
59             # TODO do something with ssh options $options
60              
61 0 0       0 if (!defined $type) {
62 0         0 warn "Invalid public key line";
63 0         0 return;
64             }
65 0         0 substr($blob,0,length($type)+1,'');
66 0         0 my($data, $comment) = split /\s+/, $blob, 2;
67 0 0 0     0 if (defined $expected_type && $expected_type ne $type) {
68 0         0 warn "Requested type '$expected_type' mismatches actual type '$type'";
69 0         0 return;
70             }
71 0         0 $type = $KEY_TYPES{$type};
72 0         0 my $key = __PACKAGE__->new($type, decode_b64($data));
73 0         0 $key->comment($comment);
74 0         0 $key;
75             }
76              
77             BEGIN {
78 1     1   7 no strict 'refs'; ## no critic
  1         1  
  1         99  
79 1     1   3 for my $meth (qw( read_private keygen )) {
80             *$meth = sub {
81 0     0   0 my $class = shift;
82 0 0       0 if ($class eq __PACKAGE__) {
83 0         0 $class .= "::" . shift();
84 0         0 eval "use $class;";
85 0 0       0 die "Key class '$class' is unsupported: $@" if $@;
86             }
87 0         0 $class->$meth(@_);
88 2         29 };
89             }
90             }
91              
92 1     1   6 use vars qw( %OBJ_MAP );
  1         2  
  1         671  
93             %OBJ_MAP = (
94             'DSA PRIVATE KEY' => [ 'DSA' ],
95             'SSH2 ENCRYPTED PRIVATE KEY' => [ 'DSA', [ 'SSH2' ] ],
96             'RSA PRIVATE KEY' => [ 'RSA' ],
97             'OPENSSH PRIVATE KEY' => [ 'Ed25519' ],
98             'EC PRIVATE KEY' => [ 'ECDSA' ],
99             );
100              
101             sub read_private_pem {
102 1     1 0 2 my $class = shift;
103 1         5 my $keyfile = $_[0];
104 1 50       38 open my $fh, '<', $keyfile or return;
105 1         16 chomp(my $desc = <$fh>);
106 1 50       8 close $fh or warn qq{Could not close "$keyfile": $!\n};
107 1 50       2 return unless $desc;
108 1         8 my($object) = $desc =~ /^-----?\s?BEGIN ([^\n\-]+)\s?-?----\s*$/;
109 1         7 $object =~ s/\s*$//;
110 1 50       5 my $rec = $OBJ_MAP{$object} or return;
111 1         2 $class = __PACKAGE__ . "::" . $rec->[0];
112 1     1   6 eval "use $class;";
  1         2  
  1         14  
  1         63  
113 1 50       4 die "Key class '$class' is unsupported: $@" if $@;
114 1 50       3 my @args = $rec->[1] ? @{ $rec->[1] } : ();
  0         0  
115 1         5 $class->read_private(@_, @args);
116             }
117              
118             sub init;
119             sub extract_public;
120             sub as_blob;
121             sub equal;
122             sub size;
123              
124             sub fingerprint {
125 0     0 1 0 my $key = shift;
126 0         0 my($type) = @_;
127 0         0 my $data = $key->fingerprint_raw;
128 0 0 0     0 $type && $type eq 'bubblebabble' ?
    0 0        
129             _fp_bubblebabble($data) :
130             $type && $type eq 'md5' ?
131             _fp_md5($data) :
132             _fp_sha256($data);
133             }
134              
135             sub _fp_bubblebabble {
136 0     0   0 eval "use Digest::BubbleBabble qw( bubblebabble )";
137 0 0       0 die "Can't load BubbleBabble implementation: $@" if $@;
138 0         0 eval "use Crypt::Digest::SHA1 qw( sha1 )";
139 0 0       0 die "Can't load SHA1: $@" if $@;
140 0         0 bubblebabble( Digest => sha1($_[0]) )
141             }
142              
143 0     0   0 sub _fp_sha256 { "SHA256:" . encode_b64(sha256(shift)) }
144 0     0   0 sub _fp_md5 { join ':', map { sprintf "%02x", ord } split //, md5($_[0]) }
  0         0  
145              
146             sub comment {
147 1     1 0 3 my $key = shift;
148 1         3 my $comment = shift;
149 1 50       6 $key->{comment} = $comment if defined $comment;
150 1         3 $key->{comment};
151             }
152              
153 0     0 1   sub dump_public { join ' ', grep { defined } $_[0]->ssh_name, encode_b64( $_[0]->as_blob ), $_[0]->comment }
  0            
154              
155             1;
156             __END__