easy-crypto 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4ad3e00c48d1045ceb274232305aff4a182022395bbe2b610e2b348cf10e18b
4
- data.tar.gz: 00adb629016b33ba4a3f905488abdbf9de0f44b4cf53a43b4d76d18d7f9d8e73
3
+ metadata.gz: badbe38cfba80b77b62dd33031021281163ca05fb1a59965e5a3a0e9cffea971
4
+ data.tar.gz: a6c9097921116ce09bca54e24acd7394ec7311448d3918f6343234ac721e8008
5
5
  SHA512:
6
- metadata.gz: 0b7d604a6a3f4b50f3c4f15d687e2c3833e5116e85b4989b1c91ee0f527e8a897596078c7fb84e77c464f68f3cfbd8a44be698a399bb7a234618c361a34aae71
7
- data.tar.gz: 96da1fe387f964f5a1d7a15ea3ef8ae1e81f2544a9a2a9a3c50922473c8acf23fb163bccad7e4670ecbb41b7e6181cdfcdbd2d587c580dcf991f39903c6987ca
6
+ metadata.gz: 3a56d205ecd4541735b72b040535d351c06710f745b30582ceed565452e52c33decc0be90db307a0d37cc83d77d0c1f4f2b98cdd86542f2c3dd8849df22a094c
7
+ data.tar.gz: 3ead7d75b180867defe24c13e200b385c7dd7abcda1b701a60595525d5360611ea00f9d6bb99a1c0f1ed47f3078f075dbaed8ad9453ddf56e09a5e44db35d3ad
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- easy-crypto (0.0.3)
4
+ easy-crypto (0.1.0)
5
5
  openssl (~> 2.1.1, >= 2.1.1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # EasyCrypto [![Build Status](https://travis-ci.org/emartech/ruby-easy-crypto.svg?branch=master)](https://travis-ci.org/emartech/ruby-easy-crypto) [![Gem Version](https://badge.fury.io/rb/easy-crypto.svg)](https://badge.fury.io/rb/easy-crypto)
2
2
 
3
- Provides simple wrappers around the openssl crypto implementation.
3
+ Provides simple wrappers around the openssl crypto implementation. The library provides two interfaces: simple and advanced. Simple mode is designed for ease-of-use and advanced mode provides some performance benefits in certain use-cases. See below for more details.
4
+
5
+ All the underlying crypto operations are the same.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,20 +20,42 @@ Or install it yourself as:
18
20
 
19
21
  $ gem install easy-crypto
20
22
 
21
- ## Usage
23
+ ## Simple usage (Recommended)
24
+
25
+ ```ruby
26
+ require 'easycrypto'
27
+
28
+ password = 'secret password'
29
+ plaintext = 'some data'
30
+
31
+ ecrypto = EasyCrypto::Crypto.new
32
+
33
+ encrypted = ecrypto.encrypt(password, plaintext)
34
+ decrypted = ecrypto.encrypt(password, encrypted)
35
+
36
+ decrypted == plaintext
37
+ ```
38
+
39
+ ## Advanced usage (Use for performance)
22
40
 
23
- ### Encrypt with previously derived key
41
+ [Key derivation](https://en.wikipedia.org/wiki/Key_derivation_function) is a resource heavy process. The simple interface abstracts this away and forces you to recompute the key before each encryption/decryption process.
42
+
43
+ This interface allows you to cache the result of the key derivation. This is required if you need to encrypt/decrypt multiple times with the same derived key. Caching the key saves you the time to have to recompute it before every encryption/decryption.
24
44
 
25
45
  ```ruby
26
46
  require 'easycrypto'
27
47
 
28
- key_password = 'secret password'
29
- plain_text = 'data to encrypt ...'
48
+ password = 'secret password'
49
+ plaintext = 'data to encrypt ...'
30
50
 
31
51
  ecrypto = EasyCrypto::Crypto.new
32
52
 
33
53
  key = EasyCrypto::Key.generate(key_password)
34
- ecrypto.encrypt_with_key(key, plain_text)
54
+
55
+ encrypted = ecrypto.encrypt_with_key(key, plaintext)
56
+ decrypted = ecrypto.decrypt_with_key(key, encrypted)
57
+
58
+ decrypted == plaintext
35
59
  ```
36
60
 
37
61
  ## License
@@ -1,4 +1,6 @@
1
1
  module EasyCrypto
2
+ DEFAULT_SALT_LENGTH = 12
3
+
2
4
  require 'easycrypto/version'
3
5
  require 'easycrypto/key'
4
6
  require 'easycrypto/crypto'
@@ -7,6 +7,17 @@ module EasyCrypto
7
7
  KEY_BITS = 256
8
8
  AES_MODE = :GCM
9
9
  IV_LEN = 12
10
+ AUTH_TAG_LEN = 16
11
+
12
+ def initialize(salt_length = DEFAULT_SALT_LENGTH)
13
+ @salt_length = salt_length
14
+ end
15
+
16
+ def encrypt(password, plaintext)
17
+ key = EasyCrypto::Key.generate(password, @salt_length)
18
+
19
+ encrypt_with_key(key, plaintext)
20
+ end
10
21
 
11
22
  def encrypt_with_key(key, plaintext)
12
23
  validate_key_type(key)
@@ -20,6 +31,27 @@ module EasyCrypto
20
31
  Base64.strict_encode64(key.salt + iv + encrypted + cipher.auth_tag)
21
32
  end
22
33
 
34
+ def decrypt(password, ciphertext)
35
+ salt = get_salt_from_ciphertext(ciphertext)
36
+ key = EasyCrypto::Key.generate_with_salt(password, salt)
37
+
38
+ decrypt_with_key(key, ciphertext)
39
+ end
40
+
41
+ def decrypt_with_key(key, ciphertext)
42
+ validate_key_type(key)
43
+
44
+ raw_ciphertext = Base64.strict_decode64(ciphertext)
45
+
46
+ iv = raw_ciphertext[key.salt.length, IV_LEN]
47
+ encrypted = raw_ciphertext[(key.salt.length + IV_LEN)..-(AUTH_TAG_LEN + 1)]
48
+ auth_tag = raw_ciphertext[-AUTH_TAG_LEN..-1]
49
+
50
+ decipher = create_decipher(key, iv, auth_tag)
51
+
52
+ decipher.update(encrypted) + decipher.final
53
+ end
54
+
23
55
  private
24
56
 
25
57
  def validate_key_type(key)
@@ -37,5 +69,18 @@ module EasyCrypto
37
69
  cipher.iv = iv
38
70
  cipher
39
71
  end
72
+
73
+ def create_decipher(key, iv, auth_tag)
74
+ decipher = OpenSSL::Cipher::AES.new(Crypto::KEY_BITS, Crypto::AES_MODE).decrypt
75
+ decipher.key = key.key
76
+ decipher.iv = iv
77
+ decipher.auth_tag = auth_tag
78
+ decipher
79
+ end
80
+
81
+ def get_salt_from_ciphertext(ciphertext)
82
+ raw_ciphertext = Base64.strict_decode64(ciphertext)
83
+ raw_ciphertext[0, @salt_length]
84
+ end
40
85
  end
41
86
  end
@@ -7,7 +7,6 @@ module EasyCrypto
7
7
  ITERATION_COUNT = 10_000
8
8
  KEY_LENGTH = 32
9
9
  HASH_ALGO = 'sha256'
10
- DEFAULT_SALT_LENGTH = 12
11
10
 
12
11
  attr_reader :key, :salt
13
12
 
@@ -18,6 +17,11 @@ module EasyCrypto
18
17
 
19
18
  def self.generate(password, salt_length = DEFAULT_SALT_LENGTH)
20
19
  salt = OpenSSL::Random.random_bytes(salt_length)
20
+
21
+ generate_with_salt(password, salt)
22
+ end
23
+
24
+ def self.generate_with_salt(password, salt)
21
25
  key = OpenSSL::KDF.pbkdf2_hmac(
22
26
  password,
23
27
  salt: salt,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EasyCrypto
4
- VERSION = '0.0.3'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -1,32 +1,89 @@
1
1
  require 'easycrypto'
2
2
 
3
3
  RSpec.describe EasyCrypto::Crypto do
4
+ let(:salt) { 'aaaaaaaaaaaa' }
5
+ let(:iv) { 'bbbbbbbbbbbb' }
6
+ let(:password) { 'some password' }
7
+ let(:data) { 'some data' }
8
+ let(:key) { EasyCrypto::Key.generate_with_salt(password, salt) }
9
+
4
10
  it 'has a version number' do
5
11
  expect(EasyCrypto::VERSION).not_to be nil
6
12
  end
7
13
 
8
- context 'encrypt' do
9
- it 'returns encrypted text in a single line' do
10
- ecrypto = EasyCrypto::Crypto.new
11
- key = EasyCrypto::Key.generate('key password', 12)
12
- encrypted = ecrypto.encrypt_with_key(key, 'plain text')
13
- expect(encrypted).not_to include("\n")
14
+ it 'can encrypt and decrypt data' do
15
+ ciphertext = subject.encrypt(password, data)
16
+ plaintext = subject.decrypt(password, ciphertext)
17
+
18
+ expect(plaintext).to eq data
19
+ end
20
+
21
+ it 'can encrypt and decrypt data with given key' do
22
+ key = EasyCrypto::Key.generate(password)
23
+
24
+ ciphertext = subject.encrypt_with_key(key, data)
25
+ plaintext = subject.decrypt_with_key(key, ciphertext)
26
+
27
+ expect(plaintext).to eq data
28
+ end
29
+
30
+ describe '#encrypt' do
31
+ before do
32
+ allow(OpenSSL::Random).to receive(:random_bytes).and_return(iv)
33
+ allow(EasyCrypto::Key).to receive(:generate).and_return(key)
34
+ end
35
+
36
+ it 'can encrypt data' do
37
+ result = subject.encrypt(password, data)
38
+ raw_result = Base64.strict_decode64(result)
39
+
40
+ expect(raw_result[0,12]).to eq salt
41
+ expect(raw_result[12,12]).to eq iv
42
+ end
43
+ end
44
+
45
+ describe '#encrypt_with_key' do
46
+ before do
47
+ allow(OpenSSL::Random).to receive(:random_bytes).and_return(iv)
48
+ end
49
+
50
+ it 'returns encrypted text as a single line' do
51
+ ciphertext = subject.encrypt_with_key(key, data)
52
+
53
+ expect(ciphertext).not_to include("\n")
14
54
  end
15
55
 
16
56
  it 'raise error if the encryptable data is not a string' do
17
- ecrypto = EasyCrypto::Crypto.new
18
- key = EasyCrypto::Key.generate('key password', 12)
19
57
  expect{
20
- ecrypto.encrypt_with_key(key, 1234)
58
+ subject.encrypt_with_key(key, 1234)
21
59
  }.to raise_error(TypeError, 'Encryptable data must be a string')
22
60
  end
23
61
 
24
62
  it 'raise error if the encryptable data is empty' do
25
- ecrypto = EasyCrypto::Crypto.new
26
- key = EasyCrypto::Key.generate('key password', 12)
27
63
  expect{
28
- ecrypto.encrypt_with_key(key, '')
64
+ subject.encrypt_with_key(key, '')
29
65
  }.to raise_error(ArgumentError, 'Encryptable data must not be empty')
30
66
  end
67
+
68
+ it 'can encrypt data with given key' do
69
+ expected_ciphertext = 'YWFhYWFhYWFhYWFhYmJiYmJiYmJiYmJifAUY9TEdvoOQ79sxKd3zv1dT67K1GM36mQ=='
70
+ expect(subject.encrypt_with_key(key, data)).to eq expected_ciphertext
71
+ end
72
+ end
73
+
74
+ describe '#decrypt' do
75
+ it 'can decrypt encrypted data' do
76
+ ciphertext = 'FPXj2e2DZrFYRVUhqoBWXhVVVGUO2ZJgayU2F1f6duLtBjYOINvAZPWIXjIVHHslgg=='
77
+
78
+ expect(subject.decrypt(password, ciphertext)).to eq data
79
+ end
80
+ end
81
+
82
+ describe '#decrypt_with_key' do
83
+ it 'can decrypt encrypted data' do
84
+ ciphertext = 'YWFhYWFhYWFhYWFhzs8I/ks+nAy2V+Q7xIrJAnOHefyfO4zYwwmz1F2y1mf1wfXDqA=='
85
+
86
+ expect(subject.decrypt_with_key(key, ciphertext)).to eq data
87
+ end
31
88
  end
32
89
  end
@@ -1,8 +1,7 @@
1
1
  require 'easycrypto'
2
2
 
3
3
  RSpec.describe EasyCrypto::Key do
4
-
5
- context 'generate' do
4
+ describe '#generate' do
6
5
  it 'returns key with a salt of the specified length' do
7
6
  key = EasyCrypto::Key.generate('key password', 24)
8
7
 
@@ -15,4 +14,21 @@ RSpec.describe EasyCrypto::Key do
15
14
  expect(key.salt.length).to be 12
16
15
  end
17
16
  end
17
+
18
+ describe '#generate_with_salt' do
19
+ let(:salt) { 'aaaaaaaaaaaa' }
20
+
21
+ it 'generates key with the given salt' do
22
+ key = EasyCrypto::Key.generate_with_salt('key password', salt)
23
+
24
+ expect(key.salt).to eq salt
25
+ end
26
+
27
+ it 'generates the same key with the same password and salt' do
28
+ key_1 = EasyCrypto::Key.generate_with_salt('key password', salt)
29
+ key_2 = EasyCrypto::Key.generate_with_salt('key password', salt)
30
+
31
+ expect(key_1.key).to eq key_2.key
32
+ end
33
+ end
18
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy-crypto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emarsys Security
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-26 00:00:00.000000000 Z
11
+ date: 2018-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler