0

I'm creating a PKCE Code Verifier (a random string).

Solution 1 from a StackOverflow answer. (You can copy/paste these code examples into a browser's inspector.)

(function generateCodeVerifier() {
    function dec2hex(dec) {
        return ("0" + dec.toString(16)).substr(-2);
    }
const len = 128;
var array = new Uint32Array( len / 2);
window.crypto.getRandomValues(array);
return Array.from(array, dec2hex).join("");

})()

Question

Why only use the last two hex characters of the numbers?

The following uses the full numbers from the array. Is it weaker than the first solution?

I do notice that the result length of the second solution can vary depending on whether the random numbers generate leading zeros.

Question: can I pad the result for cases where the length must be exact?

(function generateCodeVerifier2() {
    const len = 128;
    var array = new Uint32Array( len / 8);
    window.crypto.getRandomValues(array);
    return Array.from(array, x => x.toString(16)).join("");
})()

Larry K
  • 103
  • 4

1 Answers1

1

generateCodeVerifier takes a random 128-byte bytestring and converts it to hex byte by byte. dec2hex handles the conversion of a byte to two hex characters. The reason for prepending a 0 and taking the rightmost two characters is that if dec is in range [0, 15] then dec.toString(16) is a single character. Oh my, the web is literally littered with that idiom.

generateCodeVerifier2 skips this normalization to two characters, thus any byte is range [0, 15] produces a single character. The average length of the output is thus 248 characters rather than 256, and the character 0 is underrepresented (proportion 1/30 rather than 1/16).

If we pad the result of generateCodeVerifier2 to 256 characters with zeroes as considered in the question, it's non-uniform. In particular the string starts or ends with a 0 (according to the padding side) with >99.97% certainty. Since no security objective is stated in the present question, we can't tell if that's an issue or not.

In the referred SO question all that matters is that the result is unique, and under that goal the non-uniformity would most likely be a non-issue: the many common ways window.crypto.getRandomValues could be broken (by generating the same value on different occasions, e.g. at each run, or on different machines at the same system time) are very unlikely to make generateCodeVerifier OK but generateCodeVerifier2 broken from the standpoint of producing a unique value.

But said referred SO question uses the code with 28 bytes, and in that case (not the question) the non-uniformity considerably decreases the expected number of draws to reach a collision with sizable probability, assuming a truly uniform window.crypto.getRandomValues. It becomes at least a certificational issue.

fgrieu
  • 140,762
  • 12
  • 307
  • 587