Module: Cryptdoh
- Defined in:
- lib/cryptdoh.rb
Constant Summary collapse
- MIN_PASSWORD_LENGTH =
8
- IV_LENGTH =
16
- SALT_LENGTH =
16
- ITERATIONS =
100 * 1000
- KEY_LENGTH =
32
- DIGEST =
OpenSSL::Digest::SHA256.new
- VERSION =
"1"
Class Method Summary collapse
- ._check_password_length(password) ⇒ Object
- ._check_password_strength(password) ⇒ Object
- ._decode(data) ⇒ Object
- ._encode(data) ⇒ Object
- ._hmac(key, message) ⇒ Object
- ._kdf(password, salt = nil) ⇒ Object
- ._verify ⇒ Object
- .decrypt(password, message) ⇒ Object
- .encrypt(password, message, args = {}) ⇒ Object
Class Method Details
._check_password_length(password) ⇒ Object
66 67 68 |
# File 'lib/cryptdoh.rb', line 66 def self._check_password_length(password) raise UserError, "Crappy password: too short. Must be at least 8 bytes" unless password.size >= MIN_PASSWORD_LENGTH end |
._check_password_strength(password) ⇒ Object
61 62 63 64 |
# File 'lib/cryptdoh.rb', line 61 def self._check_password_strength(password) c = CrackLib::Fascist(password) raise UserError, "Crappy password: #{c.reason}" unless c.ok? end |
._decode(data) ⇒ Object
86 87 88 89 90 |
# File 'lib/cryptdoh.rb', line 86 def self._decode(data) Base64.decode64(data) rescue raise EvilError, 'Bad base64 data' end |
._encode(data) ⇒ Object
82 83 84 |
# File 'lib/cryptdoh.rb', line 82 def self._encode(data) Base64.encode64(data).chomp end |
._hmac(key, message) ⇒ Object
77 78 79 80 |
# File 'lib/cryptdoh.rb', line 77 def self._hmac(key, ) # Only require 128 bits of security, so cut in half OpenSSL::HMAC.digest(DIGEST, key, )[0..15] end |
._kdf(password, salt = nil) ⇒ Object
70 71 72 73 74 75 |
# File 'lib/cryptdoh.rb', line 70 def self._kdf(password, salt = nil) salt ||= SecureRandom.random_bytes(SALT_LENGTH) raise UserError, "Salt is the wrong size" unless salt.size == SALT_LENGTH key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, ITERATIONS, KEY_LENGTH * 2, DIGEST) [salt, key] end |
._verify ⇒ Object
92 93 94 95 96 97 98 99 |
# File 'lib/cryptdoh.rb', line 92 def self._verify = 'this is a secret message' password = 'dZ]av}a]i4qK2:1Z:t |Ju.' decrypt(password, encrypt(password, )) == rescue false end |
.decrypt(password, message) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/cryptdoh.rb', line 43 def self.decrypt(password, ) (version, encoded_iv, encoded_salt, encoded_ciphertext, encoded_hmac) = .split('.') (salt, key) = _kdf(password, _decode(encoded_salt)) cipher_key = key[0..KEY_LENGTH-1] hmac_key = key[KEY_LENGTH..-1] hmac = _hmac(hmac_key, [version, encoded_iv, encoded_salt, encoded_ciphertext].join('.')) raise EvilError, 'Invalid HMAC' unless _decode(encoded_hmac) == hmac decipher = OpenSSL::Cipher::AES.new(KEY_LENGTH * 8, :CBC) decipher.decrypt decipher.iv = _decode(encoded_iv) decipher.key = cipher_key decipher.update(_decode(encoded_ciphertext)) + decipher.final end |
.encrypt(password, message, args = {}) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/cryptdoh.rb', line 22 def self.encrypt(password, , args = {}) _check_password_length(password) _check_password_strength(password) unless args[:skip_strength_check] (salt, key) = _kdf(password) cipher_key = key[0..KEY_LENGTH-1] hmac_key = key[KEY_LENGTH..-1] cipher = OpenSSL::Cipher::AES.new(KEY_LENGTH * 8, :CBC) cipher.encrypt iv = cipher.random_iv cipher.key = cipher_key ciphertext = cipher.update() + cipher.final = [VERSION, _encode(iv), _encode(salt), _encode(ciphertext)].join('.') hmac = _hmac(hmac_key, ) [, _encode(hmac)].join('.') end |