| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Copyrights 2012-2016 by [Mark Overmeer]. |
|
2
|
|
|
|
|
|
|
# For other contributors see ChangeLog. |
|
3
|
|
|
|
|
|
|
# See the manual pages for details on the licensing terms. |
|
4
|
|
|
|
|
|
|
# Pod stripped from pm file by OODoc 2.02. |
|
5
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
25
|
|
|
6
|
1
|
|
|
1
|
|
3
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
23
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package XML::Compile::WSS::SignedInfo; |
|
9
|
1
|
|
|
1
|
|
3
|
use vars '$VERSION'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
43
|
|
|
10
|
|
|
|
|
|
|
$VERSION = '2.02'; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
3
|
use Log::Report 'xml-compile-wss-sig'; |
|
|
1
|
|
|
|
|
0
|
|
|
|
1
|
|
|
|
|
4
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
1
|
|
|
1
|
|
639
|
use Digest::SHA (); |
|
|
1
|
|
|
|
|
1911
|
|
|
|
1
|
|
|
|
|
23
|
|
|
16
|
1
|
|
|
1
|
|
38
|
use XML::Compile::C14N; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use XML::Compile::Util qw/type_of_node/; |
|
18
|
|
|
|
|
|
|
use XML::Compile::WSS::Util qw/:wss11 :dsig/; |
|
19
|
|
|
|
|
|
|
use XML::Compile::C14N::Util qw/:c14n is_canon_constant/; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# Quite some problems to get canonicalization compatible between |
|
22
|
|
|
|
|
|
|
# client and server. Especially where some xmlns's are optional. |
|
23
|
|
|
|
|
|
|
# It may help to enforce some namespaces via $wsdl->prefixFor($ns) |
|
24
|
|
|
|
|
|
|
my @default_canon_ns = qw(SOAP-ENV); # qw/wsu/; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# There can only be one c14n rule active, because it would otherwise |
|
27
|
|
|
|
|
|
|
# produce a prefix |
|
28
|
|
|
|
|
|
|
my $c14n; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub new(@) { my $class = shift; (bless {}, $class)->init({@_}) } |
|
32
|
|
|
|
|
|
|
sub init($) |
|
33
|
|
|
|
|
|
|
{ my ($self, $args) = @_; |
|
34
|
|
|
|
|
|
|
$self->{XCWS_pref} = $args->{prefix_list} || \@default_canon_ns; |
|
35
|
|
|
|
|
|
|
my $wss = $args->{wss}; |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Immediately try-out the configured digest method. |
|
38
|
|
|
|
|
|
|
my $digest = $self->{XCWS_dig} |
|
39
|
|
|
|
|
|
|
= $args->{digest_method} || DSIG_SHA1; |
|
40
|
|
|
|
|
|
|
try { $self->_get_digester($digest, undef) }; |
|
41
|
|
|
|
|
|
|
error __x"digest method {name} is not useable: {err}" |
|
42
|
|
|
|
|
|
|
, name => $digest, err => $@ |
|
43
|
|
|
|
|
|
|
if $@; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $canon = $self->{XCWS_can} |
|
46
|
|
|
|
|
|
|
= $args->{canon_method} || C14N_EXC_NO_COMM; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
$self->{XCWS_c14n} = $args->{c14n} ||= $c14n |
|
49
|
|
|
|
|
|
|
||= XML::Compile::C14N->new(for => $canon, schema => $wss->schema); |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
$self; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub fromConfig(@) |
|
56
|
|
|
|
|
|
|
{ my $class = shift; |
|
57
|
|
|
|
|
|
|
$class->new(@_==1 ? %{$_[0]} : @_); |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
#----------------- |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub defaultDigestMethod() { shift->{XCWS_dig} } |
|
63
|
|
|
|
|
|
|
sub defaultCanonMethod() { shift->{XCWS_can} } |
|
64
|
|
|
|
|
|
|
sub defaultPrefixList() { shift->{XCWS_pref} } |
|
65
|
|
|
|
|
|
|
sub c14n() { shift->{XCWS_c14n} } |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
#----------------- |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub builder($%) |
|
70
|
|
|
|
|
|
|
{ my ($self, $wss, %args) = @_; |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
my $schema = $wss->schema; |
|
73
|
|
|
|
|
|
|
my $digest = $args{digest_method} || $self->defaultDigestMethod; |
|
74
|
|
|
|
|
|
|
my $canon = $args{canon_method} || $self->defaultCanonMethod; |
|
75
|
|
|
|
|
|
|
my $preflist = $args{prefix_list} || $self->defaultPrefixList; |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
my $canonic = $self->_get_canonic($canon, $preflist); |
|
78
|
|
|
|
|
|
|
$schema->prefixFor($canon); # enforce inclusion of c14n namespace |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my $digester = $self->_get_digester($digest, $canonic); |
|
81
|
|
|
|
|
|
|
my $cleanup = $self->_get_repair_xml($wss); |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $infow = $schema->writer('ds:SignedInfo'); |
|
84
|
|
|
|
|
|
|
my $inclw = $self->_canon_incl($wss); |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub { |
|
87
|
|
|
|
|
|
|
my ($doc, $elems, $sign_method) = @_; |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# warn "SIGN ELEMS @$elems"; |
|
90
|
|
|
|
|
|
|
my @refs; |
|
91
|
|
|
|
|
|
|
foreach (@$elems) |
|
92
|
|
|
|
|
|
|
{ my $node = $cleanup->($_, @$preflist); |
|
93
|
|
|
|
|
|
|
my $value = $digester->($node); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $transform = |
|
96
|
|
|
|
|
|
|
+{ Algorithm => $canon |
|
97
|
|
|
|
|
|
|
, cho_any => [ +{$inclw->($doc, $preflist)} ] |
|
98
|
|
|
|
|
|
|
}; |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $id = $node->getAttribute('Id') # for the Signatures |
|
101
|
|
|
|
|
|
|
|| $node->getAttributeNS(WSU_NS, 'Id'); # or else |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
push @refs, |
|
104
|
|
|
|
|
|
|
+{ URI => '#'.$id |
|
105
|
|
|
|
|
|
|
, ds_Transforms => { ds_Transform => [$transform] } |
|
106
|
|
|
|
|
|
|
, ds_DigestValue => $value |
|
107
|
|
|
|
|
|
|
, ds_DigestMethod => { Algorithm => $digest } |
|
108
|
|
|
|
|
|
|
}; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my $canonical = +{ Algorithm => $canon, $inclw->($doc, $preflist) }; |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my $siginfo = $infow->($doc, |
|
114
|
|
|
|
|
|
|
+{ ds_CanonicalizationMethod => $canonical |
|
115
|
|
|
|
|
|
|
, ds_Reference => \@refs |
|
116
|
|
|
|
|
|
|
, ds_SignatureMethod => { Algorithm => $sign_method } |
|
117
|
|
|
|
|
|
|
} ); |
|
118
|
|
|
|
|
|
|
# warn "SIGINFO = $siginfo"; |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
my $si_canon = $canonic->($cleanup->($siginfo, @$preflist)); # to sign |
|
121
|
|
|
|
|
|
|
($siginfo, $si_canon); |
|
122
|
|
|
|
|
|
|
}; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# the digest algorithms can be distiguish by pure lowercase, no dash. |
|
127
|
|
|
|
|
|
|
my $digest_algorithm =qr/^(?: |
|
128
|
|
|
|
|
|
|
\Q${\DSIG_NS}\E |
|
129
|
|
|
|
|
|
|
| \Q${\DSIG_MORE_NS}\E |
|
130
|
|
|
|
|
|
|
| \Q${\XENC_NS}\E |
|
131
|
|
|
|
|
|
|
) ([a-z0-9]+)$ |
|
132
|
|
|
|
|
|
|
/x; |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub _get_digester($$) |
|
135
|
|
|
|
|
|
|
{ my ($self, $method, $canonic) = @_; |
|
136
|
|
|
|
|
|
|
$method =~ $digest_algorithm |
|
137
|
|
|
|
|
|
|
or error __x"digest {name} is not supported", name => $method; |
|
138
|
|
|
|
|
|
|
my $algo = uc $1; |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub { |
|
141
|
|
|
|
|
|
|
my $node = shift; |
|
142
|
|
|
|
|
|
|
my $digest = try |
|
143
|
|
|
|
|
|
|
{ Digest::SHA->new($algo) # Digest objects cannot be reused |
|
144
|
|
|
|
|
|
|
->add($canonic->($node)) |
|
145
|
|
|
|
|
|
|
->digest; # becomes base64 via XML field type |
|
146
|
|
|
|
|
|
|
}; |
|
147
|
|
|
|
|
|
|
#use MIME::Base64; |
|
148
|
|
|
|
|
|
|
#warn "DIGEST=", encode_base64 $digest; |
|
149
|
|
|
|
|
|
|
$@ or return $digest; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
error __x"digest method {short} (for {name}): {err}" |
|
152
|
|
|
|
|
|
|
, short => $algo, name => $method, err => $@->wasFatal; |
|
153
|
|
|
|
|
|
|
}; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub _digest_check($$) |
|
157
|
|
|
|
|
|
|
{ my ($self, $wss) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# The horrible reality is that these settings may change per message, |
|
160
|
|
|
|
|
|
|
# so we cannot keep the knowledge of the previous message. In practice, |
|
161
|
|
|
|
|
|
|
# the settings will probably never ever change for an implementation. |
|
162
|
|
|
|
|
|
|
sub { |
|
163
|
|
|
|
|
|
|
my ($elem, $ref) = @_; |
|
164
|
|
|
|
|
|
|
my $canon = $self->defaultCanonMethod; |
|
165
|
|
|
|
|
|
|
my $preflist; # warning: prefixlist [] ne 'undef'! |
|
166
|
|
|
|
|
|
|
my @removed; |
|
167
|
|
|
|
|
|
|
foreach my $transf (@{$ref->{ds_Transforms}{ds_Transform}}) |
|
168
|
|
|
|
|
|
|
{ my $algo = $transf->{Algorithm}; |
|
169
|
|
|
|
|
|
|
if(is_canon_constant $algo) |
|
170
|
|
|
|
|
|
|
{ $canon = $algo; |
|
171
|
|
|
|
|
|
|
if(my $r = $transf->{cho_any}) |
|
172
|
|
|
|
|
|
|
{ my ($inclns, $p) = %{$r->[0]}; # only 1 kv pair |
|
173
|
|
|
|
|
|
|
$preflist = $p->{PrefixList}; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
elsif($algo eq DSIG_ENV_SIG) |
|
177
|
|
|
|
|
|
|
{ # enveloped-signature. $elem is am inside signed object |
|
178
|
|
|
|
|
|
|
# it must be removed before signing. However, later we |
|
179
|
|
|
|
|
|
|
# will use the content of the signature, so we have to |
|
180
|
|
|
|
|
|
|
# glue it back. |
|
181
|
|
|
|
|
|
|
push @removed, $elem->removeChild($_) |
|
182
|
|
|
|
|
|
|
for $elem->getChildrenByLocalName('Signature'); |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
else |
|
185
|
|
|
|
|
|
|
{ trace __x"unknown transform algorithm {name} ignored" |
|
186
|
|
|
|
|
|
|
, name => $algo; |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
my $digmeth = $ref->{ds_DigestMethod}{Algorithm} |
|
190
|
|
|
|
|
|
|
|| $self->defaultDigestMethod; |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
my $canonic = $self->_get_canonic($canon, $preflist); |
|
193
|
|
|
|
|
|
|
my $digester = $self->_get_digester($digmeth, $canonic); |
|
194
|
|
|
|
|
|
|
#use MIME::Base64; |
|
195
|
|
|
|
|
|
|
#warn "IS? ".encode_base64($digester->($elem)), '==', encode_base64($ref->{ds_DigestValue}); |
|
196
|
|
|
|
|
|
|
my $correct = $digester->($elem) eq $ref->{ds_DigestValue}; |
|
197
|
|
|
|
|
|
|
#warn "CORRECT? $correct#"; |
|
198
|
|
|
|
|
|
|
$elem->addChild($_) for @removed; |
|
199
|
|
|
|
|
|
|
$correct; |
|
200
|
|
|
|
|
|
|
}; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub _get_canonic($$) |
|
205
|
|
|
|
|
|
|
{ my ($self, $canon, $preflist) = @_; |
|
206
|
|
|
|
|
|
|
my $c14n = $self->c14n; |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub |
|
209
|
|
|
|
|
|
|
{ my $node = shift or return ''; |
|
210
|
|
|
|
|
|
|
$c14n->normalize($canon, $node, prefix_list => $preflist); |
|
211
|
|
|
|
|
|
|
}; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# only the inclusiveNamespaces of the Canon, while that's an 'any' |
|
215
|
|
|
|
|
|
|
sub _canon_incl($) |
|
216
|
|
|
|
|
|
|
{ my ($self, $wss) = @_; |
|
217
|
|
|
|
|
|
|
my $schema = $wss->schema; |
|
218
|
|
|
|
|
|
|
my $type = $schema->findName('c14n:InclusiveNamespaces'); |
|
219
|
|
|
|
|
|
|
my $inclw = $schema->writer($type, include_namespaces => 0); |
|
220
|
|
|
|
|
|
|
my $prefix = $schema->prefixed($type); |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub { |
|
223
|
|
|
|
|
|
|
my ($doc, $preflist) = @_; |
|
224
|
|
|
|
|
|
|
defined $preflist or return; |
|
225
|
|
|
|
|
|
|
($type => $inclw->($doc, {PrefixList => $preflist})); |
|
226
|
|
|
|
|
|
|
}; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# XML::Compile plays nasty tricks while constructing the XML tree, |
|
230
|
|
|
|
|
|
|
# which break normalisation. The only way around that -on the moment- |
|
231
|
|
|
|
|
|
|
# is to reparse the XML produced :( |
|
232
|
|
|
|
|
|
|
# The next can be slow and is ugly, Sorry. MO |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub _get_repair_xml($) |
|
235
|
|
|
|
|
|
|
{ my ($self, $wss) = @_; |
|
236
|
|
|
|
|
|
|
my $preftab = $wss->schema->byPrefixTable; |
|
237
|
|
|
|
|
|
|
my %preftab = map +($_ => $preftab->{$_}{uri}), keys %$preftab; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub { |
|
240
|
|
|
|
|
|
|
my ($xc_out_dom, @preflist) = @_; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# only doc element does charsets correctly |
|
243
|
|
|
|
|
|
|
my $doc = XML::LibXML::Document->new('1.0', 'UTF8'); |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# building bottom up: be sure we have all namespaces which may be |
|
246
|
|
|
|
|
|
|
# declared later, on higher in the hierarchy. |
|
247
|
|
|
|
|
|
|
my $env = $doc->createElement('Dummy'); |
|
248
|
|
|
|
|
|
|
$env->setNamespace($preftab{$_}, $_) |
|
249
|
|
|
|
|
|
|
for keys %preftab; |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# reparse tree |
|
252
|
|
|
|
|
|
|
$env->addChild($xc_out_dom->cloneNode(1)); |
|
253
|
|
|
|
|
|
|
my $fixed_dom = XML::LibXML->load_xml(string => $env->toString(0)); |
|
254
|
|
|
|
|
|
|
my $new_out = ($fixed_dom->documentElement->childNodes)[0]; |
|
255
|
|
|
|
|
|
|
$doc->importNode($new_out); |
|
256
|
|
|
|
|
|
|
$new_out; |
|
257
|
|
|
|
|
|
|
}; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub checker($$$) |
|
261
|
|
|
|
|
|
|
{ my ($self, $wss, %args) = @_; |
|
262
|
|
|
|
|
|
|
my $check = $self->_digest_check; |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub { |
|
265
|
|
|
|
|
|
|
my ($info, $elems, $tokens) = @_; |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
my %references; |
|
268
|
|
|
|
|
|
|
foreach my $ref (@{$info->{ds_Reference}}) |
|
269
|
|
|
|
|
|
|
{ my $uri = $ref->{URI}; |
|
270
|
|
|
|
|
|
|
$uri =~ s/^#//; |
|
271
|
|
|
|
|
|
|
$references{$uri} = $ref; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
foreach my $node (@$elems) |
|
275
|
|
|
|
|
|
|
{ # Sometimes "id" (Signature), sometimes "wsu:Id" (other) |
|
276
|
|
|
|
|
|
|
my $id = $node->getAttribute('Id') # Signature/KeyInfo |
|
277
|
|
|
|
|
|
|
|| $node->getAttributeNS(WSU_NS, 'Id') |
|
278
|
|
|
|
|
|
|
|| $node->getAttribute('id'); # SMD::SignedMark |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
$id or error __x"node to check signature without Id, {type}" |
|
281
|
|
|
|
|
|
|
, type => type_of_node $node; |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
my $ref = delete $references{$id} |
|
284
|
|
|
|
|
|
|
or next; # maybe in other signature block |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
$check->($node, $ref) |
|
287
|
|
|
|
|
|
|
or error __x"digest info of {elem} is wrong", elem => $id; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
trace __x"reference {uri} not used", uri => $_ |
|
291
|
|
|
|
|
|
|
for keys %references; |
|
292
|
|
|
|
|
|
|
}; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
1; |