diff --git a/Makefile.PL b/Makefile.PL
index 33fb190..820b216 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -17,6 +17,7 @@ my %WriteMakefileArgs = (
"NAME" => "XML::Enc",
"PREREQ_PM" => {
"Carp" => 0,
+ "Crypt::AuthEnc::GCM" => 0,
"Crypt::Mode::CBC" => 0,
"Crypt::OpenSSL::Bignum" => 0,
"Crypt::OpenSSL::RSA" => 0,
@@ -33,7 +34,7 @@ my %WriteMakefileArgs = (
"File::Which" => 0,
"Test::More" => 0
},
- "VERSION" => "0.03",
+ "VERSION" => "0.04",
"test" => {
"TESTS" => "t/*.t"
}
@@ -42,6 +43,7 @@ my %WriteMakefileArgs = (
my %FallbackPrereqs = (
"Carp" => 0,
+ "Crypt::AuthEnc::GCM" => 0,
"Crypt::Mode::CBC" => 0,
"Crypt::OpenSSL::Bignum" => 0,
"Crypt::OpenSSL::RSA" => 0,
diff --git a/README b/README
index 44b9870..ef41995 100644
--- a/README
+++ b/README
@@ -2,7 +2,7 @@ NAME
XML::Enc - XML::Enc Encryption Support
VERSION
- version 0.03
+ version 0.04
SYNOPSIS
my $decrypter = XML::Enc->new(
diff --git a/cpanfile b/cpanfile
index 013e3ec..46e584d 100644
--- a/cpanfile
+++ b/cpanfile
@@ -1,6 +1,7 @@
# Do not edit this file directly. To change prereqs, edit the `dist.ini` file.
requires "Carp" => "0";
+requires "Crypt::AuthEnc::GCM" => "0";
requires "Crypt::Mode::CBC" => "0";
requires "Crypt::OpenSSL::Bignum" => "0";
requires "Crypt::OpenSSL::RSA" => "0";
diff --git a/dist.ini b/dist.ini
index cfd2572..8d03553 100644
--- a/dist.ini
+++ b/dist.ini
@@ -20,6 +20,7 @@ MIME::Base64 = 0
XML::LibXML = 0
Crypt::Mode::CBC = 0
Crypt::Random = 0
+Crypt::AuthEnc::GCM = 0
[Prereqs / TestRequires]
Test::More = 0
diff --git a/lib/XML/Enc.pm b/lib/XML/Enc.pm
index 94fa358..72eaeb9 100644
--- a/lib/XML/Enc.pm
+++ b/lib/XML/Enc.pm
@@ -9,12 +9,13 @@ use Carp;
use XML::LibXML;
use Crypt::OpenSSL::RSA;
use Crypt::Mode::CBC;
+use Crypt::AuthEnc::GCM;
use MIME::Base64 qw/decode_base64 encode_base64/;
use Crypt::Random qw( makerandom_octet );
use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
-our $VERSION = '0.03';
+our $VERSION = '0.04';
our $DEBUG = 0;
@@ -52,10 +53,37 @@ XML::Enc - XML Encryption
# 5.2.2 AES - 128 bit initialization vector (IV) (16 bytes)
my %encmethods = (
- 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => { ivsize => 8, keysize => 24, modename => 'DES_EDE' },
- 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => { ivsize => '16', keysize => 16, modename => 'AES' },
- 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => { ivsize => '16', keysize => 24, modename => 'AES' },
- 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => { ivsize => '16', keysize => 32, modename => 'AES' },
+ 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => {
+ ivsize => 8,
+ keysize => 24,
+ modename => 'DES_EDE' },
+ 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => {
+ ivsize => '16',
+ keysize => 16,
+ modename => 'AES' },
+ 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => {
+ ivsize => '16',
+ keysize => 24,
+ modename => 'AES' },
+ 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => {
+ ivsize => '16',
+ keysize => 32,
+ modename => 'AES' },
+ 'http://www.w3.org/2009/xmlenc11#aes128-gcm' => {
+ ivsize => '12',
+ keysize => 16,
+ modename => 'AES',
+ tagsize => 16 },
+ 'http://www.w3.org/2009/xmlenc11#aes192-gcm' => {
+ ivsize => '12',
+ keysize => 24,
+ modename => 'AES',
+ tagsize => 16 },
+ 'http://www.w3.org/2009/xmlenc11#aes256-gcm' => {
+ ivsize => '12',
+ keysize => 32,
+ modename => 'AES',
+ tagsize => 16 },
);
=head2 new( ... )
@@ -293,6 +321,9 @@ sub _setEncryptionMethod {
'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes192-cbc',
'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
+ 'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
+ 'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm',
+ 'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm',
);
return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
@@ -327,7 +358,7 @@ sub _setKeyEncryptionMethod {
'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
);
- return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-1_5'};
+ return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-oaep-mgf1p'};
}
sub _DecryptData {
@@ -338,23 +369,35 @@ sub _DecryptData {
my $iv;
my $encrypted;
- my $cbc;
+ my $plaintext;
+
+ my $ivsize = $encmethods{$method}->{ivsize};
+ my $tagsize = $encmethods{$method}->{tagsize};
+
+ $iv = substr $encrypteddata, 0, $ivsize;
+ $encrypted = substr $encrypteddata, $ivsize;
# XML Encryption 5.2 Block Encryption Algorithms
# The resulting cipher text is prefixed by the IV.
- if (defined $encmethods{$method}){
- my $blksize = $encmethods{$method}->{ivsize};
- #print "Block Size: ", $blksize;
- $iv = substr $encrypteddata, 0, $blksize;
- $encrypted = substr $encrypteddata, $blksize;
- $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
+ if (defined $encmethods{$method} & $method !~ /gcm/ ){
+ my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
+ $plaintext = $self->_remove_padding($cbc->decrypt($encrypted, $key, $iv));
+ } elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
+ my $gcm = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
+
+ # Note that GCM support for additional authentication
+ # data is not used in the XML specification.
+ my $tag = substr $encrypted, - $tagsize;
+ $encrypted = substr $encrypted, 0, (length $encrypted) - $tagsize;
+ $plaintext = $gcm->decrypt_add($encrypted);
+ if ( ! $gcm->decrypt_done($tag) ) {
+ die "Tag expected did not match returned Tag";
+ }
} else {
die "Unsupported Encryption Algorithm";
}
- my $plaintext = $cbc->decrypt($encrypted, $key, $iv);
-
- return $self->_remove_padding($plaintext);
+ return $plaintext;
}
sub _EncryptData {
@@ -363,25 +406,33 @@ sub _EncryptData {
my $data = shift;
my $key = shift;
- my $iv;
- my $cbc;
+ my $cipherdata;
+ my $ivsize = $encmethods{$method}->{ivsize};
+ my $keysize = $encmethods{$method}->{keysize};
- # XML Encryption 5.2 Block Encryption Algorithms
- # The resulting cipher text is prefixed by the IV.
- if (defined $encmethods{$method}){
- my $blksize = $encmethods{$method}->{ivsize};
- my $keysize = $encmethods{$method}->{keysize};
- $iv = makerandom_octet ( Length => $blksize);
- ${$key} = makerandom_octet ( Length => $keysize);
- $data = $self->_add_padding($data, $blksize);
- $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
+ my $iv = makerandom_octet ( Length => $ivsize);
+ ${$key} = makerandom_octet ( Length => $keysize);
+
+ if (defined $encmethods{$method} & $method !~ /gcm/ ){
+ my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
+ # XML Encryption 5.2 Block Encryption Algorithms
+ # The resulting cipher text is prefixed by the IV.
+ $data = $self->_add_padding($data, $ivsize);
+ $cipherdata = $iv . $cbc->encrypt($data, ${$key}, $iv);
+ } elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
+ my $gcm = Crypt::AuthEnc::GCM->new($encmethods{$method}->{modename}, ${$key}, $iv);
+
+ # Note that GCM support for additional authentication
+ # data is not used in the XML specification.
+ my $encrypted = $gcm->encrypt_add($data);
+ my $tag = $gcm->encrypt_done();
+
+ $cipherdata = $iv . $encrypted . $tag;
} else {
die "Unsupported Encryption Algorithm";
}
- my $encrypted = $iv . $cbc->encrypt($data, ${$key}, $iv);
-
- return $encrypted;
+ return $cipherdata;
}
sub _DecryptKey {
diff --git a/t/06-test-encryption-methods.t b/t/06-test-encryption-methods.t
index 6e4c1bb..c56bde2 100644
--- a/t/06-test-encryption-methods.t
+++ b/t/06-test-encryption-methods.t
@@ -1,6 +1,6 @@
use strict;
use warnings;
-use Test::More tests => 32;
+use Test::More tests => 56;
use XML::Enc;
use MIME::Base64 qw/decode_base64 encode_base64/;
use File::Which;
@@ -13,7 +13,7 @@ my $xml = <<'XML';
XML
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
-my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc/;
+my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
foreach my $km (@key_methods) {
foreach my $dm (@data_methods) {
@@ -34,6 +34,11 @@ foreach my $km (@key_methods) {
SKIP: {
skip "xmlsec1 not installed", 2 unless which('xmlsec1');
+ my $version;
+ if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
+ $version = $1;
+ };
+ skip "xmlsec version 1.2.27 minimum for GCM", 2 if $version lt '1.2.27';
ok( open XML, '>', 'tmp.xml' );
print XML $encrypted;
close XML;
diff --git a/t/07-decrypt-xmlsec.t b/t/07-decrypt-xmlsec.t
index a4ec989..f7ce229 100644
--- a/t/07-decrypt-xmlsec.t
+++ b/t/07-decrypt-xmlsec.t
@@ -1,6 +1,6 @@
use strict;
use warnings;
-use Test::More tests => 40;
+use Test::More tests => 70;
use XML::Enc;
use MIME::Base64 qw/decode_base64/;
use File::Which;
@@ -19,13 +19,26 @@ my $plaintext = <<'UNENCRYPTED';
UNENCRYPTED
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
-my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc/;
+my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
+
+my %uri = (
+ 'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
+ 'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
+ 'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
+ 'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
+ 'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#',
+ 'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#',
+ 'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#',
+ );
my %sesskey = (
'aes128-cbc' => 'aes-128',
'aes192-cbc' => 'aes-192',
'aes256-cbc' => 'aes-256',
'tripledes-cbc' => 'des-192',
+ 'aes128-gcm' => 'aes-128-GCM',
+ 'aes192-gcm' => 'aes-192-GCM',
+ 'aes256-gcm' => 'aes-256-GCM',
);
foreach my $km (@key_methods) {
@@ -41,7 +54,7 @@ XML Security Library example: Original XML
xmlns="http://www.w3.org/2001/04/xmlenc#"
Type="http://www.w3.org/2001/04/xmlenc#Element">
+ "$uri{$dm}$dm"/>
+ "$uri{$dm}$dm"/>
', 'plaintext.xml' );
print XML $plaintext;