easy-crypto 0.0.3 → 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 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