Skip to content

Commit 8b15844

Browse files
committed
Fix MGF support
1 parent 0d24262 commit 8b15844

File tree

3 files changed

+234
-36
lines changed

3 files changed

+234
-36
lines changed

lib/XML/Enc.pm

Lines changed: 164 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,14 @@ 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
}
115117

116-
117118
=head2 new( ... )
118119
119120
Constructor. Creates an instance of the XML::Enc object
@@ -198,6 +199,39 @@ Used in encryption. Optional. Default method: mgf1sha1
198199
199200
=back
200201
202+
=item B<oaep_params>
203+
204+
Specify the OAEPparams value to use as part of the mask generation function (MGF).
205+
It is optional but can be specified for rsa-oaep and rsa-oaep-mgf1p EncryptionMethods.
206+
207+
It is base64 encoded and stored in the XML as OAEPparams.
208+
209+
If specified you MAY specify the oaep_label_hash that should be used. You should note
210+
that not all implementations support an oaep_label_hash that differs from that of the
211+
MGF specified in the xenc11:MGF element or the default MGF1 with SHA1.
212+
213+
The oaep_label_hash is stored in the DigestMethod child element of the EncryptionMethod.
214+
215+
=item B<oaep_label_hash>
216+
217+
Specify the Hash Algorithm to use for the rsa-oaep label as specified by oaep_params.
218+
219+
The default is sha1. Supported algorithms are:
220+
221+
=over
222+
223+
=item * L<sha1|http://www.w3.org/2000/09/xmldsig#sha1>
224+
225+
=item * L<sha224|http://www.w3.org/2001/04/xmldsig-more#sha224>
226+
227+
=item * L<sha256|http://www.w3.org/2001/04/xmlenc#sha256>
228+
229+
=item * L<sha384|http://www.w3.org/2001/04/xmldsig-more#sha384>
230+
231+
=item * L<sha512|http://www.w3.org/2001/04/xmlenc#sha512>
232+
233+
=back
234+
201235
=cut
202236

203237
sub new {
@@ -225,8 +259,12 @@ sub new {
225259
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
226260
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
227261

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);
262+
if (exists $params->{'oaep_mgf_alg'}) {
263+
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
264+
}
265+
if (exists $params->{'oaep_label_hash'} ) {
266+
$self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
267+
}
230268

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

@@ -444,6 +482,23 @@ sub _get_oaep_params {
444482
return;
445483
}
446484

485+
sub _get_oaep_hash {
486+
my $self = shift;
487+
my $node = shift;
488+
my $xpc = shift;
489+
490+
print "==================================================\n";
491+
print $node, "\n";
492+
print "--------------------------------------------------\n";
493+
print $xpc, "\n";
494+
my $value = $xpc->findvalue('./xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm', $node);
495+
print "--------------------------------------------------\n";
496+
print $value, "\n";
497+
print "==================================================\n";
498+
return _assert_encryption_digest($value) if $value;
499+
return;
500+
}
501+
447502
sub _get_digest_method {
448503
my $self = shift;
449504
my $node = shift;
@@ -576,6 +631,36 @@ sub _getOAEPAlgorithm {
576631
return $OAEPAlgorithm->{$method} // 'SHA1';
577632
}
578633

634+
sub _setOAEPDigest {
635+
my $self = shift;
636+
my $method = shift;
637+
638+
state $OAEPDigest = {
639+
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
640+
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
641+
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
642+
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
643+
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
644+
};
645+
646+
return $OAEPDigest->{$method} // $OAEPDigest->{'sha256'};
647+
}
648+
649+
sub _getParamsAlgorithm {
650+
my $self = shift;
651+
my $method = shift;
652+
653+
state $ParamsAlgorithm = {
654+
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
655+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
656+
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
657+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
658+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
659+
};
660+
661+
return $ParamsAlgorithm->{$method} // $ParamsAlgorithm->{'http://www.w3.org/2000/09/xmldsig#sha1'};
662+
}
663+
579664
sub _setKeyEncryptionMethod {
580665
my $self = shift;
581666
my $method = shift;
@@ -681,23 +766,45 @@ sub _decrypt_key {
681766
if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
682767
return _decrypt(
683768
sub {
684-
$self->{key_obj}->decrypt(
685-
$key, 'oaep',
686-
$digest_name // 'SHA1',
687-
$oaep // ''
688-
);
769+
if ($CryptX::VERSION le 0.077) {
770+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
771+
$self->{key_obj}->decrypt(
772+
$key, 'oaep',
773+
#$self->_getOAEPAlgorithm($mgf),
774+
$digest_name // 'SHA1',
775+
$oaep // '',
776+
);
777+
} else {
778+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
779+
#print "digest_name: ", $digest_name, "\n";
780+
$self->{key_obj}->decrypt(
781+
$key, 'oaep',
782+
$mgf // 'SHA1',
783+
$oaep // '',
784+
$digest_name // 'SHA1',
785+
);
786+
}
689787
}
690788
);
691789
}
692790

693791
if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
694792
return _decrypt(
695793
sub {
696-
$self->{key_obj}->decrypt(
697-
$key, 'oaep',
698-
$self->_getOAEPAlgorithm($mgf),
699-
$oaep // '',
700-
);
794+
if ($CryptX::VERSION le 0.077) {
795+
$self->{key_obj}->decrypt(
796+
$key, 'oaep',
797+
$self->_getOAEPAlgorithm($mgf),
798+
$oaep // '',
799+
);
800+
} else {
801+
$self->{key_obj}->decrypt(
802+
$key, 'oaep',
803+
$self->_getOAEPAlgorithm($mgf),
804+
$oaep // '',
805+
$digest_name // '',
806+
);
807+
}
701808
}
702809
);
703810
}
@@ -716,10 +823,35 @@ sub _EncryptKey {
716823
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
717824
}
718825
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
719-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
826+
if ($CryptX::VERSION le 0.077) {
827+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
828+
} else {
829+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
830+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
831+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
832+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
833+
#print "Y_mgf_hash: ", $mgf_hash, "\n";
834+
#print "Xoaep_label_hash: ", $oaep_label_hash, "\n";
835+
#print "Xoaep_params: ", $self->{oaep_params}, "\n";
836+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
837+
#print "Got Here\n";
838+
}
720839
}
721840
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});
841+
#FIXME
842+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
843+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
844+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
845+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
846+
#print "Y_mgf_hash: ", $mgf_hash, "\n";
847+
#print "Y_oaep_label_hash: ", $self->{oaep_label_hash}, "\n";
848+
#print "Y_oaep_label_hash: ", $oaep_label_hash, "\n";
849+
#print "Y_oaep_params: ", $self->{oaep_params}, "\n";
850+
if ($CryptX::VERSION le 0.077) {
851+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
852+
} else {
853+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
854+
}
723855
} else {
724856
die "Unsupported algorithm for key encyption $keymethod}";
725857
}
@@ -1030,6 +1162,20 @@ sub _create_encrypted_data_xml {
10301162
}
10311163
);
10321164

1165+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
1166+
$self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
1167+
$self->{oaep_label_hash}) {
1168+
my $digestmethod = $self->_create_node(
1169+
$doc,
1170+
$dsigns,
1171+
$kencmethod,
1172+
'dsig:DigestMethod',
1173+
{
1174+
Algorithm => $self->{oaep_label_hash},
1175+
}
1176+
);
1177+
};
1178+
10331179
if ($self->{'oaep_params'} ne '') {
10341180
my $oaep_params = $self->_create_node(
10351181
$doc,
@@ -1039,7 +1185,8 @@ sub _create_encrypted_data_xml {
10391185
);
10401186
};
10411187

1042-
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1188+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1189+
$self->{oaep_mgf_alg}) {
10431190
my $oaepmethod = $self->_create_node(
10441191
$doc,
10451192
$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)