Module: Rijn
- Defined in:
- lib/rijn/crypto.rb,
lib/rijn/errors.rb,
lib/rijn/version.rb
Defined Under Namespace
Classes: AuthenticationError, InvalidKeyError
Constant Summary collapse
- KEY_LENGTHS =
{ 128 => 16, 192 => 24, 256 => 32 }.freeze
- CIPHERS =
{ 16 => "aes-128-gcm", 24 => "aes-192-gcm", 32 => "aes-256-gcm" }.freeze
- NONCE_LENGTH =
Rijn uses a 12-byte nonce for AES-GCM.
12- AUTH_TAG_LENGTH =
Rijn stores a 16-byte authentication tag.
16- VERSION =
The current version of the Rijn library.
"0.4.0"
Class Method Summary collapse
- .cipher_for(key) ⇒ Object
- .decode_key(key) ⇒ Object
-
.decrypt(encrypted_value, key) ⇒ String
Decrypts a value encrypted with AES-GCM.
-
.encrypt(value, key) ⇒ String
Encrypts a value using AES-GCM.
-
.generate_key(bits = 256) ⇒ String
Generates a cryptographically secure AES-GCM encryption key.
- .validate_key!(key) ⇒ Object
Class Method Details
.cipher_for(key) ⇒ Object
113 114 115 |
# File 'lib/rijn/crypto.rb', line 113 def self.cipher_for(key) OpenSSL::Cipher.new(CIPHERS.fetch(key.bytesize)) end |
.decode_key(key) ⇒ Object
101 102 103 104 105 |
# File 'lib/rijn/crypto.rb', line 101 def self.decode_key(key) Base64.strict_decode64(key) rescue ArgumentError raise InvalidKeyError, "Key is not valid Base64." end |
.decrypt(encrypted_value, key) ⇒ String
Decrypts a value encrypted with AES-GCM.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/rijn/crypto.rb', line 69 def self.decrypt(encrypted_value, key) key = decode_key(key) validate_key!(key) # Rijn stores encrypted values as: # # nonce (12 bytes) + ciphertext + authentication tag (16 bytes) # # The nonce is not secret and is required for decryption. # The authentication tag is used by AES-GCM to verify the ciphertext. decoded = Base64.strict_decode64(encrypted_value) nonce = decoded.byteslice(0, NONCE_LENGTH) ciphertext = decoded.byteslice(NONCE_LENGTH...-AUTH_TAG_LENGTH) tag = decoded.byteslice(-AUTH_TAG_LENGTH, AUTH_TAG_LENGTH) cipher = cipher_for(key) cipher.decrypt cipher.key = key cipher.iv = nonce cipher.auth_tag = tag begin cipher.update(ciphertext) + cipher.final rescue OpenSSL::Cipher::CipherError raise AuthenticationError, "Unable to decrypt value." end end |
.encrypt(value, key) ⇒ String
Encrypts a value using AES-GCM.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/rijn/crypto.rb', line 43 def self.encrypt(value, key) key = decode_key(key) validate_key!(key) cipher = cipher_for(key) cipher.encrypt nonce = SecureRandom.random_bytes(NONCE_LENGTH) cipher.key = key cipher.iv = nonce ciphertext = cipher.update(value) + cipher.final tag = cipher.auth_tag Base64.strict_encode64(nonce + ciphertext + tag) end |
.generate_key(bits = 256) ⇒ String
Generates a cryptographically secure AES-GCM encryption key.
29 30 31 32 33 34 35 |
# File 'lib/rijn/crypto.rb', line 29 def self.generate_key(bits = 256) length = KEY_LENGTHS.fetch(bits) do raise InvalidKeyError, "Key must be 128, 192, or 256 bits." end Base64.strict_encode64(SecureRandom.random_bytes(length)) end |
.validate_key!(key) ⇒ Object
107 108 109 110 111 |
# File 'lib/rijn/crypto.rb', line 107 def self.validate_key!(key) unless KEY_LENGTHS.value?(key.bytesize) raise InvalidKeyError, "Key must be 128, 192, or 256 bits." end end |