Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS Twin-Bcrypt salt pattern

I've been exploring Twin-Bcrypt JavaScript library, and found a strange thing. At one moment, I've made my own salt on server side with PHP base64_encode(openssl_random_pseudo_bytes(16)) and used it in TwinBcrypt.hash() function, which responded that salt is invalid because of the regular pattern mismatch in library. So, the pattern is:

var SALT_PATTERN = /^\$2[ay]\$(0[4-9]|[12][0-9]|3[01])\$[.\/A-Za-z0-9]{21}[.Oeu]/;

and it looks fine for my salt, except for one thing - what the hell is this - [.Oeu]?

My first question is, why they expect from salt to end with a dot, or O, or e, or u? As far as I know openssl_random_pseudo_bytes() generates secure CSPRNG, but because of the pattern JS library does not want to accept it.

Second question - is there any security reason for the salt to end with /[.Oeu]/ pattern?

I would be very grateful for any help, because there's no much info about it.

like image 422
Damaged Organic Avatar asked Sep 29 '22 16:09

Damaged Organic


1 Answers

I don't know bcrypt really well, but it appears that Twin-Bcrypt expects the salt parameter to include the full type-tag (2a/2y/2x), cost parameter, and actual cryptographic salt value. (I believe this is done to match the expectations of other libraries.) The cryptographic salt value is only the final 22 characters of the salt parameter string, which represent a 128-bit value.

To actually solve your problem, you'll need to do two things:

  1. Prefix your salt with $2y$10$ so that it has a type-tag and a cost parameter.

  2. Map your PHP base64_encode result onto bcrypt's nonstandard base64 values. For reference, the values are:

    Standard: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
    bcrypt:   ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
    

    If you simply want to make a standard base64 into a brcypt-base64 string, you can replace all + with ., but that will result in a different value (because standard + is a 62 while . is a 0, and all of the other values are offset as well). If you want a brypt string with the same value as the original, you'll need to replace each character with its bcrypt equivalent.

    You may also need to eliminate trailing = from the standard string, if any exist.


Extra information: Why must the salt end with [.Oeu]?

A salt in bcrypt is 128 bits. Each base64 character communicates 6 bits of information. That means that 21 base64 characters can evenly communicate 126 bits. The final 2 bits must be encoded in a final 22nd character. Since that character is defined by only 2 bits, it can only have 4 possible values.

When we examine the base64 character pool for brypt, we see:

./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

Counting from zero, we see that those values appear at indicies:

. => 0  = 000000
O => 16 = 010000 
e => 32 = 100000
u => 48 = 110000

So those final two bits are setting the high-end bits of the final character.

like image 64
apsillers Avatar answered Oct 03 '22 09:10

apsillers