I'd like to store an scrypt-hashed password in a database. What is the maximum length I can expect?
Asked
Active
Viewed 4,648 times
2 Answers
11
According to https://github.com/wg/scrypt the output format is $s0$params$salt$key
where:
s0
denotes version 0 of the format, with 128-bit salt and 256-bit derived key.params
is a 32-bit hex integer containing log2(N) (16 bits), r (8 bits), and p (8 bits).salt
is the base64-encoded salt.key
is the base64-encoded derived key.
According to https://stackoverflow.com/a/13378842/14731 the length of a base64-encoded string is where
n
denotes the number of bytes being encoded.
Let's break this down:
- The dollar signs makes up 4 characters.
- The version numbers makes up 2 characters.
- Each hex character represents 4 bits
( log2(16) = 4 )
, so theparams
field makes up (32-bit / 4 bits) = 8 characters. - The 128-bit salt is equivalent to 16 bytes. The base64-encoded format makes up
(4 * ceil(16 / 3))
= 24 characters. - The 256-bit derived key is equivalent to 32 bytes. The base64-encoded format makes up
(4 * ceil(32 / 3))
= 44 characters.
Putting that all together, we get: 4 + 2 + 8 + 24 + 44
= 82 characters.
-
1It should be noted that there is no standard for using scrypt for password storage. There are different encoded formats, different salt lengths, and different derived byte password lengths. But the world does seem to gravitate towards the `wg-scrypt` format. – Ian Boyd May 18 '15 at 16:06
-
Not sure why WG's crypt format for scrypt allows values log2(N) to be a 16-bit number. The implementation uses an `int` for N, so the maximum practical value for log2(N) is 31 (`0x1F`). Even if a `long` were used an extra digit would not be required. The implementation converts the parameters to a hex string using Java's `toString` with base 16 and no padding, so it can never actually use the 8 characters that the specification allows. The practical maximum for parameters is 6 characters, and thus 80 for the full string. – Rand Aug 25 '16 at 19:15
-
@Rand You are right. The implementation-specific answer is 80 characters but I'd rather err with the maximum limit set out by the specification (in case the implementation changes over time). – Gili Aug 25 '16 at 19:22
-
@Gili Of course, you are absolutely correct about what the specification says. But it's more than the difference between `int` and `long`. `0x100` means that N is 2^256, which is 1x10^77! Even if the algorithm were implemented to perform that many iterations it would not be possible to do so. What I am really saying is that 16 bits for log2(N) makes no sense. It appears to be an error in the specification. – Rand Aug 25 '16 at 20:32
-
@Rand The reason is because they're using a 32-bit DWORD. 8-bits for `r`, 8-bits for `p`. You have 16 bits left over: use it for the *number of rounds factor*. Why contrive `0x00nnrrpp` when you can just use `0xnnnnrrpp`. – Ian Boyd Oct 03 '16 at 15:45
-
@Ian Boyd Given the way the string is parsed, there's no technical advantage to specifying 32-bits rather than 24-bits. In fact, it makes the code more complex because `n` requires a different bit mask (`0xfff`) than the other values (`0xff`). However, 32-bits creates usability risk. `0x100` is _not_ a valid input value. The specification should constrain the input range to usable values, especially in a security library. – Rand Oct 03 '16 at 18:00
-
@Rand The only limits i can find for N is that `N < 2^(128*r/8)`. Which works out to a *costFactor* = log2(N) = 4096 = 0x1000. Which means we two, out of the four, bytes. – Ian Boyd Oct 03 '16 at 19:02
-
@Ian Boyd wg's scrypt Java implementation uses an `int` to store `N`, so the maximum usable `log2(N)` is 31 (same as bcrypt). The implementation _could_ be changed to use a `long`, that increases that value to 63. The reference C implementation uses an unsigned 64-bit field, so the max is 64 (`0x40`). Even these values are not practical to calculate, so there's no reason to allow higher values. – Rand Oct 03 '16 at 19:46
2
In Colin Percival's own implementation, the tarsnap scrypt header is 96 bytes. This comprises:
- 6 bytes 'scrypt'
- 10 bytes N, r, p parameters
- 32 bytes salt
- 16 bytes SHA256 checksum of bytes 0-47
- 32 bytes HMAC hash of bytes 0-63 (using scrypt hash as key)
This is also the format used by node-scrypt. There is an explanation of the rationale behind the checksum and the HMAC hash on stackexchange.
As a base64-encoded string, this makes 128 characters.