Skip to content

Commit 8059d02

Browse files
authored
Merge pull request #1 from perl-net-saml2/gcm-support
Add support fot AES GCM based encryption Algorithms
2 parents 0b69310 + 423d5c6 commit 8059d02

File tree

7 files changed

+116
-38
lines changed

7 files changed

+116
-38
lines changed

Makefile.PL

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ my %WriteMakefileArgs = (
1717
"NAME" => "XML::Enc",
1818
"PREREQ_PM" => {
1919
"Carp" => 0,
20+
"Crypt::AuthEnc::GCM" => 0,
2021
"Crypt::Mode::CBC" => 0,
2122
"Crypt::OpenSSL::Bignum" => 0,
2223
"Crypt::OpenSSL::RSA" => 0,
@@ -33,7 +34,7 @@ my %WriteMakefileArgs = (
3334
"File::Which" => 0,
3435
"Test::More" => 0
3536
},
36-
"VERSION" => "0.03",
37+
"VERSION" => "0.04",
3738
"test" => {
3839
"TESTS" => "t/*.t"
3940
}
@@ -42,6 +43,7 @@ my %WriteMakefileArgs = (
4243

4344
my %FallbackPrereqs = (
4445
"Carp" => 0,
46+
"Crypt::AuthEnc::GCM" => 0,
4547
"Crypt::Mode::CBC" => 0,
4648
"Crypt::OpenSSL::Bignum" => 0,
4749
"Crypt::OpenSSL::RSA" => 0,

README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ NAME
22
XML::Enc - XML::Enc Encryption Support
33

44
VERSION
5-
version 0.03
5+
version 0.04
66

77
SYNOPSIS
88
my $decrypter = XML::Enc->new(

cpanfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Do not edit this file directly. To change prereqs, edit the `dist.ini` file.
22

33
requires "Carp" => "0";
4+
requires "Crypt::AuthEnc::GCM" => "0";
45
requires "Crypt::Mode::CBC" => "0";
56
requires "Crypt::OpenSSL::Bignum" => "0";
67
requires "Crypt::OpenSSL::RSA" => "0";

dist.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ MIME::Base64 = 0
2020
XML::LibXML = 0
2121
Crypt::Mode::CBC = 0
2222
Crypt::Random = 0
23+
Crypt::AuthEnc::GCM = 0
2324

2425
[Prereqs / TestRequires]
2526
Test::More = 0

lib/XML/Enc.pm

Lines changed: 81 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use Carp;
99
use XML::LibXML;
1010
use Crypt::OpenSSL::RSA;
1111
use Crypt::Mode::CBC;
12+
use Crypt::AuthEnc::GCM;
1213
use MIME::Base64 qw/decode_base64 encode_base64/;
1314
use Crypt::Random qw( makerandom_octet );
1415

1516
use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
1617

17-
our $VERSION = '0.03';
18+
our $VERSION = '0.04';
1819

1920
our $DEBUG = 0;
2021

@@ -52,10 +53,37 @@ XML::Enc - XML Encryption
5253
# 5.2.2 AES - 128 bit initialization vector (IV) (16 bytes)
5354

5455
my %encmethods = (
55-
'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => { ivsize => 8, keysize => 24, modename => 'DES_EDE' },
56-
'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => { ivsize => '16', keysize => 16, modename => 'AES' },
57-
'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => { ivsize => '16', keysize => 24, modename => 'AES' },
58-
'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => { ivsize => '16', keysize => 32, modename => 'AES' },
56+
'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => {
57+
ivsize => 8,
58+
keysize => 24,
59+
modename => 'DES_EDE' },
60+
'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => {
61+
ivsize => '16',
62+
keysize => 16,
63+
modename => 'AES' },
64+
'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => {
65+
ivsize => '16',
66+
keysize => 24,
67+
modename => 'AES' },
68+
'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => {
69+
ivsize => '16',
70+
keysize => 32,
71+
modename => 'AES' },
72+
'http://www.w3.org/2009/xmlenc11#aes128-gcm' => {
73+
ivsize => '12',
74+
keysize => 16,
75+
modename => 'AES',
76+
tagsize => 16 },
77+
'http://www.w3.org/2009/xmlenc11#aes192-gcm' => {
78+
ivsize => '12',
79+
keysize => 24,
80+
modename => 'AES',
81+
tagsize => 16 },
82+
'http://www.w3.org/2009/xmlenc11#aes256-gcm' => {
83+
ivsize => '12',
84+
keysize => 32,
85+
modename => 'AES',
86+
tagsize => 16 },
5987
);
6088

6189
=head2 new( ... )
@@ -293,6 +321,9 @@ sub _setEncryptionMethod {
293321
'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes192-cbc',
294322
'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
295323
'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
324+
'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
325+
'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm',
326+
'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm',
296327
);
297328

298329
return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
@@ -327,7 +358,7 @@ sub _setKeyEncryptionMethod {
327358
'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
328359
);
329360

330-
return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-1_5'};
361+
return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-oaep-mgf1p'};
331362
}
332363

333364
sub _DecryptData {
@@ -338,23 +369,35 @@ sub _DecryptData {
338369

339370
my $iv;
340371
my $encrypted;
341-
my $cbc;
372+
my $plaintext;
373+
374+
my $ivsize = $encmethods{$method}->{ivsize};
375+
my $tagsize = $encmethods{$method}->{tagsize};
376+
377+
$iv = substr $encrypteddata, 0, $ivsize;
378+
$encrypted = substr $encrypteddata, $ivsize;
342379

343380
# XML Encryption 5.2 Block Encryption Algorithms
344381
# The resulting cipher text is prefixed by the IV.
345-
if (defined $encmethods{$method}){
346-
my $blksize = $encmethods{$method}->{ivsize};
347-
#print "Block Size: ", $blksize;
348-
$iv = substr $encrypteddata, 0, $blksize;
349-
$encrypted = substr $encrypteddata, $blksize;
350-
$cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
382+
if (defined $encmethods{$method} & $method !~ /gcm/ ){
383+
my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
384+
$plaintext = $self->_remove_padding($cbc->decrypt($encrypted, $key, $iv));
385+
} elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
386+
my $gcm = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
387+
388+
# Note that GCM support for additional authentication
389+
# data is not used in the XML specification.
390+
my $tag = substr $encrypted, - $tagsize;
391+
$encrypted = substr $encrypted, 0, (length $encrypted) - $tagsize;
392+
$plaintext = $gcm->decrypt_add($encrypted);
393+
if ( ! $gcm->decrypt_done($tag) ) {
394+
die "Tag expected did not match returned Tag";
395+
}
351396
} else {
352397
die "Unsupported Encryption Algorithm";
353398
}
354399

355-
my $plaintext = $cbc->decrypt($encrypted, $key, $iv);
356-
357-
return $self->_remove_padding($plaintext);
400+
return $plaintext;
358401
}
359402

360403
sub _EncryptData {
@@ -363,25 +406,33 @@ sub _EncryptData {
363406
my $data = shift;
364407
my $key = shift;
365408

366-
my $iv;
367-
my $cbc;
409+
my $cipherdata;
410+
my $ivsize = $encmethods{$method}->{ivsize};
411+
my $keysize = $encmethods{$method}->{keysize};
368412

369-
# XML Encryption 5.2 Block Encryption Algorithms
370-
# The resulting cipher text is prefixed by the IV.
371-
if (defined $encmethods{$method}){
372-
my $blksize = $encmethods{$method}->{ivsize};
373-
my $keysize = $encmethods{$method}->{keysize};
374-
$iv = makerandom_octet ( Length => $blksize);
375-
${$key} = makerandom_octet ( Length => $keysize);
376-
$data = $self->_add_padding($data, $blksize);
377-
$cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
413+
my $iv = makerandom_octet ( Length => $ivsize);
414+
${$key} = makerandom_octet ( Length => $keysize);
415+
416+
if (defined $encmethods{$method} & $method !~ /gcm/ ){
417+
my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
418+
# XML Encryption 5.2 Block Encryption Algorithms
419+
# The resulting cipher text is prefixed by the IV.
420+
$data = $self->_add_padding($data, $ivsize);
421+
$cipherdata = $iv . $cbc->encrypt($data, ${$key}, $iv);
422+
} elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
423+
my $gcm = Crypt::AuthEnc::GCM->new($encmethods{$method}->{modename}, ${$key}, $iv);
424+
425+
# Note that GCM support for additional authentication
426+
# data is not used in the XML specification.
427+
my $encrypted = $gcm->encrypt_add($data);
428+
my $tag = $gcm->encrypt_done();
429+
430+
$cipherdata = $iv . $encrypted . $tag;
378431
} else {
379432
die "Unsupported Encryption Algorithm";
380433
}
381434

382-
my $encrypted = $iv . $cbc->encrypt($data, ${$key}, $iv);
383-
384-
return $encrypted;
435+
return $cipherdata;
385436
}
386437

387438
sub _DecryptKey {

t/06-test-encryption-methods.t

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use strict;
22
use warnings;
3-
use Test::More tests => 32;
3+
use Test::More tests => 56;
44
use XML::Enc;
55
use MIME::Base64 qw/decode_base64 encode_base64/;
66
use File::Which;
@@ -13,7 +13,7 @@ my $xml = <<'XML';
1313
XML
1414

1515
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
16-
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc/;
16+
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
1717

1818
foreach my $km (@key_methods) {
1919
foreach my $dm (@data_methods) {
@@ -34,6 +34,11 @@ foreach my $km (@key_methods) {
3434

3535
SKIP: {
3636
skip "xmlsec1 not installed", 2 unless which('xmlsec1');
37+
my $version;
38+
if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
39+
$version = $1;
40+
};
41+
skip "xmlsec version 1.2.27 minimum for GCM", 2 if $version lt '1.2.27';
3742
ok( open XML, '>', 'tmp.xml' );
3843
print XML $encrypted;
3944
close XML;

t/07-decrypt-xmlsec.t

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use strict;
22
use warnings;
3-
use Test::More tests => 40;
3+
use Test::More tests => 70;
44
use XML::Enc;
55
use MIME::Base64 qw/decode_base64/;
66
use File::Which;
@@ -19,13 +19,26 @@ my $plaintext = <<'UNENCRYPTED';
1919
UNENCRYPTED
2020

2121
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
22-
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc/;
22+
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
23+
24+
my %uri = (
25+
'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
26+
'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
27+
'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
28+
'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#',
29+
'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#',
30+
'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#',
31+
'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#',
32+
);
2333

2434
my %sesskey = (
2535
'aes128-cbc' => 'aes-128',
2636
'aes192-cbc' => 'aes-192',
2737
'aes256-cbc' => 'aes-256',
2838
'tripledes-cbc' => 'des-192',
39+
'aes128-gcm' => 'aes-128-GCM',
40+
'aes192-gcm' => 'aes-192-GCM',
41+
'aes256-gcm' => 'aes-256-GCM',
2942
);
3043

3144
foreach my $km (@key_methods) {
@@ -41,7 +54,7 @@ XML Security Library example: Original XML
4154
xmlns="http://www.w3.org/2001/04/xmlenc#"
4255
Type="http://www.w3.org/2001/04/xmlenc#Element">
4356
<EncryptionMethod Algorithm=
44-
"http://www.w3.org/2001/04/xmlenc#$dm"/>
57+
"$uri{$dm}$dm"/>
4558
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
4659
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
4760
<EncryptionMethod Algorithm=
@@ -70,7 +83,7 @@ XML Security Library example: Original XML
7083
xmlns="http://www.w3.org/2001/04/xmlenc#"
7184
Type="http://www.w3.org/2001/04/xmlenc#Content">
7285
<EncryptionMethod Algorithm=
73-
"http://www.w3.org/2001/04/xmlenc#$dm"/>
86+
"$uri{$dm}$dm"/>
7487
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
7588
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
7689
<EncryptionMethod Algorithm=
@@ -91,6 +104,11 @@ CONTENT
91104

92105
SKIP: {
93106
skip "xmlsec1 not installed", 5 unless which('xmlsec1');
107+
my $version;
108+
if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
109+
$version = $1;
110+
};
111+
skip "xmlsec version 1.2.27 minimum for GCM", 5 if $version lt '1.2.27';
94112

95113
ok( open XML, '>', 'plaintext.xml' );
96114
print XML $plaintext;

0 commit comments

Comments
 (0)