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 | =encoding utf8 | ||||||
17 | |||||||
18 - 28 | =head1 NAME Dpkg::OpenPGP - multi-backend OpenPGP support =head1 DESCRIPTION This module provides a class for transparent multi-backend OpenPGP support. B<Note>: This is a private module, its API can change at any time. =cut | ||||||
29 | |||||||
30 | package Dpkg::OpenPGP 0.01; | ||||||
31 | |||||||
32 | 216 216 216 | 570 216 4455 | use strict; | ||||
33 | 216 216 216 | 495 144 6786 | use warnings; | ||||
34 | |||||||
35 | 216 216 216 | 495 72 7374 | use List::Util qw(none); | ||||
36 | |||||||
37 | 216 216 216 | 501 207 7035 | use Dpkg::Gettext; | ||||
38 | 216 216 216 | 495 213 9231 | use Dpkg::ErrorHandling; | ||||
39 | 216 216 216 | 498 210 4629 | use Dpkg::IPC; | ||||
40 | 216 216 216 | 432 207 96942 | use Dpkg::Path qw(find_command); | ||||
41 | |||||||
42 | my @BACKENDS = qw( | ||||||
43 | sop | ||||||
44 | sq | ||||||
45 | gpg | ||||||
46 | ); | ||||||
47 | my %BACKEND = ( | ||||||
48 | sop => 'SOP', | ||||||
49 | sq => 'Sequoia', | ||||||
50 | gpg => 'GnuPG', | ||||||
51 | ); | ||||||
52 | |||||||
53 | sub new { | ||||||
54 | 735 | 0 | 2927 | my ($this, %opts) = @_; | |||
55 | 735 | 4432 | my $class = ref($this) || $this; | ||||
56 | |||||||
57 | 735 | 844 | my $self = {}; | ||||
58 | 735 | 911 | bless $self, $class; | ||||
59 | |||||||
60 | 735 | 1823 | my $backend = $opts{backend} // 'auto'; | ||||
61 | my %backend_opts = ( | ||||||
62 | cmdv => $opts{cmdv} // 'auto', | ||||||
63 | 735 | 4726 | cmd => $opts{cmd} // 'auto', | ||||
64 | ); | ||||||
65 | |||||||
66 | 735 | 2554 | if ($backend eq 'auto') { | ||||
67 | # Defaults for stateless full API auto-detection. | ||||||
68 | 216 | 1047 | $opts{needs}{api} //= 'full'; | ||||
69 | 216 | 576 | $opts{needs}{keystore} //= 0; | ||||
70 | |||||||
71 | 216 225 | 1224 642 | if (none { $opts{needs}{api} eq $_ } qw(full verify)) { | ||||
72 | 0 | 0 | error(g_('unknown OpenPGP api requested %s'), $opts{needs}{api}); | ||||
73 | } | ||||||
74 | |||||||
75 | 216 | 708 | $self->{backend} = $self->_auto_backend($opts{needs}, %backend_opts); | ||||
76 | } elsif (exists $BACKEND{$backend}) { | ||||||
77 | 519 | 2806 | $self->{backend} = $self->_load_backend($BACKEND{$backend}, %backend_opts); | ||||
78 | 519 | 2782 | if (! $self->{backend}) { | ||||
79 | 0 | 0 | error(g_('cannot load OpenPGP backend %s'), $backend); | ||||
80 | } | ||||||
81 | } else { | ||||||
82 | 0 | 0 | error(g_('unknown OpenPGP backend %s'), $backend); | ||||
83 | } | ||||||
84 | |||||||
85 | 735 | 7628 | return $self; | ||||
86 | } | ||||||
87 | |||||||
88 | sub _load_backend { | ||||||
89 | 735 | 2156 | my ($self, $backend, %opts) = @_; | ||||
90 | |||||||
91 | 735 | 1148 | my $module = "Dpkg::OpenPGP::Backend::$backend"; | ||||
92 | 735 | 58995 | eval qq{ | ||||
93 | require $module; | ||||||
94 | }; | ||||||
95 | 735 | 3608 | return if $@; | ||||
96 | |||||||
97 | 735 | 12252 | return $module->new(%opts); | ||||
98 | } | ||||||
99 | |||||||
100 | sub _auto_backend { | ||||||
101 | 216 | 291 | my ($self, $needs, %opts) = @_; | ||||
102 | |||||||
103 | 216 | 282 | foreach my $backend (@BACKENDS) { | ||||
104 | 216 | 426 | my $module = $self->_load_backend($BACKEND{$backend}, %opts); | ||||
105 | |||||||
106 | 216 | 504 | if ($needs->{api} eq 'verify') { | ||||
107 | 9 | 18 | next if ! $module->has_verify_cmd(); | ||||
108 | } else { | ||||||
109 | 207 | 483 | next if ! $module->has_backend_cmd(); | ||||
110 | } | ||||||
111 | 216 | 435 | next if $needs->{keystore} && ! $module->has_keystore(); | ||||
112 | |||||||
113 | 216 | 711 | return $module; | ||||
114 | } | ||||||
115 | |||||||
116 | # Otherwise load a dummy backend. | ||||||
117 | 0 | 0 | return Dpkg::OpenPGP::Backend->new(); | ||||
118 | } | ||||||
119 | |||||||
120 | sub can_use_secrets { | ||||||
121 | 570 | 0 | 1286 | my ($self, $key) = @_; | |||
122 | |||||||
123 | 570 | 3752 | return 0 unless $self->{backend}->has_backend_cmd(); | ||||
124 | 570 | 2285 | return 0 if $key->type eq 'keyfile' && ! -f $key->handle; | ||||
125 | 570 | 994 | return 0 if $key->type eq 'keystore' && ! -e $key->handle; | ||||
126 | 570 | 3369 | return 0 unless $self->{backend}->can_use_key($key); | ||||
127 | 570 | 1244 | return 1; | ||||
128 | } | ||||||
129 | |||||||
130 | sub get_trusted_keyrings { | ||||||
131 | 0 | 0 | 0 | my $self = shift; | |||
132 | |||||||
133 | 0 | 0 | return $self->{backend}->get_trusted_keyrings(); | ||||
134 | } | ||||||
135 | |||||||
136 | sub armor { | ||||||
137 | 2121 | 0 | 8131 | my ($self, $type, $in, $out) = @_; | |||
138 | |||||||
139 | 2121 | 10303 | return $self->{backend}->armor($type, $in, $out); | ||||
140 | } | ||||||
141 | |||||||
142 | sub dearmor { | ||||||
143 | 1404 | 0 | 4896 | my ($self, $type, $in, $out) = @_; | |||
144 | |||||||
145 | 1404 | 5504 | return $self->{backend}->dearmor($type, $in, $out); | ||||
146 | } | ||||||
147 | |||||||
148 | sub inline_verify { | ||||||
149 | 1854 | 0 | 7034 | my ($self, $inlinesigned, $data, @certs) = @_; | |||
150 | |||||||
151 | 1854 | 9362 | return $self->{backend}->inline_verify($inlinesigned, $data, @certs); | ||||
152 | } | ||||||
153 | |||||||
154 | sub verify { | ||||||
155 | 1212 | 0 | 3329 | my ($self, $data, $sig, @certs) = @_; | |||
156 | |||||||
157 | 1212 | 5697 | return $self->{backend}->verify($data, $sig, @certs); | ||||
158 | } | ||||||
159 | |||||||
160 | sub inline_sign { | ||||||
161 | 570 | 0 | 1105 | my ($self, $data, $inlinesigned, $key) = @_; | |||
162 | |||||||
163 | 570 | 1559 | return $self->{backend}->inline_sign($data, $inlinesigned, $key); | ||||
164 | } | ||||||
165 | |||||||
166 - 172 | =head1 CHANGES =head2 Version 0.xx This is a private module. =cut | ||||||
173 | |||||||
174 | 1; |