Module: Nvoi::Utils::Crypto
- Defined in:
- lib/nvoi/utils/crypto.rb
Overview
Crypto handles AES-256-GCM encryption/decryption
Constant Summary collapse
- KEY_SIZE =
256 bits
32- NONCE_SIZE =
GCM nonce size
12- KEY_HEX_LENGTH =
64 hex characters
KEY_SIZE * 2
Class Method Summary collapse
-
.decrypt(ciphertext, hex_key) ⇒ Object
Decrypt ciphertext using AES-256-GCM with the provided hex-encoded key Expects format: [12-byte nonce][16-byte auth tag].
-
.encrypt(plaintext, hex_key) ⇒ Object
Encrypt plaintext using AES-256-GCM with the provided hex-encoded key Returns: [12-byte nonce][16-byte auth tag].
-
.generate_key ⇒ Object
Generate a new random 32-byte key and return it as hex string.
-
.validate_key(hex_key) ⇒ Object
Validate a hex-encoded key.
Class Method Details
.decrypt(ciphertext, hex_key) ⇒ Object
Decrypt ciphertext using AES-256-GCM with the provided hex-encoded key Expects format: [12-byte nonce][16-byte auth tag]
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/nvoi/utils/crypto.rb', line 43 def decrypt(ciphertext, hex_key) key = decode_key(hex_key) min_size = NONCE_SIZE + 16 # nonce + auth tag if ciphertext.bytesize < min_size raise Errors::DecryptionError, "ciphertext too short: need at least #{min_size} bytes, got #{ciphertext.bytesize}" end # Extract nonce and auth tag nonce = ciphertext[0, NONCE_SIZE] auth_tag = ciphertext[-16, 16] encrypted_data = ciphertext[NONCE_SIZE...-16] cipher = OpenSSL::Cipher.new("aes-256-gcm") cipher.decrypt cipher.key = key cipher.iv = nonce cipher.auth_tag = auth_tag begin cipher.update(encrypted_data) + cipher.final rescue OpenSSL::Cipher::CipherError => e raise Errors::DecryptionError, "decryption failed (wrong key or corrupted data): #{e.}" end end |
.encrypt(plaintext, hex_key) ⇒ Object
Encrypt plaintext using AES-256-GCM with the provided hex-encoded key Returns: [12-byte nonce][16-byte auth tag]
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/nvoi/utils/crypto.rb', line 22 def encrypt(plaintext, hex_key) key = decode_key(hex_key) cipher = OpenSSL::Cipher.new("aes-256-gcm") cipher.encrypt cipher.key = key # Generate random nonce nonce = SecureRandom.random_bytes(NONCE_SIZE) cipher.iv = nonce # Encrypt ciphertext = cipher.update(plaintext) + cipher.final auth_tag = cipher.auth_tag # Return: nonce + ciphertext + auth_tag nonce + ciphertext + auth_tag end |
.generate_key ⇒ Object
Generate a new random 32-byte key and return it as hex string
16 17 18 |
# File 'lib/nvoi/utils/crypto.rb', line 16 def generate_key SecureRandom.hex(KEY_SIZE) end |
.validate_key(hex_key) ⇒ Object
Validate a hex-encoded key
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/nvoi/utils/crypto.rb', line 70 def validate_key(hex_key) unless hex_key.length == KEY_HEX_LENGTH raise Errors::InvalidKeyError, "invalid key length: expected #{KEY_HEX_LENGTH} hex characters, got #{hex_key.length}" end unless hex_key.match?(/\A[0-9a-fA-F]+\z/) raise Errors::InvalidKeyError, "invalid hex key: contains non-hex characters" end true end |