Class: Chef::EncryptedAttribute::EncryptedMash::Version2

Inherits:
Version1 show all
Includes:
Assertions
Defined in:
lib/chef/encrypted_attribute/encrypted_mash/version2.rb

Overview

EncryptedMash Version2 format: using RSA with a shared secret and GCM.

Uses public key cryptography (PKI) to encrypt a shared secret. Then this shared secret is used to encrypt the data using GCM.

  • This protocol version is based on the Chef 12 Encrypted Data Bags Version 3 implementation.
  • To use it, the following special requirements must be met: Ruby >= 2 and OpenSSL >= 1.0.1.
  • This implementation can be improved, is not optimized either for performance or for space.
  • Every time the EncryptedAttribute is updated, all the shared secrets are regenerated.

EncryptedMash::Version2 Structure

If you try to read this encrypted attribute structure, you can see a Mash attribute with the following content:

EncryptedMash
├── chef_type: "encrypted_attribute" (string).
├── x_json_class: The used `EncryptedMash` version class name (string).
├── encrypted_data
│   ├── cipher: The used PKI algorithm, "aes-256-gcm" (string).
│   ├── data: PKI encrypted data (base64).
│   ├── auth_tag: GCM authentication tag (base64).
│   └── iv: Initialization vector (in base64).
└── encrypted_secret
    ├── pub_key_hash1: The shared secret encrypted for the public key 1
    │     (base64).
    ├── pub_key_hash2: The shared secret encrypted for the public key 2
    │     (base64).
    └── ...
  • x_json_class field is used, with the x_ prefix, to be easily integrated with Chef in the future.

EncryptedMash[encrypted_data][data]

The data inside encrypted_data is symmetrically encrypted using the secret shared key. The data is converted to JSON before the encryption, then encrypted and finally encoded in base64. By default, the 'aes-256-gcm' algorithm is used for encryption.

After decryption, the JSON has the following structure:

└── encrypted_data
    └── data (symmetrically encrypted JSON in base64)
        └── content: attribute content as a Mash.
  • In the future, this structure may contain some metadata like default configuration values.

EncryptedMash[encrypted_secret][pub_key_hash1]

The public_key_hash1 key value is the SHA1 of the public key used for encryption.

Its content is the encrypted shared secret in raw. The encryption is done using the RSA algorithm (PKI).

After decryption, you find the shared secret in raw (in Version1 this is a JSON in base64).

Constant Summary collapse

ALGORITHM =

Symmetric AEAD algorithm to use by default.

'aes-256-gcm'

Constants inherited from Version1

Chef::EncryptedAttribute::EncryptedMash::Version1::HMAC_ALGORITHM, Chef::EncryptedAttribute::EncryptedMash::Version1::SYMM_ALGORITHM

Constants inherited from Chef::EncryptedAttribute::EncryptedMash

CHEF_TYPE, CHEF_TYPE_VALUE, JSON_CLASS, VERSION_PREFIX

Instance Method Summary collapse

Methods included from Assertions

#assert_aead_requirements_met!

Methods inherited from Version1

#can_be_decrypted_by?, #needs_update?

Methods inherited from Version0

#can_be_decrypted_by?, #needs_update?

Methods inherited from Chef::EncryptedAttribute::EncryptedMash

create, exist?, exists?, #for_json, json_create, string_to_klass, #to_json, #update_from!, version_klass

Constructor Details

#initialize(enc_hs = nil) ⇒ Version2

EncrytpedMash::Version2 constructor.

Checks that GCM is correctly supported by Ruby and OpenSSL.

Raises:


112
113
114
115
# File 'lib/chef/encrypted_attribute/encrypted_mash/version2.rb', line 112

def initialize(enc_hs = nil)
  assert_aead_requirements_met!(ALGORITHM)
  super
end

Instance Method Details

#decrypt(key) ⇒ Mixed

Decrypts the current Chef::EncryptedAttribute::EncryptedMash object.

Parameters:

  • key (String, OpenSSL::PKey::RSA)

    RSA private key used to decrypt.

Returns:

  • (Mixed)

    the value decrypted.

Raises:


132
133
134
135
136
137
138
139
140
141
# File 'lib/chef/encrypted_attribute/encrypted_mash/version2.rb', line 132

def decrypt(key)
  key = parse_decryption_key(key)
  enc_value = self['encrypted_data'].dup  # decrypt the shared secret

  enc_value['secret'] =
    rsa_decrypt_multi_key(self['encrypted_secret'], key)  # decrypt the data

  value_json = symmetric_decrypt_value(enc_value)
  json_decode(value_json)
end

#encrypt(value, public_keys) ⇒ EncryptedMash

Encrypts data inside the current Chef::EncryptedAttribute::EncryptedMash object.

Parameters:

  • value (Mixed)

    value to encrypt, will be converted to JSON.

  • public_keys (Array<String, OpenSSL::PKey::RSA>)

    publics keys that will be able to decrypt the Chef::EncryptedAttribute::EncryptedMash.

Returns:

Raises:


118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/chef/encrypted_attribute/encrypted_mash/version2.rb', line 118

def encrypt(value, public_keys)
  value_json = json_encode(value)
  public_keys = parse_public_keys(public_keys)  # encrypt the data

  encrypted_data = symmetric_encrypt_value(value_json)  # should no include the secret in clear

  secret = encrypted_data.delete('secret')
  self['encrypted_data'] = encrypted_data  # encrypt the shared secret

  self['encrypted_secret'] = rsa_encrypt_multi_key(secret, public_keys)
  self
end