Skip to content

Commit 544b250

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

File tree

6 files changed

+267
-30
lines changed

6 files changed

+267
-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: 163 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,40 @@ 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{'http://www.w3.org/2001/04/xmlenc#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} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
486+
}
487+
412488
sub _getKeyEncryptionMethod {
413489
my $self = shift;
414490
my $xpc = shift;
@@ -422,19 +498,45 @@ sub _getKeyEncryptionMethod {
422498
$id =~ s/#//g;
423499

424500
my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
501+
$keyinfo->[0]->setNamespace('http://www.w3.org/2000/09/xmldsig#', 'dsig', 0);
502+
$keyinfo->[0]->setNamespace('http://www.w3.org/2001/04/xmlenc#', 'xenc', 0);
503+
$keyinfo->[0]->setNamespace('http://www.w3.org/2009/xmlenc11#', 'xenc11', 0);
425504
if (! $keyinfo ) {
426505
die "Unable to find EncryptedKey";
427506
}
428507
$method{Algorithm} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
429508
$method{KeySize} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
430509
$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);
510+
511+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
512+
$method{MGF} = $keyinfo->[0]->findvalue(
513+
'//xenc:EncryptedKey/xenc:EncryptionMethod/xmlenc11:MGF/@Algorithm'
514+
, $context);
515+
}
516+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
517+
$method{MGF} = undef;
518+
}
519+
520+
$method{oaep_label_hash} = $keyinfo->[0]->findvalue(
521+
'xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm',
522+
$context) || _setOAEPDigest('sha1');
432523
return \%method;
433524
}
434525
$method{Algorithm} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
435526
$method{KeySize} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
436527
$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);
528+
if ($method{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
529+
$method{MGF} = $xpc->findvalue(
530+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc11:MGF/@Algorithm',
531+
$context);
532+
}
533+
if ($method{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
534+
$method{MGF} = undef;
535+
}
536+
$method{oaep_label_hash} = $xpc->findvalue(
537+
'dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm',
538+
$context) || _setOAEPDigest('sha1');
539+
438540
return \%method;
439541
}
440542

@@ -534,10 +636,30 @@ sub _DecryptKey {
534636
return $self->{key_obj}->decrypt($encryptedkey, 'v1.5');
535637
}
536638
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}));
639+
my $oaep_params = defined $keymethod->{OAEPparams} ?
640+
decode_base64(_trim($keymethod->{OAEPparams}) ) : undef;
641+
my $oaep_label_hash = defined $keymethod->{oaep_label_hash} ?
642+
$self->_getParamsAlgorithm($keymethod->{oaep_label_hash}) : 'SHA1';
643+
644+
if ($CryptX::VERSION le 0.077) {
645+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', $oaep_params);
646+
} else {
647+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', $oaep_params, $oaep_label_hash);
648+
}
538649
}
539650
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}));
651+
my $oaep_params = defined $keymethod->{OAEPparams} ?
652+
decode_base64(_trim($keymethod->{OAEPparams}) ) : '';
653+
my $mgf_hash = defined $keymethod->{MGF} ? $self->_getOAEPAlgorithm($keymethod->{MGF}) : undef;
654+
my $oaep_label_hash = defined $keymethod->{oaep_label_hash} ?
655+
$self->_getParamsAlgorithm($keymethod->{oaep_label_hash}) :
656+
$mgf_hash;
657+
658+
if ($CryptX::VERSION le 0.077) {
659+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $mgf_hash, $oaep_params);
660+
} else {
661+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $mgf_hash, $oaep_params, $oaep_label_hash);
662+
}
541663
} else {
542664
die "Unsupported Key Encryption Method";
543665
}
@@ -557,10 +679,24 @@ sub _EncryptKey {
557679
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
558680
}
559681
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
560-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
682+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
683+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
684+
if ($CryptX::VERSION le 0.077) {
685+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
686+
} else {
687+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
688+
}
561689
}
562690
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});
691+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
692+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
693+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
694+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
695+
if ($CryptX::VERSION le 0.077) {
696+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
697+
} else {
698+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
699+
}
564700
} else {
565701
die "Unsupported Key Encryption Method";
566702
}
@@ -861,6 +997,7 @@ sub _create_encrypted_data_xml {
861997
my $doc = XML::LibXML::Document->new();
862998

863999
my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
1000+
my $xenc11ns = 'http://www.w3.org/2009/xmlenc11#';
8641001
my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
8651002

8661003
my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
@@ -914,12 +1051,27 @@ sub _create_encrypted_data_xml {
9141051
);
9151052
};
9161053

917-
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1054+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
1055+
$self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
1056+
$self->{oaep_label_hash}) {
1057+
my $digestmethod = $self->_create_node(
1058+
$doc,
1059+
$dsigns,
1060+
$kencmethod,
1061+
'dsig:DigestMethod',
1062+
{
1063+
Algorithm => $self->{oaep_label_hash},
1064+
}
1065+
);
1066+
};
1067+
1068+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1069+
$self->{oaep_mgf_alg}) {
9181070
my $oaepmethod = $self->_create_node(
9191071
$doc,
920-
$xencns,
1072+
$xenc11ns,
9211073
$kencmethod,
922-
'xenc:MGF',
1074+
'xenc11:MGF',
9231075
{
9241076
Algorithm => $self->{oaep_mgf_alg},
9251077
}

0 commit comments

Comments
 (0)