#!/usr/bin/env ruby # frozen_string_literal: true require 'openssl' require 'base64' require 'json' class SecretBox ALGORITHM = 'aes-256-gcm'.freeze def initialize(master_key_content) # Generar una clave de 32 bytes usando SHA256 sobre el contenido del master_key @key = Digest::SHA256.digest(master_key_content.strip) end def encrypt(plaintext) cipher = OpenSSL::Cipher.new(ALGORITHM) cipher.encrypt cipher.key = @key iv = cipher.random_iv ciphertext = cipher.update(plaintext.to_s) + cipher.final auth_tag = cipher.auth_tag # Base64 encodear las 3 partes para un JSON payload = { iv: Base64.strict_encode64(iv), tag: Base64.strict_encode64(auth_tag), data: Base64.strict_encode64(ciphertext) } Base64.strict_encode64(payload.to_json) end def decrypt(encrypted_payload) parsed_payload = JSON.parse(Base64.strict_decode64(encrypted_payload), symbolize_names: true) cipher = OpenSSL::Cipher.new(ALGORITHM) cipher.decrypt cipher.key = @key cipher.iv = Base64.strict_decode64(parsed_payload[:iv]) cipher.auth_tag = Base64.strict_decode64(parsed_payload[:tag]) cipher.update(Base64.strict_decode64(parsed_payload[:data])) + cipher.final rescue => e raise "Error al desencriptar. Clave incorrecta o payload dañado. Detalles: #{e.message}" end end if __FILE__ == $0 action = ARGV[0] unless ['encrypt', 'decrypt'].include?(action) puts "Uso:" puts " ruby #{$0} encrypt " puts " ruby #{$0} decrypt " exit 1 end text = ARGV[1] if text.nil? || text.empty? puts "Por favor, proporciona el texto a #{action}." puts "Ejemplo:" puts " ruby #{$0} encrypt \"MiSuperPassword\"" exit 1 end # Buscar el archivo .master.key en el root del proyecto key_path = File.join(File.expand_path('..', __dir__), '.master.key') unless File.exist?(key_path) require 'securerandom' File.write(key_path, SecureRandom.hex(32)) puts "⚠️ ATENCIÓN: Se ha creado una nueva clave maestra en '#{key_path}'" puts " Asegúrate de NO subir este archivo a ningún repositorio (ya está cubierto por **/*.key en .gitignore)." puts " Cualquiera que tenga este archivo podrá desencriptar los textos.\n\n" end box = SecretBox.new(File.read(key_path)) begin if action == 'encrypt' puts box.encrypt(text) else puts box.decrypt(text) end rescue => e puts "Error: #{e.message}" exit 1 end end