Fix and secure random number generation code
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 17 Sep 2021 07:22:15 +0000 (09:22 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 17 Sep 2021 07:22:15 +0000 (09:22 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/utils/Utils.ts

index 963042a46684b0754de10962fd8c9e102f9ac819..756f5b80a6adc752d38a7e98ba20f4b1b03eb088 100644 (file)
@@ -79,7 +79,7 @@ export default class AutomaticTransactionGenerator {
         this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
       logger.info(this.logPrefix(connectorId) + ' waiting for ' + Utils.milliSecondsToHHMMSS(wait));
       await Utils.sleep(wait);
-      const start = Math.random();
+      const start = Utils.secureRandom();
       if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
         transactionSkip = 0;
         // Start transaction
index fe2b5d8dd9abe16644fc6c3594b31f322cd594a0..575d70962c21022c1888ebac3f1ec1e90079f18b 100644 (file)
@@ -85,7 +85,7 @@ export default class ChargingStation {
   }
 
   public getRandomTagId(): string {
-    const index = Math.floor(Math.random() * this.authorizedTags.length);
+    const index = Math.floor(Utils.secureRandom() * this.authorizedTags.length);
     return this.authorizedTags[index];
   }
 
@@ -453,7 +453,7 @@ export default class ChargingStation {
     const stationInfo: ChargingStationInfo = stationTemplateFromFile ?? {} as ChargingStationInfo;
     if (!Utils.isEmptyArray(stationTemplateFromFile.power)) {
       stationTemplateFromFile.power = stationTemplateFromFile.power as number[];
-      const powerArrayRandomIndex = Math.floor(Math.random() * stationTemplateFromFile.power.length);
+      const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplateFromFile.power.length);
       stationInfo.maxPower = stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT
         ? stationTemplateFromFile.power[powerArrayRandomIndex] * 1000
         : stationTemplateFromFile.power[powerArrayRandomIndex];
@@ -924,7 +924,7 @@ export default class ChargingStation {
         indexUrl = this.index % supervisionUrls.length;
       } else {
         // Get a random url
-        indexUrl = Math.floor(Math.random() * supervisionUrls.length);
+        indexUrl = Math.floor(Utils.secureRandom() * supervisionUrls.length);
       }
       return new URL(supervisionUrls[indexUrl]);
     }
index 73e5e3f96e421b7f2bbec4bb1021ab58f5c09d47..c94d9397b52c77e975ad49738f4702c6c48aba83 100644 (file)
@@ -1,6 +1,7 @@
 import Configuration from './Configuration';
 import { WebSocketCloseEventStatusString } from '../types/WebSocket';
 import { WorkerProcessType } from '../types/Worker';
+import crypto from 'crypto';
 import { v4 as uuid } from 'uuid';
 
 export default class Utils {
@@ -98,14 +99,14 @@ export default class Utils {
   }
 
   static getRandomFloat(max: number, min = 0): number {
-    return Math.random() < 0.5 ? (1 - Math.random()) * (max - min) + min : Math.random() * (max - min) + min;
+    return (crypto.randomBytes(4).readUInt32LE() / 0xffffffff) * (max - min) + min;
   }
 
   static getRandomInt(max: number, min = 0): number {
     if (min) {
-      return Math.floor(Math.random() * (max - min + 1) + min);
+      return Math.floor(Utils.secureRandom() * (max - min + 1)) + min;
     }
-    return Math.floor(Math.random() * max + 1);
+    return Math.floor(Utils.secureRandom() * (max + 1));
   }
 
   static roundTo(numberValue: number, scale: number): number {
@@ -195,7 +196,7 @@ export default class Utils {
    */
   static exponentialDelay(retryNumber = 0): number {
     const delay = Math.pow(2, retryNumber) * 100;
-    const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
+    const randomSum = delay * 0.2 * Utils.secureRandom(); // 0-20% of the delay
     return delay + randomSum;
   }
 
@@ -232,4 +233,13 @@ export default class Utils {
   static workerDynamicPoolInUse(): boolean {
     return Configuration.getWorkerProcess() === WorkerProcessType.DYNAMIC_POOL;
   }
+
+  /**
+   * Generate a cryptographically secure number in the [0, 1[ range
+   *
+   * @returns
+   */
+  static secureRandom(): number {
+    return crypto.randomBytes(4).readUInt32LE() / 0x100000000;
+  }
 }