ribbon-encrypted_store 0.1.5 → 0.2.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
  SHA1:
3
- metadata.gz: 1d3aaf094d554be5178e1d81b38408165cd02e88
4
- data.tar.gz: 65fd270a315e816a1090f080b80ba9c5dbea421c
3
+ metadata.gz: 2ed9c1b5768713771553fc0ae858ba9230f12e7a
4
+ data.tar.gz: 4ca77eb35ee661056d33ad308f9a6132b7ea94d6
5
5
  SHA512:
6
- metadata.gz: 393874c9a5123284c259e83191b20ae9307bddcee2743f8d446a6bb01b864d68165c0c9beb7f2763b0e0ebe205464c823a0d22d43dd992e141ba315ae68b04eb
7
- data.tar.gz: ef0d1e2e453110e96310beb44b0e1d3caedbd87e99ea3b2cc41be99201e8ea7341e07f17c1d8f1cf1847680c0cd0eec3afac6e85206aab8cc650811edf9df66e
6
+ metadata.gz: 1abc59705a77c7d009823b388681a4e004a472308b6885012e6af6b59b65deeabc09daeddf56594e30affb21de333679bbf7cf84c4157b79d981acb0fa399551
7
+ data.tar.gz: b626cd351cdffbb155cebdc9c43fc30fed4f6e18c630e32c91ef4106a88ce289e325e5a04f62e9ee40f7784a3ca12d06a1adde53c614ed7bf61c9c5b75cd40bb
@@ -11,32 +11,31 @@ module Ribbon::EncryptedStore
11
11
 
12
12
  ##
13
13
  # Encrypts the hash using the data encryption key and salt.
14
- # Returns a blob:
15
- #
16
- # | Byte 0 | Byte 1 | Bytes 2...S | Bytes S+1...E | Bytes E+1..E+4 |
17
- # --------------------------------------------------------------------------
18
- # | Version | Salt Length | Salt | Encrypted Data | CRC32 |
19
14
  #
20
- def encrypt(dek, salt)
15
+ # Returns a blob:
16
+ # | Byte 0 | Byte 1 | Byte 2 | Bytes 3...S | Bytes S+1...E | Bytes E+1..E+4 |
17
+ # ------------------------------------------------------------------------------------------------
18
+ # | Version | Salt Length | Iteration Magnitude | Salt | Encrypted Data | CRC32 |
19
+ def encrypt(dek, salt, iter_mag=10)
21
20
  return nil if empty?
22
- raise Errors::SaltTooBigError if salt.bytes.length > 255
21
+ raise Errors::InvalidSaltSize, 'too long' if salt.bytes.length > 255
23
22
 
24
- key, iv = _keyiv_gen(dek, salt)
23
+ key, iv = _keyiv_gen(dek, salt, iter_mag)
25
24
 
26
25
  encryptor = OpenSSL::Cipher::AES256.new(:CBC).encrypt
27
26
  encryptor.key = key
28
27
  encryptor.iv = iv
29
28
 
30
- data_packet = _encrypted_data_header(salt) + encryptor.update(self.to_json) + encryptor.final
29
+ data_packet = _encrypted_data_header_v2(salt, iter_mag) + encryptor.update(self.to_json) + encryptor.final
31
30
  _append_crc32(data_packet)
32
31
  end
33
32
 
34
33
  class << self
35
34
  def decrypt(dek, data)
36
35
  return CryptoHash.new unless data
37
- salt, data = _split_binary_data(data)
36
+ salt, iter_mag, data = _split_binary_data(data)
38
37
 
39
- key, iv = _keyiv_gen(dek, salt)
38
+ key, iv = _keyiv_gen(dek, salt, iter_mag)
40
39
 
41
40
  decryptor = OpenSSL::Cipher::AES256.new(:CBC).decrypt
42
41
  decryptor.key = key
@@ -47,12 +46,18 @@ module Ribbon::EncryptedStore
47
46
  CryptoHash.new(new_hash)
48
47
  end
49
48
 
50
- def _keyiv_gen(key, salt)
51
- digest = OpenSSL::Digest::SHA256.new
52
- key_and_iv = OpenSSL::PKCS5.pbkdf2_hmac(key, salt, 4096, 48, digest)
49
+ def _keyiv_gen(key, salt, iter_mag)
50
+ if iter_mag == -1
51
+ raise Errors::InvalidKeySize, 'must be exactly 256 bits' unless key.bytes.length == 32
52
+ raise Errors::InvalidSaltSize, 'must be exactly 128 bits' unless salt.bytes.length == 16
53
+ iv = salt
54
+ else
55
+ digest = OpenSSL::Digest::SHA256.new
56
+ key_and_iv = OpenSSL::PKCS5.pbkdf2_hmac(key, salt, 1 << iter_mag, 48, digest)
53
57
 
54
- key = key_and_iv[0..31]
55
- iv = key_and_iv[32..-1]
58
+ key = key_and_iv[0..31]
59
+ iv = key_and_iv[32..-1]
60
+ end
56
61
 
57
62
  [key, iv]
58
63
  end
@@ -61,7 +66,18 @@ module Ribbon::EncryptedStore
61
66
  # Split encrypted data and CRC
62
67
  bytes = encrypted_data.bytes
63
68
 
64
- version = bytes[0]
69
+ version = bytes[0]
70
+ version_method = "_split_binary_data_v#{version}"
71
+
72
+ if respond_to?(version_method)
73
+ send(version_method, encrypted_data)
74
+ else
75
+ raise Errors::UnsupportedVersionError, "Unsupported encrypted data version: #{version}"
76
+ end
77
+ end
78
+
79
+ def _split_binary_data_v1(encrypted_data)
80
+ bytes = encrypted_data.bytes
65
81
  salt_length = bytes[1]
66
82
 
67
83
  salt_start_index = 2
@@ -72,9 +88,26 @@ module Ribbon::EncryptedStore
72
88
  crc = bytes[-4..-1]
73
89
  raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes
74
90
 
75
- [salt, data]
91
+ [salt, 12, data]
92
+ end
93
+
94
+ def _split_binary_data_v2(encrypted_data)
95
+ bytes = encrypted_data.bytes
96
+ salt_length = bytes[1]
97
+ iter_mag = bytes[2].chr.unpack('c').first
98
+
99
+ salt_start_index = 3
100
+ salt_end_index = salt_start_index + salt_length - 1
101
+ salt = bytes[salt_start_index..salt_end_index].pack('c*')
102
+ data = bytes[salt_end_index+1..-5].pack('c*')
103
+
104
+ crc = bytes[-4..-1]
105
+ raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes
106
+
107
+ [salt, iter_mag, data]
76
108
  end
77
109
 
110
+
78
111
  def _calc_crc32(data)
79
112
  [Zlib.crc32(data)].pack('N')
80
113
  end
@@ -83,17 +116,27 @@ module Ribbon::EncryptedStore
83
116
  private
84
117
 
85
118
  ##
86
- # Generates the encrypted data header:
119
+ # Generates the version 1 encrypted data header:
87
120
  # | Byte 0 | Byte 1 | Bytes 2...S
88
121
  # ---------------------------------------------------
89
122
  # | Version | Salt Length | Salt
90
123
  #
91
- def _encrypted_data_header(salt)
124
+ def _encrypted_data_header_v1(salt)
92
125
  "\x01" + salt.bytes.length.chr + salt
93
126
  end
94
127
 
95
- def _keyiv_gen(key, salt)
96
- self.class._keyiv_gen(key, salt)
128
+ ##
129
+ # Generates the version 2 encrypted data header:
130
+ # | Byte 0 | Byte 1 | Byte 2 | Bytes 3...S
131
+ # ----------------------------------------------------------------------
132
+ # | Version | Salt Length | Iteration Magnitude | Salt
133
+ #
134
+ def _encrypted_data_header_v2(salt, iter_mag)
135
+ "\x02" + salt.bytes.length.chr + [iter_mag].pack('c') + salt
136
+ end
137
+
138
+ def _keyiv_gen(key, salt, iter_mag)
139
+ self.class._keyiv_gen(key, salt, iter_mag)
97
140
  end
98
141
 
99
142
  def _append_crc32(data)
@@ -9,6 +9,8 @@ module Ribbon::EncryptedStore
9
9
  # CryptoHash Errors
10
10
  class CryptoHashError < Error; end
11
11
  class ChecksumFailedError < CryptoHashError; end
12
- class SaltTooBigError < CryptoHashError; end
12
+ class InvalidSaltSize < CryptoHashError; end
13
+ class InvalidKeySize < CryptoHashError; end
14
+ class UnsupportedVersionError < CryptoHashError; end
13
15
  end # Errors
14
- end # Ribbon::EncryptedStore
16
+ end # Ribbon::EncryptedStore
@@ -35,7 +35,7 @@ module Ribbon::EncryptedStore
35
35
 
36
36
  def attr_encrypted(*args)
37
37
  # Store attrs in class data
38
- _encrypted_store_data[:encrypted_attributes] = args
38
+ _encrypted_store_data[:encrypted_attributes] = args.map(&:to_sym)
39
39
 
40
40
  args.each { |arg|
41
41
  define_method(arg) { _encrypted_store_get(arg) }
@@ -83,7 +83,7 @@ module Ribbon::EncryptedStore
83
83
  end
84
84
 
85
85
  def _encrypted_store_save
86
- if !(self.changed & _encrypted_store_data[:encrypted_attributes]).empty? || @_reencrypting
86
+ if !(self.changed.map(&:to_sym) & _encrypted_store_data[:encrypted_attributes]).empty? || @_reencrypting
87
87
  # Obtain a lock without overriding attribute values for this record.
88
88
  record = self.class.unscoped { self.class.lock.find(id) } unless new_record?
89
89
 
@@ -91,8 +91,16 @@ module Ribbon::EncryptedStore
91
91
  self.encryption_key_id = record.encryption_key_id if record && record.encryption_key_id
92
92
  end
93
93
 
94
+ iter_mag = Ribbon::EncryptedStore.config.iteration_magnitude? ?
95
+ Ribbon::EncryptedStore.config.iteration_magnitude :
96
+ -1
97
+
94
98
  @_reencrypting = false
95
- self.encrypted_store = _crypto_hash.encrypt(_decrypted_key, EncryptionKeySalt.generate_salt(_encryption_key_id))
99
+ self.encrypted_store = _crypto_hash.encrypt(
100
+ _decrypted_key,
101
+ EncryptionKeySalt.generate_salt(_encryption_key_id),
102
+ iter_mag
103
+ )
96
104
  end
97
105
  end
98
106
  end # ActiveRecordMixin
@@ -1,5 +1,5 @@
1
1
  module Ribbon
2
2
  module EncryptedStore
3
- VERSION = '0.1.5'
3
+ VERSION = '0.2.0'
4
4
  end # EncryptedStore
5
5
  end # Ribbon
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ribbon-encrypted_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Honer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-07-24 00:00:00.000000000 Z
12
+ date: 2015-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bcrypt