File Coverage

lib/XML/Compile/WSS/KeyInfo.pm
Criterion Covered Total %
statement 21 183 11.4
branch 0 66 0.0
condition 0 18 0.0
subroutine 7 44 15.9
pod 8 9 88.8
total 36 320 11.2


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         24  
6 1     1   3 use strict;
  1         0  
  1         22  
7              
8             package XML::Compile::WSS::KeyInfo;
9 1     1   2 use vars '$VERSION';
  1         1  
  1         33  
10             $VERSION = '2.02';
11              
12              
13 1     1   3 use Log::Report 'xml-compile-wss-sig';
  1         1  
  1         3  
14              
15 1     1   171 use XML::Compile::WSS::Util qw/:wsm10 :wsm11 :xtp10/;
  1         1  
  1         96  
16 1     1   4 use XML::Compile::WSS::SecToken::X509v3 ();
  1         1  
  1         16  
17 1     1   3 use Crypt::OpenSSL::X509 qw/FORMAT_ASN1/;
  1         1  
  1         1659  
18              
19              
20 0     0 1   sub new(@) { my $class = shift; (bless {}, $class)->init({@_}) }
  0            
21             sub init($)
22 0     0 0   { my ($self, $args) = @_;
23 0           $self->{XCWK_tokens} = [];
24              
25             # It's too large to create accessors, so for this hack.
26 0           $self->{XCWK_config} = $args;
27 0           $self;
28             }
29              
30              
31             sub fromConfig(@)
32 0     0 1   { my $class = shift;
33 0 0         $class->new(@_==1 ? %{$_[0]} : @_); # also clones the HASH
  0            
34             }
35              
36             #-----------------
37              
38 0 0   0 1   sub config() { my $c = shift->{XCWK_config}; wantarray ? %$c : $c }
  0            
39              
40             #-----------------------------
41              
42             sub addToken($)
43 0     0 1   { my $self = shift;
44 0           push @{$self->{XCWK_tokens}}, @_;
  0            
45 0           $self;
46             }
47              
48              
49             sub findToken(%)
50 0     0 1   { my ($self, %args) = @_;
51 0 0         if(my $fu = $args{uri})
52 0           { foreach my $token ($self->tokens)
53 0 0         { my $tu = $token->uri or next;
54 0 0         return $token if $tu eq $fu;
55             }
56             }
57 0 0         if(my $fp = $args{fingerprint})
58 0           { foreach my $token ($self->tokens)
59 0 0         { my $tp = $token->fingerprint or next;
60 0 0         return $token if $tp eq $fp;
61             }
62             }
63 0 0         if(my $fn = $args{name})
64 0           { foreach my $token ($self->tokens)
65 0 0         { my $tn = $token->fingerprint or next;
66 0 0         return $token if $tn eq $fn;
67             }
68             }
69 0           ();
70             }
71              
72              
73 0     0 1   sub tokens() { @{shift->{XCWK_tokens}} }
  0            
74              
75             #-----------------
76              
77             # See dump/keyinfo/template
78              
79             sub getTokens($%)
80 0     0 1   { my ($self, $wss) = (shift, shift);
81 0           my %args = ($self->config, @_);
82              
83 0           my %keyinfo_handlers =
84             ( ds_KeyName => $self->_get_by_keyname($wss, \%args)
85             , ds_KeyValue => undef
86             , ds_RetrievalMethod => undef
87             , ds_X509Data => $self->_get_as_x509data($wss, \%args)
88             , ds_PGPData => undef
89             , ds_SPKIData => undef
90             , ds_MgmtData => undef
91             , wsse_SecurityTokenReference
92             => $self->_get_by_sectokref($wss, \%args)
93             );
94              
95             sub {
96 0     0     my ($h, $sec, $up_id) = @_;
97 0   0       my $id = $h->{Id} || $up_id;
98 0           my @tokens;
99 0 0         foreach (@{$h->{cho_ds_KeyName} ||[]})
  0            
100 0           { my ($way, $how) = %$_; # hash of one pair
101 0 0         my $handler = $keyinfo_handlers{$way}
102             or error __x"unsupported key-info type {type} for {id}"
103             , type => $way, id => $id;
104              
105 0           push @tokens, $handler->($id, $sec, $how);
106             }
107 0           @tokens;
108 0           };
109             }
110              
111             # ds_keyname
112             sub _get_by_keyname($$)
113 0     0     { my ($self, $wss, $args) = @_;
114 0     0     sub { my ($id, $sec, $h) = @_; $self->findToken(name => $h) };
  0            
  0            
115             }
116              
117             # ds_X509Data
118             sub _get_as_x509data($$)
119 0     0     { my ($self, $wss, $args) = @_;
120              
121             sub {
122 0     0     my ($id, $sec, $h) = @_;
123              
124 0           my @tokens;
125 0 0         foreach my $rec ( @{$h->{seq_ds_X509IssuerSerial} || []} )
  0            
126 0 0         { my $bin_cert = $rec->{ds_X509Certificate} or next;
127 0           push @tokens, XML::Compile::WSS::SecToken::X509v3
128             ->new(id => $id, binary => $bin_cert);
129             }
130 0           @tokens;
131 0           };
132             }
133              
134             # wsse_SecurityTokenReference
135             sub _get_by_sectokref($$$)
136 0     0     { my ($self, $wss, $args) = @_;
137              
138 0           my %str_handlers =
139             ( wsse_KeyIdentifier => $self->_get_str_keyid($wss, $args)
140             , wsse_Reference => $self->_get_str_uri($wss, $args)
141             );
142              
143             sub {
144 0     0     my ($id, $sec, $h) = @_;
145 0           my @tokens;
146 0           foreach (@{$h->{cho_any}})
  0            
147 0           { my ($ref, $d) = %$_; # one pair
148 0 0         my $handler = $str_handlers{$ref}
149             or error __x"Keyinfo {id}: {type} not supported"
150             , id => $id, type => $ref;
151              
152 0           push @tokens, $handler->($id, $sec, $d);
153             }
154              
155 0           @tokens;
156 0           };
157             }
158              
159             sub _get_str_keyid($$) # SECTOKREF_KEYID
160 0     0     { my ($self, $wss, $args) = @_;
161             sub {
162 0     0     my ($id, $sec, $d) = @_;
163 0           my $valuet = $d->{ValueType};
164 0 0         if($valuet eq WSM11_PRINT_SHA1)
165 0           { my $p = wsm_decoded $d->{EncodingType}, $d->{_};
166 0           return $self->findToken(fingerprint => $p);
167             }
168              
169 0           error __x"Keyinfo {id}: {type} not supported", id => $id, type => $valuet;
170 0           };
171             }
172              
173             sub _get_str_uri($$) # SECTOKREF_URI
174 0     0     { my ($self, $wss, $args) = @_;
175             sub {
176 0     0     my ($id, $sec, $d) = @_;
177 0           my $uri = $d->{URI};
178 0           my $token = $self->findToken(uri => $uri);
179 0 0         return $token if $token; # already taken
180              
181 0           my $valuet = $d->{ValueType};
182 0 0         if($valuet eq XTP10_X509v3)
183 0 0         { substr($uri, 0, 1) eq '#'
184             or error __x"Keyinfo {id}: only inlined token references supported, got {uri}"
185             , id => $id, uri => $uri;
186              
187             my $binsec = $sec->{wsse_BinarySecurityToken}
188 0 0         or error __x"Keyinfo {id}: cannot find BinarySecurityToken"
189             , id => $id;
190              
191 0           my $have_id = '#'.$binsec->{wsu_Id};
192 0 0         $have_id eq $uri
193             or error __x"Keyinfo {id}: wrong BinarySecurityToken {uri}, expected {expect}"
194             , id => $id, uri => $have_id, expect => $uri;
195              
196             my $token = XML::Compile::WSS::SecToken::X509v3->new
197             ( id => $binsec->{wsu_Id}, uri => $uri, type => $valuet
198             , binary => wsm_decoded($binsec->{EncodingType}, $binsec->{_})
199 0           );
200              
201 0           $self->addToken($token);
202 0           return $token;
203             }
204              
205 0           panic "Keyinfo $id: $valuet not supported";
206 0           };
207             }
208              
209              
210             sub builder($%)
211 0     0 1   { my ($self, $wss) = @_;
212 0           my %args = ($self->config, @_);
213 0   0       my $type = $args{publish_token} || 'SECTOKREF_URI';
214 0 0         return undef if $type eq 'NO';
215              
216 0           my %str_handlers =
217             ( KEYNAME => '_make_keyname'
218             , SECTOKREF_KEYID => '_make_sectokref_keyid'
219             , SECTOKREF_URI => '_make_sectokref_uri'
220             , INCLUDE_BY_REF => '_make_sectokref_uri' # name is pre 2.00
221             , X509DATA => '_make_x509data'
222             );
223              
224 0 0         my $handler = $str_handlers{$type}
225             or panic "unknown keyinfo type $type";
226              
227 0           my $nest = $self->$handler($wss, \%args);
228 0           my $ki_id = $args{keyinfo_id};
229             sub {
230 0     0     my ($doc, $token, $sec) = @_;
231 0           +{ cho_ds_KeyName => [ $nest->($doc, $token, $sec) ]
232             , Id => $ki_id
233             };
234 0           };
235             }
236              
237             sub _make_keyname($$$)
238 0     0     { my ($self, $wss, $args) = @_;
239             sub {
240 0     0     my ($doc, $token, $sec) = @_;
241 0 0         my $name = $token->name
242             or panic "token $token has no name for KEYNAME";
243 0           +{ ds_KeyName => $name };
244 0           };
245             }
246              
247             sub _make_x509data($$$)
248 0     0     { my ($self, $wss, $args) = @_;
249 0   0       my $as = $args->{x509data_type} || 'ASN1DER';
250              
251             my $put
252             = $as eq 'ASN1DER'
253 0     0     ? sub { ds_X509Certificate => $_[0]->as_string(FORMAT_ASN1) }
254             : $as eq 'SERIAL'
255 0     0     ? sub { ds_X509IssuerSerial =>
256             { ds_X509IssuerName => $_[0]->issuer
257             , ds_X509SerialNumber => $_[0]->serial }
258             }
259 0     0     : $as eq 'SKI' ? sub { ds_X509SKI => $_[0]->hash}
260 0     0     : $as eq 'SUBJECT' ? sub { ds_X509SubjectName => $_[0]->subject }
261 0 0         : error __x"write key-info as X509Data, unknown format `{name}'"
    0          
    0          
    0          
262             , name => $as;
263              
264             # No idea how we can use this Cert Revocation List, ds_X509CRL
265             # Other elements, not in ds:, not (yet) supported for writing
266              
267             # This routine can handle an ARRAY, but the rest of the module
268             # probably not.
269             sub {
270 0     0     my ($doc, $token, $sec) = @_;
271 0 0         my @data = map $put->($_)
272             , ref $token eq 'ARRAY' ? @$token : $token;
273              
274 0           +{ seq_ds_X509IssuerSerial => \@data }
275 0           };
276             }
277              
278             sub _make_sectokref($$$)
279 0     0     { my ($self, $wss, $args) = @_;
280 0           my $refid = $args->{sectokref_id};
281 0           my $usage = $args->{usage};
282 0           my $refw = $wss->schema->writer('wsse:SecurityTokenReference'
283             , include_namespaces => 0);
284              
285             sub {
286 0     0     my ($doc, $token, $sec, $payload) = @_;
287 0           my $ref = $refw->($doc, +{wsu_Id => $refid, wsse_Usage => $usage
288             , cho_any => $payload});
289 0           +{ 'wsse:SecurityTokenReference' => $ref };
290 0           };
291             }
292              
293             sub _make_sectokref_keyid($$$)
294 0     0     { my ($self, $wss, $args) = @_;
295              
296 0   0       my $valuet = $args->{keyid_value} || WSM11_PRINT_SHA1;
297 0   0       my $enct = $args->{keyid_encoding} || WSM10_BASE64;
298 0           my $keyid = $args->{keyident_id};
299              
300 0           my $valuep; # first param is call is $token
301 0 0         if($valuet eq WSM11_PRINT_SHA1)
302 0 0   0     { $valuep = sub {shift->fingerprint or panic "token has no fingerprint" };
  0            
303             }
304 0           else { panic "unsupported security token reference value type '$valuet'" }
305              
306 0     0     my $encp = sub { wsm_encoded $enct, $valuep->($_[0]) };
  0            
307 0           my $kidw = $wss->schema->writer('wsse:KeyIdentifier'
308             , include_namespaces=>0);
309 0           my $refer = $self->_make_sectokref($wss, $args);
310              
311             sub {
312 0     0     my ($doc, $token, $sec) = @_;
313 0           my $elem = $kidw->($doc
314             , +{ wsu_Id => $keyid, ValueType => $valuet, EncodingType => $enct
315             , _ => $encp->($token) });
316 0           $refer->($doc, $token, $sec, +{'wsse:KeyIdentifier' => $elem});
317 0           };
318             }
319              
320             sub _make_sectokref_uri($$$)
321 0     0     { my ($self, $wss, $args) = @_;
322              
323 0           my $schema = $wss->schema;
324 0   0       my $binenc = $args->{binsec_encoding} || WSM10_BASE64;
325 0           my $kidw = $schema->writer('wsse:Reference', include_namespaces => 0);
326 0           my $refer = $self->_make_sectokref($wss, $args);
327 0           my $bstw = $schema->writer('wsse:BinarySecurityToken');
328 0           my $default_uri = $args->{sectokref_uri};
329              
330             sub {
331 0     0     my ($doc, $token, $sec) = @_;
332 0   0       my $uri = $default_uri || $token->uri || '#abc';
333 0           my $intern = $uri !~ m!^\w+://!;
334 0           my $type = $token->type;
335 0           my $elem = $kidw->($doc, +{ValueType => $type, URI => $uri} );
336              
337 0 0 0       if($intern && $token->can('asBinary'))
338 0           { (my $id = $uri) =~ s/^#//;
339              
340 0           my $bst = $bstw->($doc,
341             +{ wsu_Id => $id
342             , ValueType => $type
343             , EncodingType => $binenc
344             , _ => wsm_encoded($binenc, $token->asBinary)
345             } );
346 0           $sec->appendChild($bst);
347             }
348 0           $refer->($doc, $token, $sec, +{'wsse:Reference' => $elem});
349 0           };
350             }
351              
352             #-----------------
353              
354             1;