From 140aeb1bf14655f369e1143f83aa78832a577de4 Mon Sep 17 00:00:00 2001
From: Tim van Dijen <tim.dijen@minbzk.nl>
Date: Tue, 15 Dec 2020 22:09:45 +0100
Subject: [PATCH] Configurable symmetric encryption algorithm (#1377)

---
 docs/simplesamlphp-reference-idp-remote.md | 11 ++++++++-
 docs/simplesamlphp-reference-sp-remote.md  | 15 ++++++++++---
 modules/saml/lib/IdP/SAML2.php             |  8 ++++++-
 modules/saml/lib/Message.php               | 26 ++++++++++++++++++----
 4 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/docs/simplesamlphp-reference-idp-remote.md b/docs/simplesamlphp-reference-idp-remote.md
index 4a12dc74c..35e1624d8 100644
--- a/docs/simplesamlphp-reference-idp-remote.md
+++ b/docs/simplesamlphp-reference-idp-remote.md
@@ -219,8 +219,17 @@ There are two modes of encryption supported by SimpleSAMLphp. One is symmetric e
 :   Note that this option overrides the option with the same name in the SP configuration.
 
 `sharedkey`
-:   Symmetric key which should be used for decryption. This should be a 128-bit key. If this option is not specified, public key encryption will be used instead.
+:   Symmetric key which should be used for decryption. This should be a 128-bit, 192-bit or 256-bit key based on the algorithm used. If this option is not specified, public key encryption will be used instead.
 
+`sharedkey_algorithm`
+:   Algorithm which should be used for decryption. Possible values are:
+
+    * http://www.w3.org/2001/04/xmlenc#aes128-cbc
+    * http://www.w3.org/2001/04/xmlenc#aes192-cbc
+    * http://www.w3.org/2001/04/xmlenc#aes256-cbc
+    * http://www.w3.org/2009/xmlenc11#aes128-gcm
+    * http://www.w3.org/2009/xmlenc11#aes192-gcm
+    * http://www.w3.org/2009/xmlenc11#aes256-gcm
 
 ### Fields for signing and validating messages
 
diff --git a/docs/simplesamlphp-reference-sp-remote.md b/docs/simplesamlphp-reference-sp-remote.md
index 63e01f860..2acc6e8e4 100644
--- a/docs/simplesamlphp-reference-sp-remote.md
+++ b/docs/simplesamlphp-reference-sp-remote.md
@@ -328,9 +328,18 @@ of the SP.
 
 `sharedkey`
 :   Symmetric key which should be used for encryption. This should be a
-    128-bit key. If this option is not specified, public key encryption
-    will be used instead.
-
+    128-bit, 192-bit or 256-bit key based on the algorithm used.
+    If this option is not specified, public key encryption will be used instead.
+
+`sharedkey_algorithm`
+:   Algorithm which should be used for encryption. Possible values are:
+
+    * http://www.w3.org/2001/04/xmlenc#aes128-cbc
+    * http://www.w3.org/2001/04/xmlenc#aes192-cbc
+    * http://www.w3.org/2001/04/xmlenc#aes256-cbc
+    * http://www.w3.org/2009/xmlenc11#aes128-gcm
+    * http://www.w3.org/2009/xmlenc11#aes192-gcm
+    * http://www.w3.org/2009/xmlenc11#aes256-gcm
 
 ### Fields for signing and validating messages
 
diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index ac692da2b..972341044 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -1294,7 +1294,13 @@ class SAML2
 
         $sharedKey = $spMetadata->getString('sharedkey', null);
         if ($sharedKey !== null) {
-            $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
+            $algo = $spMetadata->getString('sharedkey_algorithm', null);
+            if ($algo === null) {
+                // If no algorithm is configured, use a sane default
+                $algo = $idpMetadata->getString('sharedkey_algorithm', XMLSecurityKey::AES128_CBC);
+            }
+
+            $key = new XMLSecurityKey($algo);
             $key->loadKey($sharedKey);
         } else {
             $keys = $spMetadata->getPublicKeys('encryption', true);
diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php
index 4955c2037..3cd86b252 100644
--- a/modules/saml/lib/Message.php
+++ b/modules/saml/lib/Message.php
@@ -236,16 +236,29 @@ class Message
      *
      * @param \SimpleSAML\Configuration $srcMetadata The metadata of the sender (IdP).
      * @param \SimpleSAML\Configuration $dstMetadata The metadata of the recipient (SP).
+     * @psalm-suppress UndefinedDocblockClass  This can be removed after upgrading to saml2v5
+     * @param \SimpleSAML\SAML2\XML\xenc\EncryptionMethod|null $encryptionMethod The EncryptionMethod from the assertion.
      *
      * @return array Array of decryption keys.
      */
     public static function getDecryptionKeys(
         Configuration $srcMetadata,
-        Configuration $dstMetadata
+        Configuration $dstMetadata,
+        $encryptionMethod = null
     ): array {
         $sharedKey = $srcMetadata->getString('sharedkey', null);
         if ($sharedKey !== null) {
-            $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
+            if ($encryptionMethod !== null) {
+                $algo = $encryptionMethod->getAlgorithm();
+            } else {
+                $algo = $srcMetadata->getString('sharedkey_algorithm', null);
+                if ($algo === null) {
+                    // If no algorithm is supplied or configured, use a sane default as a last resort
+                    $algo = $dstMetadata->getString('sharedkey_algorithm', XMLSecurityKey::AES128_CBC);
+                }
+            }
+
+            $key = new XMLSecurityKey($algo);
             $key->loadKey($sharedKey);
             return [$key];
         }
@@ -324,7 +337,7 @@ class Message
         Configuration $dstMetadata,
         $assertion
     ): Assertion {
-        Assert::isInstanceOfAny($assertion, [\SAML2\Assertion::class, \SAML2\EncryptedAssertion::class]);
+        Assert::isInstanceOfAny($assertion, [Assertion::class, EncryptedAssertion::class]);
 
         if ($assertion instanceof Assertion) {
             $encryptAssertion = $srcMetadata->getBoolean('assertion.encryption', null);
@@ -340,7 +353,12 @@ class Message
         }
 
         try {
-            $keys = self::getDecryptionKeys($srcMetadata, $dstMetadata);
+            // @todo Enable this code for saml2v5 to automatically determine encryption algorithm
+            //$encryptionMethod = $assertion->getEncryptedData()->getEncryptionMethod();
+            //$keys = self::getDecryptionKeys($srcMetadata, $dstMetadata, $encryptionMethod);
+
+            $encryptionMethod = null;
+            $keys = self::getDecryptionKeys($srcMetadata, $dstMetadata, $encryptionMethod);
         } catch (\Exception $e) {
             throw new SSP_Error\Exception('Error decrypting assertion: ' . $e->getMessage());
         }
-- 
GitLab