Today this came to my attention.
When generating random secrets for e.g. JWT (in node.js the most common way is using the crypto.randomBytes()
method), I have noticed a lot of people save these tokens in a base64-encoded manner (i.e. crypto.randomBytes(len).toString('base64')
.
Charset
However, I thought to my self: doesn't saving a random byte buffer in a base64 encoded string undermine the whole principle of them being 'random bytes'? Base64 has a charset of only 64 characters while the native crypto.randomBytes.toString()
method supports 2^8 = 256 characters.
Ratio
Lets say we have a buffer with length n. For a not-base64-encoded buffer of n the encoded counterpart has the length of , which means a base64 encoded string has a overhead of approximately 133% their non encoded counterpart.
Many of you already know this, but for those who don't know: each base64 character represents 6 bits ().
4 * 6 bits = 12 bits = 3 bytes. this means there are 4 characters encoded for a three byte buffer.
However, I said approximately 133% because the output length is rounded up to a multiple of 4. This means that e.g. 1, 2, 3 bytes become 4 bytes; while 4, 5 and 6 are rounded up to 8 bytes. (this is the trailing =
you see on base64 encoded buffers most of the time).
Thus, the ratio is approximately 1 to 4 thirds (1:1.33)
With the following explaination, what is the smartest thing to do? Saving the buffer itself (short with big charset) or saving the base64 encoded buffer (long with small charset)?
Or doesn't it matter for bruteforce applications because the amount of bits is almost the same? Or is base64 even safer because base64 is always 0-2 characters longer?
const crypto = require('crypto');
const random = crypto.randomBytes(128);
const lenBuffer = random.length;
const lenBase64 = encodeURI(random.toString('base64')).split(/%..|./).length - 1;
console.log(lenBuffer, lenBase64); // 128 172 => 128 * 1.33 = 170
Edit:
I might not have been clear in my question, my apologies. My primary question here is - what would be faster to bruteforce, the short and complex byte buffer or the longer and less complex base64 encoded string? According to password entropy the length and complexity are not equally proportional, for they are logaritmic instead.