Class: SafeDb::KdfSCrypt

Inherits:
Object
  • Object
show all
Defined in:
lib/keytools/kdf.scrypt.rb

Overview

SCrypt is a Key Derivation Function (KDF) with a reliable OpenSSL implementation that converts low entropy password-like text to a high entropy key that is computationally infeasible to acquire through brute force.

SCrypt is incredibly resistant to attacks using dedicated hardware with massive memory to boot.

Constant Summary collapse

SCRYPT_SALT_BYTE_LENGTH =

SCrypt salts are recommended to contain 16 and 32 bytes inclusive. Here we opt for 24 bytes which unrolls out to 192 bits which serializes into 32 base64 characters.

24
SCRYPT_ITERATION_INTEGER =

The iteration count is determined using the powers of two so if the iteration integer is 12 there will be two to the power of 12 ( 2^12 ) giving 4096 iterations. The minimum number is 4 (16 iterations) and the max is 31.

Examples:

Configuring 16 into this directive results in
   2^16 = 65,536 iterations

This is a safe default and will slow the derivation time
to about a second on a powerful 2020 laptop.
16
SCRYPT_KEY_LENGTH =

The scrypt algorithm produces a key that is 181 bits in length. The algorithm then converts the binary 181 bits into a (6-bit) Radix64 character.

181 / 6 = 30 remainder 1 (so 31 characters are needed).

31
SCRYPT_KEY_TRANSPORT_LENGTH =

When the key is transported using a 64 character set where each character is represented by 6 bits - the Scrypt key expands to 186 bits rather than the original 181 bits.

This expansion is because of the remainder.

181 bits divided by 6 is 30 characters plus 1 character
for the extra bit.

The 31 transported characters then appear as
31 times 6 which equals 186 bits.
186
SCRYPT_SALT_LENGTH =

The scrypt algorithm salt string should be 22 characters and may include forward slashes and periods.

22
SCRYPT_OUTPUT_TEXT_PREFIX =

Scrypt outputs a single line of text that holds the prefix then the Radix64 encoded salt and finally the Radix64 encoded hash key.

The prefix consists of two sections sandwiched within two dollar $ signs at the extremeties and a third dollar separating them.

The two sections are the

  • Scrypt algorithm version number (2a or 2b) and

  • a power of 2 integer defining the no. of interations

"$2a$#{SCRYPT_ITERATION_INTEGER}$"

Class Method Summary collapse

Class Method Details

.generate_key(secret_text, scrypt_salt) ⇒ Key

Key generators should first use the generate_salt method to create a Scrypt salt string and then submit it to this method together with a human generated password in order to derive a key.

The salt can be persisted and then resubmitted again to this method in order to regenerate the same key at any time in the future.

Generate a binary key from the scrypt password derivation function.

This differs from a server side password to hash usage in that we are interested in the 186bit key that scrypt produces. This method returns this reproducible key for use during symmetric encryption and decryption.

Parameters:

  • secret_text (String)

    a robust human generated password with as much entropy as can be mustered. Remember that 40 characters spread randomly over the key space of about 90 characters and not relating to any dictionary word or name is the way to generate a powerful key that has embedded a near 100% entropy rating.

  • scrypt_salt (String)

    the salt string that has either been recently generated via the generate_salt method or read from a persistence store and resubmitted here (in the future) to regenerate the same key.

Returns:

  • (Key)

    a key holder containing the key which can then be accessed via many different formats.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/keytools/kdf.scrypt.rb', line 124

def self.generate_key secret_text, scrypt_salt

  binary_salt = Key.to_binary_from_bit_string( scrypt_salt )

  puts ""
  puts $LOADED_FEATURES.grep(/openssl/)
  puts ""

  scrypt_key = OpenSSL::KDF.scrypt(secret_text, salt: binary_salt, N: 2**SCRYPT_ITERATION_INTEGER, r: 8, p: 1, length: 33)



=begin
  hashed_secret = Scrypt::Engine.hash_secret( secret_text, to_scrypt_salt(scrypt_salt) )
  encoded64_key = Scrypt::Password.new( hashed_secret ).to_s
  key_begin_index = SCRYPT_OUTPUT_TEXT_PREFIX.length + SCRYPT_SALT_LENGTH
  radix64_key_str = encoded64_key[ key_begin_index .. -1 ]
  key_length_mesg = "The scrypt key length should have #{SCRYPT_KEY_LENGTH} characters."
  raise RuntimeError, key_length_mesg unless radix64_key_str.length == SCRYPT_KEY_LENGTH

  return Key.new(radix64_key_str)
=end

  return scrypt_key
end

.generate_scrypt_saltString

Generate a secure random and unpredictable salt suitable for the SCrypt algorithm. SCrypt salts are recommended to contain 16 and 32 bytes inclusive. Here we opt for 24 bytes which unrolls to 192 bits which in turn is 32 base64 characters.

The SCRYPT_SALT_BYTE_LENGTH constant defines the number of random bytes required for a robust SCrypt salt.

The salt can be persisted and then resubmitted in order to regenerate the same key in the future.

Returns:

  • (String)

    the salt in a bit string format which can be converted to in order to feed the derivation function or indeed converted to base64 in order to persist it.



89
90
91
# File 'lib/keytools/kdf.scrypt.rb', line 89

def self.generate_scrypt_salt
  return Key.to_random_bits( SCRYPT_SALT_BYTE_LENGTH )
end