From 97b3ffa254543833a6dcb19de00c6f485b08ff85 Mon Sep 17 00:00:00 2001 From: Olav Morken <olav.morken@uninett.no> Date: Thu, 27 Oct 2011 09:17:26 +0000 Subject: [PATCH] Minimal backport of key oracle fixes. This is a backport of the changes in r2953, with one small change from r2952. git-svn-id: https://simplesamlphp.googlecode.com/svn/branches/simplesamlphp-1.8@2955 44740490-163a-0410-bde0-09ae8108e29a --- lib/SAML2/Utils.php | 35 ++++++++++++++++++++++++++++++++++- lib/xmlseclibs.php | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/lib/SAML2/Utils.php b/lib/SAML2/Utils.php index df08db622..8cc872639 100644 --- a/lib/SAML2/Utils.php +++ b/lib/SAML2/Utils.php @@ -344,8 +344,41 @@ class SAML2_Utils { $encKey = $symmetricKeyInfo->encryptedCtx; $symmetricKeyInfo->key = $inputKey->key; - $key = $encKey->decryptKey($symmetricKeyInfo); + + $keySizes = array( + XMLSecurityKey::TRIPLEDES_CBC => 24, + XMLSecurityKey::AES128_CBC => 16, + XMLSecurityKey::AES192_CBC => 24, + XMLSecurityKey::AES256_CBC => 32, + ); + if (!isset($keySizes[$symmetricKey->type])) { + /* To protect against "key oracle" attacks, we need to be able to create a + * symmetric key, and for that we need to know the key size. + */ + throw new Exception('Unsupported encryption algorithm: ' . var_export($symmetricKey->type, TRUE)); + } + $keySize = $keySizes[$symmetricKey->type]; + + try { + $key = $encKey->decryptKey($symmetricKeyInfo); + } catch (Exception $e) { + /* We failed to decrypt this key. Log it, and substitute a "random" key. */ + SimpleSAML_Logger::error('Failed to decrypt symmetric key: ' . $e->getMessage()); + /* Create a replacement key, so that it looks like we fail in the same way as if the key was correctly padded. */ + + /* We base the symmetric key on the encrypted key, so that we always behave the same way for a given input key. */ + $encryptedKey = $encKey->getCipherValue(); + $key = md5($encryptedKey, TRUE); + + /* Make sure that the key has the correct length. */ + if (strlen($key) > $keySize) { + $key = substr($key, 0, $keySize); + } elseif (strlen($key) < $keySize) { + $key = str_pad($key, $keySize); + } + } $symmetricKey->loadkey($key); + } else { $symKeyAlgo = $symmetricKey->getAlgorith(); /* Make sure that the input key has the correct format. */ diff --git a/lib/xmlseclibs.php b/lib/xmlseclibs.php index c74b3b2b7..a7efb688a 100644 --- a/lib/xmlseclibs.php +++ b/lib/xmlseclibs.php @@ -1454,23 +1454,50 @@ class XMLSecEnc { $this->type = $curType; } - public function decryptNode($objKey, $replace=TRUE) { - $data = ''; + /** + * Retrieve the CipherValue text from this encrypted node. + * + * @return string|NULL The Ciphervalue text, or NULL if no CipherValue is found. + */ + public function getCipherValue() { if (empty($this->rawNode)) { throw new Exception('Node to decrypt has not been set'); } - if (! $objKey instanceof XMLSecurityKey) { - throw new Exception('Invalid Key'); - } + $doc = $this->rawNode->ownerDocument; $xPath = new DOMXPath($doc); $xPath->registerNamespace('xmlencr', XMLSecEnc::XMLENCNS); /* Only handles embedded content right now and not a reference */ $query = "./xmlencr:CipherData/xmlencr:CipherValue"; $nodeset = $xPath->query($query, $this->rawNode); + $node = $nodeset->item(0); + + if (!$node) { + return NULL; + } + + return base64_decode($node->nodeValue); + } + + /** + * Decrypt this encrypted node. + * + * The behaviour of this function depends on the value of $replace. + * If $replace is FALSE, we will return the decrypted data as a string. + * If $replace is TRUE, we will insert the decrypted element(s) into the + * document, and return the decrypted element(s). + * + * @params XMLSecurityKey $objKey The decryption key that should be used when decrypting the node. + * @params boolean $replace Whether we should replace the encrypted node in the XML document with the decrypted data. The default is TRUE. + * @return string|DOMElement The decrypted data. + */ + public function decryptNode($objKey, $replace=TRUE) { + if (! $objKey instanceof XMLSecurityKey) { + throw new Exception('Invalid Key'); + } - if ($node = $nodeset->item(0)) { - $encryptedData = base64_decode($node->nodeValue); + $encryptedData = $this->getCipherValue(); + if ($encryptedData) { $decrypted = $objKey->decryptData($encryptedData); if ($replace) { switch ($this->type) { -- GitLab