Skip to content

Commit 9ee6653

Browse files
committed
Fix rsa-oaep when the MGF and OAEPparams digests differ
1 parent 95fd5b9 commit 9ee6653

File tree

6 files changed

+268
-30
lines changed

6 files changed

+268
-30
lines changed

Makefile.PL

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ my %WriteMakefileArgs = (
3030
},
3131
"TEST_REQUIRES" => {
3232
"Crypt::OpenSSL::Guess" => 0,
33+
"CryptX" => 0,
3334
"Exporter" => 0,
3435
"File::Slurper" => 0,
3536
"File::Which" => 0,
@@ -53,6 +54,7 @@ my %FallbackPrereqs = (
5354
"Crypt::OpenSSL::X509" => 0,
5455
"Crypt::PK::RSA" => 0,
5556
"Crypt::PRNG" => 0,
57+
"CryptX" => 0,
5658
"Exporter" => 0,
5759
"File::Slurper" => 0,
5860
"File::Which" => 0,

README

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,37 @@ METHODS
103103

104104
* mgf1sha512 <http://www.w3.org/2009/xmlenc11#mgf1sha512>
105105

106+
oaep_params
107+
Specify the OAEPparams value to use as part of the mask generation
108+
function (MGF). It is optional but can be a specified for rsa-oaep
109+
and rsa-oaep-mgf1p EncryptionMethods.
110+
111+
It is base64 encoded and stored in the XML as OAEPparams.
112+
113+
If specified you MAY specify the oaep_label_hash that should be
114+
used. You should note that not all implementations support an
115+
oaep_label_hash that differs from that of the MGF secified in the
116+
xenc11:MGF element or the default MGF1 with SHA1.
117+
118+
The oaep_label_hash is stored in the DigestMethod child element of
119+
the EncryptionMethod.
120+
121+
oaep_label_hash
122+
Specify the Hash Algorithm to use for the rsa-oaep Label. Supported
123+
algorithms are:
124+
125+
The default is sha1.
126+
127+
* sha1 <http://www.w3.org/2000/09/xmldsig#sha1>
128+
129+
* sha224 <http://www.w3.org/2001/04/xmldsig-more#sha224>
130+
131+
* sha256 <http://www.w3.org/2001/04/xmlenc#sha256>
132+
133+
* sha384 <http://www.w3.org/2001/04/xmldsig-more#sha384>
134+
135+
* sha512 <http://www.w3.org/2001/04/xmlenc#sha512>
136+
106137
decrypt( ... )
107138
Main decryption function.
108139

cpanfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ requires "warnings" => "0";
1515

1616
on 'test' => sub {
1717
requires "Crypt::OpenSSL::Guess" => "0";
18+
requires "CryptX" => "0";
1819
requires "Exporter" => "0";
1920
requires "File::Slurper" => "0";
2021
requires "File::Which" => "0";

lib/XML/Enc.pm

Lines changed: 164 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,39 @@ Used in encryption. Optional. Default method: mgf1sha1
167167
168168
=back
169169
170+
=item B<oaep_params>
171+
172+
Specify the OAEPparams value to use as part of the mask generation function (MGF).
173+
It is optional but can be specified for rsa-oaep and rsa-oaep-mgf1p EncryptionMethods.
174+
175+
It is base64 encoded and stored in the XML as OAEPparams.
176+
177+
If specified you MAY specify the oaep_label_hash that should be used. You should note
178+
that not all implementations support an oaep_label_hash that differs from that of the
179+
MGF specified in the xenc11:MGF element or the default MGF1 with SHA1.
180+
181+
The oaep_label_hash is stored in the DigestMethod child element of the EncryptionMethod.
182+
183+
=item B<oaep_label_hash>
184+
185+
Specify the Hash Algorithm to use for the rsa-oaep label as specified by oaep_params.
186+
187+
The default is sha1. Supported algorithms are:
188+
189+
=over
190+
191+
=item * L<sha1|http://www.w3.org/2000/09/xmldsig#sha1>
192+
193+
=item * L<sha224|http://www.w3.org/2001/04/xmldsig-more#sha224>
194+
195+
=item * L<sha256|http://www.w3.org/2001/04/xmlenc#sha256>
196+
197+
=item * L<sha384|http://www.w3.org/2001/04/xmldsig-more#sha384>
198+
199+
=item * L<sha512|http://www.w3.org/2001/04/xmlenc#sha512>
200+
201+
=back
202+
170203
=back
171204
172205
=cut
@@ -196,8 +229,12 @@ sub new {
196229
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
197230
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
198231

199-
my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
200-
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
232+
if (exists $params->{'oaep_mgf_alg'}) {
233+
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
234+
}
235+
if (exists $params->{'oaep_label_hash'} ) {
236+
$self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
237+
}
201238

202239
$self->{'oaep_params'} = exists($params->{'oaep_params'}) ? $params->{'oaep_params'} : '';
203240

@@ -233,6 +270,7 @@ sub decrypt {
233270
my $xpc = XML::LibXML::XPathContext->new($doc);
234271
$xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
235272
$xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
273+
$xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
236274
$xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
237275

238276
my $data;
@@ -383,6 +421,8 @@ sub _setOAEPAlgorithm {
383421
my $self = shift;
384422
my $method = shift;
385423

424+
return if ! defined $method;
425+
386426
my %methods = (
387427
'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
388428
'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
@@ -398,6 +438,8 @@ sub _getOAEPAlgorithm {
398438
my $self = shift;
399439
my $method = shift;
400440

441+
return if ! defined $method;
442+
401443
my %methods = (
402444
'http://www.w3.org/2009/xmlenc11#mgf1sha1' => 'SHA1',
403445
'http://www.w3.org/2009/xmlenc11#mgf1sha224' => 'SHA224',
@@ -409,6 +451,41 @@ sub _getOAEPAlgorithm {
409451
return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
410452
}
411453

454+
sub _setOAEPDigest {
455+
my $self = shift;
456+
my $method = shift;
457+
458+
return if ! defined $method;
459+
460+
my %methods = (
461+
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
462+
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
463+
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
464+
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
465+
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
466+
);
467+
468+
return exists($methods{$method}) ? $methods{$method} : $methods{'sha256'};
469+
}
470+
471+
sub _getParamsAlgorithm {
472+
my $self = shift;
473+
my $method = shift;
474+
475+
return if ! defined $method;
476+
477+
my %methods = (
478+
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
479+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
480+
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
481+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
482+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
483+
);
484+
485+
return exists($methods{$method}) ? $methods{$method} :
486+
$methods{'http://www.w3.org/2000/09/xmldsig#sha1'};
487+
}
488+
412489
sub _getKeyEncryptionMethod {
413490
my $self = shift;
414491
my $xpc = shift;
@@ -422,19 +499,45 @@ sub _getKeyEncryptionMethod {
422499
$id =~ s/#//g;
423500

424501
my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
502+
$keyinfo->[0]->setNamespace('http://www.w3.org/2000/09/xmldsig#', 'dsig', 0);
503+
$keyinfo->[0]->setNamespace('http://www.w3.org/2001/04/xmlenc#', 'xenc', 0);
504+
$keyinfo->[0]->setNamespace('http://www.w3.org/2009/xmlenc11#', 'xenc11', 0);
425505
if (! $keyinfo ) {
426506
die "Unable to find EncryptedKey";
427507
}
428508
$method{Algorithm} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
429509
$method{KeySize} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
430510
$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);
511+
512+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
513+
$method{MGF} = $keyinfo->[0]->findvalue(
514+
'//xenc:EncryptedKey/xenc:EncryptionMethod/xmlenc11:MGF/@Algorithm'
515+
, $context);
516+
}
517+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
518+
$method{MGF} = undef;
519+
}
520+
521+
$method{oaep_label_hash} = $keyinfo->[0]->findvalue(
522+
'xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm',
523+
$context) || _setOAEPDigest('sha1');
432524
return \%method;
433525
}
434526
$method{Algorithm} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
435527
$method{KeySize} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
436528
$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);
529+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
530+
$method{MGF} = $xpc->findvalue(
531+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc11:MGF/@Algorithm',
532+
$context);
533+
}
534+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
535+
$method{MGF} = undef;
536+
}
537+
$method{oaep_label_hash} = $xpc->findvalue(
538+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm',
539+
$context) || _setOAEPDigest('sha1');
540+
438541
return \%method;
439542
}
440543

@@ -534,10 +637,30 @@ sub _DecryptKey {
534637
return $self->{key_obj}->decrypt($encryptedkey, 'v1.5');
535638
}
536639
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}));
640+
my $oaep_params = defined $keymethod->{OAEPparams} ?
641+
decode_base64(_trim($keymethod->{OAEPparams}) ) : undef;
642+
my $oaep_label_hash = defined $keymethod->{oaep_label_hash} ?
643+
$self->_getParamsAlgorithm($keymethod->{oaep_label_hash}) : 'SHA1';
644+
645+
if ($CryptX::VERSION le 0.077) {
646+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', $oaep_params);
647+
} else {
648+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', $oaep_params, $oaep_label_hash);
649+
}
538650
}
539651
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}));
652+
my $oaep_params = defined $keymethod->{OAEPparams} ?
653+
decode_base64(_trim($keymethod->{OAEPparams}) ) : undef;
654+
my $mgf_hash = defined $keymethod->{MGF} ? $self->_getOAEPAlgorithm($keymethod->{MGF}) : undef;
655+
my $oaep_label_hash = defined $keymethod->{oaep_label_hash} ?
656+
$self->_getParamsAlgorithm($keymethod->{oaep_label_hash}) :
657+
$mgf_hash;
658+
659+
if ($CryptX::VERSION le 0.077) {
660+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $mgf_hash, $oaep_params);
661+
} else {
662+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $mgf_hash, $oaep_params, $oaep_label_hash);
663+
}
541664
} else {
542665
die "Unsupported Key Encryption Method";
543666
}
@@ -557,10 +680,24 @@ sub _EncryptKey {
557680
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
558681
}
559682
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
560-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
683+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
684+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
685+
if ($CryptX::VERSION le 0.077) {
686+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
687+
} else {
688+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
689+
}
561690
}
562691
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});
692+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
693+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
694+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
695+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
696+
if ($CryptX::VERSION le 0.077) {
697+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
698+
} else {
699+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
700+
}
564701
} else {
565702
die "Unsupported Key Encryption Method";
566703
}
@@ -861,6 +998,7 @@ sub _create_encrypted_data_xml {
861998
my $doc = XML::LibXML::Document->new();
862999

8631000
my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
1001+
my $xenc11ns = 'http://www.w3.org/2009/xmlenc11#';
8641002
my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
8651003

8661004
my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
@@ -914,12 +1052,27 @@ sub _create_encrypted_data_xml {
9141052
);
9151053
};
9161054

917-
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1055+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
1056+
$self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
1057+
$self->{oaep_label_hash}) {
1058+
my $digestmethod = $self->_create_node(
1059+
$doc,
1060+
$dsigns,
1061+
$kencmethod,
1062+
'dsig:DigestMethod',
1063+
{
1064+
Algorithm => $self->{oaep_label_hash},
1065+
}
1066+
);
1067+
};
1068+
1069+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1070+
$self->{oaep_mgf_alg}) {
9181071
my $oaepmethod = $self->_create_node(
9191072
$doc,
920-
$xencns,
1073+
$xenc11ns,
9211074
$kencmethod,
922-
'xenc:MGF',
1075+
'xenc11:MGF',
9231076
{
9241077
Algorithm => $self->{oaep_mgf_alg},
9251078
}

0 commit comments

Comments
 (0)