Module: GoSecure
- Defined in:
- lib/go_secure.rb
Defined Under Namespace
Modules: SecureJson
Class Method Summary collapse
- .browser_token ⇒ Object
- .decrypt(str, salt, ref, encryption_key = nil) ⇒ Object
- .encrypt(str, ref, encryption_key = nil) ⇒ Object
- .encryption_key ⇒ Object
- .generate_password(password) ⇒ Object
- .hmac(str, salt, level, encryption_key = nil) ⇒ Object
- .matches_password?(attempt, password_hash) ⇒ Boolean
- .nonce(str) ⇒ Object
- .outdated_password?(password_hash) ⇒ Boolean
- .sha512(str, salt, encryption_key = nil) ⇒ Object
- .valid_browser_token?(token) ⇒ Boolean
- .valid_browser_token_signature?(token) ⇒ Boolean
- .validate_encryption_key ⇒ Object
Class Method Details
.browser_token ⇒ Object
110 111 112 113 114 |
# File 'lib/go_secure.rb', line 110 def self.browser_token # TODO: checks around whether it's actually a web browser?? stamp = Time.now.strftime('%Y%j') stamp += '-' + GoSecure.sha512(stamp, 'browser_token') end |
.decrypt(str, salt, ref, encryption_key = nil) ⇒ Object
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/go_secure.rb', line 33 def self.decrypt(str, salt, ref, encryption_key=nil) require 'base64' c = OpenSSL::Cipher::Cipher.new('aes-256-cbc') c.decrypt c.key = Digest::SHA2.hexdigest(ref + "_" + (encryption_key || self.encryption_key)) c.iv = Base64.decode64(salt) d = c.update(Base64.decode64(str)) d << c.final d.to_s end |
.encrypt(str, ref, encryption_key = nil) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/go_secure.rb', line 21 def self.encrypt(str, ref, encryption_key=nil) require 'base64' c = OpenSSL::Cipher::Cipher.new('aes-256-cbc') c.encrypt c.key = Digest::SHA2.hexdigest(ref + "_" + (encryption_key || self.encryption_key)) c.iv = iv = c.random_iv e = c.update(str) e << c.final res = [Base64.encode64(e), Base64.encode64(iv)] res end |
.encryption_key ⇒ Object
106 107 108 |
# File 'lib/go_secure.rb', line 106 def self.encryption_key ENV['SECURE_ENCRYPTION_KEY'] end |
.generate_password(password) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/go_secure.rb', line 44 def self.generate_password(password) raise "password required" if password == nil || password.length == 0 pw = {} # pw['hash_type'] = 'sha512' # pw['hash_type'] = 'bcrypt' pw['hash_type'] = 'pbkdf2-sha256-2' pw['salt'] = Digest::MD5.hexdigest(OpenSSL::Random.pseudo_bytes(4) + Time.now.to_i.to_s + self.encryption_key + "pw" + OpenSSL::Random.pseudo_bytes(16)) # pw['hashed_password'] = Digest::SHA512.hexdigest(self.encryption_key + pw['salt'] + password.to_s) # salted = Digest::SHA256.hexdigest(self.encryption_key + pw['salt'] + password.to_s) # pw['hashed_password'] = BCrypt::Password.create(salted) digest = OpenSSL::Digest::SHA512.new(self.encryption_key) pw['hashed_password'] = Base64.urlsafe_encode64(OpenSSL::PKCS5.pbkdf2_hmac(password.to_s, pw['salt'], 100000, digest.digest_length, digest)) pw end |
.hmac(str, salt, level, encryption_key = nil) ⇒ Object
10 11 12 13 14 15 |
# File 'lib/go_secure.rb', line 10 def self.hmac(str, salt, level, encryption_key=nil) # level is here so we can upgrade in the future without breaking backwards compatibility raise "invalid level" unless level == 1 digest = OpenSSL::Digest::SHA512.new(encryption_key || self.encryption_key) res = Base64.urlsafe_encode64(OpenSSL::PKCS5.pbkdf2_hmac(str.to_s, salt.to_s, 100000, digest.digest_length, digest)) end |
.matches_password?(attempt, password_hash) ⇒ Boolean
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/go_secure.rb', line 63 def self.matches_password?(attempt, password_hash) if password_hash && password_hash['hash_type'] == 'sha512' && password_hash['salt'] str = Digest::SHA512.hexdigest(self.encryption_key + password_hash['salt'] + attempt.to_s) res = str == password_hash['hashed_password'] if !res && password_hash['old_passwords'] # TODO: support for migrating to new hashing algorithms else res end elsif password_hash && password_hash['hash_type'] == 'bcrypt' && password_hash['salt'] pw = BCrypt::Password.new(password_hash['hashed_password']) salted = Digest::SHA256.hexdigest(self.encryption_key + password_hash['salt'] + attempt.to_s) res = pw == salted elsif password_hash && password_hash['hash_type'] == 'pbkdf2-sha256' && password_hash['salt'] digest = OpenSSL::Digest::SHA256.new str = Base64.encode64(OpenSSL::PKCS5.pbkdf2_hmac(attempt.to_s, password_hash['salt'], 100000, digest.digest_length, digest)) res = str == password_hash['hashed_password'] elsif password_hash && password_hash['hash_type'] == 'pbkdf2-sha256-2' && password_hash['salt'] digest = OpenSSL::Digest::SHA512.new(self.encryption_key) str = Base64.urlsafe_encode64(OpenSSL::PKCS5.pbkdf2_hmac(attempt.to_s, password_hash['salt'], 100000, digest.digest_length, digest)) res = str == password_hash['hashed_password'] else false end end |
.nonce(str) ⇒ Object
17 18 19 |
# File 'lib/go_secure.rb', line 17 def self.nonce(str) Digest::SHA512.hexdigest(str.to_s + Time.now.to_i.to_s + rand(999999).to_s + self.encryption_key)[0, 24] end |
.outdated_password?(password_hash) ⇒ Boolean
59 60 61 |
# File 'lib/go_secure.rb', line 59 def self.outdated_password?(password_hash) return password_hash && password_hash['hash_type'] != 'pbkdf2-sha256-2' end |
.sha512(str, salt, encryption_key = nil) ⇒ Object
6 7 8 |
# File 'lib/go_secure.rb', line 6 def self.sha512(str, salt, encryption_key=nil) Digest::SHA512.hexdigest(str.to_s + salt.to_s + (encryption_key || self.encryption_key)) end |
.valid_browser_token?(token) ⇒ Boolean
121 122 123 124 125 126 127 128 |
# File 'lib/go_secure.rb', line 121 def self.valid_browser_token?(token) return false if !token || token.length == 0 || !token.match(/-/) stamp, hash = token.split(/-/, 2) if Time.now.strftime('%Y%j').to_i - stamp.to_i < 14 # 14 days?! return valid_browser_token_signature?(token) end false end |
.valid_browser_token_signature?(token) ⇒ Boolean
116 117 118 119 |
# File 'lib/go_secure.rb', line 116 def self.valid_browser_token_signature?(token) stamp, hash = token.split(/-/, 2) return hash == GoSecure.sha512(stamp, 'browser_token') end |
.validate_encryption_key ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/go_secure.rb', line 89 def self.validate_encryption_key if !self.encryption_key || self.encryption_key.length < 24 raise "SECURE_ENCRYPTION_KEY env variable should be at least 24 characters" end return if !ActiveRecord::Base.connection.data_source_exists?('settings') config_hash = Digest::SHA1.hexdigest(self.encryption_key) stored_hash = Setting.get('encryption_hash') return if stored_hash == config_hash if stored_hash.nil? Setting.set('encryption_hash', config_hash); else raise "SECURE_ENCRYPTION_KEY env variable doesn't match the value stored in the database." + " If this is intentional you can try DELETE FROM settings WHERE key='encryption_hash' to reset." end end |