require 'securerandom' module Legion module Crypt module Cipher def encrypt(message) cipher = OpenSSL::Cipher.new('aes-256-cbc') cipher.encrypt cipher.key = cs iv = cipher.random_iv { enciphered_message: Base64.encode64(cipher.update(message) + cipher.final), iv: Base64.encode64(iv) } end def decrypt(message, iv) until cs.is_a?(String) || Legion::Settings[:client][:shutting_down] Legion::Logging.debug('sleeping Legion::Crypt.decrypt due to CS not being set') sleep(0.5) end decipher = OpenSSL::Cipher.new('aes-256-cbc') decipher.decrypt decipher.key = cs decipher.iv = Base64.decode64(iv) message = Base64.decode64(message) decipher.update(message) + decipher.final end def encrypt_from_keypair(message:, pub_key: public_key) rsa_public_key = OpenSSL::PKey::RSA.new(pub_key) Base64.encode64(rsa_public_key.public_encrypt(message)) end def decrypt_from_keypair(message:, **_opts) private_key.private_decrypt(Base64.decode64(message)) end def public_key @public_key ||= private_key.public_key.to_s end def private_key @private_key ||= OpenSSL::PKey::RSA.new 2048 end def cs @cs ||= Digest::SHA256.digest fetch_cs end def fetch_cs # rubocop:disable Metrics/AbcSize if Legion::Settings[:crypt][:vault][:read_cluster_secret] && Legion::Settings[:crypt][:vault][:connected] && Legion::Crypt.exist?('crypt') # rubocop:disable Layout/LineLength Legion::Crypt.get('crypt')[:cluster_secret] elsif Legion::Settings[:crypt][:cluster_secret].is_a? String Legion::Settings[:crypt][:cluster_secret] elsif Legion::Transport::Queue.new('node.crypt', passive: true).consumer_count.zero? Legion::Settings[:crypt][:cluster_secret] = generate_secure_random elsif Legion::Transport::Queue.new('node.crypt', passive: true).consumer_count.positive? require 'legion/transport/messages/request_cluster_secret' Legion::Logging.info 'Requesting cluster secret via public key' start = Time.now Legion::Transport::Messages::RequestClusterSecret.new.publish sleep_time = 0.001 until !Legion::Settings[:crypt][:cluster_secret].nil? || (Time.now - start) > Legion::Settings[:crypt][:cluster_secret_timeout] sleep(sleep_time) sleep_time *= 2 unless sleep_time > 0.5 end unless Legion::Settings[:crypt][:cluster_secret].nil? Legion::Logging.info "Received cluster secret in #{((Time.new - start) * 1000.0).round}ms" end Legion::Logging.warn 'Cluster secret is still nil' if Legion::Settings[:crypt][:cluster_secret].nil? else Legion::Settings[:crypt][:cluster_secret] = generate_secure_random end Legion::Settings[:crypt][:cs_encrypt_ready] = true Legion::Settings[:crypt][:cluster_secret] rescue StandardError => e Legion::Logging.error(e.message) Legion::Logging.error(e.backtrace) Legion::Settings[:crypt][:cluster_secret] = generate_secure_random Legion::Settings[:crypt][:cs_encrypt_ready] = true Legion::Settings[:crypt][:cluster_secret] end def generate_secure_random SecureRandom.uuid end end end end