cryptdoh 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cryptdoh.rb +100 -0
  3. metadata +60 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15c9aee6732af55538874d25d581bd5da3cf6bc4
4
+ data.tar.gz: 2ce02dfb3f15742f778b9b1dccbf551629b557c5
5
+ SHA512:
6
+ metadata.gz: 4153032d11cbce77c85122e26e6737114028f60d6afd4c8efeaaf98cc387d37eedd0ec46a1f6454f779296cfbf37e9e556d724e0568686a7bdbf9e6351228377
7
+ data.tar.gz: f273763c41ff1ebefa309ba22eb13806d6f35cfd89b75f778284a2c7ff9b5ae65b453b60914ee2bb7920fc4c9671f8feb9d4a94d1e8f4fd6246e884bba3da209
data/lib/cryptdoh.rb ADDED
@@ -0,0 +1,100 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'securerandom'
4
+ require 'cracklib'
5
+
6
+ class UserError < StandardError
7
+ end
8
+
9
+ class EvilError < StandardError
10
+ end
11
+
12
+ module Cryptdoh
13
+
14
+ MIN_PASSWORD_LENGTH = 8
15
+ IV_LENGTH = 16
16
+ SALT_LENGTH = 16
17
+ ITERATIONS = 100 * 1000
18
+ KEY_LENGTH = 32
19
+ DIGEST = OpenSSL::Digest::SHA256.new
20
+ VERSION = "1"
21
+
22
+ def self.encrypt(password, message, args = {})
23
+ _check_password_length(password)
24
+ _check_password_strength(password) unless args[:skip_strength_check]
25
+
26
+ (salt, key) = _kdf(password)
27
+ cipher_key = key[0..KEY_LENGTH-1]
28
+ hmac_key = key[KEY_LENGTH..-1]
29
+
30
+ cipher = OpenSSL::Cipher::AES.new(KEY_LENGTH * 8, :CBC)
31
+ cipher.encrypt
32
+ iv = cipher.random_iv
33
+ cipher.key = cipher_key
34
+
35
+ ciphertext = cipher.update(message) + cipher.final
36
+
37
+ cipher_message = [VERSION, _encode(iv), _encode(salt), _encode(ciphertext)].join('.')
38
+ hmac = _hmac(hmac_key, cipher_message)
39
+
40
+ [cipher_message, _encode(hmac)].join('.')
41
+ end
42
+
43
+ def self.decrypt(password, message)
44
+ (version, encoded_iv, encoded_salt, encoded_ciphertext, encoded_hmac) = message.split('.')
45
+
46
+ (salt, key) = _kdf(password, _decode(encoded_salt))
47
+ cipher_key = key[0..KEY_LENGTH-1]
48
+ hmac_key = key[KEY_LENGTH..-1]
49
+
50
+ hmac = _hmac(hmac_key, [version, encoded_iv, encoded_salt, encoded_ciphertext].join('.'))
51
+ raise EvilError, 'Invalid HMAC' unless _decode(encoded_hmac) == hmac
52
+
53
+ decipher = OpenSSL::Cipher::AES.new(KEY_LENGTH * 8, :CBC)
54
+ decipher.decrypt
55
+ decipher.iv = _decode(encoded_iv)
56
+ decipher.key = cipher_key
57
+
58
+ decipher.update(_decode(encoded_ciphertext)) + decipher.final
59
+ end
60
+
61
+ def self._check_password_strength(password)
62
+ c = CrackLib::Fascist(password)
63
+ raise UserError, "Crappy password: #{c.reason}" unless c.ok?
64
+ end
65
+
66
+ def self._check_password_length(password)
67
+ raise UserError, "Crappy password: too short. Must be at least 8 bytes" unless password.size >= MIN_PASSWORD_LENGTH
68
+ end
69
+
70
+ def self._kdf(password, salt = nil)
71
+ salt ||= SecureRandom.random_bytes(SALT_LENGTH)
72
+ raise UserError, "Salt is the wrong size" unless salt.size == SALT_LENGTH
73
+ key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, ITERATIONS, KEY_LENGTH * 2, DIGEST)
74
+ [salt, key]
75
+ end
76
+
77
+ def self._hmac(key, message)
78
+ # Only require 128 bits of security, so cut in half
79
+ OpenSSL::HMAC.digest(DIGEST, key, message)[0..15]
80
+ end
81
+
82
+ def self._encode(data)
83
+ Base64.encode64(data).chomp
84
+ end
85
+
86
+ def self._decode(data)
87
+ Base64.decode64(data)
88
+ rescue
89
+ raise EvilError, 'Bad base64 data'
90
+ end
91
+
92
+ def self._verify
93
+ message = 'this is a secret message'
94
+ password = 'dZ]av}a]i4qK2:1Z:t |Ju.'
95
+
96
+ decrypt(password, encrypt(password, message)) == message
97
+ rescue
98
+ false
99
+ end
100
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptdoh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fraser Scott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-cracklib
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ description: |2
28
+ Most crypto libraries require the user to make significant usage decisions. Without understanding the concepts behind all the options, it is easy for the users to pick something inappropriate, resulting in insecure systems. Also, libraries often allow silly defaults, such as an IV set to all 0s or forgetting a salt etc. This library enforces best-practices, so if you need more control you should use a lower level library.
29
+ email: fraser.scott@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/cryptdoh.rb
35
+ homepage: https://github.com/zeroXten/cryptdoh
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.2.2
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: An easy to use, secure, and opinionated encryption wrapper library for Ruby.
59
+ test_files: []
60
+ has_rdoc: