From 29c803227f0daea2664c16966596c012b303cec2 Mon Sep 17 00:00:00 2001 From: Nuria Ruiz Date: Tue, 21 Aug 2018 20:11:59 -0700 Subject: [PATCH] mediawiki.user: Increase entropy of random token generator generateRandomSessionId now generates a token with 80 bits and it is unique in a 150 billion space. The token is used to identify sessions in MediaWiki but also as a generic random generator across the MediaWiki codebase. Bug: T201124 Change-Id: I63a241a70b99d9f9e691eac25bb5cfe099a748fc --- resources/src/mediawiki.user.js | 42 +++++++++++-------- .../mediawiki/mediawiki.user.test.js | 6 +-- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/resources/src/mediawiki.user.js b/resources/src/mediawiki.user.js index b2985d1e13..bf23da1809 100644 --- a/resources/src/mediawiki.user.js +++ b/resources/src/mediawiki.user.js @@ -2,7 +2,7 @@ * @class mw.user * @singleton */ -/* global Uint32Array */ +/* global Uint16Array */ ( function ( mw, $ ) { var userInfoPromise, pageviewRandomId; @@ -36,34 +36,40 @@ * mobile usages of this code is probably higher. * * Rationale: - * We need about 64 bits to make sure that probability of collision - * on 500 million (5*10^8) is <= 1% - * See https://en.wikipedia.org/wiki/Birthday_problem#Probability_table + * We need about 80 bits to make sure that probability of collision + * on 155 billion is <= 1% * - * @return {string} 64 bit integer in hex format, padded + * See https://en.wikipedia.org/wiki/Birthday_attack#Mathematics + * n(p;H) = n(0.01,2^80)= sqrt (2 * 2^80 * ln(1/(1-0.01))) + + * @return {string} 80 bit integer in hex format, padded */ generateRandomSessionId: function () { var rnds, i, - hexRnds = new Array( 2 ), + hexRnds = new Array( 5 ), // Support: IE 11 crypto = window.crypto || window.msCrypto; - if ( crypto && crypto.getRandomValues && typeof Uint32Array === 'function' ) { - // Fill an array with 2 random values, each of which is 32 bits. - // Note that Uint32Array is array-like but does not implement Array. - rnds = new Uint32Array( 2 ); + if ( crypto && crypto.getRandomValues && typeof Uint16Array === 'function' ) { + + // Fill an array with 5 random values, each of which is 16 bits. + // Note that Uint16Array is array-like but does not implement Array. + rnds = new Uint16Array( 5 ); crypto.getRandomValues( rnds ); + } else { - rnds = [ - Math.floor( Math.random() * 0x100000000 ), - Math.floor( Math.random() * 0x100000000 ) - ]; + + // 0x10000 is 2^16 so the operation below will return a number + // between 2^16 and zero + for ( i = 0; i < 5; i++ ) { + rnds[ i ] = Math.floor( Math.random() * 0x10000 ); + } } - // Convert number to a string with 16 hex characters - for ( i = 0; i < 2; i++ ) { - // Add 0x100000000 before converting to hex and strip the extra character + // Convert the 5 16bit-numbers into 20 characters (4 hex chars per 16 bits) + for ( i = 0; i < 5; i++ ) { + // Add 0x1000 before converting to hex and strip the extra character // after converting to keep the leading zeros. - hexRnds[ i ] = ( rnds[ i ] + 0x100000000 ).toString( 16 ).slice( 1 ); + hexRnds[ i ] = ( rnds[ i ] + 0x10000 ).toString( 16 ).slice( 1 ); } // Concatenation of two random integers with entropy n and m diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js index 7044069bca..f223ef75f2 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js @@ -70,7 +70,7 @@ result = mw.user.generateRandomSessionId(); assert.strictEqual( typeof result, 'string', 'type' ); assert.strictEqual( result.trim(), result, 'no whitespace at beginning or end' ); - assert.strictEqual( result.length, 16, 'size' ); + assert.strictEqual( result.length, 20, 'size' ); result2 = mw.user.generateRandomSessionId(); assert.notEqual( result, result2, 'different when called multiple times' ); @@ -91,7 +91,7 @@ result = mw.user.generateRandomSessionId(); assert.strictEqual( typeof result, 'string', 'type' ); assert.strictEqual( result.trim(), result, 'no whitespace at beginning or end' ); - assert.strictEqual( result.length, 16, 'size' ); + assert.strictEqual( result.length, 20, 'size' ); result2 = mw.user.generateRandomSessionId(); assert.notEqual( result, result2, 'different when called multiple times' ); @@ -101,7 +101,7 @@ var result = mw.user.getPageviewToken(), result2 = mw.user.getPageviewToken(); assert.strictEqual( typeof result, 'string', 'type' ); - assert.strictEqual( /^[a-f0-9]{16}$/.test( result ), true, '16 HEX symbols string' ); + assert.strictEqual( /^[a-f0-9]{20}$/.test( result ), true, '20 HEX symbols string' ); assert.strictEqual( result2, result, 'sticky' ); } ); -- 2.20.1