# Randomly generated secrets: encoding the random bytes in base64 vs keeping them

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 $4[n/3]$, 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 ($^2log(64) = 6$).
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.

• If it would be faster for an attacker to brute force one or the other, the conversion is trivial, so it would just become an implementation detail in the algorithm, no meaningful difference here even without knowing anything about the entropy. Only way these representations could be different is achieved in security through obscurity - that is, if the attackers do not know how many possible values per byte you are using in each representation. Commented Jan 10, 2023 at 5:11
• @Lodinn: That should really be the answer. If a key in base64 was easier to crack, then any hacker would first convert your binary key to base64. Commented Jan 10, 2023 at 9:00

It doesn't matter. A number doesn't change because you change the encoding of it.

1012 and 510 is the same number, and contain the same amount of information.

The reason we use base64 is that it is safe printable characters; they won't screw up your terminal if you output them to it, and they will transmit nicely in any computer system capable of handling 7-bit ASCII. The drawback is as you observed the increased overhead.

Or doesn't it matter for brute force applications because the amount of bits is almost the same? Or is base64 even safer because base64 is always 0-2 characters longer?

It's not almost the same. It is the same. Computers treat information as numbers – large numbers. If you represent that number as 8-bit bytes, each digit conveys 8 bits of information. If you represent that number as base64, each digit conveys 6 bits of information. It's still the same number, but the number of digits increased due to lower information content per digit.

Your question is like asking which bus is the soonest: the one in 600 seconds, the one in ten minutes or the one in 10 minutes.

• No. Furthermore, base64 is not an representation used in a computer. It's meant for transport and storage. It's converted to a binary format before being used by the CPU. This conversion happens on the fly usually. Your question is essentially asking if it takes longer for you to count to 10 than to ten. The answer is it's the same number. Commented Jan 9, 2023 at 6:43
• @Sam Maybe this Javascript analogy will make more sense: which number is bigger? `Number("0b101")` or `5`? Neither, they're the same number. Commented Jan 9, 2023 at 7:27
• Obviously a 256 bit key will be stored in about 344 bits, but when the key is needed, it is converted back to the original 256 bits. Commented Jan 9, 2023 at 9:55
• @Sam, if you apply a reversible transformation that trades between length and complexity, then length and complexity are equally important factors. Commented Jan 9, 2023 at 10:39
• @Sam Length and complexity are both proxies for what matters: the conditional likelihood, given knowledge an attacker possesses, that their first n guesses will contain the answer. Commented Jan 9, 2023 at 12:55

As you say, base64 uses 4/3 times as many symbols to represent the same value.

Each symbol of base64 encodes 6 bits, instead of 8. That's a ratio of 3/4.

(4/3) * (3/4) = 1. The information content is the same.

The binary password has 256^n possibilities, where n is the password length in bytes.

The base64 password has 64^(n*4/3) possibilities, where n is the original password length in bytes and, since the base64 string becomes longer, it's multiplied by 4/3 (as you found out, ignoring potential padding, because you can remove it). We have a smaller base, though (64).

Now,

``````256^n =
(2^8)^n =
2^(8n) =
2^(4/3*3/4 *8n) =      | 4/3 * 3/4 == 1
2^(3/4*8 * 4/3*n) =
2^(6* 4/3*n) =
(2^6)^(4/3*n) =
64^(4/3*n)
``````

Qed. The number of possibilities to be brute forced is the same in both cases.

You may be wondering which option is more "secure" if you were to take the output and use it directly as a password in, say, a password field on a website.

In that case, it depends on the brute force attack. A straightforward one that uses every possible input value for length n before moving on to length (n+1), of course the better answer is the longer one; the base64 version. But you could also imagine brute force attacks that don't use really uncommon characters at all, in which case the "raw" one might never be cracked. There isn't a single correct answer here.

Additionally, there are a couple problems:

• Using the "raw" output isn't "short with big charset" -- it's likely got bytes in it that don't represent valid character data at all. You need to encode the data in some way to actually turn it into a "charset".
• JWT keys aren't used like passwords on a website. As the other answers correctly state, JWT encryption and signing algorithms use the number directly, not the encoded number.
• Another consideration is that base64 strings have a visible representation which many systems will display by default, while binary blobs generally don't. Given the ubiquity of things like high resolution cell phone cameras, information which is displayed in a publicly-visible location, even briefly, may be compromised as a result. If a binary blob can't be displayed, such accidental release can't happen. Commented Jan 11, 2023 at 18:59

Thanks to Lodinn, here is the simplest answer:

If it was easier to crack a base64 encoded key rather than a binary key, then the very first thing an attacker with a binary key would do is to base64-encode it.

• And, hackers don't do that? How do we know for sure? Citation needed. Commented Jan 10, 2023 at 14:25
• Worth noting that it only applies when the encoding is lossless (which base64 is of course). For example, a checksum might be much easier to crack than a hash, but doesn't necessarily give you the right answer.
– Fax
Commented Jan 10, 2023 at 23:48
• @ThomasWeller: It's immaterial whether they actually do or not. The fact that any given message can be converted to or from base64 with trival effort, means that the two forms are exactly equivalent for purposes of security. The security of an algorithm involves its resistance to all possible cracking methods, which necessarily includes methods that begin by translating it into some other form. If base64 encoding or decoding "reduced" the security of an algorithm, then the algorithm didn't actually have that security in the first place. Commented Jan 11, 2023 at 21:21