Module: Age
- Defined in:
- lib/age.rb,
lib/age/errors.rb,
lib/age/version.rb,
lib/age/bindings.rb
Overview
Ruby bindings for [age](github.com/FiloSottile/age) using a CGO shared library with FFI bindings.
Age is a simple, modern, and secure file encryption tool, format, and Go library. This gem provides a Ruby interface to age’s encryption and decryption capabilities.
Features:
-
Encrypt and decrypt data using age public/private key pairs
-
Encrypt and decrypt files directly
-
Generate age keypairs programmatically
-
Multiple recipients support for encryption
-
ASCII armor format support for text-safe encrypted output
-
FFI-based integration with Go’s age implementation
-
Binary data handling with proper encoding
Defined Under Namespace
Modules: Bindings Classes: DecryptionError, EncryptionError, GenerateKeyPairError
Constant Summary collapse
- VERSION =
'0.1.0'
Class Method Summary collapse
-
.decrypt(privkeys, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided age private keys.
-
.decrypt_file(privkeys, infile, outfile = nil) ⇒ void
Decrypts a file using the provided age private keys.
-
.decrypt_file_with_passphrase(passphrase, infile, outfile = nil) ⇒ void
Decrypts a file using the provided passphrase.
-
.decrypt_file_with_ssh_keys(ssh_privkeys, infile, outfile = nil) ⇒ void
Decrypts a file using the provided SSH private keys.
-
.decrypt_with_passphrase(passphrase, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided passphrase.
-
.decrypt_with_ssh_keys(ssh_privkeys, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided SSH private keys.
-
.detect_armor_format?(encrypted) ⇒ Boolean
Detects if the encrypted data is in armor format.
-
.encrypt(pubkeys, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided age public keys.
-
.encrypt_file(pubkeys, infile, outfile = nil, armor: false) ⇒ void
Encrypts a file using the provided age public keys.
-
.encrypt_file_with_passphrase(passphrase, infile, outfile = nil, armor: false) ⇒ void
Encrypts a file using the provided passphrase.
-
.encrypt_file_with_ssh_keys(ssh_pubkeys, infile, outfile = nil, armor: false) ⇒ void
Encrypts a file using the provided SSH public keys.
-
.encrypt_with_passphrase(passphrase, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided passphrase.
-
.encrypt_with_ssh_keys(ssh_pubkeys, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided SSH public keys.
-
.generate_keypair(postquantum: false) ⇒ Hash{Symbol => String}
Generates a new age key pair.
-
.perform_decryption(encrypted) {|input, output| ... } ⇒ Bytes
Performs decryption operation with common FFI setup.
-
.perform_encryption(plain) {|input, output| ... } ⇒ Bytes
Performs encryption operation with common FFI setup.
-
.perform_file_decryption(infile, outfile) {|encrypted, armor| ... } ⇒ void
Performs file decryption with common file handling and armor detection.
-
.perform_file_encryption(infile, outfile) {|plain| ... } ⇒ void
Performs file encryption with common file handling.
-
.read_bytes_from_pointer(ptr, length) ⇒ String?
Reads bytes from a pointer and frees the memory.
-
.read_string_from_pointer(ptr) ⇒ String?
Reads a string from a pointer and frees the memory.
Class Method Details
.decrypt(privkeys, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided age private keys.
79 80 81 82 83 84 |
# File 'lib/age.rb', line 79 def decrypt(privkeys, encrypted, armor: false) privkeys = privkeys.join(',') if privkeys.is_a?(Array) perform_decryption(encrypted) do |input, output| Age::Bindings.decrypt(privkeys, input, output, armor ? 1 : 0) end end |
.decrypt_file(privkeys, infile, outfile = nil) ⇒ void
This method returns an undefined value.
Decrypts a file using the provided age private keys.
166 167 168 169 170 |
# File 'lib/age.rb', line 166 def decrypt_file(privkeys, infile, outfile = nil) perform_file_decryption(infile, outfile) do |encrypted, armor| decrypt(privkeys, encrypted, armor:) end end |
.decrypt_file_with_passphrase(passphrase, infile, outfile = nil) ⇒ void
This method returns an undefined value.
Decrypts a file using the provided passphrase.
180 181 182 183 184 |
# File 'lib/age.rb', line 180 def decrypt_file_with_passphrase(passphrase, infile, outfile = nil) perform_file_decryption(infile, outfile) do |encrypted, armor| decrypt_with_passphrase(passphrase, encrypted, armor:) end end |
.decrypt_file_with_ssh_keys(ssh_privkeys, infile, outfile = nil) ⇒ void
This method returns an undefined value.
Decrypts a file using the provided SSH private keys.
194 195 196 197 198 |
# File 'lib/age.rb', line 194 def decrypt_file_with_ssh_keys(ssh_privkeys, infile, outfile = nil) perform_file_decryption(infile, outfile) do |encrypted, armor| decrypt_with_ssh_keys(ssh_privkeys, encrypted, armor:) end end |
.decrypt_with_passphrase(passphrase, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided passphrase.
94 95 96 97 98 |
# File 'lib/age.rb', line 94 def decrypt_with_passphrase(passphrase, encrypted, armor: false) perform_decryption(encrypted) do |input, output| Age::Bindings.decrypt_with_passphrase(passphrase, input, output, armor ? 1 : 0) end end |
.decrypt_with_ssh_keys(ssh_privkeys, encrypted, armor: false) ⇒ Bytes
Decrypts encrypted data using the provided SSH private keys.
108 109 110 111 112 113 |
# File 'lib/age.rb', line 108 def decrypt_with_ssh_keys(ssh_privkeys, encrypted, armor: false) ssh_privkeys = ssh_privkeys.join(',') if ssh_privkeys.is_a?(Array) perform_decryption(encrypted) do |input, output| Age::Bindings.decrypt_with_ssh_keys(ssh_privkeys, input, output, armor ? 1 : 0) end end |
.detect_armor_format?(encrypted) ⇒ Boolean
Detects if the encrypted data is in armor format.
330 331 332 333 |
# File 'lib/age.rb', line 330 def detect_armor_format?(encrypted) encrypted.start_with?('-----BEGIN AGE ENCRYPTED FILE-----') && encrypted.end_with?("-----END AGE ENCRYPTED FILE-----\n") end |
.encrypt(pubkeys, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided age public keys.
36 37 38 39 40 41 |
# File 'lib/age.rb', line 36 def encrypt(pubkeys, plain, armor: false) pubkeys = pubkeys.join(',') if pubkeys.is_a?(Array) perform_encryption(plain) do |input, output| Age::Bindings.encrypt(pubkeys, input, output, armor ? 1 : 0) end end |
.encrypt_file(pubkeys, infile, outfile = nil, armor: false) ⇒ void
This method returns an undefined value.
Encrypts a file using the provided age public keys.
123 124 125 126 127 |
# File 'lib/age.rb', line 123 def encrypt_file(pubkeys, infile, outfile = nil, armor: false) perform_file_encryption(infile, outfile) do |plain| encrypt(pubkeys, plain, armor:) end end |
.encrypt_file_with_passphrase(passphrase, infile, outfile = nil, armor: false) ⇒ void
This method returns an undefined value.
Encrypts a file using the provided passphrase.
138 139 140 141 142 |
# File 'lib/age.rb', line 138 def encrypt_file_with_passphrase(passphrase, infile, outfile = nil, armor: false) perform_file_encryption(infile, outfile) do |plain| encrypt_with_passphrase(passphrase, plain, armor:) end end |
.encrypt_file_with_ssh_keys(ssh_pubkeys, infile, outfile = nil, armor: false) ⇒ void
This method returns an undefined value.
Encrypts a file using the provided SSH public keys.
153 154 155 156 157 |
# File 'lib/age.rb', line 153 def encrypt_file_with_ssh_keys(ssh_pubkeys, infile, outfile = nil, armor: false) perform_file_encryption(infile, outfile) do |plain| encrypt_with_ssh_keys(ssh_pubkeys, plain, armor:) end end |
.encrypt_with_passphrase(passphrase, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided passphrase.
50 51 52 53 54 |
# File 'lib/age.rb', line 50 def encrypt_with_passphrase(passphrase, plain, armor: false) perform_encryption(plain) do |input, output| Age::Bindings.encrypt_with_passphrase(passphrase, input, output, armor ? 1 : 0) end end |
.encrypt_with_ssh_keys(ssh_pubkeys, plain, armor: false) ⇒ Bytes
Encrypts plain data using the provided SSH public keys.
64 65 66 67 68 69 |
# File 'lib/age.rb', line 64 def encrypt_with_ssh_keys(ssh_pubkeys, plain, armor: false) ssh_pubkeys = ssh_pubkeys.join(',') if ssh_pubkeys.is_a?(Array) perform_encryption(plain) do |input, output| Age::Bindings.encrypt_with_ssh_keys(ssh_pubkeys, input, output, armor ? 1 : 0) end end |
.generate_keypair(postquantum: false) ⇒ Hash{Symbol => String}
Generates a new age key pair.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/age.rb', line 206 def generate_keypair(postquantum: false) pubkey_ptr = FFI::MemoryPointer.new(:pointer) privkey_ptr = FFI::MemoryPointer.new(:pointer) keypair = Age::Bindings::AgeKeyPair.new keypair[:public_key] = pubkey_ptr keypair[:private_key] = privkey_ptr err_ptr = Age::Bindings.generate_keypair(keypair, postquantum ? 1 : 0) unless err_ptr.null? err_msg = read_string_from_pointer(err_ptr) raise GenerateKeyPairError, err_msg end pubkey = read_string_from_pointer(pubkey_ptr.read_pointer) privkey = read_string_from_pointer(privkey_ptr.read_pointer) { public_key: pubkey, private_key: privkey } end |
.perform_decryption(encrypted) {|input, output| ... } ⇒ Bytes
Performs decryption operation with common FFI setup.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/age.rb', line 266 def perform_decryption(encrypted) encrypted = encrypted.b if encrypted.respond_to?(:b) encrypted_ptr = FFI::MemoryPointer.new(:char, encrypted.bytesize) encrypted_ptr.put_bytes(0, encrypted) input = Age::Bindings::AgeInput.new input[:data] = encrypted_ptr input[:length] = encrypted.bytesize plain_ptr = FFI::MemoryPointer.new(:pointer) plain_len_ptr = FFI::MemoryPointer.new(:int) output = Age::Bindings::AgeOutput.new output[:data] = plain_ptr output[:length] = plain_len_ptr err_ptr = yield(input, output) unless err_ptr.null? err_msg = read_string_from_pointer(err_ptr) raise DecryptionError, err_msg end bytes = read_bytes_from_pointer(plain_ptr.read_pointer, plain_len_ptr.read_int) bytes.force_encoding('BINARY') end |
.perform_encryption(plain) {|input, output| ... } ⇒ Bytes
Performs encryption operation with common FFI setup.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/age.rb', line 232 def perform_encryption(plain) plain = plain.b if plain.respond_to?(:b) plain_ptr = FFI::MemoryPointer.new(:char, plain.bytesize) plain_ptr.put_bytes(0, plain) input = Age::Bindings::AgeInput.new input[:data] = plain_ptr input[:length] = plain.bytesize encrypted_ptr = FFI::MemoryPointer.new(:pointer) encrypted_len_ptr = FFI::MemoryPointer.new(:int) output = Age::Bindings::AgeOutput.new output[:data] = encrypted_ptr output[:length] = encrypted_len_ptr err_ptr = yield(input, output) unless err_ptr.null? err_msg = read_string_from_pointer(err_ptr) raise EncryptionError, err_msg end bytes = read_bytes_from_pointer(encrypted_ptr.read_pointer, encrypted_len_ptr.read_int) bytes.force_encoding('BINARY') end |
.perform_file_decryption(infile, outfile) {|encrypted, armor| ... } ⇒ void
This method returns an undefined value.
Performs file decryption with common file handling and armor detection.
316 317 318 319 320 321 322 |
# File 'lib/age.rb', line 316 def perform_file_decryption(infile, outfile) outfile ||= File.basename(infile, '.*') encrypted = File.binread(infile) armor = detect_armor_format?(encrypted) plain = yield(encrypted, armor) File.binwrite(outfile, plain) end |
.perform_file_encryption(infile, outfile) {|plain| ... } ⇒ void
This method returns an undefined value.
Performs file encryption with common file handling.
301 302 303 304 305 306 |
# File 'lib/age.rb', line 301 def perform_file_encryption(infile, outfile) outfile ||= "#{infile}.age" plain = File.binread(infile) encrypted = yield(plain) File.binwrite(outfile, encrypted) end |
.read_bytes_from_pointer(ptr, length) ⇒ String?
Reads bytes from a pointer and frees the memory.
357 358 359 360 361 362 363 364 |
# File 'lib/age.rb', line 357 def read_bytes_from_pointer(ptr, length) return nil if ptr.null? || length.zero? bytes = ptr.read_bytes(length) Age::Bindings.free_memory(ptr) bytes end |