Skip to content

Commit 753e6fc

Browse files
committed
Fix MGF support
1 parent 0d24262 commit 753e6fc

File tree

3 files changed

+217
-35
lines changed

3 files changed

+217
-35
lines changed

lib/XML/Enc.pm

Lines changed: 147 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ sub _assert_encryption_digest {
107107
state $ENC_DIGEST = {
108108
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
109109
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
110+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
111+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
112+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
110113
};
111-
112114
die "Unsupported encryption digest algo $algo" unless $ENC_DIGEST->{ $algo };
113115
return $ENC_DIGEST->{ $algo };
114116
}
@@ -198,6 +200,39 @@ Used in encryption. Optional. Default method: mgf1sha1
198200
199201
=back
200202
203+
=item B<oaep_params>
204+
205+
Specify the OAEPparams value to use as part of the mask generation function (MGF).
206+
It is optional but can be specified for rsa-oaep and rsa-oaep-mgf1p EncryptionMethods.
207+
208+
It is base64 encoded and stored in the XML as OAEPparams.
209+
210+
If specified you MAY specify the oaep_label_hash that should be used. You should note
211+
that not all implementations support an oaep_label_hash that differs from that of the
212+
MGF specified in the xenc11:MGF element or the default MGF1 with SHA1.
213+
214+
The oaep_label_hash is stored in the DigestMethod child element of the EncryptionMethod.
215+
216+
=item B<oaep_label_hash>
217+
218+
Specify the Hash Algorithm to use for the rsa-oaep label as specified by oaep_params.
219+
220+
The default is sha1. Supported algorithms are:
221+
222+
=over
223+
224+
=item * L<sha1|http://www.w3.org/2000/09/xmldsig#sha1>
225+
226+
=item * L<sha224|http://www.w3.org/2001/04/xmldsig-more#sha224>
227+
228+
=item * L<sha256|http://www.w3.org/2001/04/xmlenc#sha256>
229+
230+
=item * L<sha384|http://www.w3.org/2001/04/xmldsig-more#sha384>
231+
232+
=item * L<sha512|http://www.w3.org/2001/04/xmlenc#sha512>
233+
234+
=back
235+
201236
=cut
202237

203238
sub new {
@@ -225,8 +260,12 @@ sub new {
225260
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
226261
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
227262

228-
my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
229-
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
263+
if (exists $params->{'oaep_mgf_alg'}) {
264+
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
265+
}
266+
if (exists $params->{'oaep_label_hash'} ) {
267+
$self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
268+
}
230269

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

@@ -576,6 +615,36 @@ sub _getOAEPAlgorithm {
576615
return $OAEPAlgorithm->{$method} // 'SHA1';
577616
}
578617

618+
sub _setOAEPDigest {
619+
my $self = shift;
620+
my $method = shift;
621+
622+
state $OAEPDigest = {
623+
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
624+
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
625+
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
626+
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
627+
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
628+
};
629+
630+
return $OAEPDigest->{$method} // $OAEPDigest->{'sha256'};
631+
}
632+
633+
sub _getParamsAlgorithm {
634+
my $self = shift;
635+
my $method = shift;
636+
637+
state $ParamsAlgorithm = {
638+
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
639+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
640+
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
641+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
642+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
643+
};
644+
645+
return $ParamsAlgorithm->{$method} // $ParamsAlgorithm->{'http://www.w3.org/2000/09/xmldsig#sha1'};
646+
}
647+
579648
sub _setKeyEncryptionMethod {
580649
my $self = shift;
581650
my $method = shift;
@@ -681,23 +750,45 @@ sub _decrypt_key {
681750
if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
682751
return _decrypt(
683752
sub {
684-
$self->{key_obj}->decrypt(
685-
$key, 'oaep',
686-
$digest_name // 'SHA1',
687-
$oaep // ''
688-
);
753+
if ($CryptX::VERSION le 0.077) {
754+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
755+
$self->{key_obj}->decrypt(
756+
$key, 'oaep',
757+
#$self->_getOAEPAlgorithm($mgf),
758+
$digest_name // 'SHA1',
759+
$oaep // '',
760+
);
761+
} else {
762+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
763+
#print "digest_name: ", $digest_name, "\n";
764+
$self->{key_obj}->decrypt(
765+
$key, 'oaep',
766+
$mgf // 'SHA1',
767+
$oaep // '',
768+
$digest_name // 'SHA1',
769+
);
770+
}
689771
}
690772
);
691773
}
692774

693775
if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
694776
return _decrypt(
695777
sub {
696-
$self->{key_obj}->decrypt(
697-
$key, 'oaep',
698-
$self->_getOAEPAlgorithm($mgf),
699-
$oaep // '',
700-
);
778+
if ($CryptX::VERSION le 0.077) {
779+
$self->{key_obj}->decrypt(
780+
$key, 'oaep',
781+
$self->_getOAEPAlgorithm($mgf),
782+
$oaep // '',
783+
);
784+
} else {
785+
$self->{key_obj}->decrypt(
786+
$key, 'oaep',
787+
$self->_getOAEPAlgorithm($mgf),
788+
$oaep // '',
789+
$digest_name // '',
790+
);
791+
}
701792
}
702793
);
703794
}
@@ -716,10 +807,35 @@ sub _EncryptKey {
716807
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
717808
}
718809
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
719-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
810+
if ($CryptX::VERSION le 0.077) {
811+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
812+
} else {
813+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
814+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
815+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
816+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
817+
#print "Y_mgf_hash: ", $mgf_hash, "\n";
818+
#print "Xoaep_label_hash: ", $oaep_label_hash, "\n";
819+
#print "Xoaep_params: ", $self->{oaep_params}, "\n";
820+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
821+
#print "Got Here\n";
822+
}
720823
}
721824
elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
722-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}), $self->{oaep_params});
825+
#FIXME
826+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
827+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
828+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
829+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
830+
#print "Y_mgf_hash: ", $mgf_hash, "\n";
831+
#print "Y_oaep_label_hash: ", $self->{oaep_label_hash}, "\n";
832+
#print "Y_oaep_label_hash: ", $oaep_label_hash, "\n";
833+
#print "Y_oaep_params: ", $self->{oaep_params}, "\n";
834+
if ($CryptX::VERSION le 0.077) {
835+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
836+
} else {
837+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
838+
}
723839
} else {
724840
die "Unsupported algorithm for key encyption $keymethod}";
725841
}
@@ -1030,6 +1146,20 @@ sub _create_encrypted_data_xml {
10301146
}
10311147
);
10321148

1149+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
1150+
$self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
1151+
$self->{oaep_label_hash}) {
1152+
my $digestmethod = $self->_create_node(
1153+
$doc,
1154+
$dsigns,
1155+
$kencmethod,
1156+
'dsig:DigestMethod',
1157+
{
1158+
Algorithm => $self->{oaep_label_hash},
1159+
}
1160+
);
1161+
};
1162+
10331163
if ($self->{'oaep_params'} ne '') {
10341164
my $oaep_params = $self->_create_node(
10351165
$doc,
@@ -1039,7 +1169,8 @@ sub _create_encrypted_data_xml {
10391169
);
10401170
};
10411171

1042-
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1172+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1173+
$self->{oaep_mgf_alg}) {
10431174
my $oaepmethod = $self->_create_node(
10441175
$doc,
10451176
$xenc11ns,

t/06-test-encryption-methods.t

Lines changed: 40 additions & 19 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 => 896;
44
use Test::Lib;
55
use Test::XML::Enc;
66
use XML::Enc;
@@ -15,10 +15,12 @@ XML
1515

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/;
18-
my @oaep_mgf_algs = qw/mgf1sha1 mgf1sha224 mgf1sha256 mgf1sha384 mgf1sha512/;
18+
my @oaep_mgf_algs = qw/rsa-oaep-mgf1p mgf1sha1 mgf1sha224 mgf1sha256 mgf1sha384 mgf1sha512/;
19+
my @oaep_label_hashes = qw/sha1 sha224 sha256 sha384 sha512/;
1920

2021
my $xmlsec = get_xmlsec_features();
2122
my $lax_key_search = $xmlsec->{lax_key_search} ? '--lax-key-search': '';
23+
my $cryptx = get_cryptx_features();
2224

2325
foreach my $km (@key_methods) {
2426
foreach my $dm (@data_methods) {
@@ -39,10 +41,6 @@ foreach my $km (@key_methods) {
3941

4042
SKIP: {
4143
skip "xmlsec1 not installed", 2 unless $xmlsec->{installed};
42-
my $version;
43-
if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
44-
$version = $1;
45-
};
4644
skip "xmlsec version 1.2.27 minimum for GCM", 2 if ! $xmlsec->{aes_gcm};
4745
ok( open XML, '>', 'tmp.xml' );
4846
print XML $encrypted;
@@ -56,22 +54,45 @@ foreach my $km (@key_methods) {
5654
}
5755

5856
foreach my $om (@oaep_mgf_algs) {
59-
foreach my $dm (@data_methods) {
60-
my $encrypter = XML::Enc->new(
61-
{
62-
key => 't/sign-private.pem',
63-
cert => 't/sign-certonly.pem',
64-
data_enc_method => $dm,
65-
key_transport => 'rsa-oaep',
66-
oaep_mgf_alg => $om,
67-
no_xml_declaration => 1
57+
foreach my $omdig (@oaep_label_hashes) {
58+
SKIP: {
59+
if (! $cryptx->{oaem_mgf_digest} && ($om ne $omdig)) {
60+
my $skip = (scalar @data_methods) * 4;
61+
skip "CryptX $cryptx->{version} does not support rsa-oaep MGF: $om and digest $omdig", $skip;
6862
}
69-
);
7063

71-
my $encrypted = $encrypter->encrypt($xml);
72-
like($encrypted, qr/EncryptedData/, "Successfully Encrypted: Key Method 'rsa-oaep' with $om Data Method $dm");
64+
my $km = ( $om eq 'rsa-oaep-mgf1p') ? 'rsa-oaep-mgf1p' : 'rsa-oaep';
65+
foreach my $dm (@data_methods) {
66+
my $encrypter = XML::Enc->new(
67+
{
68+
key => 't/sign-private.pem',
69+
cert => 't/sign-certonly.pem',
70+
data_enc_method => $dm,
71+
key_transport => $km,
72+
oaep_mgf_alg => $om,
73+
oaep_label_hash => $omdig,
74+
oaep_params => 'encrypt',
75+
no_xml_declaration => 1,
76+
}
77+
);
7378

74-
like($encrypter->decrypt($encrypted), qr/XML-SIG_1/, "Successfully Decrypted with XML::Enc");
79+
my $encrypted = $encrypter->encrypt($xml);
80+
ok($encrypted =~ /EncryptedData/, "Successful Encrypted: Key Method:$km MGF:$om, param:$omdig Data Method:$dm");
81+
82+
SKIP: {
83+
skip "xmlsec1 not installed", 2 unless $xmlsec->{installed};
84+
skip "xmlsec version 1.2.27 minimum for GCM", 2 if ! $xmlsec->{aes_gcm};
85+
ok( open XML, '>', "$km-$om-$omdig-$dm-tmp.xml" );
86+
print XML $encrypted;
87+
close XML;
88+
my $verify_response = `xmlsec1 --decrypt $lax_key_search --privkey-pem t/sign-private.pem $km-$om-$omdig-$dm-tmp.xml 2>&1`;
89+
ok( $verify_response =~ m/XML-SIG_1/, "Successfully decrypted with xmlsec1" )
90+
or warn "calling xmlsec1 failed: '$verify_response'\n";
91+
unlink "$km-$om-$omdig-$dm-tmp.xml";
92+
}
93+
ok($encrypter->decrypt($encrypted) =~ /XML-SIG_1/, "Successfully Decrypted with XML::Enc");
94+
}
95+
}
7596
}
7697
}
7798
done_testing;

t/lib/Test/XML/Enc/Util.pm

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ our @ISA = qw(Exporter);
99
our @EXPORT = qw(
1010
get_xmlsec_features
1111
get_openssl_features
12+
get_cryptx_features
1213
);
1314

1415
our @EXPORT_OK;
@@ -87,6 +88,35 @@ sub get_openssl_features {
8788
return \%openssl;
8889
}
8990

91+
#########################################################################
92+
# get_cryptx_features
93+
#
94+
# Parameter: none
95+
#
96+
# Returns a hash of the version and any features that are needed
97+
# if proper the version is installed
98+
#
99+
# Response: hash
100+
#
101+
# %features = (
102+
# version => '0.077',
103+
# oaem_mgf_digest => 0,
104+
# );
105+
##########################################################################
106+
sub get_cryptx_features {
107+
108+
require CryptX;
109+
110+
my $version = $CryptX::VERSION;
111+
112+
my %cryptx = (
113+
version => $version,
114+
oaem_mgf_digest => ($version gt '0.080') ? 1 : 0,
115+
);
116+
117+
return \%cryptx;
118+
}
119+
90120
1;
91121

92122
__END__

0 commit comments

Comments
 (0)