diff --git a/.travis.yml b/.travis.yml index 4a1c5331..4c975328 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,21 +2,41 @@ sudo: false language: php +sudo: required +dist: trusty +group: edge + +env: + global: + COMPOSER_DISABLE_XDEBUG_WARN=true + +cache: + directories: + - $HOME/.composer/cache + matrix: fast_finish: true include: - - php: 5.3 - - php: 5.4 - - php: 5.5 - - php: 5.6 - env: - - EXECUTE_COVERAGE=true - - php: 7 - - php: hhvm + - php: 7.0 + - php: 7.1 + env: EXECUTE_COVERAGE=true + - php: nightly + - php: hhvm-3.12 + env: COMPOSER_INSTALL_FLAGS=--ignore-platform-reqs + - php: hhvm-3.15 + env: COMPOSER_INSTALL_FLAGS=--ignore-platform-reqs + - php: hhvm-nightly + env: COMPOSER_INSTALL_FLAGS=--ignore-platform-reqs + allow-failures: + - php: nightly + - php: hhvm-nightly + +install: + - travis_retry composer install --prefer-dist $COMPOSER_INSTALL_FLAGS script: - - if [[ $EXECUTE_COVERAGE == 'true' ]]; then phpunit --coverage-clover clover.xml tests; fi - - if [[ $EXECUTE_COVERAGE != 'true' ]]; then phpunit tests; fi + - if [[ $EXECUTE_COVERAGE == 'true' ]]; then vendor/bin/phpunit --coverage-clover clover.xml; fi + - if [[ $EXECUTE_COVERAGE != 'true' ]]; then vendor/bin/phpunit; fi after_success: - if [[ $EXECUTE_COVERAGE == 'true' ]]; then bash <(curl -s https://codecov.io/bash); fi diff --git a/composer.json b/composer.json index 1fa8dd19..e4e6371c 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,16 @@ "RobRichards\\XMLSecLibs\\": "src" } }, + "autoload-dev": { + "psr-4": { + "RobRichards\\XMLSecLibs\\Tests\\": "tests/src" + } + }, "require": { - "php": ">= 5.3" - }, - "suggest": { - "ext-openssl": "OpenSSL extension", - "ext-mcrypt": "MCrypt extension" + "ext-openssl": "*", + "php": ">= 7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.4" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..cf81d6e4 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + tests/ + + + tests/src/ + + + + + src/ + + + + + + diff --git a/src/XMLSecEnc.php b/src/XMLSecEnc.php index 380ffcbe..12e90b46 100644 --- a/src/XMLSecEnc.php +++ b/src/XMLSecEnc.php @@ -315,7 +315,7 @@ public function encryptKey($srcKey, $rawKey, $append=true) $this->encKey = $encKey; } $encMethod = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod')); - $encMethod->setAttribute('Algorithm', $srcKey->getAlgorith()); + $encMethod->setAttribute('Algorithm', $srcKey->getAlgorithm()); if (! empty($srcKey->name)) { $keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo')); $keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name)); diff --git a/src/XMLSecurityDSig.php b/src/XMLSecurityDSig.php index 5767cfc8..476eca5b 100644 --- a/src/XMLSecurityDSig.php +++ b/src/XMLSecurityDSig.php @@ -152,7 +152,7 @@ private function getXPathObj() */ public static function generateGUID($prefix='pfx') { - $uuid = md5(uniqid(mt_rand(), true)); + $uuid = bin2hex(random_bytes(16)); $guid = $prefix.substr($uuid, 0, 8)."-". substr($uuid, 8, 4)."-". substr($uuid, 12, 4)."-". diff --git a/src/XMLSecurityKey.php b/src/XMLSecurityKey.php index 9bb914a0..1509611c 100644 --- a/src/XMLSecurityKey.php +++ b/src/XMLSecurityKey.php @@ -111,32 +111,36 @@ public function __construct($type, $params=null) { switch ($type) { case (self::TRIPLEDES_CBC): - $this->cryptParams['library'] = 'mcrypt'; - $this->cryptParams['cipher'] = MCRYPT_TRIPLEDES; - $this->cryptParams['mode'] = MCRYPT_MODE_CBC; + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'des-ede3-cbc'; + $this->cryptParams['type'] = 'symmetric'; $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'; $this->cryptParams['keysize'] = 24; + $this->cryptParams['blocksize'] = 8; break; case (self::AES128_CBC): - $this->cryptParams['library'] = 'mcrypt'; - $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128; - $this->cryptParams['mode'] = MCRYPT_MODE_CBC; + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-128-cbc'; + $this->cryptParams['type'] = 'symmetric'; $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'; $this->cryptParams['keysize'] = 16; + $this->cryptParams['blocksize'] = 16; break; case (self::AES192_CBC): - $this->cryptParams['library'] = 'mcrypt'; - $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128; - $this->cryptParams['mode'] = MCRYPT_MODE_CBC; + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-192-cbc'; + $this->cryptParams['type'] = 'symmetric'; $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'; $this->cryptParams['keysize'] = 24; + $this->cryptParams['blocksize'] = 16; break; case (self::AES256_CBC): - $this->cryptParams['library'] = 'mcrypt'; - $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128; - $this->cryptParams['mode'] = MCRYPT_MODE_CBC; + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-256-cbc'; + $this->cryptParams['type'] = 'symmetric'; $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'; $this->cryptParams['keysize'] = 32; + $this->cryptParams['blocksize'] = 16; break; case (self::RSA_1_5): $this->cryptParams['library'] = 'openssl'; @@ -235,9 +239,8 @@ public function getSymmetricKeySize() } /** - * Generates a session key using the openssl-extension or using the mcrypt-extension as a fallback. - * In case of using DES3-CBC the key is checked for a proper parity bits set - Mcrypt doesn't care about the parity bits, - * but others may care. + * Generates a session key using `random_bytes`. + * In case of using DES3-CBC the key is checked for a proper parity bits set * @return string * @throws Exception */ @@ -247,20 +250,11 @@ public function generateSessionKey() throw new Exception('Unknown key size for type "' . $this->type . '".'); } $keysize = $this->cryptParams['keysize']; - - if (function_exists('openssl_random_pseudo_bytes')) { - /* We have PHP >= 5.3 - use openssl to generate session key. */ - $key = openssl_random_pseudo_bytes($keysize); - } else { - /* Generating random key using iv generation routines */ - $key = mcrypt_create_iv($keysize, MCRYPT_RAND); - } - + $key = random_bytes($keysize); + + // Make sure that the generated key has the proper parity bits set. if ($this->type === self::TRIPLEDES_CBC) { - /* Make sure that the generated key has the proper parity bits set. - * Mcrypt doesn't care about the parity bits, but others may care. - */ - for ($i = 0; $i < strlen($key); $i++) { + for ($i = 0; $i < $this->byteStrLen($key); $i++) { $byte = ord($key[$i]) & 0xfe; $parity = 1; for ($j = 1; $j < 8; $j++) { @@ -270,7 +264,7 @@ public function generateSessionKey() $key[$i] = chr($byte); } } - + $this->key = $key; return $key; } @@ -331,8 +325,9 @@ public function loadKey($key, $isFile=false, $isCert = false) } else { $this->x509Certificate = null; } - if ($this->cryptParams['library'] == 'openssl') { - if ($this->cryptParams['type'] == 'public') { + + switch ($this->cryptParams['type']) { + case 'public': if ($isCert) { /* Load the thumbprint if this is an X509 certificate. */ $this->X509Thumbprint = self::getRawThumbprint($this->key); @@ -341,73 +336,53 @@ public function loadKey($key, $isFile=false, $isCert = false) if (! $this->key) { throw new Exception('Unable to extract public key'); } - } else { + break; + + case 'private': $this->key = openssl_get_privatekey($this->key, $this->passphrase); - } - } else if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) { - /* Check key length */ - switch ($this->type) { - case (self::AES256_CBC): - if (strlen($this->key) < 25) { + break; + + case'symmetric': + if ($this->byteStrLen($this->key) < $this->cryptParams['keysize']) { throw new Exception('Key must contain at least 25 characters for this cipher'); } break; - case (self::AES192_CBC): - if (strlen($this->key) < 17) { - throw new Exception('Key must contain at least 17 characters for this cipher'); - } - break; - } + default: + throw new Exception('Unknown type'); } } /** - * Encrypts the given data (string) using the mcrypt-extension + * Encrypts the given data (string) using the openssl-extension * * @param string $data * @return string + * @throws Exception */ - private function encryptMcrypt($data) + private function encryptSymmetric($data) { - $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], ''); - $this->iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); - mcrypt_generic_init($td, $this->key, $this->iv); - if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) { - $bs = mcrypt_enc_get_block_size($td); - for ($datalen0 = $datalen = strlen($data); (($datalen % $bs) != ($bs - 1)); $datalen++) - $data .= chr(mt_rand(1, 127)); - $data .= chr($datalen - $datalen0 + 1); + $this->iv = random_bytes(openssl_cipher_iv_length($this->cryptParams['cipher'])); + $data = $this->padISO10126($data, $this->cryptParams['blocksize']); + $encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); + if (false === $encrypted) { + throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string()); } - $encrypted_data = $this->iv.mcrypt_generic($td, $data); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - return $encrypted_data; + return $this->iv . $encrypted; } /** - * Decrypts the given data (string) using the mcrypt-extension + * Encrypts the given data (string) using the openssl-extension * * @param string $data * @return string + * @throws Exception */ - private function decryptMcrypt($data) + private function encryptPublic($data) { - $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], ''); - $iv_length = mcrypt_enc_get_iv_size($td); - - $this->iv = substr($data, 0, $iv_length); - $data = substr($data, $iv_length); - - mcrypt_generic_init($td, $this->key, $this->iv); - $decrypted_data = mdecrypt_generic($td, $data); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) { - $dataLen = strlen($decrypted_data); - $paddingLength = substr($decrypted_data, $dataLen - 1, 1); - $decrypted_data = substr($decrypted_data, 0, $dataLen - ord($paddingLength)); + if (! openssl_public_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure encrypting Data (openssl public) - ' . openssl_error_string()); } - return $decrypted_data; + return $encrypted; } /** @@ -417,18 +392,12 @@ private function decryptMcrypt($data) * @return string * @throws Exception */ - private function encryptOpenSSL($data) + private function encryptPrivate($data) { - if ($this->cryptParams['type'] == 'public') { - if (! openssl_public_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) { - throw new Exception('Failure encrypting Data'); - } - } else { - if (! openssl_private_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) { - throw new Exception('Failure encrypting Data'); - } + if (! openssl_private_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure encrypting Data (openssl private) - ' . openssl_error_string()); } - return $encrypted_data; + return $encrypted; } /** @@ -438,20 +407,109 @@ private function encryptOpenSSL($data) * @return string * @throws Exception */ - private function decryptOpenSSL($data) + private function decryptSymmetric($data) { - if ($this->cryptParams['type'] == 'public') { - if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { - throw new Exception('Failure decrypting Data'); - } - } else { - if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { - throw new Exception('Failure decrypting Data'); - } + $iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']); + $this->iv = $this->byteSubStr($data, 0, $iv_length); + $data = $this->byteSubStr($data, $iv_length); + $decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); + if (false === $decrypted) { + throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string()); + } + return $this->unpadISO10126($decrypted); + } + + /** + * Decrypts the given data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function decryptPublic($data) + { + if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure decrypting Data (openssl public) - ' . openssl_error_string); } return $decrypted; } + /** + * Decrypts the given data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function decryptPrivate($data) + { + if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure decrypting Data (openssl private) - ' . openssl_error_string); + } + return $decrypted; + } + + /** + * ISO 10126 Padding + * + * @param string $data + * @param integer $blockSize + * @throws Exception + * @return string + */ + private function padISO10126($data, $blockSize) + { + if ($blockSize > 256) { + throw new Exception('Block size higher than 256 not allowed'); + } + $padChr = $blockSize - ($this->byteStrLen($data) % $blockSize); + $pattern = chr($padChr); + return $data . str_repeat($pattern, $padChr); + } + + /** + * Remove ISO 10126 Padding + * + * @param string $data + * @return string + */ + private function unpadISO10126($data) + { + $padChr = $this->byteSubStr($data, -1); + $padLen = ord($padChr); + return $this->byteSubStr($data, 0, -$padLen); + } + + /** + * Byte strlen wrapper to compensate for mbstring.func_overload + * + * @param string $string + * @return integer + */ + private function byteStrLen($string) + { + return function_exists('mb_byteStrLen') ? + mb_strlen($string, '8bit') : strlen($string); + } + + /** + * Byte substr wrapper to compensate for mbstring.func_overload + * + * @param string $string + * @param integer $start + * @param integer $length + * @return string + */ + private function byteSubStr($string, $start, $length = null) + { + if (null === $length) { + $length = $this->byteStrLen($string); + } + + return function_exists('mb_byteSubStr') ? + mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length); + } + /** * Signs the given data (string) using the openssl-extension * @@ -488,34 +546,44 @@ private function verifyOpenSSL($data, $signature) } /** - * Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. + * Encrypts the given data (string) using openssl, depending on the library assigned to algorithm in the contructor. * * @param string $data * @return mixed|string + * @throws Exception */ public function encryptData($data) { - switch ($this->cryptParams['library']) { - case 'mcrypt': - return $this->encryptMcrypt($data); - case 'openssl': - return $this->encryptOpenSSL($data); + switch ($this->cryptParams['type']) { + case 'symmetric': + return $this->encryptSymmetric($data); + case 'public': + return $this->encryptPublic($data); + case 'private': + return $this->encryptPrivate($data); + default: + throw new Exception('Invalid encryption type'); } } /** - * Decrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. + * Decrypts the given data (string) using openssl, depending on the library assigned to algorithm in the contructor. * * @param string $data * @return mixed|string + * @throws Exception */ public function decryptData($data) { - switch ($this->cryptParams['library']) { - case 'mcrypt': - return $this->decryptMcrypt($data); - case 'openssl': - return $this->decryptOpenSSL($data); + switch ($this->cryptParams['type']) { + case 'symmetric': + return $this->decryptSymmetric($data); + case 'public': + return $this->decryptPublic($data); + case 'private': + return $this->decryptPrivate($data); + default: + throw new Exception('Invalid openssl decryption type'); } } @@ -588,7 +656,7 @@ public static function makeAsnSegment($type, $string) break; } - $length = strlen($string); + $length = $this->byteStrLen($string); if ($length < 128) { $output = sprintf("%c%c%s", $type, $length, $string); @@ -623,7 +691,7 @@ public static function convertRSA($modulus, $exponent) $publicKeyInfoBase64 = base64_encode($publicKeyInfo); $encoding = "-----BEGIN PUBLIC KEY-----\n"; $offset = 0; - while ($segment = substr($publicKeyInfoBase64, $offset, 64)) { + while ($segment = $this->byteSubStr($publicKeyInfoBase64, $offset, 64)) { $encoding = $encoding.$segment."\n"; $offset += 64; } diff --git a/tests/encrypteddata-node-order.phpt b/tests/encrypteddata-node-order.phpt index dce2ee14..4d681002 100644 --- a/tests/encrypteddata-node-order.phpt +++ b/tests/encrypteddata-node-order.phpt @@ -5,7 +5,7 @@ Makes sure that the child elements of EncryptedData appear in the correct order. --FILE-- + +jUzXhKjpKTTOavStGjH7+6lLoShei5Dk9B+Bg0hfUy4MFV3aq8E/HdfBYh36vm6bFroHF+6KP+K7Z2S9odN1zsbjF5oQjXLHDYnDEerMGiVbhlkXXaTWckThchZV8WoowXwSIkg8FjU01P+p8F6+lg9CC1h5B0xOWe9hQ3M0JSMSN1BZ15PVwXARDi/LuxJqf9dDZhqCFoYRUR7Q3612TNSgU/livb2VcRY7EbyRpuuSGfSjXNQPM1d/OlsfmZUYhd7aFO/OkHeUoFEx+BKkkRzrAfkRt7M5ilj7UHilNfzRsxSx0EQG3dfbJ0pbBgGgmiM9iLYnj087rzluN+x8kw== + + iUIRz5HJvgGADfkQAqXBC8WjmhoofuLAJedYn8DfwA614RQX5iB4PYw7GiVng+m/MVGYqvoudsg6lNprV0vN8Q== + + diff --git a/tests/src/Fixture/basic-doc-encrypted-aes128-cbc.xml b/tests/src/Fixture/basic-doc-encrypted-aes128-cbc.xml new file mode 100644 index 00000000..d07ebaa8 --- /dev/null +++ b/tests/src/Fixture/basic-doc-encrypted-aes128-cbc.xml @@ -0,0 +1,7 @@ + + +pKChAdt8YXFRkOfgARrW2IEwlnK1ZWEqnSvVhKK9VSiC5yICrf/dHL2BmkjJvG1wbOqBfJXCDCn/F+CeVDcZBa4kg/oQeUpIF7FMYaUK7Q9529uni/P5tMegQkOWeD7M76vlt5TXXbhRV/jZqCa5W5WIkhns53/2e97FKWOujPxrnydhvgzP9ztOPjOgbqIeJBkW432XQkgOSq6AANgfsgwrugQSusQcsJzuYRRhfSKTkH79t2sCGDlqV9XlAs1DOv2+elWEyL5G58/nDwaecRoZgo3EV4EdOudeNesvrNnZrsNRaR/qchUH+G+R9RDnXO4l3qookkH+6o222osxcQ== + + r0YRkEixBnAKU032/ux7avHcVTH1CIIyKaPA2qr4KlIs0LVZp5CuwQKRRi6lji4cnaFbH4jETtJhMSEfbpSdvg== + + diff --git a/tests/src/Fixture/basic-doc-encrypted-aes192-cbc.xml b/tests/src/Fixture/basic-doc-encrypted-aes192-cbc.xml new file mode 100644 index 00000000..fb795ada --- /dev/null +++ b/tests/src/Fixture/basic-doc-encrypted-aes192-cbc.xml @@ -0,0 +1,7 @@ + + +GXVyq5+hysMutP0IXxmJ3SZxBoLwWpMkgvqUYdiZGpH18Y8apPSetryE6wy8wgblFF0FnzpZttDdMWV646M+YhJtXXc+PaSpeYUM5qCHX2s0mROvmq1NGybNgy3tC1CuYnETAr+nfBt4vaQkcF+gBG5fd4Z2lkk9ZuQS+Fsyimtt59wVQoNfwkddBgphMp/XtUa120+c5cQpkVvTAUIs1AWnpcxFL6lOdLVND+59n2GGSkWJLcCrv96LO3aTPeSCnAWCx6wBSVVRSkhVpqC1zDTqz6Nq4hlUvcMt7rzTq6NMA6WUZOtG1iHiIrYDtqyHwOKYAVKnIKjPhLLewWMQ9g== + + ptSPO8ShXZrxkIVDvFwAuNAWEZIPKI/VpmsdWsVH/nQfMWpmFr9RKdzh6ezhr73i1sB6VZkafV6hErzEHVv+5w== + + diff --git a/tests/src/Fixture/basic-doc-encrypted-tripledes-cbc.xml b/tests/src/Fixture/basic-doc-encrypted-tripledes-cbc.xml new file mode 100644 index 00000000..8d24927e --- /dev/null +++ b/tests/src/Fixture/basic-doc-encrypted-tripledes-cbc.xml @@ -0,0 +1,7 @@ + + +11Tlnb3B2M+N5Uu7k3iozyVVVy7+Ylrzex79A0CiaBIWzdag36X6uk8ukdYiOxbGrb1iL0M/48h3pf2DA3gUj13d8cma3e2GprIphFWl57FJcBd5n/E/PRuVR6CfBqxzHTYKWuVntOkiA+l4QMWozmTNMmv3zWphwk5eMerob3kRPs5tFA+IFes9B++hKx3USXUVLfLJIhEf0AZB9o3eyZeG3pqzF6mkiy6epZKGM6JPA5oZJDKxCk+jscc4pDXftqIDdTu7Ukjbt2iD+OcMEO3zwpIoz8w0SksLH0WRRgZK/naPRSDCKHlqc/2nJaY8CcoLvC+pN93QaKHWzh+JHA== + + D+3dKq7MFK7U+8bqdlyRcvO12JV5Lahl5ALhF5eJXSfi+cbYKRbkRjvJsMKPp2Mk + + diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php new file mode 100644 index 00000000..4f29e4cb --- /dev/null +++ b/tests/src/TestCase.php @@ -0,0 +1,23 @@ + + * + */ +class TestCase extends \PHPUnit_Framework_TestCase +{ + /** + * Get fixture filename + * @param string $file File name + * @param boolean $legacy Use legacy file location + * @return string + */ + protected function getFixtureFileName($file, $legacy = false) + { + return $legacy ? __DIR__ . '/../' . $file : __DIR__ . '/Fixture/' . $file; + } +} diff --git a/tests/src/XMLSecEncTest.php b/tests/src/XMLSecEncTest.php new file mode 100644 index 00000000..d3667752 --- /dev/null +++ b/tests/src/XMLSecEncTest.php @@ -0,0 +1,241 @@ + + * + */ +class XMLSecEncTest extends TestCase +{ + /** + * Basic encryption + * (taken from xmlsec-encrypt.phpt) + * + * @param string $source Source filename + * @param string $keyType Security key type + * + * @dataProvider providerTestBasicEncryptDecrypt + * @group functional + */ + public function testBasicEncrypt($source, $keyType) + { + $dom = new DOMDocument(); + $dom->load($source); + + $objKey = new XMLSecurityKey($keyType); + $objKey->generateSessionKey(); + + $enc = new XMLSecEnc(); + $enc->setNode($dom->documentElement); + $enc->encryptKey($this->createSiteKey(), $objKey); + + $enc->type = XMLSecEnc::Element; + $encNode = $enc->encryptNode($objKey); + + $this->assertSame('EncryptedData', $dom->documentElement->localName); + $this->assertSame('EncryptedData', $encNode->localName); + $this->assertSame(XMLSecEnc::XMLENCNS, $encNode->namespaceURI); + } + + /** + * Basic encryption content + * (taken from xmlsec-encrypt-content.phpt) + * + * @param string $source Source filename + * @param string $keyType Security key type + * + * @dataProvider providerTestBasicEncryptDecrypt + * @group functional + */ + public function testBasicEncryptContent($source, $keyType) + { + $dom = new DOMDocument(); + $dom->load($source); + + $objKey = new XMLSecurityKey($keyType); + $objKey->generateSessionKey(); + + $enc = new XMLSecEnc(); + $enc->setNode($dom->documentElement); + $enc->encryptKey($this->createSiteKey(), $objKey); + + $enc->type = XMLSecEnc::Content; + $encNode = $enc->encryptNode($objKey); + + $this->assertSame('Root', $dom->documentElement->localName); + $this->assertSame('EncryptedData', $encNode->localName); + $this->assertSame(XMLSecEnc::XMLENCNS, $encNode->namespaceURI); + } + + /** + * Basic encryption content without modifying original data + * (taken from xmlsec-encrypt-noreplace.phpt) + * + * @param string $source Source filename + * @param string $keyType Security key type + * + * @dataProvider providerTestBasicEncryptDecrypt + * @group functional + */ + public function testBasicEncryptNoModify($source, $keyType) + { + $dom = new DOMDocument(); + $dom->load($source); + $origData = $dom->saveXML(); + + $objKey = new XMLSecurityKey($keyType); + $objKey->generateSessionKey(); + + $enc = new XMLSecEnc(); + $enc->setNode($dom->documentElement); + $enc->encryptKey($this->createSiteKey(), $objKey); + + $enc->type = XMLSecEnc::Element; + $encNode = $enc->encryptNode($objKey, false); + + $this->assertSame($origData, $dom->saveXML()); + $this->assertSame('EncryptedData', $encNode->localName); + $this->assertSame(XMLSecEnc::XMLENCNS, $encNode->namespaceURI); + } + + /** + * Basic decryption content + * (taken from xmlsec-decrypt-content.phpt) + * + * @param string $source Source filename + * @param string $keyType Security key type + * @param string $encrypted Encrypted filename + * + * @dataProvider providerTestBasicEncryptDecrypt + * @group functional + */ + public function testBasicDecrypt($source, $keyType, $encrypted) + { + $doc = new DOMDocument(); + $doc->load($encrypted); + + $objenc = new XMLSecEnc(); + $encData = $objenc->locateEncryptedData($doc); + + $objenc->setNode($encData); + $objenc->type = $encData->getAttribute("Type"); + + $objKey = $objenc->locateKey(); + $key = null; + + if ($objKeyInfo = $objenc->locateKeyInfo($objKey)) { + if ($objKeyInfo->isEncrypted) { + $objencKey = $objKeyInfo->encryptedCtx; + $objKeyInfo->loadKey($this->getFixtureFileName('privkey.pem', true), true); + $key = $objencKey->decryptKey($objKeyInfo); + } + } + + if (empty($objKey->key)) { + $objKey->loadKey($key); + } + + if ($decrypt = $objenc->decryptNode($objKey, true)) { + $output = null; + if ($decrypt instanceof DOMNode) { + if ($decrypt instanceof DOMDocument) { + $output = $decrypt->saveXML(); + } else { + $output = $decrypt->ownerDocument->saveXML(); + } + } else { + $output = $decrypt; + } + } + + $resDoc = new DOMDocument(); + $resDoc->load($source); + $res = $resDoc->saveXML(); + + $this->assertSame($res, $output); + } + + /** + * Create site key + * @return XMLSecurityKey + */ + protected function createSiteKey() + { + $siteKey = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type'=>'public')); + $siteKey->loadKey($this->getFixtureFileName('mycert.pem', true), true, true); + return $siteKey; + } + + public function providerTestBasicEncryptDecrypt() + { + return array( + + // legacy fixtures + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::AES256_CBC, + $this->getFixtureFileName('oaep_sha1-res.xml', true), + ), + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::AES256_CBC, + $this->getFixtureFileName('oaep_sha1-content-res.xml', true), + ), + + // new fixtures + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::TRIPLEDES_CBC, + $this->getFixtureFileName('basic-doc-encrypted-tripledes-cbc.xml'), + ), + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::AES128_CBC, + $this->getFixtureFileName('basic-doc-encrypted-aes128-cbc.xml'), + ), + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::AES192_CBC, + $this->getFixtureFileName('basic-doc-encrypted-aes192-cbc.xml'), + ), + array( + $this->getFixtureFileName('basic-doc.xml', true), + XMLSecurityKey::AES256_CBC, + $this->getFixtureFileName('basic-doc-encrypt-aes256-cbc.xml'), + ), + ); + } + + /** + * @covers ::encryptNode + * @group unit + * @expectedException Exception + * @expectedExceptionMessage Node to encrypt has not been set + */ + public function testEncryptNodeRequiresNode() + { + $sut = new XMLSecEnc(); + $sut->encryptNode(null); + } + + /** + * @covers ::encryptNode + * @group unit + * @expectedException Exception + * @expectedExceptionMessage Invalid Key + */ + public function testEncryptNodeInvalidKey() + { + $sut = new XMLSecEnc(); + $sut->setNode(new DOMNode()); + $sut->encryptNode(null); + } +} diff --git a/tests/thumbprint.phpt b/tests/thumbprint.phpt index eb5b8160..e9f357ff 100644 --- a/tests/thumbprint.phpt +++ b/tests/thumbprint.phpt @@ -2,7 +2,7 @@ Certificate thumbprint check --FILE-- 'public')); diff --git a/tests/validate_digest_sha512.phpt b/tests/validate_digest_sha512.phpt index bf438848..f4cf8876 100755 --- a/tests/validate_digest_sha512.phpt +++ b/tests/validate_digest_sha512.phpt @@ -2,7 +2,7 @@ Validate Digest SHA 512 --FILE-- --EXPECTF-- -OK \ No newline at end of file +OK diff --git a/tests/withcomment-id-uri.phpt b/tests/withcomment-id-uri.phpt index a48c9098..0211e51f 100644 --- a/tests/withcomment-id-uri.phpt +++ b/tests/withcomment-id-uri.phpt @@ -4,7 +4,7 @@ WithComments with an ID reference. Checks that comments are removed when using an ID URI in a Reference. --FILE-- --FILE-- --FILE-- . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Robert Richards nor the names of his - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @author Robert Richards - * @copyright 2007-2016 Robert Richards - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version 2.0.1 - */ - -$xmlseclibs_srcdir = dirname(__FILE__) . '/src/'; -require $xmlseclibs_srcdir . '/XMLSecurityKey.php'; -require $xmlseclibs_srcdir . '/XMLSecurityDSig.php'; -require $xmlseclibs_srcdir . '/XMLSecEnc.php';