File: | Dpkg/OpenPGP.pm |
Coverage: | 75.2% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | # Copyright © 2017 Guillem Jover <guillem@debian.org> | ||||||
2 | # | ||||||
3 | # This program is free software; you can redistribute it and/or modify | ||||||
4 | # it under the terms of the GNU General Public License as published by | ||||||
5 | # the Free Software Foundation; either version 2 of the License, or | ||||||
6 | # (at your option) any later version. | ||||||
7 | # | ||||||
8 | # This program is distributed in the hope that it will be useful, | ||||||
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
11 | # GNU General Public License for more details. | ||||||
12 | # | ||||||
13 | # You should have received a copy of the GNU General Public License | ||||||
14 | # along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
15 | |||||||
16 | package Dpkg::OpenPGP; | ||||||
17 | |||||||
18 | 44 44 44 | 85 44 522 | use strict; | ||||
19 | 44 44 44 | 88 0 1032 | use warnings; | ||||
20 | |||||||
21 | 44 44 44 | 88 41 1120 | use List::Util qw(none); | ||||
22 | |||||||
23 | 44 44 44 | 85 44 1003 | use Dpkg::Gettext; | ||||
24 | 44 44 44 | 85 44 1170 | use Dpkg::ErrorHandling; | ||||
25 | 44 44 44 | 88 41 704 | use Dpkg::IPC; | ||||
26 | 44 44 44 | 44 44 14761 | use Dpkg::Path qw(find_command); | ||||
27 | |||||||
28 | our $VERSION = '0.01'; | ||||||
29 | |||||||
30 | my @BACKENDS = qw( | ||||||
31 | sop | ||||||
32 | sq | ||||||
33 | gpg | ||||||
34 | ); | ||||||
35 | my %BACKEND = ( | ||||||
36 | sop => 'SOP', | ||||||
37 | sq => 'Sequoia', | ||||||
38 | gpg => 'GnuPG', | ||||||
39 | ); | ||||||
40 | |||||||
41 | sub new { | ||||||
42 | 109 | 0 | 333 | my ($this, %opts) = @_; | |||
43 | 109 | 460 | my $class = ref($this) || $this; | ||||
44 | |||||||
45 | 109 | 107 | my $self = {}; | ||||
46 | 109 | 109 | bless $self, $class; | ||||
47 | |||||||
48 | 109 | 132 | my $backend = $opts{backend} // 'auto'; | ||||
49 | my %backend_opts = ( | ||||||
50 | cmdv => $opts{cmdv} // 'auto', | ||||||
51 | 109 | 754 | cmd => $opts{cmd} // 'auto', | ||||
52 | ); | ||||||
53 | |||||||
54 | 109 | 299 | if ($backend eq 'auto') { | ||||
55 | # Defaults for stateless full API auto-detection. | ||||||
56 | 44 | 126 | $opts{needs}{api} //= 'full'; | ||||
57 | 44 | 88 | $opts{needs}{keystore} //= 0; | ||||
58 | |||||||
59 | 44 47 | 173 53 | if (none { $opts{needs}{api} eq $_ } qw(full verify)) { | ||||
60 | 0 | 0 | error(g_('unknown OpenPGP api requested %s'), $opts{needs}{api}); | ||||
61 | } | ||||||
62 | |||||||
63 | 44 | 88 | $self->{backend} = $self->_auto_backend($opts{needs}, %backend_opts); | ||||
64 | } elsif (exists $BACKEND{$backend}) { | ||||||
65 | 65 | 380 | $self->{backend} = $self->_load_backend($BACKEND{$backend}, %backend_opts); | ||||
66 | 65 | 340 | if (! $self->{backend}) { | ||||
67 | 0 | 0 | error(g_('cannot load OpenPGP backend %s'), $backend); | ||||
68 | } | ||||||
69 | } else { | ||||||
70 | 0 | 0 | error(g_('unknown OpenPGP backend %s'), $backend); | ||||
71 | } | ||||||
72 | |||||||
73 | 109 | 386 | return $self; | ||||
74 | } | ||||||
75 | |||||||
76 | sub _load_backend { | ||||||
77 | 109 | 236 | my ($self, $backend, %opts) = @_; | ||||
78 | |||||||
79 | 109 | 139 | my $module = "Dpkg::OpenPGP::Backend::$backend"; | ||||
80 | 109 | 7324 | eval qq{ | ||||
81 | pop \@INC if \$INC[-1] eq '.'; | ||||||
82 | require $module; | ||||||
83 | }; | ||||||
84 | 109 | 492 | return if $@; | ||||
85 | |||||||
86 | 109 | 1431 | return $module->new(%opts); | ||||
87 | } | ||||||
88 | |||||||
89 | sub _auto_backend { | ||||||
90 | 44 | 47 | my ($self, $needs, %opts) = @_; | ||||
91 | |||||||
92 | 44 | 41 | foreach my $backend (@BACKENDS) { | ||||
93 | 44 | 47 | my $module = $self->_load_backend($BACKEND{$backend}, %opts); | ||||
94 | |||||||
95 | 44 | 47 | if ($needs->{api} eq 'verify') { | ||||
96 | 3 | 3 | next if ! $module->has_verify_cmd(); | ||||
97 | } else { | ||||||
98 | 41 | 82 | next if ! $module->has_backend_cmd(); | ||||
99 | } | ||||||
100 | 44 | 85 | next if $needs->{keystore} && ! $module->has_keystore(); | ||||
101 | |||||||
102 | 44 | 88 | return $module; | ||||
103 | } | ||||||
104 | |||||||
105 | # Otherwise load a dummy backend. | ||||||
106 | 0 | 0 | return Dpkg::OpenPGP::Backend->new(); | ||||
107 | } | ||||||
108 | |||||||
109 | sub can_use_secrets { | ||||||
110 | 75 | 0 | 196 | my ($self, $key) = @_; | |||
111 | |||||||
112 | 75 | 535 | return 0 unless $self->{backend}->has_backend_cmd(); | ||||
113 | 75 | 182 | return 0 if $key->type eq 'keyfile' && ! -f $key->handle; | ||||
114 | 75 | 121 | return 0 if $key->type eq 'keystore' && ! -e $key->handle; | ||||
115 | 75 | 624 | return 0 unless $self->{backend}->can_use_key($key); | ||||
116 | 75 | 110 | return 1; | ||||
117 | } | ||||||
118 | |||||||
119 | sub get_trusted_keyrings { | ||||||
120 | 0 | 0 | 0 | my $self = shift; | |||
121 | |||||||
122 | 0 | 0 | return $self->{backend}->get_trusted_keyrings(); | ||||
123 | } | ||||||
124 | |||||||
125 | sub armor { | ||||||
126 | 305 | 0 | 2110 | my ($self, $type, $in, $out) = @_; | |||
127 | |||||||
128 | 305 | 2254 | return $self->{backend}->armor($type, $in, $out); | ||||
129 | } | ||||||
130 | |||||||
131 | sub dearmor { | ||||||
132 | 200 | 0 | 770 | my ($self, $type, $in, $out) = @_; | |||
133 | |||||||
134 | 200 | 796 | return $self->{backend}->dearmor($type, $in, $out); | ||||
135 | } | ||||||
136 | |||||||
137 | sub inline_verify { | ||||||
138 | 248 | 0 | 994 | my ($self, $inlinesigned, $data, @certs) = @_; | |||
139 | |||||||
140 | 248 | 1634 | return $self->{backend}->inline_verify($inlinesigned, $data, @certs); | ||||
141 | } | ||||||
142 | |||||||
143 | sub verify { | ||||||
144 | 162 | 0 | 331 | my ($self, $data, $sig, @certs) = @_; | |||
145 | |||||||
146 | 162 | 613 | return $self->{backend}->verify($data, $sig, @certs); | ||||
147 | } | ||||||
148 | |||||||
149 | sub inline_sign { | ||||||
150 | 75 | 0 | 124 | my ($self, $data, $inlinesigned, $key) = @_; | |||
151 | |||||||
152 | 75 | 150 | return $self->{backend}->inline_sign($data, $inlinesigned, $key); | ||||
153 | } | ||||||
154 | |||||||
155 | 1; |