# Copyright (c) 2016-2022 The Ruby-Eth Contributors## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# Provides the {Eth} module.moduleEth# The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.classKey::Encrypter# Provides a specific encrypter error if decryption fails.classEncrypterError<StandardError;end# Class method {Eth::Key::Encrypter.perform} to performa an key-store# encryption.## @param key [Eth::Key] representing a secret key-pair used for encryption.# @param options [Hash] the options to encrypt with.# @option options [String] :kdf key derivation function defaults to pbkdf2.# @option options [String] :id uuid given to the secret key.# @option options [String] :iterations number of iterations for the hash function.# @option options [String] :salt passed to PBKDF.# @option options [String] :iv 128-bit initialisation vector for the cipher.# @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.# @option options [Integer] :block_size for scrypt, defaults to 1.# @return [JSON] formatted with encrypted key (cyphertext) and [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256).defself.perform(key,password,options={})new(key,options).perform(password)end# Constructor of the {Eth::Key::Encrypter} class for secret key# encryption. Should not be used; use {Eth::Key::Encrypter.perform}# instead.## @param key [Eth::Key] representing a secret key-pair used for encryption.# @param options [Hash] the options to encrypt with.# @option options [String] :kdf key derivation function defaults to pbkdf2.# @option options [String] :id uuid given to the secret key.# @option options [String] :iterations number of iterations for the hash function.# @option options [String] :salt passed to PBKDF.# @option options [String] :iv 128-bit initialisation vector for the cipher.# @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.# @option options [Integer] :block_size for scrypt, defaults to 1.definitialize(key,options={})key=Key.new(priv: key)ifkey.is_a?String@key=key@options=options# the key derivation functions default to pbkdf2 if no option is specified# however, if an option is given then it must be either pbkdf2 or scryptifkdf!="scrypt"&&kdf!="pbkdf2"raiseEncrypterError,"Unsupported key derivation function: #{kdf}!"endend# Encrypt the key with a given password.## @param password [String] a secret key used for encryption# @return [String] a JSON-formatted keystore string.defperform(password)derive_keypasswordencryptdata.to_jsonend# Output containing the encrypted key and# [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256)## @return [Hash] the encrypted keystore data.defdata# default to pbkdf2kdfparams=ifkdf=="scrypt"{dklen: 32,n: iterations,p: parallelization,r: block_size,salt: Util.bin_to_hex(salt),}else{c: iterations,dklen: 32,prf: prf,salt: Util.bin_to_hex(salt),}end{crypto: {cipher: cipher_name,cipherparams: {iv: Util.bin_to_hex(iv),},ciphertext: Util.bin_to_hex(encrypted_key),kdf: kdf,kdfparams: kdfparams,mac: Util.bin_to_hex(mac),},id: id,version: 3,}endprivateattr_reader:derived_key,:encrypted_key,:key,:optionsdefcipher@cipher||=OpenSSL::Cipher.new(cipher_name).tapdo|cipher|cipher.encryptcipher.iv=ivcipher.key=derived_key[0,(key_length/2)]endenddefdigest@digest||=OpenSSL::Digest.newdigest_nameenddefderive_key(password)ifkdf=="scrypt"@derived_key=SCrypt::Engine.scrypt(password,salt,iterations,block_size,parallelization,key_length)else@derived_key=OpenSSL::PKCS5.pbkdf2_hmac(password,salt,iterations,key_length,digest)endenddefencrypt@encrypted_key=cipher.update(Util.hex_to_binkey.private_hex)+cipher.finalenddefmacUtil.keccak256(derived_key[(key_length/2),key_length]+encrypted_key)enddefkdfoptions[:kdf]||"pbkdf2"enddefcipher_name"aes-128-ctr"enddefdigest_name"sha256"enddefprf"hmac-#{digest_name}"enddefkey_length32enddefsalt_length32enddefiv_length16enddefid@id||=options[:id]||SecureRandom.uuidenddefiterationsoptions[:iterations]||262_144enddefsalt@salt||=ifoptions[:salt]Util.hex_to_binoptions[:salt]elseSecureRandom.random_bytes(salt_length)endenddefiv@iv||=ifoptions[:iv]Util.hex_to_binoptions[:iv]elseSecureRandom.random_bytes(iv_length)endenddefparallelizationoptions[:parallelization]||8enddefblock_sizeoptions[:block_size]||1endendend