class Ribbon::EncryptedStore::CryptoHash

Public Class Methods

_calc_crc32(data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 111
def _calc_crc32(data)
  [Zlib.crc32(data)].pack('N')
end
_keyiv_gen(key, salt, iter_mag) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 49
def _keyiv_gen(key, salt, iter_mag)
  if iter_mag == -1
    raise Errors::InvalidKeySize, 'must be exactly 256 bits' unless key.bytes.length == 32
    raise Errors::InvalidSaltSize, 'must be exactly 128 bits' unless salt.bytes.length == 16
    iv = salt
  else
    digest = OpenSSL::Digest::SHA256.new
    key_and_iv = OpenSSL::PKCS5.pbkdf2_hmac(key, salt, 1 << iter_mag, 48, digest)

    key = key_and_iv[0..31]
    iv  = key_and_iv[32..-1]
  end

  [key, iv]
end
_split_binary_data(encrypted_data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 65
def _split_binary_data(encrypted_data)
  # Split encrypted data and CRC
  bytes = encrypted_data.bytes

  version = bytes[0]
  version_method = "_split_binary_data_v#{version}"

  if respond_to?(version_method)
    send(version_method, encrypted_data)
  else
    raise Errors::UnsupportedVersionError, "Unsupported encrypted data version: #{version}"
  end
end
_split_binary_data_v1(encrypted_data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 79
def _split_binary_data_v1(encrypted_data)
  bytes = encrypted_data.bytes
  salt_length = bytes[1]

  salt_start_index = 2
  salt_end_index   = salt_start_index + salt_length - 1
  salt = bytes[salt_start_index..salt_end_index].pack('c*')
  data = bytes[salt_end_index+1..-5].pack('c*')

  crc = bytes[-4..-1]
  raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes

  [salt, 12, data]
end
_split_binary_data_v2(encrypted_data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 94
def _split_binary_data_v2(encrypted_data)
  bytes = encrypted_data.bytes
  salt_length = bytes[1]
  iter_mag    = bytes[2].chr.unpack('c').first

  salt_start_index = 3
  salt_end_index   = salt_start_index + salt_length - 1
  salt = bytes[salt_start_index..salt_end_index].pack('c*')
  data = bytes[salt_end_index+1..-5].pack('c*')

  crc = bytes[-4..-1]
  raise Errors::ChecksumFailedError unless crc == _calc_crc32(encrypted_data[0..-5]).bytes

  [salt, iter_mag, data]
end
decrypt(dek, data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 34
def decrypt(dek, data)
  return CryptoHash.new unless data
  salt, iter_mag, data = _split_binary_data(data)

  key, iv = _keyiv_gen(dek, salt, iter_mag)

  decryptor = OpenSSL::Cipher::AES256.new(:CBC).decrypt
  decryptor.key = key
  decryptor.iv = iv

  new_hash = JSON.parse(decryptor.update(data) + decryptor.final)
  new_hash = Hash[new_hash.map { |k,v| [k.to_sym, v] }]
  CryptoHash.new(new_hash)
end
new(data={}) click to toggle source
Calls superclass method
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 7
def initialize(data={})
  super()
  merge!(data)
end

Public Instance Methods

encrypt(dek, salt, iter_mag=10) click to toggle source

Encrypts the hash using the data encryption key and salt.

Returns a blob: | Byte 0 | Byte 1 | Byte 2 | Bytes 3…S | Bytes S+1…E | Bytes E+1..E+4 |


| Version | Salt Length | Iteration Magnitude | Salt | Encrypted Data | CRC32 |

# File lib/ribbon/encrypted_store/crypto_hash.rb, line 19
def encrypt(dek, salt, iter_mag=10)
  return nil if empty?
  raise Errors::InvalidSaltSize, 'too long' if salt.bytes.length > 255

  key, iv = _keyiv_gen(dek, salt, iter_mag)

  encryptor = OpenSSL::Cipher::AES256.new(:CBC).encrypt
  encryptor.key = key
  encryptor.iv = iv

  data_packet = _encrypted_data_header_v2(salt, iter_mag) + encryptor.update(self.to_json) + encryptor.final
  _append_crc32(data_packet)
end

Private Instance Methods

_append_crc32(data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 142
def _append_crc32(data)
  data + _calc_crc32(data)
end
_calc_crc32(data) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 146
def _calc_crc32(data)
  self.class._calc_crc32(data)
end
_encrypted_data_header_v1(salt) click to toggle source

Generates the version 1 encrypted data header: | Byte 0 | Byte 1 | Bytes 2…S


| Version | Salt Length | Salt

# File lib/ribbon/encrypted_store/crypto_hash.rb, line 124
def _encrypted_data_header_v1(salt)
  "\x01" + salt.bytes.length.chr + salt
end
_encrypted_data_header_v2(salt, iter_mag) click to toggle source

Generates the version 2 encrypted data header: | Byte 0 | Byte 1 | Byte 2 | Bytes 3…S


| Version | Salt Length | Iteration Magnitude | Salt

# File lib/ribbon/encrypted_store/crypto_hash.rb, line 134
def _encrypted_data_header_v2(salt, iter_mag)
  "\x02" + salt.bytes.length.chr + [iter_mag].pack('c') + salt
end
_keyiv_gen(key, salt, iter_mag) click to toggle source
# File lib/ribbon/encrypted_store/crypto_hash.rb, line 138
def _keyiv_gen(key, salt, iter_mag)
  self.class._keyiv_gen(key, salt, iter_mag)
end