Class: SecurityClient::Encryption

Inherits:
Object
  • Object
show all
Defined in:
lib/security_client.rb

Instance Method Summary collapse

Constructor Details

#initialize(creds, uses) ⇒ Encryption

Returns a new instance of Encryption.

Raises:

  • (RuntimeError)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/security_client.rb', line 51

def initialize(creds, uses)

  raise RuntimeError, 'Some of your credentials are missing, please check!' if !validate_creds(creds)

  # Set host, either the default or the one given by caller
  @host = creds.host.blank? ? VOLTRON_HOST : creds.host

  # Set the credentials in instance varibales to be used among methods
  # The client's public API key (used to identify the client to the server
  @papi = creds.access_key_id

  # The client's secret API key (used to authenticate HTTP requests)
  @sapi = creds.secret_signing_key

  # The client's secret RSA encryption key/password (used to decrypt the client's RSA key from the server). This key is not retained by this object.
  @srsa = creds.secret_crypto_access_key

  # Build the endpoint URL
  url = endpoint_base + '/encryption/key'

  # Build the Request Body with the number of uses of key
  query = {uses: uses}

  # Retrieve the necessary headers to make the request using Auth Object
  headers = SecurityClient::Auth.build_headers(@papi, @sapi, endpoint, query, @host,'post')

  @encryption_started = false
  @encryption_ready = true

  # Request a new encryption key from the server. if the request
  # fails, the function raises a HTTPError indicating
  # the status code returned by the server. this exception is
  # propagated back to the caller

  begin
    response = HTTParty.post(
      url,
      body: query.to_json,
      headers: headers
    )
  rescue HTTParty::Error
    raise RuntimeError, 'Cant reach server'
  end

  # Response status is 201 Created
  if response.code == WEBrick::HTTPStatus::RC_CREATED
    # The code below largely assumes that the server returns
    # a json object that contains the members and is formatted
    # according to the Voltron REST specification.

    # Build the key object
    @key = {}
    @key['id'] = response['key_fingerprint']
    @key['session'] = response['encryption_session']
    @key['security_model'] = response['security_model']
    @key['algorithm'] = response['security_model']['algorithm'].downcase
    @key['max_uses'] = response['max_uses']
    @key['uses'] = 0
    @key['encrypted'] = Base64.strict_decode64(response['encrypted_data_key'])

    # Get encrypted private key from response body
    encrypted_private_key = response['encrypted_private_key']
    # Get wrapped data key from response body
    wrapped_data_key = response['wrapped_data_key']
    # Decrypt the encryped private key using @srsa supplied
    private_key = OpenSSL::PKey::RSA.new(encrypted_private_key,@srsa)
    # Decode WDK from base64 format
    wdk = Base64.strict_decode64(wrapped_data_key)
    # Use private key to decrypt the wrapped data key
    dk = private_key.private_decrypt(wdk,OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
    @key['raw'] = dk
    # Build the algorithm object
    @algo = SecurityClient::Algo.new.get_algo(@key['algorithm'])
  else
    # Raise the error if response is not 201
    raise RuntimeError, "HTTPError Response: Expected 201, got #{response.code}"
  end

end

Instance Method Details

#beginObject

Raises:

  • (RuntimeError)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/security_client.rb', line 131

def begin
  # Begin the encryption process

  # When this function is called, the encryption object increments
  # the number of uses of the key and creates a new internal context
  # to be used to encrypt the data.
  # If the encryption object is not yet ready to be used, throw an error
  raise RuntimeError, 'Encryption not ready' if !@encryption_ready

  # if Encryption cipher context already exists
  raise RuntimeError, 'Encryption already in progress' if @encryption_started
  # If max uses > uses
  raise RuntimeError, 'Maximum key uses exceeded' if @key['uses'] >= @key['max_uses']
  @key['uses'] += 1
  # create a new Encryption context and initialization vector
  @enc , @iv = SecurityClient::Algo.new.encryptor(@algo, @key['raw'])

  # Pack the result into bytes to get a byte string
  struct = [0, 0, @algo[:id], @iv.length, @key['encrypted'].length].pack('CCCCn')
  @encryption_started = true
  return struct + @iv + @key['encrypted']
end

#closeObject

Raises:

  • (RuntimeError)


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/security_client.rb', line 179

def close
  raise RuntimeError, 'Encryption currently running' if @encryption_started
  # If the key was used less times than was requested, send an update to the server
  if @key['uses'] < @key['max_uses']
    query_url = "#{endpoint}/#{@key['id']}/#{@key['session']}"
      url = "#{endpoint_base}/encryption/key/#{@key['id']}/#{@key['session']}"
      query = {actual: @key['uses'], requested: @key['max_uses']}
      headers = Auth.build_headers(@papi, @sapi, query_url, query, @host, 'patch')
      response = HTTParty.patch(
        url,
        body: query.to_json,
        headers: headers
      )
      remove_instance_variable(:@key)
    @encryption_ready = false;
  end
end

#endObject

Raises:

  • (RuntimeError)


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/security_client.rb', line 161

def end
  raise RuntimeError, 'Encryption is not Started' if !@encryption_started
  # This function finalizes the encryption (producing the final
  # cipher text for the encryption, if necessary) and adds any
  # authentication information (if required by the algorithm).
  # Any data produced is returned by the function.

  # Finalize an encryption
  res = @enc.final
  if @algo[:tag_length] != 0
    # Add the tag to the cipher text
    res+= @enc.auth_tag
  end
  @encryption_started = false
  # Return the encrypted result
  return res
end

#endpointObject



201
202
203
# File 'lib/security_client.rb', line 201

def endpoint
  '/api/v0/encryption/key'
end

#endpoint_baseObject



197
198
199
# File 'lib/security_client.rb', line 197

def endpoint_base
  @host + '/api/v0'
end

#update(data) ⇒ Object

Raises:

  • (RuntimeError)


154
155
156
157
158
159
# File 'lib/security_client.rb', line 154

def update(data)
  raise RuntimeError, 'Encryption is not Started' if !@encryption_started
  # Encryption of some plain text is perfomed here
  # Any cipher text produced by the operation is returned
  @enc.update(data)
end

#validate_creds(credentials) ⇒ Object



205
206
207
208
# File 'lib/security_client.rb', line 205

def validate_creds(credentials)
  # This method checks for the presence of the credentials
  !credentials.access_key_id.blank? and !credentials.secret_signing_key.blank? and !credentials.secret_crypto_access_key.blank?
end