diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..b08c76c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing + +First of all, **thank you** for contributing. + +Bugs or feature requests can be posted online on the GitHub issues section of the project. + +Few rules to ease code reviews and merges: + +- You MUST follow the [PSR-12](http://www.php-fig.org/psr/psr-12/) coding standard. +- You MUST run the test suite. +- You MUST write (or update) unit tests when bugs are fixed or features are added. +- You SHOULD write documentation. + +To contribute use [Pull Requests](https://help.github.com/articles/using-pull-requests), please, write commit messages that make sense, and rebase your branch before submitting your PR. + +May be asked to squash your commits too. This is used to "clean" your Pull Request before merging it, avoiding commits such as fix tests, fix 2, fix 3, etc. + +Run test suite +------------ + +* install composer: `curl -s http://getcomposer.org/installer | php` +* install dependencies: `php composer.phar install` +* run tests: `vendor/bin/phpunit` diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..726574c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: Spomky +patreon: FlorentMorselli diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.md b/.github/ISSUE_TEMPLATE/1_Bug_report.md new file mode 100644 index 0000000..3a3e9fd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1_Bug_report.md @@ -0,0 +1,21 @@ +--- +name: 🐛 Bug Report +about: ⚠️ See below for security reports +labels: Bug + +--- + +**Version(s) affected**: x.y.z + +**Description** + + +**How to reproduce** + + +**Possible Solution** + + +**Additional context** + diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.md b/.github/ISSUE_TEMPLATE/2_Feature_request.md new file mode 100644 index 0000000..8ffd974 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_Feature_request.md @@ -0,0 +1,12 @@ +--- +name: 🚀 Feature Request +about: Ideas for new features and improvements + +--- + +**Description** + + +**Example** + diff --git a/.github/ISSUE_TEMPLATE/3_Support_question.md b/.github/ISSUE_TEMPLATE/3_Support_question.md new file mode 100644 index 0000000..6b4f060 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3_Support_question.md @@ -0,0 +1,11 @@ +--- +name: ⛔ Support Question +about: We usually do not provide support. Please ask your question on https://stackoverflow.com/ + +--- + +We use GitHub issues only to discuss bugs and new features. +For this kind of questions about using the library or the bundle, please use +https://stackoverflow.com/ using the tags `[aes]` and `[php]` + +Thanks! \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/4_Documentation_issue.md b/.github/ISSUE_TEMPLATE/4_Documentation_issue.md new file mode 100644 index 0000000..26cd199 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4_Documentation_issue.md @@ -0,0 +1,5 @@ +--- +name: 📖 Documentation Issue +about: To report typo or obsolete section in the documentation + +--- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..1beb0a3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +| Q | A +| ------------- | --- +| Branch? | +| Bug fix? | yes/no +| New feature? | yes/no +| Deprecations? | yes/no +| Tickets | Fix #... +| License | MIT + \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..3c84124 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,8 @@ +daysUntilStale: 60 +daysUntilClose: 7 +staleLabel: wontfix +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +closeComment: false diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml new file mode 100644 index 0000000..68fe9d1 --- /dev/null +++ b/.github/workflows/coding-standards.yml @@ -0,0 +1,32 @@ +name: Coding Standards + +on: [push] + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ubuntu-latest] + php-versions: ['8.0'] + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json, mbstring, openssl, sqlite3 + coverage: xdebug + + - name: Install Composer dependencies + run: | + composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: CONDING STANDARDS + run: make ci-cs diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml new file mode 100644 index 0000000..132555a --- /dev/null +++ b/.github/workflows/mutation-tests.yml @@ -0,0 +1,35 @@ +name: Mutation Testing + +on: [push] + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ubuntu-latest] + php-versions: ['8.0'] + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json, mbstring, openssl, sqlite3 + coverage: xdebug + + - name: Install Composer dependencies + run: | + composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Fetch Git base reference + run: git fetch --depth=1 origin $GITHUB_BASE_REF + + - name: Infection + run: make ci-mu diff --git a/.github/workflows/rector_checkstyle.yaml b/.github/workflows/rector_checkstyle.yaml new file mode 100644 index 0000000..e0d3f9c --- /dev/null +++ b/.github/workflows/rector_checkstyle.yaml @@ -0,0 +1,29 @@ +name: Rector Checkstyle + +on: [push] + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ ubuntu-latest ] + php-versions: [ '8.0' ] + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json, mbstring, openssl, sqlite3 + coverage: none + + - name: Install Composer dependencies + run: composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Rector + run: make rector diff --git a/.github/workflows/static-analyze.yml b/.github/workflows/static-analyze.yml new file mode 100644 index 0000000..45dedcf --- /dev/null +++ b/.github/workflows/static-analyze.yml @@ -0,0 +1,32 @@ +name: Static Analyze + +on: [push] + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ubuntu-latest] + php-versions: ['8.0'] + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json, mbstring, openssl, sqlite3 + coverage: xdebug + + - name: Install Composer dependencies + run: | + composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: PHPStan + run: make st diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..740fc75 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,32 @@ +name: Unit and Functional Tests + +on: [push] + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ ubuntu-latest ] + php-versions: [ '8.0', '8.1' ] + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json, mbstring, openssl, sqlite3 + coverage: xdebug + + - name: Install Composer dependencies + run: | + composer update --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Run tests + run: make ci-cc diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index c95e823..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,25 +0,0 @@ -before_commands: - - "composer install --no-interaction" - -tools: - php_sim: false - php_changetracking: true - php_cpd: true - php_mess_detector: true - php_code_sniffer: true - php_analyzer: true - sensiolabs_security_checker: true - php_code_coverage: false - php_pdepend: - excluded_dirs: [vendor, doc] -filter: - excluded_paths: [vendor/*, doc/*] -build_failure_conditions: - - 'elements.rating(<= C).exists' # No classes/methods with a rating of C or worse - - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse - - 'patches.label("Doc Comments").exists' # No doc comments patches allowed - - 'patches.label("Spacing").new.count > 1' # More than 1 new spacing patch - - 'issues.label("coding-style").exists' # No coding style issues allowed - - 'issues.label("coding-style").new.exists' # No new coding style issues allowed - - 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity - - 'project.metric("scrutinizer.quality", < 9)' # Code Quality Rating drops below 9 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f43c80b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: php - -matrix: - allow_failures: - - php: nightly - fast_finish: true - include: - - php: 7.2 - - php: 7.3 - - php: 7.4 - - php: nightly - -before_script: - - composer self-update - - composer install - - mkdir -p build/logs - -script: - - vendor/bin/phpunit --coverage-clover build/logs/clover.xml - -after_success: - - vendor/bin/coveralls --no-interaction - - php -r "require_once 'vendor/autoload.php'; AESKW\Tests\Performance::run();"; diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1ca240e --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +mu: vendor ## Mutation tests + vendor/bin/infection -s --threads=$(nproc) --min-msi=75 --min-covered-msi=75 + +tests: vendor ## Run all tests + vendor/bin/phpunit --color + +cc: vendor ## Show test coverage rates (HTML) + vendor/bin/phpunit --coverage-html ./build + +cs: vendor ## Fix all files using defined ECS rules + vendor/bin/ecs check --fix + +tu: vendor ## Run only unit tests + vendor/bin/phpunit --color --group Unit + +ti: vendor ## Run only integration tests + vendor/bin/phpunit --color --group Integration + +tf: vendor ## Run only functional tests + vendor/bin/phpunit --color --group Functional + +st: vendor ## Run static analyse + vendor/bin/phpstan analyse + + +################################################ + +ci-mu: vendor ## Mutation tests (for Github only) + vendor/bin/infection --logger-github -s --threads=$(nproc) --min-msi=75 --min-covered-msi=75 + +ci-cc: vendor ## Show test coverage rates (console) + vendor/bin/phpunit --coverage-text + +ci-cs: vendor ## Check all files using defined ECS rules + vendor/bin/ecs check + +################################################ + + +vendor: composer.json composer.lock + composer validate + composer install + +rector: vendor ## Check all files using Rector + vendor/bin/rector process --ansi --dry-run --xdebug + +.DEFAULT_GOAL := help +help: + @grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/' +.PHONY: help diff --git a/README.md b/README.md index 564d223..60d2eaa 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ PHP AES Key Wrap ================ -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Spomky-Labs/aes-key-wrap/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Spomky-Labs/aes-key-wrap/?branch=master) -[![Coverage Status](https://coveralls.io/repos/Spomky-Labs/aes-key-wrap/badge.svg?branch=master&service=github)](https://coveralls.io/github/Spomky-Labs/aes-key-wrap?branch=master) +![Build Status](https://github.com/Spomky-Labs/aes-key-wrap/workflows/Coding%20Standards/badge.svg) +![Build Status](https://github.com/Spomky-Labs/aes-key-wrap/workflows/Static%20Analyze/badge.svg) +![Build Status](https://github.com/Spomky-Labs/aes-key-wrap/workflows/Rector%20Checkstyle/badge.svg) -[![Build Status](https://travis-ci.org/Spomky-Labs/aes-key-wrap.svg?branch=master)](https://travis-ci.org/Spomky-Labs/aes-key-wrap) +![Build Status](https://github.com/Spomky-Labs/aes-key-wrap/workflows/Unit%20and%20Functional%20Tests/badge.svg) -[![SensioLabsInsight](https://insight.sensiolabs.com/projects/e61c91cf-1860-4416-946b-4c7b74ea01a5/big.png)](https://insight.sensiolabs.com/projects/e61c91cf-1860-4416-946b-4c7b74ea01a5) - -[![Latest Stable Version](https://poser.pugx.org/spomky-labs/aes-key-wrap/v/stable.png)](https://packagist.org/packages/spomky-labs/aes-key-wrap) -[![Latest Unstable Version](https://poser.pugx.org/spomky-labs/aes-key-wrap/v/unstable.png)](https://packagist.org/packages/spomky-labs/aes-key-wrap) -[![Total Downloads](https://poser.pugx.org/spomky-labs/aes-key-wrap/downloads.png)](https://packagist.org/packages/spomky-labs/aes-key-wrap) -[![License](https://poser.pugx.org/spomky-labs/aes-key-wrap/license.png)](https://packagist.org/packages/spomky-labs/aes-key-wrap) +![Build Status](https://github.com/Spomky-Labs/aes-key-wrap/workflows/Mutation%20Testing/badge.svg) +[![Latest Stable Version](https://poser.pugx.org/Spomky-Labs/aes-key-wrap/v/stable.png)](https://packagist.org/packages/Spomky-Labs/aes-key-wrap) +[![Total Downloads](https://poser.pugx.org/Spomky-Labs/aes-key-wrap/downloads.png)](https://packagist.org/packages/Spomky-Labs/aes-key-wrap) +[![Latest Unstable Version](https://poser.pugx.org/Spomky-Labs/aes-key-wrap/v/unstable.png)](https://packagist.org/packages/Spomky-Labs/aes-key-wrap) +[![License](https://poser.pugx.org/Spomky-Labs/aes-key-wrap/license.png)](https://packagist.org/packages/Spomky-Labs/aes-key-wrap) This library provides an implementation of the [RFC 3394 (Advanced Encryption Standard (AES) Key Wrap Algorithm)](https://tools.ietf.org/html/rfc3394) and the [RFC 5649 (Advanced Encryption Standard (AES) Key Wrap with Padding Algorithm)](https://tools.ietf.org/html/rfc5649). @@ -22,10 +22,8 @@ The release process [is described here](doc/Release.md). # Prerequisites -This library needs at least `PHP 7.1`. -It has been successfully tested using `PHP 7.1`, `PHP 7.2` and `HHVM`. - -For `PHP 5.4+`, please use the version `3.x` of this library. +This library needs at least `PHP 8.0`. +It has been successfully tested using `8.0` and `8.1`. # Installation @@ -45,12 +43,20 @@ I bring solutions to your problems and answer your questions. If you really love that project and the work I have done or if you want I prioritize your issues, then you can help me out for a couple of :beers: or more! +[Become a sponsor](https://github.com/sponsors/Spomky) + +Or + [![Become a Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/FlorentMorselli) # Contributing Requests for new features, bug fixed and all other ideas to make this library useful are welcome. [Please follow these best practices](doc/Contributing.md). +If you discover a security vulnerability within the project, please **don't use the bug tracker and don't publish it publicly**. +Instead, all security issues must be sent to security [at] spomky-labs.com. + + # Licence This software is release under [MIT licence](LICENSE). diff --git a/composer.json b/composer.json index 7b1b616..025ada5 100644 --- a/composer.json +++ b/composer.json @@ -22,24 +22,20 @@ } }, "require": { - "php": ">=7.2", + "php": ">=8.0", "ext-mbstring": "*", - "lib-openssl": "*", - "thecodingmachine/safe": "^1.1" + "ext-openssl": "*" }, "require-dev": { - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-beberlei-assert": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", - "thecodingmachine/phpstan-safe-rule": "^1.0" - }, - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } + "phpunit/phpunit": "^9.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-beberlei-assert": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/extension-installer": "^1.1", + "symplify/easy-coding-standard": "^10.0", + "rector/rector": "^0.12.5", + "infection/infection": "^0.25.4" } } diff --git a/doc/Contributing.md b/doc/Contributing.md index 67ca7ac..82becb8 100644 --- a/doc/Contributing.md +++ b/doc/Contributing.md @@ -1,5 +1,5 @@ Contributing -============================= +============ First of all, **thank you** for contributing. diff --git a/doc/Release.md b/doc/Release.md index e52a90c..dbce213 100644 --- a/doc/Release.md +++ b/doc/Release.md @@ -9,8 +9,6 @@ We manage its releases through features and time-based models. The meaning of "patch" "minor" and "major" comes from the Semantic [Versioning strategy](http://semver.org/). -This release process applies from version 1.0.x. - ### Backwards Compatibility We allow developers to upgrade with confidence from one minor version to the next one. diff --git a/ecs.php b/ecs.php new file mode 100644 index 0000000..d489c29 --- /dev/null +++ b/ecs.php @@ -0,0 +1,124 @@ +import(SetList::PSR_12); + $containerConfigurator->import(SetList::PHP_CS_FIXER); + $containerConfigurator->import(SetList::PHP_CS_FIXER_RISKY); + $containerConfigurator->import(SetList::CLEAN_CODE); + $containerConfigurator->import(SetList::SYMFONY); + $containerConfigurator->import(SetList::DOCTRINE_ANNOTATIONS); + $containerConfigurator->import(SetList::SPACES); + $containerConfigurator->import(SetList::PHPUNIT); + $containerConfigurator->import(SetList::SYMPLIFY); + $containerConfigurator->import(SetList::ARRAY); + $containerConfigurator->import(SetList::COMMON); + $containerConfigurator->import(SetList::COMMENTS); + $containerConfigurator->import(SetList::CONTROL_STRUCTURES); + $containerConfigurator->import(SetList::DOCBLOCK); + $containerConfigurator->import(SetList::NAMESPACES); + $containerConfigurator->import(SetList::STRICT); + + $services = $containerConfigurator->services(); + $services->set(StrictParamFixer::class); + $services->set(StrictComparisonFixer::class); + $services->set(ArraySyntaxFixer::class) + ->call('configure', [[ + 'syntax' => 'short', + ]]) + ; + $services->set(ArrayIndentationFixer::class); + $services->set(OrderedImportsFixer::class); + $services->set(ProtectedToPrivateFixer::class); + $services->set(DeclareStrictTypesFixer::class); + $services->set(NativeConstantInvocationFixer::class); + $services->set(NativeFunctionInvocationFixer::class) + ->call('configure', [[ + 'include' => ['@compiler_optimized'], + 'scope' => 'namespaced', + 'strict' => true, + ]]) + ; + $services->set(MbStrFunctionsFixer::class); + $services->set(LinebreakAfterOpeningTagFixer::class); + $services->set(CombineConsecutiveIssetsFixer::class); + $services->set(CombineConsecutiveUnsetsFixer::class); + $services->set(CompactNullableTypehintFixer::class); + $services->set(NoSuperfluousElseifFixer::class); + $services->set(NoSuperfluousPhpdocTagsFixer::class); + $services->set(PhpdocTrimConsecutiveBlankLineSeparationFixer::class); + $services->set(PhpdocOrderFixer::class); + $services->set(SimplifiedNullReturnFixer::class); + $services->set(HeaderCommentFixer::class) + ->call('configure', [[ + 'header' => $header, + ]]) + ; + $services->set(AlignMultilineCommentFixer::class) + ->call('configure', [[ + 'comment_type' => 'all_multiline', + ]]) + ; + $services->set(PhpUnitTestAnnotationFixer::class) + ->call('configure', [[ + 'style' => 'annotation', + ]]) + ; + $services->set(PhpUnitTestCaseStaticMethodCallsFixer::class); + $services->set(GlobalNamespaceImportFixer::class) + ->call('configure', [[ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => true, + ]]) + ; + + $services->remove(PhpUnitTestClassRequiresCoversFixer::class); + + $parameters = $containerConfigurator->parameters(); + $parameters + ->set(Option::PARALLEL, true) + ->set(Option::PATHS, [__DIR__]) + ->set(Option::SKIP, [ + __DIR__ . '/src/Kernel.php', + __DIR__ . '/assets', + __DIR__ . '/bin', + __DIR__ . '/config', + __DIR__ . '/heroku', + __DIR__ . '/public', + __DIR__ . '/var', + ]) + ; +}; diff --git a/infection.json b/infection.json new file mode 100644 index 0000000..17bf976 --- /dev/null +++ b/infection.json @@ -0,0 +1,15 @@ +{ + "source": { + "directories": [ + "src" + ] + }, + "timeout": 3, + "logs": { + "text": "infection.txt" + }, + "mutators": { + "@default": true, + "@function_signature": true + } +} diff --git a/infection.txt b/infection.txt new file mode 100644 index 0000000..4a97421 --- /dev/null +++ b/infection.txt @@ -0,0 +1,721 @@ +Escaped mutants: +================ + +1) /home/florent/PhpstormProjects/aes-key-wrap/src/A128KW.php:11 [M] ProtectedVisibility + +--- Original ++++ New +@@ @@ + final class A128KW implements Wrapper + { + use AESKW; +- protected static function getMethod() : string ++ private static function getMethod() : string + { + return 'aes-128-ecb'; + } + } + + +2) /home/florent/PhpstormProjects/aes-key-wrap/src/A192KW.php:11 [M] ProtectedVisibility + +--- Original ++++ New +@@ @@ + final class A192KW implements Wrapper + { + use AESKW; +- protected static function getMethod() : string ++ private static function getMethod() : string + { + return 'aes-192-ecb'; + } + } + + +3) /home/florent/PhpstormProjects/aes-key-wrap/src/A256KW.php:11 [M] ProtectedVisibility + +--- Original ++++ New +@@ @@ + final class A256KW implements Wrapper + { + use AESKW; +- protected static function getMethod() : string ++ private static function getMethod() : string + { + return 'aes-256-ecb'; + } + } + + +4) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:31 [M] MBString + +--- Original ++++ New +@@ @@ + { + $A = self::getInitialValue($key, $padding_enabled); + self::checkKeySize($key, $padding_enabled); +- $P = mb_str_split($key, 8, '8bit'); ++ $P = str_split($key, 8); + $N = count($P); + $C = []; + if ($N === 1) { + + +5) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:38 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + if ($N === 1) { + $B = self::encrypt($kek, $A . $P[0]); + $C[0] = self::getMSB($B); +- $C[1] = self::getLSB($B); ++ $C[2] = self::getLSB($B); + } elseif ($N > 1) { + $R = $P; + for ($j = 0; $j <= 5; ++$j) { + + +6) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:39 [M] GreaterThan + +--- Original ++++ New +@@ @@ + $B = self::encrypt($kek, $A . $P[0]); + $C[0] = self::getMSB($B); + $C[1] = self::getLSB($B); +- } elseif ($N > 1) { ++ } elseif ($N >= 1) { + $R = $P; + for ($j = 0; $j <= 5; ++$j) { + for ($i = 1; $i <= $N; ++$i) { + + +7) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:62 [M] FalseValue + +--- Original ++++ New +@@ @@ + * + * @return string The key unwrapped + */ +- public static function unwrap(string $kek, string $key, bool $padding_enabled = false) : string ++ public static function unwrap(string $kek, string $key, bool $padding_enabled = true) : string + { + $P = mb_str_split($key, 8, '8bit'); + $A = $P[0]; + + +8) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:64 [M] MBString + +--- Original ++++ New +@@ @@ + */ + public static function unwrap(string $kek, string $key, bool $padding_enabled = false) : string + { +- $P = mb_str_split($key, 8, '8bit'); ++ $P = str_split($key, 8); + $A = $P[0]; + $N = count($P); + if ($N < 2) { + + +9) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:110 [M] FalseValue + +--- Original ++++ New +@@ @@ + { + if ($padding_enabled === false) { + $bin = hex2bin('A6A6A6A6A6A6A6A6'); +- if ($bin === false) { ++ if ($bin === true) { + throw new InvalidArgumentException('Unable to convert the data'); + } + return $bin; + + +10) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:117 [M] MBString + +--- Original ++++ New +@@ @@ + } + return $bin; + } +- $MLI = mb_strlen($key, '8bit'); ++ $MLI = strlen($key); + $iv = hex2bin('A65959A6') . self::toXBits(32, $MLI); + $n = (int) ceil($MLI / 8); + $key = str_pad($key, 8 * $n, "\x00", STR_PAD_RIGHT); + + +11) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:120 [M] DecrementInteger + +--- Original ++++ New +@@ @@ + } + $MLI = mb_strlen($key, '8bit'); + $iv = hex2bin('A65959A6') . self::toXBits(32, $MLI); +- $n = (int) ceil($MLI / 8); ++ $n = (int) ceil($MLI / 7); + $key = str_pad($key, 8 * $n, "\x00", STR_PAD_RIGHT); + return $iv; + } + + +12) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:120 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + } + $MLI = mb_strlen($key, '8bit'); + $iv = hex2bin('A65959A6') . self::toXBits(32, $MLI); +- $n = (int) ceil($MLI / 8); ++ $n = (int) ceil($MLI / 9); + $key = str_pad($key, 8 * $n, "\x00", STR_PAD_RIGHT); + return $iv; + } + + +13) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:120 [M] RoundingFamily + +--- Original ++++ New +@@ @@ + } + $MLI = mb_strlen($key, '8bit'); + $iv = hex2bin('A65959A6') . self::toXBits(32, $MLI); +- $n = (int) ceil($MLI / 8); ++ $n = (int) round($MLI / 8); + $key = str_pad($key, 8 * $n, "\x00", STR_PAD_RIGHT); + return $iv; + } + + +14) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:143 [M] MBString + +--- Original ++++ New +@@ @@ + if (hex2bin('A65959A6') !== self::getMSB($iv)) { + return false; + } +- $n = mb_strlen($key, '8bit') / 8; ++ $n = strlen($key) / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + + +15) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:143 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + if (hex2bin('A65959A6') !== self::getMSB($iv)) { + return false; + } +- $n = mb_strlen($key, '8bit') / 8; ++ $n = mb_strlen($key, '8bit') / 9; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + + +16) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:144 [M] UnwrapLtrim + +--- Original ++++ New +@@ @@ + return false; + } + $n = mb_strlen($key, '8bit') / 8; +- $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); ++ $MLI = (int) hexdec(bin2hex(self::getLSB($iv))); + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } + + +17) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:144 [M] CastInt + +--- Original ++++ New +@@ @@ + return false; + } + $n = mb_strlen($key, '8bit') / 8; +- $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); ++ $MLI = hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } + + +18) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] DecrementInteger + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(7 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +19) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(9 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +20) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 2) < $MLI && $MLI <= 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +21) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] LessThan + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 1) <= $MLI && $MLI <= 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +22) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] DecrementInteger + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 1) < $MLI && $MLI <= 7 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +23) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] IncrementInteger + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 1) < $MLI && $MLI <= 9 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +24) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] LessThanOrEqualTo + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 1) < $MLI && $MLI < 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +25) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:146 [M] LogicalAnd + +--- Original ++++ New +@@ @@ + } + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); +- if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { ++ if (!(8 * ($n - 1) < $MLI || $MLI <= 8 * $n)) { + return false; + } + $b = 8 * $n - $MLI; + + +26) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:150 [M] DecrementInteger + +--- Original ++++ New +@@ @@ + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } +- $b = 8 * $n - $MLI; ++ $b = 7 * $n - $MLI; + for ($i = 0; $i < $b; ++$i) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { + return false; + + +27) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:150 [M] Multiplication + +--- Original ++++ New +@@ @@ + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + return false; + } +- $b = 8 * $n - $MLI; ++ $b = 8 / $n - $MLI; + for ($i = 0; $i < $b; ++$i) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { + return false; + + +28) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:151 [M] LessThanNegotiation + +--- Original ++++ New +@@ @@ + return false; + } + $b = 8 * $n - $MLI; +- for ($i = 0; $i < $b; ++$i) { ++ for ($i = 0; $i >= $b; ++$i) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { + return false; + } + + +29) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:151 [M] For_ + +--- Original ++++ New +@@ @@ + return false; + } + $b = 8 * $n - $MLI; +- for ($i = 0; $i < $b; ++$i) { ++ for ($i = 0; false; ++$i) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { + return false; + } + + +30) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:152 [M] MBString + +--- Original ++++ New +@@ @@ + } + $b = 8 * $n - $MLI; + for ($i = 0; $i < $b; ++$i) { +- if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { ++ if (substr($key, $MLI + $i, 1) !== "\x00") { + return false; + } + } + + +31) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:156 [M] MBString + +--- Original ++++ New +@@ @@ + return false; + } + } +- $key = mb_substr($key, 0, $MLI, '8bit'); ++ $key = substr($key, 0, $MLI); + return true; + } + private static function checkKeySize(string $key, bool $padding_enabled) : void + + +32) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:166 [M] MBString + +--- Original ++++ New +@@ @@ + if ($key === '') { + throw new InvalidArgumentException('Bad key size'); + } +- if ($padding_enabled === false && mb_strlen($key, '8bit') % 8 !== 0) { ++ if ($padding_enabled === false && strlen($key) % 8 !== 0) { + throw new InvalidArgumentException('Bad key size'); + } + } + + +33) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:174 [M] FalseValue + +--- Original ++++ New +@@ @@ + private static function toXBits(int $bits, int $value) : string + { + $bin = hex2bin(str_pad(dechex($value), $bits / 4, '0', STR_PAD_LEFT)); +- if ($bin === false) { ++ if ($bin === true) { + throw new InvalidArgumentException('Unable to convert the data'); + } + return $bin; + + +34) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:183 [M] MBString + +--- Original ++++ New +@@ @@ + } + private static function getMSB(string $value) : string + { +- return mb_substr($value, 0, mb_strlen($value, '8bit') / 2, '8bit'); ++ return mb_substr($value, 0, strlen($value) / 2, '8bit'); + } + private static function getLSB(string $value) : string + { + + +35) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:183 [M] MBString + +--- Original ++++ New +@@ @@ + } + private static function getMSB(string $value) : string + { +- return mb_substr($value, 0, mb_strlen($value, '8bit') / 2, '8bit'); ++ return substr($value, 0, mb_strlen($value, '8bit') / 2); + } + private static function getLSB(string $value) : string + { + + +36) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:188 [M] MBString + +--- Original ++++ New +@@ @@ + } + private static function getLSB(string $value) : string + { +- return mb_substr($value, mb_strlen($value, '8bit') / 2, null, '8bit'); ++ return mb_substr($value, strlen($value) / 2, null, '8bit'); + } + private static function encrypt(string $kek, string $data) : string + { + + +37) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:188 [M] MBString + +--- Original ++++ New +@@ @@ + } + private static function getLSB(string $value) : string + { +- return mb_substr($value, mb_strlen($value, '8bit') / 2, null, '8bit'); ++ return substr($value, mb_strlen($value, '8bit') / 2, null); + } + private static function encrypt(string $kek, string $data) : string + { + + +38) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:194 [M] FalseValue + +--- Original ++++ New +@@ @@ + private static function encrypt(string $kek, string $data) : string + { + $result = openssl_encrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); +- if ($result === false) { ++ if ($result === true) { + throw new InvalidArgumentException('Unable to encrypt the data'); + } + return $result; + + +39) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:204 [M] FalseValue + +--- Original ++++ New +@@ @@ + private static function decrypt(string $kek, string $data) : string + { + $result = openssl_decrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); +- if ($result === false) { ++ if ($result === true) { + throw new InvalidArgumentException('Unable to decrypt the data'); + } + return $result; + } + } + + +Timed Out mutants: +================== + +1) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:41 [M] Increment + +--- Original ++++ New +@@ @@ + $C[1] = self::getLSB($B); + } elseif ($N > 1) { + $R = $P; +- for ($j = 0; $j <= 5; ++$j) { ++ for ($j = 0; $j <= 5; --$j) { + for ($i = 1; $i <= $N; ++$i) { + $B = self::encrypt($kek, $A . $R[$i - 1]); + $t = $i + $j * $N; + + +2) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:77 [M] Decrement + +--- Original ++++ New +@@ @@ + $A = self::getMSB($B); + } else { + $R = $P; +- for ($j = 5; $j >= 0; --$j) { ++ for ($j = 5; $j >= 0; ++$j) { + for ($i = $N - 1; $i >= 1; --$i) { + $t = $i + $j * ($N - 1); + $B = self::decrypt($kek, (self::toXBits(64, $t) ^ $A) . $R[$i]); + + +Skipped mutants: +================ + +Not Covered mutants: +==================== + +1) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:111 [M] Throw_ + +--- Original ++++ New +@@ @@ + if ($padding_enabled === false) { + $bin = hex2bin('A6A6A6A6A6A6A6A6'); + if ($bin === false) { +- throw new InvalidArgumentException('Unable to convert the data'); ++ new InvalidArgumentException('Unable to convert the data'); + } + return $bin; + } + + +2) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:147 [M] FalseValue + +--- Original ++++ New +@@ @@ + $n = mb_strlen($key, '8bit') / 8; + $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\x00"))); + if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { +- return false; ++ return true; + } + $b = 8 * $n - $MLI; + for ($i = 0; $i < $b; ++$i) { + + +3) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:153 [M] FalseValue + +--- Original ++++ New +@@ @@ + $b = 8 * $n - $MLI; + for ($i = 0; $i < $b; ++$i) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\x00") { +- return false; ++ return true; + } + } + $key = mb_substr($key, 0, $MLI, '8bit'); + + +4) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:175 [M] Throw_ + +--- Original ++++ New +@@ @@ + { + $bin = hex2bin(str_pad(dechex($value), $bits / 4, '0', STR_PAD_LEFT)); + if ($bin === false) { +- throw new InvalidArgumentException('Unable to convert the data'); ++ new InvalidArgumentException('Unable to convert the data'); + } + return $bin; + } + + +5) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:195 [M] Throw_ + +--- Original ++++ New +@@ @@ + { + $result = openssl_encrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + if ($result === false) { +- throw new InvalidArgumentException('Unable to encrypt the data'); ++ new InvalidArgumentException('Unable to encrypt the data'); + } + return $result; + } + + +6) /home/florent/PhpstormProjects/aes-key-wrap/src/AESKW.php:205 [M] Throw_ + +--- Original ++++ New +@@ @@ + { + $result = openssl_decrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + if ($result === false) { +- throw new InvalidArgumentException('Unable to decrypt the data'); ++ new InvalidArgumentException('Unable to decrypt the data'); + } + return $result; + } + } diff --git a/phpstan.neon b/phpstan.neon index c03c598..e4d68ff 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,12 +1,11 @@ parameters: - level: 7 + level: max paths: - src + ignoreErrors: + - + message: '#Comparison operation .* between .* and 1 is always true\.#' + count: 3 + path: src/AESKW.php includes: - - vendor/phpstan/phpstan-strict-rules/rules.neon - - vendor/phpstan/phpstan-phpunit/extension.neon - - vendor/phpstan/phpstan-phpunit/rules.neon - - vendor/phpstan/phpstan-deprecation-rules/rules.neon - - vendor/phpstan/phpstan-beberlei-assert/extension.neon - vendor/phpstan/phpstan/conf/bleedingEdge.neon - - vendor/thecodingmachine/phpstan-safe-rule/phpstan-safe-rule.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a0d3b34..faaf12b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,15 +14,9 @@ ./tests - - - - ./ - - ./tests - ./doc - ./vendor - - - + + + src + + diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..762ee5b --- /dev/null +++ b/rector.php @@ -0,0 +1,26 @@ +import(SetList::DEAD_CODE); + $containerConfigurator->import(SetList::PHP_80); + $containerConfigurator->import(SymfonySetList::SYMFONY_52); + $containerConfigurator->import(SymfonySetList::SYMFONY_CODE_QUALITY); + $parameters = $containerConfigurator->parameters(); + $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/tests']); + $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80); + $parameters->set(Option::AUTO_IMPORT_NAMES, true); + $parameters->set(Option::IMPORT_SHORT_CLASSES, false); + $parameters->set(Option::IMPORT_DOC_BLOCKS, false); + + $services = $containerConfigurator->services(); + $services->set(TypedPropertyRector::class); +}; diff --git a/src/A128KW.php b/src/A128KW.php index 531b4f8..73f97e1 100644 --- a/src/A128KW.php +++ b/src/A128KW.php @@ -2,18 +2,9 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW; -final class A128KW +final class A128KW implements Wrapper { use AESKW; diff --git a/src/A192KW.php b/src/A192KW.php index b432046..773e182 100644 --- a/src/A192KW.php +++ b/src/A192KW.php @@ -2,18 +2,9 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW; -final class A192KW +final class A192KW implements Wrapper { use AESKW; diff --git a/src/A256KW.php b/src/A256KW.php index 47123ea..5f3088a 100644 --- a/src/A256KW.php +++ b/src/A256KW.php @@ -2,18 +2,9 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW; -final class A256KW +final class A256KW implements Wrapper { use AESKW; diff --git a/src/AESKW.php b/src/AESKW.php index 14403c0..b9c4a3f 100644 --- a/src/AESKW.php +++ b/src/AESKW.php @@ -2,23 +2,18 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW; use function count; +use function hex2bin; use InvalidArgumentException; -use function Safe\hex2bin; -use function Safe\mb_str_split; -use function Safe\openssl_decrypt; -use function Safe\openssl_encrypt; +use function mb_str_split; +use function openssl_decrypt; +use function openssl_encrypt; +use const OPENSSL_RAW_DATA; +use const OPENSSL_ZERO_PADDING; +use const STR_PAD_LEFT; +use const STR_PAD_RIGHT; trait AESKW { @@ -37,15 +32,15 @@ public static function wrap(string $kek, string $key, bool $padding_enabled = fa $N = count($P); $C = []; - if (1 === $N) { - $B = self::encrypt($kek, $A.$P[0]); + if ($N === 1) { + $B = self::encrypt($kek, $A . $P[0]); $C[0] = self::getMSB($B); $C[1] = self::getLSB($B); - } elseif (1 < $N) { + } elseif ($N > 1) { $R = $P; for ($j = 0; $j <= 5; ++$j) { for ($i = 1; $i <= $N; ++$i) { - $B = self::encrypt($kek, $A.$R[$i - 1]); + $B = self::encrypt($kek, $A . $R[$i - 1]); $t = $i + $j * $N; $A = self::toXBits(64, $t) ^ self::getMSB($B); $R[$i - 1] = self::getLSB($B); @@ -70,11 +65,11 @@ public static function unwrap(string $kek, string $key, bool $padding_enabled = $A = $P[0]; $N = count($P); - if (2 > $N) { + if ($N < 2) { throw new InvalidArgumentException('Bad data'); } - if (2 === $N) { - $B = self::decrypt($kek, $P[0].$P[1]); + if ($N === 2) { + $B = self::decrypt($kek, $P[0] . $P[1]); $unwrapped = self::getLSB($B); $A = self::getMSB($B); } else { @@ -82,7 +77,7 @@ public static function unwrap(string $kek, string $key, bool $padding_enabled = for ($j = 5; $j >= 0; --$j) { for ($i = $N - 1; $i >= 1; --$i) { $t = $i + $j * ($N - 1); - $B = self::decrypt($kek, (self::toXBits(64, $t) ^ $A).$R[$i]); + $B = self::decrypt($kek, (self::toXBits(64, $t) ^ $A) . $R[$i]); $A = self::getMSB($B); $R[$i] = self::getLSB($B); } @@ -91,7 +86,7 @@ public static function unwrap(string $kek, string $key, bool $padding_enabled = $unwrapped = implode('', $R); } - if (false === self::checkInitialValue($unwrapped, $padding_enabled, $A)) { + if (self::checkInitialValue($unwrapped, $padding_enabled, $A) === false) { throw new InvalidArgumentException('Integrity check failed!'); } @@ -99,9 +94,9 @@ public static function unwrap(string $kek, string $key, bool $padding_enabled = } /** - * The initial value used to wrap the key and check the integrity when unwrapped. - * The RFC3394 set this value to 0xA6A6A6A6A6A6A6A6 - * The RFC5649 set this value to 0xA65959A6XXXXXXXX (The part with XXXXXXXX is the MLI, depends on the padding). + * The initial value used to wrap the key and check the integrity when unwrapped. The RFC3394 set this value to + * 0xA6A6A6A6A6A6A6A6 The RFC5649 set this value to 0xA65959A6XXXXXXXX (The part with XXXXXXXX is the MLI, depends + * on the padding). * * @param string $key The key * @param bool $padding_enabled Enable padding (RFC5649) @@ -110,12 +105,17 @@ public static function unwrap(string $kek, string $key, bool $padding_enabled = */ private static function getInitialValue(string &$key, bool $padding_enabled): string { - if (false === $padding_enabled) { - return hex2bin('A6A6A6A6A6A6A6A6'); + if ($padding_enabled === false) { + $bin = hex2bin('A6A6A6A6A6A6A6A6'); + if ($bin === false) { + throw new InvalidArgumentException('Unable to convert the data'); + } + + return $bin; } $MLI = mb_strlen($key, '8bit'); - $iv = hex2bin('A65959A6').self::toXBits(32, $MLI); + $iv = hex2bin('A65959A6') . self::toXBits(32, $MLI); $n = (int) ceil($MLI / 8); $key = str_pad($key, 8 * $n, "\0", STR_PAD_RIGHT); @@ -131,7 +131,7 @@ private static function checkInitialValue(string &$key, bool $padding_enabled, s } // The RFC3394 is required but the previous check is not satisfied => invalid - if (false === $padding_enabled) { + if ($padding_enabled === false) { return false; } @@ -143,13 +143,13 @@ private static function checkInitialValue(string &$key, bool $padding_enabled, s $n = mb_strlen($key, '8bit') / 8; $MLI = (int) hexdec(bin2hex(ltrim(self::getLSB($iv), "\0"))); - if (!(8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { + if (! (8 * ($n - 1) < $MLI && $MLI <= 8 * $n)) { return false; } $b = 8 * $n - $MLI; for ($i = 0; $i < $b; ++$i) { - if ("\0" !== mb_substr($key, $MLI + $i, 1, '8bit')) { + if (mb_substr($key, $MLI + $i, 1, '8bit') !== "\0") { return false; } } @@ -160,17 +160,22 @@ private static function checkInitialValue(string &$key, bool $padding_enabled, s private static function checkKeySize(string $key, bool $padding_enabled): void { - if ('' === $key) { + if ($key === '') { throw new InvalidArgumentException('Bad key size'); } - if (false === $padding_enabled && 0 !== mb_strlen($key, '8bit') % 8) { + if ($padding_enabled === false && mb_strlen($key, '8bit') % 8 !== 0) { throw new InvalidArgumentException('Bad key size'); } } private static function toXBits(int $bits, int $value): string { - return hex2bin(str_pad(dechex($value), $bits / 4, '0', STR_PAD_LEFT)); + $bin = hex2bin(str_pad(dechex($value), $bits / 4, '0', STR_PAD_LEFT)); + if ($bin === false) { + throw new InvalidArgumentException('Unable to convert the data'); + } + + return $bin; } private static function getMSB(string $value): string @@ -185,11 +190,21 @@ private static function getLSB(string $value): string private static function encrypt(string $kek, string $data): string { - return openssl_encrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + $result = openssl_encrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + if ($result === false) { + throw new InvalidArgumentException('Unable to encrypt the data'); + } + + return $result; } private static function decrypt(string $kek, string $data): string { - return openssl_decrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + $result = openssl_decrypt($data, self::getMethod(), $kek, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA); + if ($result === false) { + throw new InvalidArgumentException('Unable to decrypt the data'); + } + + return $result; } } diff --git a/src/Wrapper.php b/src/Wrapper.php new file mode 100644 index 0000000..446d811 --- /dev/null +++ b/src/Wrapper.php @@ -0,0 +1,26 @@ + $value) { $data[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); } $data = hex2bin(implode('', $data)); $wrapped = A128KW::wrap($kek, $data); - static::assertEquals(base64_decode('6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ', true), $wrapped); + static::assertSame(base64_decode('6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ', true), $wrapped); $unwrapped = A128KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } } diff --git a/tests/One64BitBlockTest.php b/tests/One64BitBlockTest.php index 2b5dbb1..238cf73 100644 --- a/tests/One64BitBlockTest.php +++ b/tests/One64BitBlockTest.php @@ -2,15 +2,6 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW\Tests; use AESKW\A128KW; @@ -36,9 +27,9 @@ public function wrap64BitsKeyDataWith128BitKEK(): void $data = hex2bin('0011223344556677'); $wrapped = A128KW::wrap($kek, $data); - static::assertEquals(hex2bin('F4740052E82A225174CE86FBD7B805E7'), $wrapped); + static::assertSame(hex2bin('F4740052E82A225174CE86FBD7B805E7'), $wrapped); $unwrapped = A128KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -50,9 +41,9 @@ public function wrap64BitsKeyDataWith192BitKEK(): void $data = hex2bin('0011223344556677'); $wrapped = A192KW::wrap($kek, $data); - static::assertEquals(hex2bin('DFE8FD5D1A3786A7351D385096CCFB29'), $wrapped); + static::assertSame(hex2bin('DFE8FD5D1A3786A7351D385096CCFB29'), $wrapped); $unwrapped = A192KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -64,8 +55,8 @@ public function wrap64BitsKeyDataWith256BitKEK(): void $data = hex2bin('0011223344556677'); $wrapped = A256KW::wrap($kek, $data); - static::assertEquals(hex2bin('794314D454E3FDE1F661BD9F31FBFA31'), $wrapped); + static::assertSame(hex2bin('794314D454E3FDE1F661BD9F31FBFA31'), $wrapped); $unwrapped = A256KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } } diff --git a/tests/Performance.php b/tests/Performance.php index 7bc9ebb..4261be1 100644 --- a/tests/Performance.php +++ b/tests/Performance.php @@ -2,15 +2,6 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW\Tests; use AESKW\A128KW; @@ -18,19 +9,18 @@ use AESKW\A256KW; use function call_user_func_array; use InvalidArgumentException; +use const PHP_EOL; /** - * This class is used to check the performance of the library on the current platform. - * You just have to call Performance::run(); - * By default, tests are performed 1000 times. - * You can modify it by passing a positive integer as first argument: - * Performance::run(10000);. + * This class is used to check the performance of the library on the current platform. You just have to call + * Performance::run(); By default, tests are performed 1000 times. You can modify it by passing a positive integer as + * first argument: Performance::run(10000);. */ final class Performance { public static function run(int $nb = 1000): void { - if (1 > $nb) { + if ($nb < 1) { throw new InvalidArgumentException('You must perform at least 1 test.'); } $cases = self::getData(); @@ -48,7 +38,7 @@ private static function wrap(int $nb, array $case): void $padding = $case['padding']; $time = self::do($class, 'wrap', $nb, $kek, $data, $padding); - printf('%s: %f milliseconds/wrap'.PHP_EOL, $case['name'], $time); + printf('%s: %f milliseconds/wrap' . PHP_EOL, $case['name'], $time); } private static function unwrap(int $nb, array $case): void @@ -59,7 +49,7 @@ private static function unwrap(int $nb, array $case): void $padding = $case['padding']; $time = self::do($class, 'unwrap', $nb, $kek, $result, $padding); - printf('%s: %f milliseconds/unwrap'.PHP_EOL, $case['name'], $time); + printf('%s: %f milliseconds/unwrap' . PHP_EOL, $case['name'], $time); } /** diff --git a/tests/RFC3394Test.php b/tests/RFC3394Test.php index 1fae988..f9aba27 100644 --- a/tests/RFC3394Test.php +++ b/tests/RFC3394Test.php @@ -2,15 +2,6 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW\Tests; use AESKW\A128KW; @@ -36,9 +27,9 @@ public function wrap128BitsKeyDataWith128BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF'); $wrapped = A128KW::wrap($kek, $data); - static::assertEquals(hex2bin('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5'), $wrapped); + static::assertSame(hex2bin('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5'), $wrapped); $unwrapped = A128KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -50,9 +41,9 @@ public function wrap128BitsKeyDataWith192BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF'); $wrapped = A192KW::wrap($kek, $data); - static::assertEquals(hex2bin('96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D'), $wrapped); + static::assertSame(hex2bin('96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D'), $wrapped); $unwrapped = A192KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -64,9 +55,9 @@ public function wrap128BitsKeyDataWith256BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF'); $wrapped = A256KW::wrap($kek, $data); - static::assertEquals(hex2bin('64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7'), $wrapped); + static::assertSame(hex2bin('64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7'), $wrapped); $unwrapped = A256KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -78,9 +69,9 @@ public function wrap192BitsKeyDataWith192BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF0001020304050607'); $wrapped = A192KW::wrap($kek, $data); - static::assertEquals(hex2bin('031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2'), $wrapped); + static::assertSame(hex2bin('031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2'), $wrapped); $unwrapped = A192KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -92,9 +83,9 @@ public function wrap192BitsKeyDataWith256BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF0001020304050607'); $wrapped = A256KW::wrap($kek, $data); - static::assertEquals(hex2bin('A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1'), $wrapped); + static::assertSame(hex2bin('A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1'), $wrapped); $unwrapped = A256KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } /** @@ -106,8 +97,11 @@ public function wrap256BitsKeyDataWith256BitKEK(): void $data = hex2bin('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F'); $wrapped = A256KW::wrap($kek, $data); - static::assertEquals(hex2bin('28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21'), $wrapped); + static::assertSame( + hex2bin('28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21'), + $wrapped + ); $unwrapped = A256KW::unwrap($kek, $wrapped); - static::assertEquals($data, $unwrapped); + static::assertSame($data, $unwrapped); } } diff --git a/tests/RFC5649Test.php b/tests/RFC5649Test.php index dc2ba20..aae1152 100644 --- a/tests/RFC5649Test.php +++ b/tests/RFC5649Test.php @@ -2,15 +2,6 @@ declare(strict_types=1); -/* - * The MIT License (MIT) - * - * Copyright (c) 2014-2020 Spomky-Labs - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - namespace AESKW\Tests; use AESKW\A192KW; @@ -34,9 +25,9 @@ public function wrap20BytesKeyDataWith192BitKEK(): void $key = hex2bin('c37b7e6492584340bed12207808941155068f738'); $wrapped = A192KW::wrap($kek, $key, true); - static::assertEquals(hex2bin('138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a'), $wrapped); + static::assertSame(hex2bin('138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a'), $wrapped); $unwrapped = A192KW::unwrap($kek, $wrapped, true); - static::assertEquals($key, $unwrapped); + static::assertSame($key, $unwrapped); } /** @@ -48,8 +39,8 @@ public function wrap7BytesKeyDataWith192BitKEK(): void $key = hex2bin('466f7250617369'); $wrapped = A192KW::wrap($kek, $key, true); - static::assertEquals(hex2bin('afbeb0f07dfbf5419200f2ccb50bb24f'), $wrapped); + static::assertSame(hex2bin('afbeb0f07dfbf5419200f2ccb50bb24f'), $wrapped); $unwrapped = A192KW::unwrap($kek, $wrapped, true); - static::assertEquals($key, $unwrapped); + static::assertSame($key, $unwrapped); } }