Skip to content

Commit 30117c9

Browse files
committed
Fix rsa-oaep when the MGF and OAEPparams digests differ
1 parent f71c65e commit 30117c9

File tree

2 files changed

+136
-22
lines changed

2 files changed

+136
-22
lines changed

lib/XML/Enc.pm

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ sub new {
199199
my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
200200
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
201201

202+
my $oaep_digest = exists($params->{'oaep_digest'}) ? $params->{'oaep_digest'} : $oaep_mgf_alg;
203+
$self->{'oaep_digest'} = $self->_setOAEPDigest($oaep_digest);
204+
202205
$self->{'oaep_params'} = exists($params->{'oaep_params'}) ? $params->{'oaep_params'} : '';
203206

204207
return $self;
@@ -233,6 +236,7 @@ sub decrypt {
233236
my $xpc = XML::LibXML::XPathContext->new($doc);
234237
$xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
235238
$xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
239+
$xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
236240
$xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
237241

238242
my $data;
@@ -383,6 +387,8 @@ sub _setOAEPAlgorithm {
383387
my $self = shift;
384388
my $method = shift;
385389

390+
return if ! defined $method;
391+
386392
my %methods = (
387393
'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
388394
'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
@@ -409,6 +415,43 @@ sub _getOAEPAlgorithm {
409415
return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
410416
}
411417

418+
sub _setOAEPDigest {
419+
my $self = shift;
420+
my $method = shift;
421+
422+
return if ! defined $method;
423+
424+
my %methods = (
425+
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
426+
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
427+
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
428+
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
429+
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
430+
'mgf1sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
431+
'mgf1sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
432+
'mgf1sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
433+
'mgf1sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
434+
'mgf1sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
435+
);
436+
437+
return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2001/04/xmlenc#sha256'};
438+
}
439+
440+
sub _getParamsAlgorithm {
441+
my $self = shift;
442+
my $method = shift;
443+
444+
my %methods = (
445+
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
446+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
447+
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
448+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
449+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
450+
);
451+
452+
return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
453+
}
454+
412455
sub _getKeyEncryptionMethod {
413456
my $self = shift;
414457
my $xpc = shift;
@@ -422,19 +465,54 @@ sub _getKeyEncryptionMethod {
422465
$id =~ s/#//g;
423466

424467
my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
468+
$keyinfo->[0]->setNamespace('http://www.w3.org/2000/09/xmldsig#', 'dsig', 0);
469+
$keyinfo->[0]->setNamespace('http://www.w3.org/2001/04/xmlenc#', 'xenc', 0);
470+
$keyinfo->[0]->setNamespace('http://www.w3.org/2009/xmlenc11#', 'xenc11', 0);
425471
if (! $keyinfo ) {
426472
die "Unable to find EncryptedKey";
427473
}
428-
$method{Algorithm} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
429-
$method{KeySize} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
430-
$method{OAEPparams} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
431-
$method{MGF} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
474+
$method{Algorithm} = $keyinfo->[0]->findvalue(
475+
'//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm',
476+
$context);
477+
$method{KeySize} = $keyinfo->[0]->findvalue(
478+
'//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize',
479+
$context);
480+
$method{OAEPparams} = $keyinfo->[0]->findvalue('
481+
//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams',
482+
$context);
483+
484+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
485+
$method{MGF} = $keyinfo->[0]->findvalue(
486+
'//xenc:EncryptedKey/xenc:EncryptionMethod/xmlenc11:MGF/@Algorithm'
487+
, $context);
488+
}
489+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
490+
$method{MGF} = undef;
491+
}
492+
493+
$method{oaep_digest} = $keyinfo->[0]->findvalue(
494+
'xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm',
495+
$context) || 'http://www.w3.org/2000/09/xmldsig#sha1';
432496
return \%method;
433497
}
434-
$method{Algorithm} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
435-
$method{KeySize} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
436-
$method{OAEPparams} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
437-
$method{MGF} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
498+
$method{Algorithm} = $xpc->findvalue(
499+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm',
500+
$context);
501+
$method{KeySize} = $xpc->findvalue(
502+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize',
503+
$context);
504+
$method{OAEPparams} = $xpc->findvalue(
505+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams',
506+
$context);
507+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
508+
$method{MGF} = $xpc->findvalue(
509+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc11:MGF/@Algorithm',
510+
$context);
511+
}
512+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
513+
$method{MGF} = undef;
514+
}
515+
$method{oaep_digest} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm', $context) || 'http://www.w3.org/2000/09/xmldsig#sha1';
438516
return \%method;
439517
}
440518

@@ -534,10 +612,20 @@ sub _DecryptKey {
534612
return $self->{key_obj}->decrypt($encryptedkey, 'v1.5');
535613
}
536614
elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
537-
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', decode_base64($keymethod->{OAEPparams}));
615+
my $oaep_params = defined $keymethod->{OAEPparams} ?
616+
decode_base64(_trim($keymethod->{OAEPparams}) ) : undef;
617+
my $params_hash = defined $keymethod->{oaep_digest} ?
618+
$self->_getParamsAlgorithm($keymethod->{oaep_digest}) : 'SHA1';
619+
620+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', $params_hash, $oaep_params);
538621
}
539622
elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
540-
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $self->_getOAEPAlgorithm($keymethod->{MGF}), decode_base64($keymethod->{OAEPparams}));
623+
my $oaep_params = defined $keymethod->{OAEPparams} ?
624+
decode_base64(_trim($keymethod->{OAEPparams}) ) : '';
625+
my $mgf_hash = defined $keymethod->{MGF} ? $self->_getOAEPAlgorithm($keymethod->{MGF}) : undef;
626+
my $params_hash = defined $keymethod->{oaep_digest} ? $self->_getParamsAlgorithm($keymethod->{oaep_digest}) : $mgf_hash;
627+
628+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $mgf_hash, $params_hash, $oaep_params);
541629
} else {
542630
die "Unsupported Key Encryption Method";
543631
}
@@ -557,10 +645,12 @@ sub _EncryptKey {
557645
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
558646
}
559647
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
560-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
648+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', 'SHA1', $self->{oaep_params});
561649
}
562650
elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
563-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}), $self->{oaep_params});
651+
my $mgf_hash = defined $self->{oaep_mgf_alg} ? $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
652+
my $params_hash = defined $self->{oaep_digest} & $self->{oaep_digest} ne '' ? $self->_getParamsAlgorithm($self->{oaep_digest}) : $mgf_hash;
653+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $params_hash, $self->{oaep_params});
564654
} else {
565655
die "Unsupported Key Encryption Method";
566656
}
@@ -861,6 +951,7 @@ sub _create_encrypted_data_xml {
861951
my $doc = XML::LibXML::Document->new();
862952

863953
my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
954+
my $xenc11ns = 'http://www.w3.org/2009/xmlenc11#';
864955
my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
865956

866957
my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
@@ -914,12 +1005,24 @@ sub _create_encrypted_data_xml {
9141005
);
9151006
};
9161007

1008+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1009+
my $digestmethod = $self->_create_node(
1010+
$doc,
1011+
$dsigns,
1012+
$kencmethod,
1013+
'dsig:DigestMethod',
1014+
{
1015+
Algorithm => $self->{oaep_digest},
1016+
}
1017+
);
1018+
};
1019+
9171020
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
9181021
my $oaepmethod = $self->_create_node(
9191022
$doc,
920-
$xencns,
1023+
$xenc11ns,
9211024
$kencmethod,
922-
'xenc:MGF',
1025+
'xenc11:MGF',
9231026
{
9241027
Algorithm => $self->{oaep_mgf_alg},
9251028
}

t/06-test-encryption-methods.t

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use strict;
22
use warnings;
3-
use Test::More tests => 126;
3+
use Test::More tests => 756;
44
use Test::Lib;
55
use Test::XML::Enc;
66
use XML::Enc;
@@ -16,9 +16,10 @@ XML
1616
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
1717
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
1818
my @oaep_mgf_algs = qw/mgf1sha1 mgf1sha224 mgf1sha256 mgf1sha384 mgf1sha512/;
19+
my @oaep_digests = qw/sha1 sha224 sha256 sha384 sha512/;
1920

2021
my $xmlsec = get_xmlsec_features();
21-
my $lax_key_search = $xmlsec->{lax_key_search} ? '--lax_key_search': '';
22+
my $lax_key_search = $xmlsec->{lax_key_search} ? '--lax-key-search': '';
2223

2324
foreach my $km (@key_methods) {
2425
foreach my $dm (@data_methods) {
@@ -39,10 +40,6 @@ foreach my $km (@key_methods) {
3940

4041
SKIP: {
4142
skip "xmlsec1 not installed", 2 unless $xmlsec->{installed};
42-
my $version;
43-
if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
44-
$version = $1;
45-
};
4643
skip "xmlsec version 1.2.27 minimum for GCM", 2 if ! $xmlsec->{aes_gcm};
4744
ok( open XML, '>', 'tmp.xml' );
4845
print XML $encrypted;
@@ -56,6 +53,7 @@ foreach my $km (@key_methods) {
5653
}
5754

5855
foreach my $om (@oaep_mgf_algs) {
56+
foreach my $omdig (@oaep_digests) {
5957
foreach my $dm (@data_methods) {
6058
my $encrypter = XML::Enc->new(
6159
{
@@ -64,14 +62,27 @@ foreach my $om (@oaep_mgf_algs) {
6462
data_enc_method => $dm,
6563
key_transport => 'rsa-oaep',
6664
oaep_mgf_alg => $om,
67-
no_xml_declaration => 1
65+
oaep_digest => $omdig,
66+
no_xml_declaration => 1,
6867
}
6968
);
7069

7170
my $encrypted = $encrypter->encrypt($xml);
72-
ok($encrypted =~ /EncryptedData/, "Successfully Encrypted: Key Method 'rsa-oaep' with $om Data Method $dm");
71+
ok($encrypted =~ /EncryptedData/, "Successful Encrypted: Key Method:rsa-oaep MGF:$om, param:$omdig Data Method:$dm");
7372

73+
SKIP: {
74+
skip "xmlsec1 not installed", 2 unless $xmlsec->{installed};
75+
skip "xmlsec version 1.2.27 minimum for GCM", 2 if ! $xmlsec->{aes_gcm};
76+
ok( open XML, '>', "$om-$omdig-$dm-tmp.xml" );
77+
print XML $encrypted;
78+
close XML;
79+
my $verify_response = `xmlsec1 --decrypt $lax_key_search --privkey-pem t/sign-private.pem $om-$omdig-$dm-tmp.xml 2>&1`;
80+
ok( $verify_response =~ m/XML-SIG_1/, "Successfully decrypted with xmlsec1" )
81+
or warn "calling xmlsec1 failed: '$verify_response'\n";
82+
unlink "$om-$omdig-$dm-tmp.xml";
83+
}
7484
ok($encrypter->decrypt($encrypted) =~ /XML-SIG_1/, "Successfully Decrypted with XML::Enc");
7585
}
86+
}
7687
}
7788
done_testing;

0 commit comments

Comments
 (0)