From 3fe0074c04755caedef4f04d4cf65b093d9a45ab Mon Sep 17 00:00:00 2001
From: Brian Wolff <bawolff+wn@gmail.com>
Date: Fri, 9 Sep 2022 17:22:11 -0700
Subject: [PATCH] SECURITY: Always use a unique nonce/IV for AES-CTR
 encryption.

AES-CTR encryption catastrophically fails if 2 different messages
are encrypted under the same key with the same nonce.

This patch generates a new random nonce everytime a new encrypted
token is generated.

Bug: T315123
---
 src/TokenManager.php | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/TokenManager.php b/src/TokenManager.php
index 1aee2d59..ba7d93db 100644
--- a/src/TokenManager.php
+++ b/src/TokenManager.php
@@ -38,13 +38,15 @@ class TokenManager {
 	 */
 	public function encode( Session $session, array $data ): string {
 		$key = $this->getSessionKey( $session );
+		$iv = $this->getInitializationVector();
 		return JWT::encode(
 			[
 				// Expiration Time https://tools.ietf.org/html/rfc7519#section-4.1.4
 				// 24 hours from now
 				'exp' => \MWTimestamp::time() + 86400,
+				'iv' => base64_encode( $iv ),
 				// Encrypt the form data to prevent it from being leaked.
-				'data' => $this->encrypt( $data, $this->getInitializationVector( $key ) ),
+				'data' => $this->encrypt( $data, $iv ),
 			],
 			$this->getSigningKey( $key ),
 			self::SIGNING_ALGO
@@ -85,7 +87,7 @@ class TokenManager {
 
 		return $this->decrypt(
 			$payload->data,
-			$this->getInitializationVector( $key )
+			base64_decode( $payload->iv )
 		);
 	}
 
@@ -115,14 +117,13 @@ class TokenManager {
 	/**
 	 * Get the initialization vector.
 	 *
-	 * This must be consistent between encryption and decryption
-	 * and must be no more than 16 bytes in length.
+	 * This must be consistent between encryption and decryption,
+	 * must be no more than 16 bytes in length and never repeat.
 	 *
-	 * @param string $sessionKey
 	 * @return string
 	 */
-	private function getInitializationVector( string $sessionKey ): string {
-		return hash_hmac( 'md5', $sessionKey, $this->secret, true );
+	private function getInitializationVector(): string {
+		return random_bytes( 16 );
 	}
 
 	/**
-- 
2.30.2

