Is this random integer function acceptable for generating passwords?












0















I've been goofing around with random numbers a lot recently and have usually just used Math.Random as I've had no need to use something more secure, however I figured it would be useful to learn stuff like this and I was wondering how secure / practical this function is and some improvements I could implement.



function random_number(max) {
let buffer=new ArrayBuffer(8);
let ints=new Int8Array(buffer);
window.crypto.getRandomValues(ints);
ints[7]=64-1;
ints[6]|=0xf0;
let float=new DataView(buffer).getFloat64(0,true)-1;
return Math.floor(float*Math.floor(max+1));
}









share|improve this question



























    0















    I've been goofing around with random numbers a lot recently and have usually just used Math.Random as I've had no need to use something more secure, however I figured it would be useful to learn stuff like this and I was wondering how secure / practical this function is and some improvements I could implement.



    function random_number(max) {
    let buffer=new ArrayBuffer(8);
    let ints=new Int8Array(buffer);
    window.crypto.getRandomValues(ints);
    ints[7]=64-1;
    ints[6]|=0xf0;
    let float=new DataView(buffer).getFloat64(0,true)-1;
    return Math.floor(float*Math.floor(max+1));
    }









    share|improve this question

























      0












      0








      0








      I've been goofing around with random numbers a lot recently and have usually just used Math.Random as I've had no need to use something more secure, however I figured it would be useful to learn stuff like this and I was wondering how secure / practical this function is and some improvements I could implement.



      function random_number(max) {
      let buffer=new ArrayBuffer(8);
      let ints=new Int8Array(buffer);
      window.crypto.getRandomValues(ints);
      ints[7]=64-1;
      ints[6]|=0xf0;
      let float=new DataView(buffer).getFloat64(0,true)-1;
      return Math.floor(float*Math.floor(max+1));
      }









      share|improve this question














      I've been goofing around with random numbers a lot recently and have usually just used Math.Random as I've had no need to use something more secure, however I figured it would be useful to learn stuff like this and I was wondering how secure / practical this function is and some improvements I could implement.



      function random_number(max) {
      let buffer=new ArrayBuffer(8);
      let ints=new Int8Array(buffer);
      window.crypto.getRandomValues(ints);
      ints[7]=64-1;
      ints[6]|=0xf0;
      let float=new DataView(buffer).getFloat64(0,true)-1;
      return Math.floor(float*Math.floor(max+1));
      }






      javascript random






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 29 '18 at 0:26









      fgorovfgorov

      31




      31
























          1 Answer
          1






          active

          oldest

          votes


















          0














          No, it isn't.



          What you're effectively doing (via a cast of binary data to a floating point value) is calculating floor(max * rand() / (RAND_MAX + 1.0)) (with RAND_MAX equal to 252−1). This will always result in a skewed distribution unless max is a factor of RAND_MAX+1, as explained here.



          This is quite easy to demonstrate:






          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          The correct way to obtain a random integer over a specified range is to start with a uniform random number whose bit length is at least as long as that of max. Discard the higher bits, and return the result if it is less than or equal to max. Otherwise repeat the process.



          Something like this, perhaps:



          function rand_int(max) {
          // Returns a uniform random integer from 0 to max (inclusive)
          var mask = 1;
          var crypto = window.crypto;
          max = Math.floor(max);

          if (!crypto) throw "window.crypto undefined";
          if (max < 1) throw "max value too small";
          if (max > 0xffffffff) throw "max value too large";

          // Generate binary mask (all 1)
          while (mask < max) mask = (mask << 1) | 1;

          // Now generate random values until one is within range
          var r = new Int32Array(1);
          do {
          crypto.getRandomValues(r);
          r[0] &= mask;
          } while (r[0] > max);
          return r[0];
          }





          share|improve this answer


























          • Thank you so much, I don't know too much about this.

            – fgorov
            Dec 5 '18 at 7:17






          • 1





            EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

            – fgorov
            Dec 5 '18 at 7:33













          • @fgorov Oops, you're right. Should have been r, not rval!

            – squeamish ossifrage
            Dec 5 '18 at 10:08












          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53530114%2fis-this-random-integer-function-acceptable-for-generating-passwords%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          0














          No, it isn't.



          What you're effectively doing (via a cast of binary data to a floating point value) is calculating floor(max * rand() / (RAND_MAX + 1.0)) (with RAND_MAX equal to 252−1). This will always result in a skewed distribution unless max is a factor of RAND_MAX+1, as explained here.



          This is quite easy to demonstrate:






          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          The correct way to obtain a random integer over a specified range is to start with a uniform random number whose bit length is at least as long as that of max. Discard the higher bits, and return the result if it is less than or equal to max. Otherwise repeat the process.



          Something like this, perhaps:



          function rand_int(max) {
          // Returns a uniform random integer from 0 to max (inclusive)
          var mask = 1;
          var crypto = window.crypto;
          max = Math.floor(max);

          if (!crypto) throw "window.crypto undefined";
          if (max < 1) throw "max value too small";
          if (max > 0xffffffff) throw "max value too large";

          // Generate binary mask (all 1)
          while (mask < max) mask = (mask << 1) | 1;

          // Now generate random values until one is within range
          var r = new Int32Array(1);
          do {
          crypto.getRandomValues(r);
          r[0] &= mask;
          } while (r[0] > max);
          return r[0];
          }





          share|improve this answer


























          • Thank you so much, I don't know too much about this.

            – fgorov
            Dec 5 '18 at 7:17






          • 1





            EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

            – fgorov
            Dec 5 '18 at 7:33













          • @fgorov Oops, you're right. Should have been r, not rval!

            – squeamish ossifrage
            Dec 5 '18 at 10:08
















          0














          No, it isn't.



          What you're effectively doing (via a cast of binary data to a floating point value) is calculating floor(max * rand() / (RAND_MAX + 1.0)) (with RAND_MAX equal to 252−1). This will always result in a skewed distribution unless max is a factor of RAND_MAX+1, as explained here.



          This is quite easy to demonstrate:






          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          The correct way to obtain a random integer over a specified range is to start with a uniform random number whose bit length is at least as long as that of max. Discard the higher bits, and return the result if it is less than or equal to max. Otherwise repeat the process.



          Something like this, perhaps:



          function rand_int(max) {
          // Returns a uniform random integer from 0 to max (inclusive)
          var mask = 1;
          var crypto = window.crypto;
          max = Math.floor(max);

          if (!crypto) throw "window.crypto undefined";
          if (max < 1) throw "max value too small";
          if (max > 0xffffffff) throw "max value too large";

          // Generate binary mask (all 1)
          while (mask < max) mask = (mask << 1) | 1;

          // Now generate random values until one is within range
          var r = new Int32Array(1);
          do {
          crypto.getRandomValues(r);
          r[0] &= mask;
          } while (r[0] > max);
          return r[0];
          }





          share|improve this answer


























          • Thank you so much, I don't know too much about this.

            – fgorov
            Dec 5 '18 at 7:17






          • 1





            EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

            – fgorov
            Dec 5 '18 at 7:33













          • @fgorov Oops, you're right. Should have been r, not rval!

            – squeamish ossifrage
            Dec 5 '18 at 10:08














          0












          0








          0







          No, it isn't.



          What you're effectively doing (via a cast of binary data to a floating point value) is calculating floor(max * rand() / (RAND_MAX + 1.0)) (with RAND_MAX equal to 252−1). This will always result in a skewed distribution unless max is a factor of RAND_MAX+1, as explained here.



          This is quite easy to demonstrate:






          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          The correct way to obtain a random integer over a specified range is to start with a uniform random number whose bit length is at least as long as that of max. Discard the higher bits, and return the result if it is less than or equal to max. Otherwise repeat the process.



          Something like this, perhaps:



          function rand_int(max) {
          // Returns a uniform random integer from 0 to max (inclusive)
          var mask = 1;
          var crypto = window.crypto;
          max = Math.floor(max);

          if (!crypto) throw "window.crypto undefined";
          if (max < 1) throw "max value too small";
          if (max > 0xffffffff) throw "max value too large";

          // Generate binary mask (all 1)
          while (mask < max) mask = (mask << 1) | 1;

          // Now generate random values until one is within range
          var r = new Int32Array(1);
          do {
          crypto.getRandomValues(r);
          r[0] &= mask;
          } while (r[0] > max);
          return r[0];
          }





          share|improve this answer















          No, it isn't.



          What you're effectively doing (via a cast of binary data to a floating point value) is calculating floor(max * rand() / (RAND_MAX + 1.0)) (with RAND_MAX equal to 252−1). This will always result in a skewed distribution unless max is a factor of RAND_MAX+1, as explained here.



          This is quite easy to demonstrate:






          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          The correct way to obtain a random integer over a specified range is to start with a uniform random number whose bit length is at least as long as that of max. Discard the higher bits, and return the result if it is less than or equal to max. Otherwise repeat the process.



          Something like this, perhaps:



          function rand_int(max) {
          // Returns a uniform random integer from 0 to max (inclusive)
          var mask = 1;
          var crypto = window.crypto;
          max = Math.floor(max);

          if (!crypto) throw "window.crypto undefined";
          if (max < 1) throw "max value too small";
          if (max > 0xffffffff) throw "max value too large";

          // Generate binary mask (all 1)
          while (mask < max) mask = (mask << 1) | 1;

          // Now generate random values until one is within range
          var r = new Int32Array(1);
          do {
          crypto.getRandomValues(r);
          r[0] &= mask;
          } while (r[0] > max);
          return r[0];
          }





          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>





          function random_number(max) {
          let buffer=new ArrayBuffer(8);
          let ints=new Int8Array(buffer);
          window.crypto.getRandomValues(ints);
          ints[7]=64-1;
          ints[6]|=0xf0;
          let float=new DataView(buffer).getFloat64(0,true)-1;
          return Math.floor(float*Math.floor(max+1));
          }

          function check_skew() {
          var m = Math.floor(Math.pow(2,52) * 2 / 3);
          var o = [0,0];
          var ns = 100000;
          for (i=0; i<ns; i++) o[random_number(m)&1]++; o;
          console.log("Out of "+ns+" random numbers, "+o[0]*100/ns+
          "% were even and "+o[1]*100/ns+"% were odd.");
          }

          <button onclick="check_skew()">Click this button a few times and check the results</button>






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 5 '18 at 10:07

























          answered Nov 29 '18 at 10:46









          squeamish ossifragesqueamish ossifrage

          17.1k32361




          17.1k32361













          • Thank you so much, I don't know too much about this.

            – fgorov
            Dec 5 '18 at 7:17






          • 1





            EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

            – fgorov
            Dec 5 '18 at 7:33













          • @fgorov Oops, you're right. Should have been r, not rval!

            – squeamish ossifrage
            Dec 5 '18 at 10:08



















          • Thank you so much, I don't know too much about this.

            – fgorov
            Dec 5 '18 at 7:17






          • 1





            EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

            – fgorov
            Dec 5 '18 at 7:33













          • @fgorov Oops, you're right. Should have been r, not rval!

            – squeamish ossifrage
            Dec 5 '18 at 10:08

















          Thank you so much, I don't know too much about this.

          – fgorov
          Dec 5 '18 at 7:17





          Thank you so much, I don't know too much about this.

          – fgorov
          Dec 5 '18 at 7:17




          1




          1





          EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

          – fgorov
          Dec 5 '18 at 7:33







          EDIT: I noticed that you use the (variable?) rval instead of directly using r, is this an accident or is there a reason I should add "var rval = r"

          – fgorov
          Dec 5 '18 at 7:33















          @fgorov Oops, you're right. Should have been r, not rval!

          – squeamish ossifrage
          Dec 5 '18 at 10:08





          @fgorov Oops, you're right. Should have been r, not rval!

          – squeamish ossifrage
          Dec 5 '18 at 10:08




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53530114%2fis-this-random-integer-function-acceptable-for-generating-passwords%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Contact image not getting when fetch all contact list from iPhone by CNContact

          count number of partitions of a set with n elements into k subsets

          A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks