File Coverage

blib/lib/XML/Enc.pm
Criterion Covered Total %
statement 428 473 90.4
branch 88 136 64.7
condition 28 50 56.0
subroutine 55 57 96.4
pod 3 3 100.0
total 602 719 83.7


line stmt bran cond sub pod time code
1 10     10   1309232 use strict;
  10         17  
  10         313  
2 10     10   45 use warnings;
  10         17  
  10         727  
3              
4             package XML::Enc;
5             our $VERSION = '0.16'; # VERSION
6              
7             # ABSTRACT: XML::Enc Encryption Support
8              
9 10     10   42 use Carp;
  10         16  
  10         686  
10 10     10   3617 use Crypt::AuthEnc::GCM 0.062;
  10         27598  
  10         512  
11 10     10   3534 use Crypt::Mode::CBC;
  10         7790  
  10         314  
12 10     10   4341 use Crypt::PK::RSA 0.081;
  10         100324  
  10         451  
13 10     10   54 use Crypt::PRNG qw( random_bytes );
  10         14  
  10         394  
14 10     10   5430 use MIME::Base64 qw/decode_base64 encode_base64/;
  10         7873  
  10         567  
15 10     10   5735 use XML::LibXML;
  10         438255  
  10         54  
16              
17             # state means perl 5.10
18 10     10   1269 use feature 'state';
  10         19  
  10         1133  
19 10     10   42 use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
  10         12  
  10         49663  
20              
21             our $DEBUG = 0;
22              
23              
24             # Source: https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Alg-Block
25             # 5.2.1 Triple DES - 64 bit Initialization Vector (IV) (8 bytes)
26             # 5.2.2 AES - 128 bit initialization vector (IV) (16 bytes)
27              
28             sub _assert_symmetric_algorithm {
29 464     464   590 my $algo = shift;
30              
31 464         799 state $SYMMETRIC = {
32             'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => {
33             ivsize => 8,
34             keysize => 24,
35             modename => 'DES_EDE'
36             },
37             'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => {
38             ivsize => '16',
39             keysize => 16,
40             modename => 'AES'
41             },
42             'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => {
43             ivsize => '16',
44             keysize => 24,
45             modename => 'AES'
46             },
47             'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => {
48             ivsize => '16',
49             keysize => 32,
50             modename => 'AES'
51             },
52             'http://www.w3.org/2009/xmlenc11#aes128-gcm' => {
53             ivsize => '12',
54             keysize => 16,
55             modename => 'AES',
56             tagsize => 16
57             },
58             'http://www.w3.org/2009/xmlenc11#aes192-gcm' => {
59             ivsize => '12',
60             keysize => 24,
61             modename => 'AES',
62             tagsize => 16
63             },
64             'http://www.w3.org/2009/xmlenc11#aes256-gcm' => {
65             ivsize => '12',
66             keysize => 32,
67             modename => 'AES',
68             tagsize => 16
69             },
70             };
71              
72 464 50       3205 die "Unsupported symmetric algo $algo" unless $SYMMETRIC->{ $algo };
73 464         762 return $SYMMETRIC->{$algo}
74             }
75              
76             sub _assert_encryption_digest {
77 212     212   334 my $algo = shift;
78              
79 212         305 state $ENC_DIGEST = {
80             'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
81             'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
82             'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
83             'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
84             'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
85             };
86 212 50       721 die "Unsupported encryption digest algo $algo" unless $ENC_DIGEST->{ $algo };
87 212         513 return $ENC_DIGEST->{ $algo };
88             }
89              
90              
91              
92             sub new {
93 240     240 1 1506903 my $class = shift;
94 240         384 my $params = shift;
95 240         458 my $self = {};
96              
97 240         548 bless $self, $class;
98              
99 240 50       1127 if ( exists $params->{ 'key' } ) {
100 240         987 $self->{key} = $params->{ 'key' };
101 240         1111 $self->_load_key( $params->{ 'key' } );
102             }
103 240 100       793 if ( exists $params->{ 'cert' } ) {
104 227         561 $self->{cert} = $params->{ 'cert' };
105 227         952 $self->_load_cert_file( $params->{ 'cert' } );
106             }
107 240 50       658 if (exists $params->{'no_xml_declaration'}) {
108 240 50       790 $self->{'no_xml_declaration'} = $params->{'no_xml_declaration'} ? $params->{'no_xml_declaration'} : 0;
109             }
110              
111 240 100       861 my $enc_method = exists($params->{'data_enc_method'}) ? $params->{'data_enc_method'} : 'aes256-cbc';
112 240         966 $self->{'data_enc_method'} = $self->_setEncryptionMethod($enc_method);
113              
114 240 100       666 my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
115 240         707 $self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
116              
117 240 100       648 if (exists $params->{'oaep_mgf_alg'}) {
118 210         764 $self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
119             }
120 240 100       630 if (exists $params->{'oaep_label_hash'} ) {
121 210         638 $self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
122             }
123              
124 240 100       925 $self->{'oaep_params'} = exists($params->{'oaep_params'}) ? $params->{'oaep_params'} : '';
125              
126 240 100       634 $self->{'key_name'} = $params->{'key_name'} if exists($params->{'key_name'});
127              
128 240         858 return $self;
129             }
130              
131              
132             sub decrypt {
133 240     240 1 341639 my $self = shift;
134 240         423 my $xml = shift;
135 240         612 my %options = @_;
136              
137 240         475 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
138              
139 240         1144 my $doc = XML::LibXML->load_xml(
140             string => $xml,
141             no_network => 1,
142             load_ext_dtd => 0,
143             expand_entities => 0
144             );
145              
146 239         69029 my $xpc = XML::LibXML::XPathContext->new($doc);
147 239         1319 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
148 239         719 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
149 239         729 $xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
150 239         525 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
151              
152 239 50       937 return $doc unless $xpc->exists('//xenc:EncryptedData');
153              
154 239 50       8645 die "You cannot decrypt XML without a private key." unless $self->{key_obj};
155              
156 239         733 my $parser = XML::LibXML->new(
157             no_network => 1,
158             load_ext_dtd => 0,
159             expand_entities => 0
160             );
161 239         16255 $self->_decrypt_encrypted_key_nodes($xpc, $parser, %options);
162 239         9867 $self->_decrypt_uri_nodes($xpc, $parser, %options);
163              
164 239         811 return $doc->serialize();
165             }
166              
167             sub _decrypt_encrypted_key_nodes {
168 239     239   371 my $self = shift;
169 239         301 my $xpc = shift;
170 239         353 my $parser = shift;
171 239         410 my %options = @_;
172              
173             my $k = $self->_get_named_key_nodes(
174             '//xenc:EncryptedData/dsig:KeyInfo/xenc:EncryptedKey',
175             $xpc, $options{key_name}
176 239         965 );
177              
178             $k->foreach(
179             sub {
180 238     238   3168 my $key = $self->_get_key_from_node($_, $xpc);
181 238 100       716 return unless $key;
182 237         3749 my $encrypted_node = $_->parentNode->parentNode;
183 237         1946 $self->_decrypt_encrypted_node($encrypted_node,
184             $key, $xpc, $parser);
185             }
186 239         1956 );
187             }
188              
189             sub _decrypt_uri_nodes {
190 239     239   412 my $self = shift;
191 239         429 my $xpc = shift;
192 239         450 my $parser = shift;
193 239         473 my %options = @_;
194              
195 239         768 my $uri_nodes = $xpc->findnodes('//dsig:KeyInfo/dsig:RetrievalMethod/@URI');
196 239     1   8871 my @uri_nodes = $uri_nodes->map(sub { my $v = $_->getValue; $v =~ s/^#//; return $v; });
  1         14  
  1         6  
  1         5  
197              
198 239         4925 foreach my $uri (@uri_nodes) {
199             my $encrypted_key_nodes = $self->_get_named_key_nodes(
200             sprintf('//xenc:EncryptedKey[@Id="%s"]', $uri),
201 1         3 $xpc, $options{key_name});
202              
203             $encrypted_key_nodes->foreach(
204             sub {
205              
206 1     1   8 my $key = $self->_get_key_from_node($_, $xpc);
207 1 50       5 return unless $key;
208              
209 1         10 my $encrypted_nodes = $xpc->findnodes(sprintf('//dsig:KeyInfo/dsig:RetrievalMethod[@URI="#%s"]/../..', $uri));
210 1 50       93 return unless $encrypted_nodes->size;
211              
212             $encrypted_nodes->foreach(sub {
213 1         10 $self->_decrypt_encrypted_node(
214             $_,
215             $key,
216             $xpc,
217             $parser
218             );
219 1         14 });
220              
221             # We don't need the encrypted key here
222 1         25 $_->removeChildNodes();
223             }
224 1         6 );
225             }
226             }
227              
228             sub _get_named_key_nodes {
229 240     240   303 my $self = shift;
230 240         293 my $xpath = shift;
231 240         362 my $xpc = shift;
232 240         468 my $name = shift;
233              
234 240         566 my $nodes = $xpc->findnodes($xpath);
235 240 50       9021 return $nodes unless $name;
236             return $nodes->grep(
237             sub {
238 0     0   0 $xpc->findvalue('dsig:KeyInfo/dsig:KeyName', $_) eq $name;
239             }
240 0         0 );
241             }
242              
243             sub _decrypt_encrypted_node {
244 238     238   6631 my $self = shift;
245 238         364 my $node = shift;
246 238         449 my $key = shift;
247 238         306 my $xpc = shift;
248 238         335 my $parser = shift;
249              
250 238         806 my $algo = $self->_get_encryption_algorithm($node, $xpc);
251 238         4940 my $cipher_value = $self->_get_cipher_value($node, $xpc);
252 238         6103 my $oaep = $self->_get_oaep_params($node, $xpc);
253              
254 238         993 my $decrypted_data = $self->_DecryptData($algo, $key, $cipher_value);
255              
256             # Sooo.. parse_balanced_chunk breaks when there is a 257             # bit in the decrypted data and thus we have to remove it.
258             # We try parsing the XML here and if that works we get all the nodes
259 238         447 my $new = eval { $parser->load_xml(string => $decrypted_data)->findnodes('//*')->[0]; };
  238         1221  
260              
261 238 100       84138 if ($new) {
262 236         2546 $node->addSibling($new);
263 236         385 $node->unbindNode();
264 236         2651 return;
265             }
266              
267 2         11 $decrypted_data = $parser->parse_balanced_chunk($decrypted_data);
268 2 50 50     341 if (($node->parentNode->localname //'') eq 'EncryptedID') {
269 0         0 $node->parentNode->replaceNode($decrypted_data);
270 0         0 return;
271             }
272 2         7 $node->replaceNode($decrypted_data);
273 2         100 return;
274             }
275              
276             sub _get_key_from_node {
277 239     239   320 my $self = shift;
278 239         352 my $node = shift;
279 239         286 my $xpc = shift;
280              
281 239         484 my $algo = $self->_get_encryption_algorithm($_, $xpc);
282 239         3936 my $cipher_value = $self->_get_cipher_value($_, $xpc);
283 239         7210 my $digest_name = $self->_get_digest_method($_, $xpc);
284 239         466 my $oaep = $self->_get_oaep_params($_, $xpc);
285 239         2651 my $mgf = $self->_get_mgf($_, $xpc);
286              
287 239         793 return $self->_decrypt_key(
288             $cipher_value,
289             $algo,
290             $digest_name,
291             $oaep,
292             $mgf,
293             );
294             }
295              
296             sub _get_encryption_algorithm {
297 477     477   634 my $self = shift;
298 477         730 my $node = shift;
299 477         591 my $xpc = shift;
300              
301 477         1887 my $nodes = $xpc->findnodes('./xenc:EncryptionMethod/@Algorithm', $node);
302 477 50       26540 return $nodes->get_node(1)->getValue if $nodes->size;
303 0         0 confess "Unable to determine encryption method algorithm from " . $node->nodePath;
304             }
305              
306             sub _get_cipher_value {
307 477     477   716 my $self = shift;
308 477         546 my $node = shift;
309 477         592 my $xpc = shift;
310              
311 477         888 my $nodes = $xpc->findnodes('./xenc:CipherData/xenc:CipherValue', $node);
312 477 50       13578 return decode_base64($nodes->get_node(1)->textContent) if $nodes->size;
313 0         0 confess "Unable to get the CipherValue from " . $node->nodePath;
314             }
315              
316             sub _get_mgf {
317 239     239   322 my $self = shift;
318 239         435 my $node = shift;
319 239         296 my $xpc = shift;
320              
321 239         433 my $value = $xpc->findvalue('./xenc:EncryptionMethod/xenc11:MGF/@Algorithm', $node);
322 239 100       10011 return $value if $value;
323 64         113 return;
324             }
325              
326             sub _get_oaep_params {
327 477     477   676 my $self = shift;
328 477         637 my $node = shift;
329 477         650 my $xpc = shift;
330              
331 477         1043 my $value = $xpc->findvalue('./xenc:EncryptionMethod/xenc:OAEPparams', $node);
332 477 100       23853 return decode_base64($value) if $value;
333 264         588 return;
334             }
335              
336             sub _get_digest_method {
337 239     239   352 my $self = shift;
338 239         384 my $node = shift;
339 239         335 my $xpc = shift;
340              
341 239         474 my $value = $xpc->findvalue(
342             './xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm', $node);
343 239 100       14262 return _assert_encryption_digest($value) if $value;
344 27         79 return;
345             }
346              
347              
348             sub encrypt {
349 226     226 1 1324 my $self = shift;
350 226         452 my ($xml) = @_;
351              
352 226         435 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
353              
354             # Create the EncryptedData node
355 226         579 my ($encrypted) = $self->_create_encrypted_data_xml();
356              
357 226         12742 my $dom = XML::LibXML->load_xml(
358             string => $xml,
359             no_network => 1,
360             load_ext_dtd => 0,
361             expand_entities => 0
362             );
363              
364 226         55558 my $xpc = XML::LibXML::XPathContext->new($encrypted);
365 226         1398 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
366 226         676 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
367 226         629 $xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
368 226         485 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
369              
370             # Encrypt the data an empty key is passed by reference to allow
371             # the key to be generated at the same time the data is being encrypted
372 226         374 my $key;
373 226         373 my $method = $self->{data_enc_method};
374 226         552 my $encrypteddata = $self->_EncryptData ($method, $dom->serialize(), \$key);
375              
376             # Encrypt the Key immediately after the data is encrypted. It is passed by
377             # reference to reduce the number of times that the unencrypted key is
378             # stored in memory
379 226         5646 $self->_EncryptKey($self->{key_transport}, \$key);
380              
381 226         1317 my $base64_key = encode_base64($key);
382 226         605 my $base64_data = encode_base64($encrypteddata);
383              
384             # Insert KeyName into the XML
385 226 100 66     1162 if (defined $self->{key_name} and $self->{key_name} ne '') {
386 224         622 $encrypted = $self->_setKeyName($encrypted, $xpc, $self->{key_name});
387             }
388              
389             # Insert OAEPparams into the XML
390 226 100       2910 if ($self->{oaep_params} ne '') {
391 211         793 $encrypted = $self->_setOAEPparams($encrypted, $xpc, encode_base64($self->{oaep_params}));
392             }
393              
394             # Insert Encrypted data into XML
395 226         2590 $encrypted = $self->_setEncryptedData($encrypted, $xpc, $base64_data);
396              
397             # Insert the Encrypted Key into the XML
398 226         2219 $self->_setKeyEncryptedData($encrypted, $xpc, $base64_key);
399              
400 226         2211 return $encrypted->serialize();
401             }
402              
403             sub _setEncryptionMethod {
404 240     240   393 my $self = shift;
405 240         506 my $method = shift;
406              
407 240         1921 my %methods = (
408             'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
409             'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes192-cbc',
410             'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
411             'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
412             'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
413             'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm',
414             'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm',
415             );
416              
417 240 50       1088 return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
418             }
419              
420             sub _setKeyName {
421 224     224   272 my $self = shift;
422 224         343 my $context = shift;
423 224         224 my $xpc = shift;
424 224         244 my $keyname = shift;
425              
426 224         856 my $node = $xpc->findnodes('//xenc:EncryptedKey/dsig:KeyInfo/dsig:KeyName', $context);
427              
428 224         13067 $node->[0]->removeChildNodes();
429 224 50       1222 $node->[0]->appendText(defined $keyname ? $keyname : 'key_name');
430 224         812 return $context;
431             }
432              
433             sub _setOAEPparams {
434 211     211   344 my $self = shift;
435 211         280 my $context = shift;
436 211         217 my $xpc = shift;
437 211         278 my $oaep_params = shift;
438              
439 211         438 my $node = $xpc->findnodes('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
440              
441 211         7219 $node->[0]->removeChildNodes();
442 211         680 $node->[0]->appendText($oaep_params);
443 211         442 return $context;
444             }
445              
446             sub _setOAEPAlgorithm {
447 210     210   333 my $self = shift;
448 210         318 my $method = shift;
449              
450 210         313 state $setOAEPAlgorithm = {
451             'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
452             'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
453             'mgf1sha256' => 'http://www.w3.org/2009/xmlenc11#mgf1sha256',
454             'mgf1sha384' => 'http://www.w3.org/2009/xmlenc11#mgf1sha384',
455             'mgf1sha512' => 'http://www.w3.org/2009/xmlenc11#mgf1sha512',
456             };
457              
458 210   66     1358 return $setOAEPAlgorithm->{$method} // $setOAEPAlgorithm->{'rsa-oaep-mgf1p'};
459             }
460              
461             sub _getOAEPAlgorithm {
462 350     350   512 my $self = shift;
463 350         490 my $method = shift;
464              
465 350         491 state $OAEPAlgorithm = {
466             'http://www.w3.org/2009/xmlenc11#mgf1sha1' => 'SHA1',
467             'http://www.w3.org/2009/xmlenc11#mgf1sha224' => 'SHA224',
468             'http://www.w3.org/2009/xmlenc11#mgf1sha256' => 'SHA256',
469             'http://www.w3.org/2009/xmlenc11#mgf1sha384' => 'SHA384',
470             'http://www.w3.org/2009/xmlenc11#mgf1sha512' => 'SHA512',
471             };
472              
473 350   50     5519711 return $OAEPAlgorithm->{$method} // 'SHA1';
474             }
475              
476             sub _setOAEPDigest {
477 210     210   332 my $self = shift;
478 210         345 my $method = shift;
479              
480 210         353 state $OAEPDigest = {
481             'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
482             'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
483             'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
484             'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
485             'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
486             };
487              
488 210   33     716 return $OAEPDigest->{$method} // $OAEPDigest->{'sha256'};
489             }
490              
491             sub _getParamsAlgorithm {
492 210     210   269 my $self = shift;
493 210         303 my $method = shift;
494              
495 210         293 state $ParamsAlgorithm = {
496             'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
497             'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
498             'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
499             'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
500             'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
501             };
502              
503 210   33     680 return $ParamsAlgorithm->{$method} // $ParamsAlgorithm->{'http://www.w3.org/2000/09/xmldsig#sha1'};
504             }
505              
506             sub _setKeyEncryptionMethod {
507 240     240   339 my $self = shift;
508 240         401 my $method = shift;
509              
510 240         337 state $enc_methods = {
511             'rsa-1_5' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
512             'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
513             'rsa-oaep' => 'http://www.w3.org/2009/xmlenc11#rsa-oaep',
514             };
515              
516 240   66     1002 return $enc_methods->{$method} // $enc_methods->{'rsa-oaep-mgf1p'};
517             }
518              
519             sub _DecryptData {
520 238     238   372 my $self = shift;
521 238         376 my $method = shift;
522 238         294 my $key = shift;
523 238         278 my $encrypteddata = shift;
524              
525 238         709 my $method_vars = _assert_symmetric_algorithm($method);
526              
527 238         538 my $ivsize = $method_vars->{ivsize};
528 238         678 my $tagsize = $method_vars->{tagsize};
529              
530 238         685 my $iv = substr $encrypteddata, 0, $ivsize;
531 238         563 my $encrypted = substr $encrypteddata, $ivsize;
532              
533             # XML Encryption 5.2 Block Encryption Algorithms
534             # The resulting cipher text is prefixed by the IV.
535 238 100       1310 if ($method !~ /gcm/ ){
536 142         1470 my $cbc = Crypt::Mode::CBC->new($method_vars->{modename}, 0);
537 142         914 return $self->_remove_padding($cbc->decrypt($encrypted, $key, $iv));
538             }
539              
540 96         20894 my $gcm = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
541              
542             # Note that GCM support for additional authentication
543             # data is not used in the XML specification.
544 96         374 my $tag = substr $encrypted, -$tagsize;
545 96         260 $encrypted = substr $encrypted, 0, (length $encrypted) - $tagsize;
546 96         660 my $plaintext = $gcm->decrypt_add($encrypted);
547              
548 96 50       575 die "Tag expected did not match returned Tag"
549             unless $gcm->decrypt_done($tag);
550              
551 96         586 return $plaintext;
552             }
553              
554             sub _EncryptData {
555 226     226   9648 my $self = shift;
556 226         412 my $method = shift;
557 226         266 my $data = shift;
558 226         290 my $key = shift;
559              
560              
561 226         462 my $method_vars = _assert_symmetric_algorithm($method);
562              
563 226         588 my $ivsize = $method_vars->{ivsize};
564 226         368 my $keysize = $method_vars->{keysize};
565              
566 226         1115 my $iv = random_bytes($ivsize);
567 226         4206 ${$key} = random_bytes($keysize);
  226         1628  
568              
569 226 100       1001 if ($method =~ /gcm/ ){
570             my $gcm
571 96         195 = Crypt::AuthEnc::GCM->new($method_vars->{modename}, ${$key}, $iv);
  96         21321  
572              
573             # Note that GCM support for additional authentication
574             # data is not used in the XML specification.
575 96         727 my $encrypted = $gcm->encrypt_add($data);
576 96         365 my $tag = $gcm->encrypt_done();
577              
578 96         816 return $iv . $encrypted . $tag;
579             }
580              
581 130         1354 my $cbc = Crypt::Mode::CBC->new($method_vars->{modename}, 0);
582             # XML Encryption 5.2 Block Encryption Algorithms
583             # The resulting cipher text is prefixed by the IV.
584 130         411 $data = $self->_add_padding($data, $ivsize);
585 130         245 return $iv . $cbc->encrypt($data, ${$key}, $iv);
  130         510  
586             }
587              
588             sub _decrypt {
589 239     239   384 my $sub = shift;
590 239         266 my $decrypt;
591 239         242 eval { $decrypt = $sub->() };
  239         344  
592 239 100       5557 return $decrypt unless $@;
593 1         16 return;
594             }
595              
596             sub _decrypt_key {
597 239     239   309 my $self = shift;
598 239         274 my $key = shift;
599 239         327 my $algo = shift;
600 239         304 my $digest_name = shift;
601 239         296 my $oaep = shift;
602 239         295 my $mgf = shift;
603              
604 239 100       613 if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
605 12     12   64 return _decrypt(sub{$self->{key_obj}->decrypt($key, 'v1.5')});
  12         384400  
606             }
607              
608 227 100       571 if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
609             return _decrypt(
610             sub {
611 52 50   52   355 if ($CryptX::VERSION lt 0.081) {
612             #print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
613             $self->{key_obj}->decrypt(
614 0   0     0 $key, 'oaep',
      0        
615             #$self->_getOAEPAlgorithm($mgf),
616             $digest_name // 'SHA1',
617             $oaep // '',
618             );
619             } else {
620             #print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
621             #print "digest_name: ", $digest_name, "\n";
622             $self->{key_obj}->decrypt(
623 52   50     1609106 $key, 'oaep',
      100        
      100        
624             $mgf // 'SHA1',
625             $oaep // '',
626             $digest_name // 'SHA1',
627             );
628             }
629             }
630 52         319 );
631             }
632              
633 175 50       458 if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
634             return _decrypt(
635             sub {
636 175 50   175   1117 if ($CryptX::VERSION lt 0.081) {
637             $self->{key_obj}->decrypt(
638 0   0     0 $key, 'oaep',
639             $self->_getOAEPAlgorithm($mgf),
640             $oaep // '',
641             );
642             } else {
643             $self->{key_obj}->decrypt(
644 175   50     485 $key, 'oaep',
      50        
645             $self->_getOAEPAlgorithm($mgf),
646             $oaep // '',
647             $digest_name // '',
648             );
649             }
650             }
651 175         1019 );
652             }
653              
654 0         0 die "Unsupported algorithm for key decryption: $algo";
655             }
656              
657             sub _EncryptKey {
658 226     226   346 my $self = shift;
659 226         303 my $keymethod = shift;
660 226         282 my $key = shift;
661              
662 226         394 my $rsa_pub = $self->{cert_obj};
663              
664             # FIXME: this could use some refactoring and some simplfication
665 226 100       1003 if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
    100          
    50          
666 7         24 ${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
  7         24  
  7         5549  
667             }
668             elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
669 44 50       423 if ($CryptX::VERSION lt 0.081) {
670 0         0 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
  0         0  
  0         0  
671             } else {
672             my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
673 44 100 66     303 $self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
674 44         72 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
  44         145  
  44         33385  
675             }
676             }
677             elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
678             my $mgf_hash = defined $self->{oaep_mgf_alg} ?
679 175 50       770 $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
680 175 50       1495 if ($CryptX::VERSION lt 0.081) {
681 0         0 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
  0         0  
  0         0  
682             } else {
683             my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
684 175 50 33     1045 $self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
685 175         289 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
  175         669  
  175         133295  
686             }
687             } else {
688 0         0 die "Unsupported algorithm for key encyption $keymethod}";
689             }
690              
691 226 50       791 print "Encrypted key: ", encode_base64(${$key}) if $DEBUG;
  0         0  
692             }
693              
694             sub _setEncryptedData {
695 226     226   307 my $self = shift;
696 226         341 my $context = shift;
697 226         295 my $xpc = shift;
698 226         335 my $cipherdata = shift;
699              
700 226         443 my $node = $xpc->findnodes('xenc:EncryptedData/xenc:CipherData/xenc:CipherValue', $context);
701              
702 226         6996 $node->[0]->removeChildNodes();
703 226         694 $node->[0]->appendText($cipherdata);
704 226         441 return $context;
705             }
706              
707             sub _setKeyEncryptedData {
708 226     226   281 my $self = shift;
709 226         290 my $context = shift;
710 226         310 my $xpc = shift;
711 226         269 my $cipherdata = shift;
712              
713 226         281 my $node;
714              
715 226 50       486 if ($xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@Type', $context)
716             eq 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
717             {
718 0         0 my $id = $xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@URI', $context);
719 0         0 $id =~ s/#//g;
720              
721 0         0 my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
722 0 0       0 if (! $keyinfo ) {
723 0         0 die "Unable to find EncryptedKey";
724             }
725              
726 0         0 $node = $keyinfo->[0]->findnodes('//xenc:EncryptedKey/xenc:CipherData', $context)->[0];
727             } else {
728 226         11838 $node = $xpc->findnodes('//dsig:KeyInfo/xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue')->[0];
729             }
730 226         7216 $node->removeChildNodes();
731 226         1163 $node->appendText($cipherdata);
732             }
733              
734             sub _remove_padding {
735 142     142   6978 my $self = shift;
736 142         202 my $padded = shift;
737              
738 142         248 my $len = length $padded;
739 142         309 my $padlen = ord substr $padded, $len - 1;
740 142         789 return substr $padded, 0, $len - $padlen;
741             }
742              
743             sub _add_padding {
744 130     130   214 my $self = shift;
745 130         200 my $data = shift;
746 130         195 my $blksize = shift;
747              
748 130         325 my $len = length $data;
749 130         282 my $padlen = $blksize - ($len % $blksize);
750 130         163 my @pad;
751 130         181 my $n = 0;
752 130         322 while ($n < $padlen -1 ) {
753 130         565 $pad[$n] = 176 + int(rand(80));
754 130         227 $n++;
755             }
756              
757 130         956 return $data . pack ("C*", @pad, $padlen);
758             }
759              
760             ##
761             ## _trim($string)
762             ##
763             ## Arguments:
764             ## $string: string String to remove whitespace
765             ##
766             ## Returns: string Trimmed String
767             ##
768             ## Trim the whitespace from the begining and end of the string
769             ##
770             sub _trim {
771 227     227   424 my $string = shift;
772 227         840 $string =~ s/^\s+//;
773 227         1985 $string =~ s/\s+$//;
774 227         634 return $string;
775             }
776              
777             ##
778             ## _load_key($file)
779             ##
780             ## Arguments: $self->{ key }
781             ##
782             ## Returns: nothing
783             ##
784             ## Load the key and process it acording to its headers
785             ##
786             sub _load_key {
787 240     240   344 my $self = shift;
788 240         545 my $file = $self->{ key };
789              
790 240 50       19686 if ( open my $KEY, '<', $file ) {
791 240         862 my $text = '';
792 240         1355 local $/ = undef;
793 240         5695 $text = <$KEY>;
794 240         2981 close $KEY;
795 240 50       3248 if ( $text =~ m/BEGIN ([DR]SA) PRIVATE KEY/ ) {
    50          
    50          
    0          
796 0         0 my $key_used = $1;
797              
798 0 0       0 if ( $key_used eq 'RSA' ) {
799 0         0 $self->_load_rsa_key( $text );
800             }
801             else {
802 0         0 $self->_load_dsa_key( $text );
803             }
804              
805 0         0 return 1;
806             } elsif ( $text =~ m/BEGIN EC PRIVATE KEY/ ) {
807 0         0 $self->_load_ecdsa_key( $text );
808             } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) {
809 240         1051 $self->_load_rsa_key( $text );
810             } elsif ($text =~ m/BEGIN CERTIFICATE/) {
811 0         0 $self->_load_x509_key( $text );
812             }
813             else {
814 0         0 confess "Could not detect type of key $file.";
815             }
816             }
817             else {
818 0         0 confess "Could not load key $file: $!";
819             }
820              
821 240         1441 return;
822             }
823              
824             ##
825             ## _load_rsa_key($key_text)
826             ##
827             ## Arguments:
828             ## $key_text: string RSA Private Key as String
829             ##
830             ## Returns: nothing
831             ##
832             ## Populate:
833             ## self->{KeyInfo}
834             ## self->{key_obj}
835             ## self->{key_type}
836             ##
837             sub _load_rsa_key {
838 240     240   473 my $self = shift;
839 240         681 my ($key_text) = @_;
840              
841 240         383 eval {
842 240         2438 require Crypt::PK::RSA;
843             };
844 240 50       752 confess "Crypt::PK::RSA needs to be installed so that we can handle RSA keys." if $@;
845              
846 240         3208 my $rsaKey = Crypt::PK::RSA->new(\$key_text );
847              
848 240 50       99841 if ( $rsaKey ) {
849 240         746 $self->{ key_obj } = $rsaKey;
850 240         508 $self->{ key_type } = 'rsa';
851              
852 240 50       865 if (!$self->{ x509 }) {
853 240         112674 my $keyhash = $rsaKey->key2hash();
854              
855 240         2704 $self->{KeyInfo} = "
856            
857            
858             $keyhash->{N}
859             $keyhash->{d}
860            
861            
862             ";
863             }
864             }
865             else {
866 0         0 confess "did not get a new Crypt::PK::RSA object";
867             }
868             }
869              
870             ##
871             ## _load_x509_key($key_text)
872             ##
873             ## Arguments:
874             ## $key_text: string RSA Private Key as String
875             ##
876             ## Returns: nothing
877             ##
878             ## Populate:
879             ## self->{key_obj}
880             ## self->{key_type}
881             ##
882             sub _load_x509_key {
883 0     0   0 my $self = shift;
884 0         0 my $key_text = shift;
885              
886 0         0 eval {
887 0         0 require Crypt::OpenSSL::X509;
888             };
889 0 0       0 confess "Crypt::OpenSSL::X509 needs to be installed so that we
890             can handle X509 Certificates." if $@;
891              
892 0         0 my $x509Key = Crypt::OpenSSL::X509->new_private_key( $key_text );
893              
894 0 0       0 if ( $x509Key ) {
895 0         0 $x509Key->use_pkcs1_padding();
896 0         0 $self->{ key_obj } = $x509Key;
897 0         0 $self->{key_type} = 'x509';
898             }
899             else {
900 0         0 confess "did not get a new Crypt::OpenSSL::X509 object";
901             }
902             }
903              
904             ##
905             ## _load_cert_file()
906             ##
907             ## Arguments: none
908             ##
909             ## Returns: nothing
910             ##
911             ## Read the file name from $self->{ cert } and
912             ## Populate:
913             ## self->{key_obj}
914             ## $self->{KeyInfo}
915             ##
916             sub _load_cert_file {
917 227     227   326 my $self = shift;
918              
919 227         371 eval {
920 227         2904 require Crypt::OpenSSL::X509;
921             };
922              
923 227 50       77284 die "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs.\n" if $@;
924              
925 227         542 my $file = $self->{ cert };
926 227 50       5192 if (!-r $file) {
927 0         0 die "Could not find certificate file $file";
928             }
929 227 50       7147 open my $CERT, '<', $file or die "Unable to open $file\n";
930 227         590 my $text = '';
931 227         939 local $/ = undef;
932 227         3539 $text = <$CERT>;
933 227         1829 close $CERT;
934              
935 227         1719 my $cert = Crypt::PK::RSA->new(\$text);
936 227 50       64683 die "Could not load certificate from $file" unless $cert;
937              
938 227         960 $self->{ cert_obj } = $cert;
939 227         926 my $cert_text = $cert->export_key_pem('public_x509');
940 227         33559 $cert_text =~ s/-----[^-]*-----//gm;
941 227         912 $self->{KeyInfo} = "\n"._trim($cert_text)."\n";
942 227         1384 return;
943             }
944              
945             sub _create_encrypted_data_xml {
946 226     226   317 my $self = shift;
947              
948 226         367 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
949 226         2277 my $doc = XML::LibXML::Document->new();
950              
951 226         377 my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
952 226         361 my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
953 226         325 my $xenc11ns = 'http://www.w3.org/2009/xmlenc11#';
954              
955 226         1077 my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
956             {
957             Type => 'http://www.w3.org/2001/04/xmlenc#Element',
958             }
959             );
960              
961 226         3766 $doc->setDocumentElement ($encdata);
962              
963             my $encmethod = $self->_create_node(
964             $doc,
965             $xencns,
966             $encdata,
967             'xenc:EncryptionMethod',
968             {
969             Algorithm => $self->{data_enc_method},
970             }
971 226         6817 );
972              
973 226         1961 my $keyinfo = $self->_create_node(
974             $doc,
975             $dsigns,
976             $encdata,
977             'dsig:KeyInfo',
978             );
979              
980 226         1855 my $enckey = $self->_create_node(
981             $doc,
982             $xencns,
983             $keyinfo,
984             'xenc:EncryptedKey',
985             );
986              
987             my $kencmethod = $self->_create_node(
988             $doc,
989             $xencns,
990             $enckey,
991             'xenc:EncryptionMethod',
992             {
993             Algorithm => $self->{key_transport},
994             }
995 226         1973 );
996              
997 226 100 100     2574 if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
      100        
998             $self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
999             $self->{oaep_label_hash}) {
1000             my $digestmethod = $self->_create_node(
1001             $doc,
1002             $dsigns,
1003             $kencmethod,
1004             'dsig:DigestMethod',
1005             {
1006             Algorithm => $self->{oaep_label_hash},
1007             }
1008 210         701 );
1009             };
1010              
1011 226 100       1655 if ($self->{'oaep_params'} ne '') {
1012 211         1806 my $oaep_params = $self->_create_node(
1013             $doc,
1014             $xencns,
1015             $kencmethod,
1016             'xenc:OAEPparams',
1017             );
1018             };
1019              
1020 226 50 66     1569 if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1021             $self->{oaep_mgf_alg}) {
1022             my $oaepmethod = $self->_create_node(
1023             $doc,
1024             $xenc11ns,
1025             $kencmethod,
1026             'xenc11:MGF',
1027             {
1028             Algorithm => $self->{oaep_mgf_alg},
1029             }
1030 175         2243 );
1031             };
1032              
1033 226         1661 my $keyinfo2 = $self->_create_node(
1034             $doc,
1035             $dsigns,
1036             $enckey,
1037             'dsig:KeyInfo',
1038             );
1039              
1040 226 100       1978 if (defined $self->{key_name}) {
1041 224         438 my $keyname = $self->_create_node(
1042             $doc,
1043             $dsigns,
1044             $keyinfo2,
1045             'dsig:KeyName',
1046             );
1047             };
1048              
1049 226         1701 my $keycipherdata = $self->_create_node(
1050             $doc,
1051             $xencns,
1052             $enckey,
1053             'xenc:CipherData',
1054             );
1055              
1056 226         1703 my $keyciphervalue = $self->_create_node(
1057             $doc,
1058             $xencns,
1059             $keycipherdata,
1060             'xenc:CipherValue',
1061             );
1062              
1063 226         1748 my $cipherdata = $self->_create_node(
1064             $doc,
1065             $xencns,
1066             $encdata,
1067             'xenc:CipherData',
1068             );
1069              
1070 226         1712 my $ciphervalue = $self->_create_node(
1071             $doc,
1072             $xencns,
1073             $cipherdata,
1074             'xenc:CipherValue',
1075             );
1076              
1077 226         1656 return $doc;
1078             }
1079              
1080             sub _create_node {
1081 3080     3080   5600 my $self = shift;
1082 3080         2759 my $doc = shift;
1083 3080         2883 my $nsuri = shift;
1084 3080         2821 my $parent = shift;
1085 3080         3162 my $name = shift;
1086 3080         3060 my $attributes = shift;
1087              
1088 3080         11928 my $node = $doc->createElementNS ($nsuri, $name);
1089 3080         4837 for (keys %$attributes) {
1090             $node->addChild (
1091             $doc->createAttribute (
1092             #$node->setAttribute (
1093 1063         8038 $_ => $attributes->{$_}
1094             )
1095             );
1096             }
1097 3080         8189 $parent->addChild($node);
1098             }
1099              
1100             1;
1101              
1102             __END__