Hello Krinkle, Hashar,

I'd like you to do a code review.  Please visit

    https://gerrit.wikimedia.org/r/289366

to review the following change.

Change subject: Add MWCryptRand::generateBase32()
......................................................................

Add MWCryptRand::generateBase32()

SessionManager::generateSessionId() generates a base32-encoded session ID by
first generating a hex ID and then converting it to base32, which is
very inefficient. So introduce an MWCryptRand::generateBase32(), which
generates encodes values use Base32 encoding, as defined in RFC 4648.

Change-Id: I0ff8a79dde907301344c730b9c75ab995e4d1bda
---
M includes/session/SessionManager.php
M includes/utils/MWCryptRand.php
A tests/phpunit/includes/utils/MWCryptRandTest.php
3 files changed, 72 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/66/289366/1

diff --git a/includes/session/SessionManager.php 
b/includes/session/SessionManager.php
index 777d3d6..dc2b062 100644
--- a/includes/session/SessionManager.php
+++ b/includes/session/SessionManager.php
@@ -1101,7 +1101,7 @@
         */
        public function generateSessionId() {
                do {
-                       $id = \Wikimedia\base_convert( 
\MWCryptRand::generateHex( 40 ), 16, 32, 32 );
+                       $id = \MWCryptRand::generateBase32( 32 );
                        $key = wfMemcKey( 'MWSession', $id );
                } while ( isset( $this->allSessionIds[$id] ) || is_array( 
$this->store->get( $key ) ) );
                return $id;
diff --git a/includes/utils/MWCryptRand.php b/includes/utils/MWCryptRand.php
index f3d72e8..1962591 100644
--- a/includes/utils/MWCryptRand.php
+++ b/includes/utils/MWCryptRand.php
@@ -39,6 +39,11 @@
        const MSEC_PER_BYTE = 0.5;
 
        /**
+        * The Base32 alphabet as defined in RFC 4648.
+        */
+       const B32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
+
+       /**
         * Singleton instance for public use
         */
        protected static $singleton = null;
@@ -343,6 +348,29 @@
        }
 
        /**
+        * @see self::generateBase32()
+        */
+       public function realGenerateBase32( $chars, $forceStrong = false ) {
+               // The encoding process represents 40-bit groups of input bits 
as output
+               // strings of 8 encoded characters.
+               $bytesNeeded = ceil( $chars / 8 ) * 5;
+               $bytes = $this->generate( $bytesNeeded, $forceStrong );
+               $b32 = '';
+               foreach( str_split( $bytes, 5 ) as $chunk ) {
+                       $n = 0;
+                       for ( $i = 0; $i < 5; $i++ ) {
+                               $n <<= 8;
+                               $n |= ord( $chunk[$i] );
+                       }
+                       for ( $i = 0; $i < 8; $i++ ) {
+                               $b32 .= self::B32_ALPHABET[ $n & 0b1111 ];
+                               $n >>= 5;
+                       }
+               }
+               return substr( $b32, 0, $chars );
+       }
+
+       /**
         * @see self::generateHex()
         */
        public function realGenerateHex( $chars, $forceStrong = false ) {
@@ -419,4 +447,20 @@
        public static function generateHex( $chars, $forceStrong = false ) {
                return self::singleton()->realGenerateHex( $chars, $forceStrong 
);
        }
+
+       /**
+        * Generate a run of (ideally) cryptographically random data and return
+        * it in Base32 string format, as defined in RFC 4648.
+        *
+        * Base32 uses 20% less space than hex.
+        *
+        * @param int $chars The number of hex chars of random data to generate
+        * @param bool $forceStrong Pass true if you want generate to prefer 
cryptographically
+        *                          strong sources of entropy even if reading 
from them may steal
+        *                          more entropy from the system than optimal.
+        * @return string Hexadecimal random data
+        */
+       public static function generateBase32( $chars, $forceStrong = false ) {
+               return self::singleton()->realGenerateBase32( $chars, 
$forceStrong );
+       }
 }
diff --git a/tests/phpunit/includes/utils/MWCryptRandTest.php 
b/tests/phpunit/includes/utils/MWCryptRandTest.php
new file mode 100644
index 0000000..e77ea52
--- /dev/null
+++ b/tests/phpunit/includes/utils/MWCryptRandTest.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @covers MWCryptRand
+ */
+class MWCryptRandTest extends MediaWikiTestCase {
+
+       public function testGenerateBase32() {
+               $a = MWCryptRand::generateBase32( 7 );
+               $this->assertRegExp( '/[2-7A-Z]{7}/', $a );
+
+               $b = MWCryptRand::generateBase32( 7 );
+               $this->assertRegExp( '/[2-7A-Z]{7}/', $b );
+
+               $this->assertNotEquals( $a, $b );
+       }
+
+       public function testGenerateHex() {
+               $a = MWCryptRand::generateHex( 3 );
+               $this->assertRegExp( '/[0-9a-f]{3}/', $a );
+
+               $b = MWCryptRand::generateHex( 3 );
+               $this->assertRegExp( '/[0-9a-f]{3}/', $b );
+
+               $this->assertNotEquals( $a, $b );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/289366
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I0ff8a79dde907301344c730b9c75ab995e4d1bda
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Ori.livneh <[email protected]>
Gerrit-Reviewer: Hashar <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to