From 8e39cd49dc7e9fc8ca079d7790719052480cdef9 Mon Sep 17 00:00:00 2001 From: Matt Schwager <schwag09@gmail.com> Date: Thu, 4 May 2017 22:56:29 -0400 Subject: [PATCH] Added tests for SimpleSAML\Utils\Crypto This also required adding an additional argument to SimpleSAML\Utils\Crypto::loadPrivateKey to ease in testing. Without this additional argument, SimpleSAML_Configuration::getBaseDir eventually gets called to determine the private key location. This doesn't work well with vfsstream. This argument shouldn't cause too much trouble, and seems cohesive enough with the function's purpose. --- lib/SimpleSAML/Utils/Crypto.php | 11 +- tests/lib/SimpleSAML/Utils/CryptoTest.php | 402 ++++++++++++++++++++++ 2 files changed, 410 insertions(+), 3 deletions(-) diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php index c7d16921a..c32799916 100644 --- a/lib/SimpleSAML/Utils/Crypto.php +++ b/lib/SimpleSAML/Utils/Crypto.php @@ -173,6 +173,8 @@ class Crypto * missing key will cause an exception. Defaults to false. * @param string $prefix The prefix which should be used when reading from the metadata * array. Defaults to ''. + * @param bool $full_path Whether the filename found in the configuration contains the + * full path to the private key or not. Default to false. * * @return array|NULL Extracted private key, or NULL if no private key is present. * @throws \InvalidArgumentException If $required is not boolean or $prefix is not a string. @@ -182,9 +184,9 @@ class Crypto * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no> * @author Olav Morken, UNINETT AS <olav.morken@uninett.no> */ - public static function loadPrivateKey(\SimpleSAML_Configuration $metadata, $required = false, $prefix = '') + public static function loadPrivateKey(\SimpleSAML_Configuration $metadata, $required = false, $prefix = '', $full_path = false) { - if (!is_bool($required) || !is_string($prefix)) { + if (!is_bool($required) || !is_string($prefix) || !is_bool($full_path)) { throw new \InvalidArgumentException('Invalid input parameters.'); } @@ -198,7 +200,10 @@ class Crypto } } - $file = Config::getCertPath($file); + if (!$full_path) { + $file = Config::getCertPath($file); + } + $data = @file_get_contents($file); if ($data === false) { throw new \SimpleSAML_Error_Exception('Unable to load private key from file "'.$file.'"'); diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php index 9563d665c..84eed30ee 100644 --- a/tests/lib/SimpleSAML/Utils/CryptoTest.php +++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php @@ -3,12 +3,30 @@ namespace SimpleSAML\Test\Utils; use SimpleSAML\Utils\Crypto; +use \SimpleSAML_Configuration as Configuration; + +use \org\bovigo\vfs\vfsStream; /** * Tests for SimpleSAML\Utils\Crypto. */ class CryptoTest extends \PHPUnit_Framework_TestCase { + const ROOTDIRNAME = 'testdir'; + const DEFAULTCERTDIR = 'certdir'; + + public function setUp() + { + $this->root = vfsStream::setup( + self::ROOTDIRNAME, + null, + array( + self::DEFAULTCERTDIR => array(), + ) + ); + $this->root_directory = vfsStream::url(self::ROOTDIRNAME); + $this->certdir = $this->root_directory.DIRECTORY_SEPARATOR.self::DEFAULTCERTDIR; + } /** * Test invalid input provided to the aesDecrypt() method. @@ -135,4 +153,388 @@ pfajpJ9ZzdyLIo6dVjdQtl+S1rpFCx7ziVN8tCCX4fAVCqRqZJaG/UMLvguVqayb PHP; $this->assertEquals(trim($pem), trim(Crypto::der2pem(Crypto::pem2der($pem)))); } + + /** + * @covers \SimpleSAML\Utils\Crypto::pwHash + */ + public function testGoodPwHash() + { + $pw = "password"; + $algorithm = "SHA1"; + + $res = Crypto::pwHash($pw, $algorithm); + + /* + * echo -n "password" | sha1sum | awk -F " " '{print $1}' | xxd -r -p | base64 + * W6ph5Mm5Pz8GgiULbPgzG37mj9g= + */ + $expected = "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g="; + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::pwHash + */ + public function testGoodSaltedPwHash() + { + $pw = "password"; + $algorithm = "SSHA1"; + $salt = "salt"; + + $res = Crypto::pwHash($pw, $algorithm, $salt); + + /* + * echo -n "password""salt" | sha1sum | awk -v salt=$(echo -n "salt" | xxd -u -p) -F " " '{print $1 salt}' | xxd -r -p | base64 + * yI6cZwQadOA1e+/f+T+H3eCQQhRzYWx0 + */ + $expected = "{SSHA}yI6cZwQadOA1e+/f+T+H3eCQQhRzYWx0"; + + $this->assertEquals($expected, $res); + } + + /** + * @expectedException \SimpleSAML_Error_Exception + * + * @covers \SimpleSAML\Utils\Crypto::pwHash + */ + public function testBadHashAlgorithm() + { + $pw = "password"; + $algorithm = "wtf"; + + Crypto::pwHash($pw, $algorithm); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::pwValid + */ + public function testGoodPwValid() + { + $pw = "password"; + $algorithm = "SHA1"; + + $hash = Crypto::pwHash($pw, $algorithm); + $res = Crypto::pwValid($hash, $pw); + + $this->assertTrue($res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::pwValid + */ + public function testGoodSaltedPwValid() + { + $pw = "password"; + $algorithm = "SSHA1"; + $salt = "salt"; + + $hash = Crypto::pwHash($pw, $algorithm, $salt); + $res = Crypto::pwValid($hash, $pw); + + $this->assertTrue($res); + } + + /** + * @expectedException \SimpleSAML_Error_Exception + * + * @covers \SimpleSAML\Utils\Crypto::pwValid + */ + public function testBadHashAlgorithmValid() + { + $pw = "password"; + $algorithm = "wtf"; + $hash = "{".$algorithm."}B64STRING"; + + Crypto::pwValid($hash, $algorithm); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::secureCompare + */ + public function testSecureCompareEqual() + { + $res = Crypto::secureCompare("string", "string"); + + $this->assertTrue($res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::secureCompare + */ + public function testSecureCompareNotEqual() + { + $res = Crypto::secureCompare("string1", "string2"); + + $this->assertFalse($res); + } + + /** + * @expectedException \SimpleSAML_Error_Exception + * + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyRequiredMetadataMissing() + { + $config = new Configuration(array(), 'test'); + $required = true; + + Crypto::loadPrivateKey($config, $required); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyNotRequiredMetadataMissing() + { + $config = new Configuration(array(), 'test'); + $required = false; + + $res = Crypto::loadPrivateKey($config, $required); + + $this->assertNull($res); + } + + /** + * @expectedException \SimpleSAML_Error_Exception + * + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyMissingFile() + { + $config = new Configuration(array('privatekey' => 'nonexistant'), 'test'); + + Crypto::loadPrivateKey($config, false, '', true); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyBasic() + { + $filename = $this->certdir.DIRECTORY_SEPARATOR.'key'; + $data = 'data'; + $config = new Configuration(array('privatekey' => $filename), 'test'); + $full_path = true; + + file_put_contents($filename, $data); + + $res = Crypto::loadPrivateKey($config, false, '', $full_path); + $expected = array('PEM' => $data); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyPassword() + { + $password = 'password'; + $filename = $this->certdir.DIRECTORY_SEPARATOR.'key'; + $data = 'data'; + $config = new Configuration( + array( + 'privatekey' => $filename, + 'privatekey_pass' => $password, + ), + 'test' + ); + $full_path = true; + + file_put_contents($filename, $data); + + $res = Crypto::loadPrivateKey($config, false, '', $full_path); + $expected = array('PEM' => $data, 'password' => $password); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPrivateKey + */ + public function testLoadPrivateKeyPrefix() + { + $prefix = 'prefix'; + $password = 'password'; + $filename = $this->certdir.DIRECTORY_SEPARATOR.'key'; + $data = 'data'; + $config = new Configuration( + array( + $prefix.'privatekey' => $filename, + $prefix.'privatekey_pass' => $password, + ), + 'test' + ); + $full_path = true; + + file_put_contents($filename, $data); + + $res = Crypto::loadPrivateKey($config, false, $prefix, $full_path); + $expected = array('PEM' => $data, 'password' => $password); + + $this->assertEquals($expected, $res); + } + + /** + * @expectedException \SimpleSAML_Error_Exception + * + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyRequiredMetadataMissing() + { + $config = new Configuration(array(), 'test'); + $required = true; + + Crypto::loadPublicKey($config, $required); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyNotRequiredMetadataMissing() + { + $config = new Configuration(array(), 'test'); + $required = false; + + $res = Crypto::loadPublicKey($config, $required); + + $this->assertNull($res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyFingerprintBasicString() + { + $fingerprint = 'fingerprint'; + $config = new Configuration(array('certFingerprint' => $fingerprint), 'test'); + + $res = Crypto::loadPublicKey($config); + $expected = array('certFingerprint' => array($fingerprint)); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyFingerprintBasicArray() + { + $fingerprint1 = 'fingerprint1'; + $fingerprint2 = 'fingerprint2'; + $config = new Configuration( + array( + 'certFingerprint' => array( + $fingerprint1, + $fingerprint2 + ), + ), + 'test' + ); + + $res = Crypto::loadPublicKey($config); + $expected = array('certFingerprint' => array($fingerprint1, $fingerprint2)); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyFingerprintLowercase() + { + $fingerprint = 'FINGERPRINT'; + $config = new Configuration(array('certFingerprint' => $fingerprint), 'test'); + + $res = Crypto::loadPublicKey($config); + $expected = array('certFingerprint' => array(strtolower($fingerprint))); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyFingerprintRemoveColons() + { + $fingerprint = 'f:i:n:g:e:r:p:r:i:n:t'; + $config = new Configuration(array('certFingerprint' => $fingerprint), 'test'); + + $res = Crypto::loadPublicKey($config); + $expected = array('certFingerprint' => array(str_replace(':', '', $fingerprint))); + + $this->assertEquals($expected, $res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyNotX509Certificate() + { + $config = new Configuration( + array( + 'keys' => array( + array( + 'X509Certificate' => '', + 'type' => 'NotX509Certificate', + 'signing' => true + ), + ), + ), + 'test' + ); + + $res = Crypto::loadPublicKey($config); + + $this->assertNull($res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyNotSigning() + { + $config = new Configuration( + array( + 'keys' => array( + array( + 'X509Certificate' => '', + 'type' => 'X509Certificate', + 'signing' => false + ), + ), + ), + 'test' + ); + + $res = Crypto::loadPublicKey($config); + + $this->assertNull($res); + } + + /** + * @covers \SimpleSAML\Utils\Crypto::loadPublicKey + */ + public function testLoadPublicKeyBasic() + { + $x509certificate = 'x509certificate'; + $config = new Configuration( + array( + 'keys' => array( + array( + 'X509Certificate' => $x509certificate, + 'type' => 'X509Certificate', + 'signing' => true + ), + ), + ), + 'test' + ); + + $res = Crypto::loadPublicKey($config)['certData']; + $expected = $x509certificate; + + $this->assertEquals($expected, $res); + } } -- GitLab