From 39e97c6120141d8b8293d764bbac30d901078c85 Mon Sep 17 00:00:00 2001
From: Torben Hansen <derhansen@gmail.com>
Date: Thu, 14 Mar 2024 09:51:16 +0100
Subject: [PATCH] [TASK] Deprecate `GeneralUtility::hmac()`

In order to ensure unique HMACs in TYPO3, all usages of
`TYPO3\CMS\Core\Utility\GeneralUtility::hmac` have been
replaced with the `hmac` function in
`\TYPO3\CMS\Core\Crypto\HashService`.

This change finally deprecates `GeneralUtility::hmac()`.

Resolves: #102762
Relates: #102761
Releases: main
Change-Id: I3624ea8e2d6ec91abddfa1580ad68c1189b9445c
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83462
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Oliver Klee <typo3-coding@oliverklee.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Oliver Klee <typo3-coding@oliverklee.de>
Tested-by: Benni Mack <benni@typo3.org>
---
 .../core/Classes/Utility/GeneralUtility.php   |  5 ++
 .../Deprecation-102762-GeneralUtilityhmac.rst | 81 +++++++++++++++++++
 .../Tests/Unit/Utility/GeneralUtilityTest.php | 30 -------
 .../Utility/GeneralUtilityTest.php            | 53 ++++++++++++
 .../Php/MethodCallStaticMatcher.php           |  7 ++
 5 files changed, 146 insertions(+), 30 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-102762-GeneralUtilityhmac.rst
 create mode 100644 typo3/sysext/core/Tests/UnitDeprecated/Utility/GeneralUtilityTest.php

diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index bc427db08e8d..f7606d145e82 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -470,9 +470,14 @@ class GeneralUtility
      * @param string $input Input string to create HMAC from
      * @param string $additionalSecret additionalSecret to prevent hmac being used in a different context
      * @return string resulting (hexadecimal) HMAC currently with a length of 40 (HMAC-SHA-1)
+     * @deprecated since TYPO3 13.1, will be removed in TYPO3 V14
      */
     public static function hmac($input, $additionalSecret = '')
     {
+        trigger_error(
+            'GeneralUtility::hmac() is deprecated and will be removed in TYPO3 v14. Use TYPO3\CMS\Core\Crypto\HashService instead.',
+            E_USER_DEPRECATED
+        );
         $hashAlgorithm = 'sha1';
         $secret = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . $additionalSecret;
         return hash_hmac($hashAlgorithm, $input, $secret);
diff --git a/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-102762-GeneralUtilityhmac.rst b/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-102762-GeneralUtilityhmac.rst
new file mode 100644
index 000000000000..3495ec070d06
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-102762-GeneralUtilityhmac.rst
@@ -0,0 +1,81 @@
+.. include:: /Includes.rst.txt
+
+.. _deprecation-102762-1710402828:
+
+=======================================================
+Deprecation: #102762 - Deprecate GeneralUtility::hmac()
+=======================================================
+
+See :issue:`102762`
+
+Description
+===========
+
+The method :php:`\TYPO3\CMS\Core\Utility\GeneralUtility::hmac()`
+has been deprecated in TYPO3 v13 and will be removed with v14 in
+favour of :ref:`feature-102761-1704532036`.
+
+Impact
+======
+
+Usage of the method will raise a deprecation level log entry in
+TYPO3 v13 and a fatal error in TYPO3 v14.
+
+
+Affected installations
+======================
+
+All 3rd party extensions using :php:`\TYPO3\CMS\Core\Utility\GeneralUtility::hmac()`.
+
+
+Migration
+=========
+
+All usages of :php:`\TYPO3\CMS\Core\Utility\GeneralUtility::hmac()`
+must be migrated to use the :php:`hmac()` method in the class
+:php:`\TYPO3\CMS\Core\Crypto\HashService`.
+
+Before
+------
+
+..  code-block:: php
+
+    //use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+    $hmac = GeneralUtility::hmac('some-input', 'some-secret');
+
+After
+-----
+
+..  code-block:: php
+    :caption: Using :php:`GeneralUtility::makeInstance()`
+
+    //use TYPO3\CMS\Core\Crypto\HashService;
+    //use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+    $hashService = GeneralUtility::makeInstance(HashService::class);
+    $hmac = $hashService->hmac('some-input', 'some-secret');
+
+..  code-block:: php
+    :caption: Using dependency injection
+
+    namespace MyVendor\MyExt\Services;
+
+    use TYPO3\CMS\Core\Crypto\HashService;
+    use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+    final readonly class MyService
+    {
+      public function __construct(
+        private HashService $hashService,
+      ) {}
+
+      public function someMethod(): void
+      {
+        $hmac = $this->hashService->hmac('some-input', 'some-secret');
+      }
+    }
+
+If possible, use dependency injection to inject :php:`HashService` to your class.
+
+.. index:: Backend, FullyScanned, ext:core
diff --git a/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
index 54767c892d94..6d6398fc823a 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
@@ -1655,36 +1655,6 @@ final class GeneralUtilityTest extends UnitTestCase
         self::assertEquals($expectedResult, $result);
     }
 
-    //////////////////////////////////
-    // Tests concerning hmac
-    //////////////////////////////////
-    #[Test]
-    public function hmacReturnsHashOfProperLength(): void
-    {
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
-        $hmac = GeneralUtility::hmac('message');
-        self::assertTrue(!empty($hmac) && is_string($hmac));
-        self::assertEquals(strlen($hmac), 40);
-    }
-
-    #[Test]
-    public function hmacReturnsEqualHashesForEqualInput(): void
-    {
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
-        $msg0 = 'message';
-        $msg1 = 'message';
-        self::assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
-    }
-
-    #[Test]
-    public function hmacReturnsNoEqualHashesForNonEqualInput(): void
-    {
-        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
-        $msg0 = 'message0';
-        $msg1 = 'message1';
-        self::assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
-    }
-
     //////////////////////////////////
     // Tests concerning quoteJSvalue
     //////////////////////////////////
diff --git a/typo3/sysext/core/Tests/UnitDeprecated/Utility/GeneralUtilityTest.php b/typo3/sysext/core/Tests/UnitDeprecated/Utility/GeneralUtilityTest.php
new file mode 100644
index 000000000000..8859de4525c7
--- /dev/null
+++ b/typo3/sysext/core/Tests/UnitDeprecated/Utility/GeneralUtilityTest.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Tests\UnitDeprecated\Utility;
+
+use PHPUnit\Framework\Attributes\Test;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+final class GeneralUtilityTest extends UnitTestCase
+{
+    #[Test]
+    public function hmacReturnsHashOfProperLength(): void
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
+        $hmac = GeneralUtility::hmac('message');
+        self::assertTrue(!empty($hmac) && is_string($hmac));
+        self::assertEquals(strlen($hmac), 40);
+    }
+
+    #[Test]
+    public function hmacReturnsEqualHashesForEqualInput(): void
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
+        $msg0 = 'message';
+        $msg1 = 'message';
+        self::assertEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
+    }
+
+    #[Test]
+    public function hmacReturnsNoEqualHashesForNonEqualInput(): void
+    {
+        $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '';
+        $msg0 = 'message0';
+        $msg1 = 'message1';
+        self::assertNotEquals(GeneralUtility::hmac($msg0), GeneralUtility::hmac($msg1));
+    }
+
+}
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
index 9c94f11fdc7f..32d0efb298c2 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php
@@ -1606,4 +1606,11 @@ return [
             'Deprecation-102895-ExtensionManagementUtilitygetExtensionIcon.rst',
         ],
     ],
+    'TYPO3\CMS\Core\Utility\GeneralUtility::hmac' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 2,
+        'restFiles' => [
+            'Deprecation-100596-GeneralUtility_GET.rst',
+        ],
+    ],
 ];
-- 
GitLab