Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix MGF support #12

Merged
merged 2 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ my %WriteMakefileArgs = (
"Crypt::AuthEnc::GCM" => "0.062",
"Crypt::Mode::CBC" => 0,
"Crypt::OpenSSL::X509" => 0,
"Crypt::PK::RSA" => 0,
"Crypt::PK::RSA" => "0.081",
"Crypt::PRNG" => 0,
"MIME::Base64" => 0,
"XML::LibXML" => 0,
Expand All @@ -31,6 +31,7 @@ my %WriteMakefileArgs = (
},
"TEST_REQUIRES" => {
"Crypt::OpenSSL::Guess" => 0,
"CryptX" => 0,
"Exporter" => 0,
"File::Slurper" => 0,
"File::Which" => 0,
Expand All @@ -53,8 +54,9 @@ my %FallbackPrereqs = (
"Crypt::Mode::CBC" => 0,
"Crypt::OpenSSL::Guess" => 0,
"Crypt::OpenSSL::X509" => 0,
"Crypt::PK::RSA" => 0,
"Crypt::PK::RSA" => "0.081",
"Crypt::PRNG" => 0,
"CryptX" => 0,
"Exporter" => 0,
"File::Slurper" => 0,
"File::Which" => 0,
Expand Down
37 changes: 36 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,41 @@ METHODS

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

oaep_params
Specify the OAEPparams value to use as part of the mask generation
function (MGF). It is optional but can be specified for rsa-oaep and
rsa-oaep-mgf1p EncryptionMethods.

It is base64 encoded and stored in the XML as OAEPparams.

If specified you MAY specify the oaep_label_hash that should be
used. You should note that not all implementations support an
oaep_label_hash that differs from that of the MGF specified in the
xenc11:MGF element or the default MGF1 with SHA1.

The oaep_label_hash is stored in the DigestMethod child element of
the EncryptionMethod.

oaep_label_hash
Specify the Hash Algorithm to use for the rsa-oaep label as
specified by oaep_params.

The default is sha1. Supported algorithms are:

* sha1 <http://www.w3.org/2000/09/xmldsig#sha1>

* sha224 <http://www.w3.org/2001/04/xmldsig-more#sha224>

* sha256 <http://www.w3.org/2001/04/xmlenc#sha256>

* sha384 <http://www.w3.org/2001/04/xmldsig-more#sha384>

* sha512 <http://www.w3.org/2001/04/xmlenc#sha512>

key_name
Specify a key name to add to the KeyName element. If it is not
specified then no KeyName element is added to the KeyInfo

decrypt( ... )
Main decryption function.

Expand All @@ -121,7 +156,7 @@ AUTHOR
Timothy Legge <timlegge@cpan.org>

COPYRIGHT AND LICENSE
This software is copyright (c) 2023 by TImothy Legge.
This software is copyright (c) 2024 by TImothy Legge.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
Expand Down
3 changes: 2 additions & 1 deletion cpanfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ requires "Carp" => "0";
requires "Crypt::AuthEnc::GCM" => "0.062";
requires "Crypt::Mode::CBC" => "0";
requires "Crypt::OpenSSL::X509" => "0";
requires "Crypt::PK::RSA" => "0";
requires "Crypt::PK::RSA" => "0.081";
requires "Crypt::PRNG" => "0";
requires "MIME::Base64" => "0";
requires "XML::LibXML" => "0";
Expand All @@ -16,6 +16,7 @@ requires "warnings" => "0";

on 'test' => sub {
requires "Crypt::OpenSSL::Guess" => "0";
requires "CryptX" => "0";
requires "Exporter" => "0";
requires "File::Slurper" => "0";
requires "File::Which" => "0";
Expand Down
184 changes: 166 additions & 18 deletions lib/XML/Enc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package XML::Enc;
use Carp;
use Crypt::AuthEnc::GCM 0.062;
use Crypt::Mode::CBC;
use Crypt::PK::RSA;
use Crypt::PK::RSA 0.081;
use Crypt::PRNG qw( random_bytes );
use MIME::Base64 qw/decode_base64 encode_base64/;
use XML::LibXML;
Expand Down Expand Up @@ -107,8 +107,10 @@ sub _assert_encryption_digest {
state $ENC_DIGEST = {
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
};

die "Unsupported encryption digest algo $algo" unless $ENC_DIGEST->{ $algo };
return $ENC_DIGEST->{ $algo };
}
Expand Down Expand Up @@ -196,6 +198,44 @@ Used in encryption. Optional. Default method: mgf1sha1

=back

=item B<oaep_params>

Specify the OAEPparams value to use as part of the mask generation function (MGF).
It is optional but can be specified for rsa-oaep and rsa-oaep-mgf1p EncryptionMethods.

It is base64 encoded and stored in the XML as OAEPparams.

If specified you MAY specify the oaep_label_hash that should be used. You should note
that not all implementations support an oaep_label_hash that differs from that of the
MGF specified in the xenc11:MGF element or the default MGF1 with SHA1.

The oaep_label_hash is stored in the DigestMethod child element of the EncryptionMethod.

=item B<oaep_label_hash>

Specify the Hash Algorithm to use for the rsa-oaep label as specified by oaep_params.

The default is sha1. Supported algorithms are:

=over

=item * L<sha1|http://www.w3.org/2000/09/xmldsig#sha1>

=item * L<sha224|http://www.w3.org/2001/04/xmldsig-more#sha224>

=item * L<sha256|http://www.w3.org/2001/04/xmlenc#sha256>

=item * L<sha384|http://www.w3.org/2001/04/xmldsig-more#sha384>

=item * L<sha512|http://www.w3.org/2001/04/xmlenc#sha512>

=back

=item B<key_name>

Specify a key name to add to the KeyName element. If it is not specified then no
KeyName element is added to the KeyInfo

=back

=cut
Expand Down Expand Up @@ -225,11 +265,17 @@ sub new {
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);

my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
if (exists $params->{'oaep_mgf_alg'}) {
$self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($params->{'oaep_mgf_alg'});
}
if (exists $params->{'oaep_label_hash'} ) {
$self->{'oaep_label_hash'} = $self->_setOAEPDigest($params->{'oaep_label_hash'});
}

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

$self->{'key_name'} = $params->{'key_name'} if exists($params->{'key_name'});

return $self;
}

Expand Down Expand Up @@ -502,6 +548,11 @@ sub encrypt {
my $base64_key = encode_base64($key);
my $base64_data = encode_base64($encrypteddata);

# Insert KeyName into the XML
if (defined $self->{key_name} and $self->{key_name} ne '') {
$encrypted = $self->_setKeyName($encrypted, $xpc, $self->{key_name});
}

# Insert OAEPparams into the XML
if ($self->{oaep_params} ne '') {
$encrypted = $self->_setOAEPparams($encrypted, $xpc, encode_base64($self->{oaep_params}));
Expand Down Expand Up @@ -533,6 +584,19 @@ sub _setEncryptionMethod {
return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
}

sub _setKeyName {
my $self = shift;
my $context = shift;
my $xpc = shift;
my $keyname = shift;

my $node = $xpc->findnodes('//xenc:EncryptedKey/dsig:KeyInfo/dsig:KeyName', $context);

$node->[0]->removeChildNodes();
$node->[0]->appendText(defined $keyname ? $keyname : 'key_name');
return $context;
}

sub _setOAEPparams {
my $self = shift;
my $context = shift;
Expand Down Expand Up @@ -576,6 +640,36 @@ sub _getOAEPAlgorithm {
return $OAEPAlgorithm->{$method} // 'SHA1';
}

sub _setOAEPDigest {
my $self = shift;
my $method = shift;

state $OAEPDigest = {
'sha1' => 'http://www.w3.org/2000/09/xmldsig#sha1',
'sha224' => 'http://www.w3.org/2001/04/xmldsig-more#sha224',
'sha256' => 'http://www.w3.org/2001/04/xmlenc#sha256',
'sha384' => 'http://www.w3.org/2001/04/xmldsig-more#sha384',
'sha512' => 'http://www.w3.org/2001/04/xmlenc#sha512',
};

return $OAEPDigest->{$method} // $OAEPDigest->{'sha256'};
}

sub _getParamsAlgorithm {
my $self = shift;
my $method = shift;

state $ParamsAlgorithm = {
'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
'http://www.w3.org/2001/04/xmldsig-more#sha224' => 'SHA224',
'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
'http://www.w3.org/2001/04/xmldsig-more#sha384' => 'SHA384',
'http://www.w3.org/2001/04/xmlenc#sha512' => 'SHA512',
};

return $ParamsAlgorithm->{$method} // $ParamsAlgorithm->{'http://www.w3.org/2000/09/xmldsig#sha1'};
}

sub _setKeyEncryptionMethod {
my $self = shift;
my $method = shift;
Expand Down Expand Up @@ -681,23 +775,45 @@ sub _decrypt_key {
if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
return _decrypt(
sub {
$self->{key_obj}->decrypt(
$key, 'oaep',
$digest_name // 'SHA1',
$oaep // ''
);
if ($CryptX::VERSION le 0.081) {
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
$self->{key_obj}->decrypt(
$key, 'oaep',
#$self->_getOAEPAlgorithm($mgf),
$digest_name // 'SHA1',
$oaep // '',
);
} else {
#print "Caller: _decrypt_key rsa-oaep-mgf1p\n";
#print "digest_name: ", $digest_name, "\n";
$self->{key_obj}->decrypt(
$key, 'oaep',
$mgf // 'SHA1',
$oaep // '',
$digest_name // 'SHA1',
);
}
}
);
}

if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
return _decrypt(
sub {
$self->{key_obj}->decrypt(
$key, 'oaep',
$self->_getOAEPAlgorithm($mgf),
$oaep // '',
);
if ($CryptX::VERSION le 0.081) {
$self->{key_obj}->decrypt(
$key, 'oaep',
$self->_getOAEPAlgorithm($mgf),
$oaep // '',
);
} else {
$self->{key_obj}->decrypt(
$key, 'oaep',
$self->_getOAEPAlgorithm($mgf),
$oaep // '',
$digest_name // '',
);
}
}
);
}
Expand All @@ -712,14 +828,29 @@ sub _EncryptKey {

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

# FIXME: this could use some refactoring and some simplfication
if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
}
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
if ($CryptX::VERSION le 0.081) {
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
} else {
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : 'SHA1';
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params}, $oaep_label_hash);
}
}
elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}), $self->{oaep_params});
my $mgf_hash = defined $self->{oaep_mgf_alg} ?
$self->_getOAEPAlgorithm($self->{oaep_mgf_alg}) : undef;
if ($CryptX::VERSION le 0.081) {
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params});
} else {
my $oaep_label_hash = (defined $self->{oaep_label_hash} && $self->{oaep_label_hash} ne '') ?
$self->_getParamsAlgorithm($self->{oaep_label_hash}) : $mgf_hash;
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $mgf_hash, $self->{oaep_params}, $oaep_label_hash);
}
} else {
die "Unsupported algorithm for key encyption $keymethod}";
}
Expand Down Expand Up @@ -1030,6 +1161,20 @@ sub _create_encrypted_data_xml {
}
);

if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep' ||
$self->{key_transport} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' &&
$self->{oaep_label_hash}) {
my $digestmethod = $self->_create_node(
$doc,
$dsigns,
$kencmethod,
'dsig:DigestMethod',
{
Algorithm => $self->{oaep_label_hash},
}
);
};

if ($self->{'oaep_params'} ne '') {
my $oaep_params = $self->_create_node(
$doc,
Expand All @@ -1039,7 +1184,8 @@ sub _create_encrypted_data_xml {
);
};

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

my $keyname = $self->_create_node(
if (defined $self->{key_name}) {
my $keyname = $self->_create_node(
$doc,
$dsigns,
$keyinfo2,
'dsig:KeyName',
);
};

my $keycipherdata = $self->_create_node(
$doc,
Expand Down
Loading
Loading