Is this random integer function acceptable for generating passwords?
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
add a comment |
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
add a comment |
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
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
javascript random
asked Nov 29 '18 at 0:26
fgorovfgorov
31
31
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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];
}
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 beenr
, notrval
!
– squeamish ossifrage
Dec 5 '18 at 10:08
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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];
}
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 beenr
, notrval
!
– squeamish ossifrage
Dec 5 '18 at 10:08
add a comment |
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];
}
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 beenr
, notrval
!
– squeamish ossifrage
Dec 5 '18 at 10:08
add a comment |
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];
}
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>
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 beenr
, notrval
!
– squeamish ossifrage
Dec 5 '18 at 10:08
add a comment |
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 beenr
, notrval
!
– 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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