cryptdoh 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/cryptdoh.rb +100 -0
- 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:
|