Skip to content

Commit 8409f27

Browse files
authored
Merge pull request #12 from timlegge/mgf-rebase
Draft: Fix MGF support
2 parents 0d24262 + 8f03f0f commit 8409f27

File tree

6 files changed

+286
-45
lines changed

6 files changed

+286
-45
lines changed

Makefile.PL

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ my %WriteMakefileArgs = (
2020
"Crypt::AuthEnc::GCM" => "0.062",
2121
"Crypt::Mode::CBC" => 0,
2222
"Crypt::OpenSSL::X509" => 0,
23-
"Crypt::PK::RSA" => 0,
23+
"Crypt::PK::RSA" => "0.081",
2424
"Crypt::PRNG" => 0,
2525
"MIME::Base64" => 0,
2626
"XML::LibXML" => 0,
@@ -31,6 +31,7 @@ my %WriteMakefileArgs = (
3131
},
3232
"TEST_REQUIRES" => {
3333
"Crypt::OpenSSL::Guess" => 0,
34+
"CryptX" => 0,
3435
"Exporter" => 0,
3536
"File::Slurper" => 0,
3637
"File::Which" => 0,
@@ -53,8 +54,9 @@ my %FallbackPrereqs = (
5354
"Crypt::Mode::CBC" => 0,
5455
"Crypt::OpenSSL::Guess" => 0,
5556
"Crypt::OpenSSL::X509" => 0,
56-
"Crypt::PK::RSA" => 0,
57+
"Crypt::PK::RSA" => "0.081",
5758
"Crypt::PRNG" => 0,
59+
"CryptX" => 0,
5860
"Exporter" => 0,
5961
"File::Slurper" => 0,
6062
"File::Which" => 0,

README

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,41 @@ 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 specified for rsa-oaep and
109+
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 specified 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 as
123+
specified by oaep_params.
124+
125+
The default is sha1. Supported algorithms are:
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+
137+
key_name
138+
Specify a key name to add to the KeyName element. If it is not
139+
specified then no KeyName element is added to the KeyInfo
140+
106141
decrypt( ... )
107142
Main decryption function.
108143

@@ -121,7 +156,7 @@ AUTHOR
121156
Timothy Legge <[email protected]>
122157

123158
COPYRIGHT AND LICENSE
124-
This software is copyright (c) 2023 by TImothy Legge.
159+
This software is copyright (c) 2024 by TImothy Legge.
125160

126161
This is free software; you can redistribute it and/or modify it under
127162
the same terms as the Perl 5 programming language system itself.

cpanfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requires "Carp" => "0";
44
requires "Crypt::AuthEnc::GCM" => "0.062";
55
requires "Crypt::Mode::CBC" => "0";
66
requires "Crypt::OpenSSL::X509" => "0";
7-
requires "Crypt::PK::RSA" => "0";
7+
requires "Crypt::PK::RSA" => "0.081";
88
requires "Crypt::PRNG" => "0";
99
requires "MIME::Base64" => "0";
1010
requires "XML::LibXML" => "0";
@@ -16,6 +16,7 @@ requires "warnings" => "0";
1616

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

lib/XML/Enc.pm

Lines changed: 166 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package XML::Enc;
99
use Carp;
1010
use Crypt::AuthEnc::GCM 0.062;
1111
use Crypt::Mode::CBC;
12-
use Crypt::PK::RSA;
12+
use Crypt::PK::RSA 0.081;
1313
use Crypt::PRNG qw( random_bytes );
1414
use MIME::Base64 qw/decode_base64 encode_base64/;
1515
use XML::LibXML;
@@ -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
}
@@ -196,6 +198,44 @@ Used in encryption. Optional. Default method: mgf1sha1
196198
197199
=back
198200
201+
=item B<oaep_params>
202+
203+
Specify the OAEPparams value to use as part of the mask generation function (MGF).
204+
It is optional but can be specified for rsa-oaep and rsa-oaep-mgf1p EncryptionMethods.
205+
206+
It is base64 encoded and stored in the XML as OAEPparams.
207+
208+
If specified you MAY specify the oaep_label_hash that should be used. You should note
209+
that not all implementations support an oaep_label_hash that differs from that of the
210+
MGF specified in the xenc11:MGF element or the default MGF1 with SHA1.
211+
212+
The oaep_label_hash is stored in the DigestMethod child element of the EncryptionMethod.
213+
214+
=item B<oaep_label_hash>
215+
216+
Specify the Hash Algorithm to use for the rsa-oaep label as specified by oaep_params.
217+
218+
The default is sha1. Supported algorithms are:
219+
220+
=over
221+
222+
=item * L<sha1|http://www.w3.org/2000/09/xmldsig#sha1>
223+
224+
=item * L<sha224|http://www.w3.org/2001/04/xmldsig-more#sha224>
225+
226+
=item * L<sha256|http://www.w3.org/2001/04/xmlenc#sha256>
227+
228+
=item * L<sha384|http://www.w3.org/2001/04/xmldsig-more#sha384>
229+
230+
=item * L<sha512|http://www.w3.org/2001/04/xmlenc#sha512>
231+
232+
=back
233+
234+
=item B<key_name>
235+
236+
Specify a key name to add to the KeyName element. If it is not specified then no
237+
KeyName element is added to the KeyInfo
238+
199239
=back
200240
201241
=cut
@@ -225,11 +265,17 @@ sub new {
225265
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
226266
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
227267

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);
268+
if (exists $params->{'oaep_mgf_alg'}) {
269+
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
270+
}
271+
if (exists $params->{'oaep_label_hash'} ) {
272+
$self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
273+
}
230274

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

277+
$self->{'key_name'} = $params->{'key_name'} if exists($params->{'key_name'});
278+
233279
return $self;
234280
}
235281

@@ -502,6 +548,11 @@ sub encrypt {
502548
my $base64_key = encode_base64($key);
503549
my $base64_data = encode_base64($encrypteddata);
504550

551+
# Insert KeyName into the XML
552+
if (defined $self->{key_name} and $self->{key_name} ne '') {
553+
$encrypted = $self->_setKeyName($encrypted, $xpc, $self->{key_name});
554+
}
555+
505556
# Insert OAEPparams into the XML
506557
if ($self->{oaep_params} ne '') {
507558
$encrypted = $self->_setOAEPparams($encrypted, $xpc, encode_base64($self->{oaep_params}));
@@ -533,6 +584,19 @@ sub _setEncryptionMethod {
533584
return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
534585
}
535586

587+
sub _setKeyName {
588+
my $self = shift;
589+
my $context = shift;
590+
my $xpc = shift;
591+
my $keyname = shift;
592+
593+
my $node = $xpc->findnodes('//xenc:EncryptedKey/dsig:KeyInfo/dsig:KeyName', $context);
594+
595+
$node->[0]->removeChildNodes();
596+
$node->[0]->appendText(defined $keyname ? $keyname : 'key_name');
597+
return $context;
598+
}
599+
536600
sub _setOAEPparams {
537601
my $self = shift;
538602
my $context = shift;
@@ -576,6 +640,36 @@ sub _getOAEPAlgorithm {
576640
return $OAEPAlgorithm->{$method} // 'SHA1';
577641
}
578642

643+
sub _setOAEPDigest {
644+
my $self = shift;
645+
my $method = shift;
646+
647+
state $OAEPDigest = {
648+
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
649+
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
650+
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
651+
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
652+
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
653+
};
654+
655+
return $OAEPDigest->{$method} // $OAEPDigest->{'sha256'};
656+
}
657+
658+
sub _getParamsAlgorithm {
659+
my $self = shift;
660+
my $method = shift;
661+
662+
state $ParamsAlgorithm = {
663+
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
664+
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
665+
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
666+
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
667+
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
668+
};
669+
670+
return $ParamsAlgorithm->{$method} // $ParamsAlgorithm->{'http://www.w3.org/2000/09/xmldsig#sha1'};
671+
}
672+
579673
sub _setKeyEncryptionMethod {
580674
my $self = shift;
581675
my $method = shift;
@@ -681,23 +775,45 @@ sub _decrypt_key {
681775
if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
682776
return _decrypt(
683777
sub {
684-
$self->{key_obj}->decrypt(
685-
$key, 'oaep',
686-
$digest_name // 'SHA1',
687-
$oaep // ''
688-
);
778+
if ($CryptX::VERSION le 0.081) {
779+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
780+
$self->{key_obj}->decrypt(
781+
$key, 'oaep',
782+
#$self->_getOAEPAlgorithm($mgf),
783+
$digest_name // 'SHA1',
784+
$oaep // '',
785+
);
786+
} else {
787+
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
788+
#print "digest_name: ", $digest_name, "\n";
789+
$self->{key_obj}->decrypt(
790+
$key, 'oaep',
791+
$mgf // 'SHA1',
792+
$oaep // '',
793+
$digest_name // 'SHA1',
794+
);
795+
}
689796
}
690797
);
691798
}
692799

693800
if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
694801
return _decrypt(
695802
sub {
696-
$self->{key_obj}->decrypt(
697-
$key, 'oaep',
698-
$self->_getOAEPAlgorithm($mgf),
699-
$oaep // '',
700-
);
803+
if ($CryptX::VERSION le 0.081) {
804+
$self->{key_obj}->decrypt(
805+
$key, 'oaep',
806+
$self->_getOAEPAlgorithm($mgf),
807+
$oaep // '',
808+
);
809+
} else {
810+
$self->{key_obj}->decrypt(
811+
$key, 'oaep',
812+
$self->_getOAEPAlgorithm($mgf),
813+
$oaep // '',
814+
$digest_name // '',
815+
);
816+
}
701817
}
702818
);
703819
}
@@ -712,14 +828,29 @@ sub _EncryptKey {
712828

713829
my $rsa_pub = $self->{cert_obj};
714830

831+
# FIXME: this could use some refactoring and some simplfication
715832
if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
716833
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
717834
}
718835
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
719-
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
836+
if ($CryptX::VERSION le 0.081) {
837+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
838+
} else {
839+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
840+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
841+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
842+
}
720843
}
721844
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});
845+
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
846+
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
847+
if ($CryptX::VERSION le 0.081) {
848+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
849+
} else {
850+
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
851+
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
852+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
853+
}
723854
} else {
724855
die "Unsupported algorithm for key encyption $keymethod}";
725856
}
@@ -1030,6 +1161,20 @@ sub _create_encrypted_data_xml {
10301161
}
10311162
);
10321163

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

1042-
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
1187+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' &&
1188+
$self->{oaep_mgf_alg}) {
10431189
my $oaepmethod = $self->_create_node(
10441190
$doc,
10451191
$xenc11ns,
@@ -1058,12 +1204,14 @@ sub _create_encrypted_data_xml {
10581204
'dsig:KeyInfo',
10591205
);
10601206

1061-
my $keyname = $self->_create_node(
1207+
if (defined $self->{key_name}) {
1208+
my $keyname = $self->_create_node(
10621209
$doc,
10631210
$dsigns,
10641211
$keyinfo2,
10651212
'dsig:KeyName',
10661213
);
1214+
};
10671215

10681216
my $keycipherdata = $self->_create_node(
10691217
$doc,

0 commit comments

Comments
 (0)