security_client 0.1.0 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +61 -1
- data/lib/security_client.rb +163 -167
- data/lib/security_client/version.rb +1 -1
- data/security_client.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25ec3c95ad9fa1853fed49499126461b7872bb04688a677239ec8c1ad6963e5a
|
4
|
+
data.tar.gz: 61975e25115a99c7be9469d08c2ab9543e2923fd9155e657973eee62dba4846a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 371bddc6ede995b027bc0f4933b7352a660ed74875f9bbd5b3059cc65c728fdf5935ea6667c203e78f8f4f5602e3c5ea840eb6bcb881fd5f9226627d4a628f48
|
7
|
+
data.tar.gz: 92a9a1a0951cf8c78082795fd597817bc9e46f6f35d2d47a2fa715e2ab3c189fd4e3771aba5d825b261c6fc4d0cc03086929d8f5c64eac6304ddd36ffba82096
|
data/README.md
CHANGED
@@ -22,7 +22,67 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
```ruby
|
26
|
+
# Initialize the Client
|
27
|
+
client = SecurityClient::Voltron.new(
|
28
|
+
access_key_id: 'Your access key',
|
29
|
+
secret_signing_key: 'Your secret signing key',
|
30
|
+
secret_crypto_access_key: 'Your secret crypto access key',
|
31
|
+
host: 'Your host'
|
32
|
+
)
|
33
|
+
|
34
|
+
# Simple Encryption
|
35
|
+
encrypted_result = client.encrypt(
|
36
|
+
uses: 'Key Uses',
|
37
|
+
data: 'Data to be encrypted'
|
38
|
+
)
|
39
|
+
|
40
|
+
# Simple Decryption
|
41
|
+
original_data = client.decrypt(
|
42
|
+
data: 'Encrypted Data'
|
43
|
+
)
|
44
|
+
|
45
|
+
# Piecewise Encryption
|
46
|
+
# Get your credentials ready
|
47
|
+
credentials = OpenStruct.new(
|
48
|
+
access_key_id: 'Your access key',
|
49
|
+
secret_signing_key: 'Your secret signing key',
|
50
|
+
secret_crypto_access_key: 'Your secret crypto access key',
|
51
|
+
host: 'Your host'
|
52
|
+
)
|
53
|
+
|
54
|
+
# Build the encryption object
|
55
|
+
enc = SecurityClient::Encryption.new(credentials, uses)
|
56
|
+
# Begin the encryption
|
57
|
+
enc.begin()
|
58
|
+
# Update the cipher with the raw data, can be supplied directly or in chunks
|
59
|
+
enc.update(data)
|
60
|
+
# End the encryption
|
61
|
+
enc.end()
|
62
|
+
# Reset the encryption object to initial state and cleanup the memory in use
|
63
|
+
enc.close()
|
64
|
+
|
65
|
+
# Piecewise Decryption
|
66
|
+
# Get your credentials ready
|
67
|
+
credentials = OpenStruct.new(
|
68
|
+
access_key_id: 'Your access key',
|
69
|
+
secret_signing_key: 'Your secret signing key',
|
70
|
+
secret_crypto_access_key: 'Your secret crypto access key',
|
71
|
+
host: 'Your host'
|
72
|
+
)
|
73
|
+
|
74
|
+
# Build the decryption object
|
75
|
+
dec = SecurityClient::Decryption.new(credentials)
|
76
|
+
# Begin the decryption
|
77
|
+
dec.begin()
|
78
|
+
# Update the cipher with the raw data, can be supplied directly or in chunks
|
79
|
+
dec.update(data)
|
80
|
+
# End the decryption
|
81
|
+
dec.end()
|
82
|
+
# Reset the decryption object to initial state and cleanup the memory in use
|
83
|
+
dec.close()
|
84
|
+
|
85
|
+
```
|
26
86
|
|
27
87
|
## Development
|
28
88
|
|
data/lib/security_client.rb
CHANGED
@@ -28,7 +28,6 @@ module SecurityClient
|
|
28
28
|
enc.close() if enc
|
29
29
|
raise
|
30
30
|
end
|
31
|
-
puts res
|
32
31
|
return res
|
33
32
|
end
|
34
33
|
|
@@ -42,7 +41,6 @@ module SecurityClient
|
|
42
41
|
dec.close() if dec
|
43
42
|
raise
|
44
43
|
end
|
45
|
-
puts res
|
46
44
|
return res
|
47
45
|
end
|
48
46
|
|
@@ -335,7 +333,7 @@ class SecurityClient::Decryption
|
|
335
333
|
# Initialize the decryption module object
|
336
334
|
# Set the credentials in instance varibales to be used among methods
|
337
335
|
# the server to which to make the request
|
338
|
-
raise RuntimeError, 'Some of your credentials are missing, please check!' if !validate_creds(creds)
|
336
|
+
# raise RuntimeError, 'Some of your credentials are missing, please check!' if !validate_creds(creds)
|
339
337
|
@host = creds.host.blank? ? VOLTRON_HOST : creds.host
|
340
338
|
|
341
339
|
# The client's public API key (used to identify the client to the server
|
@@ -350,194 +348,192 @@ class SecurityClient::Decryption
|
|
350
348
|
@decryption_ready = true
|
351
349
|
@decryption_started = false
|
352
350
|
|
353
|
-
end
|
351
|
+
end
|
354
352
|
|
355
|
-
def endpoint_base
|
356
|
-
|
357
|
-
end
|
353
|
+
def endpoint_base
|
354
|
+
@host + '/api/v0'
|
355
|
+
end
|
358
356
|
|
359
|
-
def endpoint
|
360
|
-
|
361
|
-
end
|
357
|
+
def endpoint
|
358
|
+
'/api/v0/decryption/key'
|
359
|
+
end
|
362
360
|
|
363
|
-
def begin
|
364
|
-
|
361
|
+
def begin
|
362
|
+
# Begin the decryption process
|
365
363
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
364
|
+
# This interface does not take any cipher text in its arguments
|
365
|
+
# in an attempt to maintain an API that corresponds to the
|
366
|
+
# encryption object. In doing so, the work that can take place
|
367
|
+
# in this function is limited. without any data, there is no
|
368
|
+
# way to determine which key is in use or decrypt any data.
|
369
|
+
#
|
370
|
+
# this function simply throws an error if starting an decryption
|
371
|
+
# while one is already in progress, and initializes the internal
|
372
|
+
# buffer
|
375
373
|
|
376
|
-
|
374
|
+
raise RuntimeError, 'Decryption is not ready' if !@decryption_ready
|
377
375
|
|
378
|
-
|
376
|
+
raise RuntimeError, 'Decryption Already Started' if @decryption_started
|
379
377
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
end
|
378
|
+
raise RuntimeError, 'Decryption already in progress' if @key.present? and @key.key?("dec")
|
379
|
+
@decryption_started = true
|
380
|
+
@data = ''
|
381
|
+
end
|
384
382
|
|
385
|
-
def update(data)
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
383
|
+
def update(data)
|
384
|
+
# Decryption of cipher text is performed here
|
385
|
+
# Cipher text must be passed to this function in the order in which it was output from the encryption.update function.
|
386
|
+
|
387
|
+
# Each encryption has a header on it that identifies the algorithm
|
388
|
+
# used and an encryption of the data key that was used to encrypt
|
389
|
+
# the original plain text. there is no guarantee how much of that
|
390
|
+
# data will be passed to this function or how many times this
|
391
|
+
# function will be called to process all of the data. to that end,
|
392
|
+
# this function buffers data internally, when it is unable to
|
393
|
+
# process it.
|
394
|
+
#
|
395
|
+
# The function buffers data internally until the entire header is
|
396
|
+
# received. once the header has been received, the encrypted data
|
397
|
+
# key is sent to the server for decryption. after the header has
|
398
|
+
# been successfully handled, this function always decrypts all of
|
399
|
+
# the data in its internal buffer *except* for however many bytes
|
400
|
+
# are specified by the algorithm's tag size. see the end() function
|
401
|
+
# for details.
|
402
|
+
|
403
|
+
raise RuntimeError, 'Decryption is not Started' if !@decryption_started
|
404
|
+
|
405
|
+
# Append the incoming data in the internal data buffer
|
406
|
+
@data = @data + data
|
407
|
+
|
408
|
+
# if there is no key or 'dec' member of key, then the code is still trying to build a complete header
|
409
|
+
if !@key.present? or !@key.key?("dec")
|
410
|
+
struct_length = [1,1,1,1,1].pack('CCCCn').length
|
411
|
+
packed_struct = @data[0...struct_length]
|
412
|
+
|
413
|
+
# Does the buffer contain enough of the header to
|
414
|
+
# determine the lengths of the initialization vector
|
415
|
+
# and the key?
|
416
|
+
if @data.length > struct_length
|
417
|
+
# Unpack the values packed in encryption
|
418
|
+
version, flag_for_later, algorithm_id, iv_length, key_length = packed_struct.unpack('CCCCn')
|
419
|
+
|
420
|
+
# verify flag and version are 0
|
421
|
+
raise RuntimeError, 'invalid encryption header' if version != 0 or flag_for_later != 0
|
422
|
+
|
423
|
+
# Does the buffer contain the entire header?
|
424
|
+
if @data.length > struct_length + iv_length + key_length
|
425
|
+
# Extract the initialization vector
|
426
|
+
iv = @data[struct_length...iv_length + struct_length]
|
427
|
+
# Extract the encryped key
|
428
|
+
encrypted_key = @data[struct_length + iv_length...key_length + struct_length + iv_length]
|
429
|
+
# Remove the header from the buffer
|
430
|
+
@data = @data[struct_length + iv_length + key_length..-1]
|
431
|
+
|
432
|
+
# generate a local identifier for the key
|
433
|
+
hash_sha512 = OpenSSL::Digest::SHA512.new
|
434
|
+
hash_sha512 << encrypted_key
|
435
|
+
client_id = hash_sha512.digest
|
436
|
+
|
437
|
+
if @key.present?
|
438
|
+
if @key['client_id'] != client_id
|
439
|
+
close()
|
440
|
+
end
|
442
441
|
end
|
443
|
-
end
|
444
442
|
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
443
|
+
# IF key object not exists, request a new one from the server
|
444
|
+
if !@key.present?
|
445
|
+
url = endpoint_base + "/decryption/key"
|
446
|
+
query = {encrypted_data_key: Base64.strict_encode64(encrypted_key)}
|
447
|
+
headers = Auth.build_headers(@papi, @sapi, endpoint, query, @host, 'post')
|
448
|
+
|
449
|
+
response = HTTParty.post(
|
450
|
+
url,
|
451
|
+
body: query.to_json,
|
452
|
+
headers: headers
|
453
|
+
)
|
454
|
+
|
455
|
+
# Response status is 200 OK
|
456
|
+
if response.code == WEBrick::HTTPStatus::RC_OK
|
457
|
+
@key = {}
|
458
|
+
@key['finger_print'] = response['key_fingerprint']
|
459
|
+
@key['client_id'] = client_id
|
460
|
+
@key['session'] = response['encryption_session']
|
461
|
+
|
462
|
+
@key['algorithm'] = 'aes-256-gcm'
|
463
|
+
|
464
|
+
encrypted_private_key = response['encrypted_private_key']
|
465
|
+
# Decrypt the encryped private key using SRSA
|
466
|
+
private_key = OpenSSL::PKey::RSA.new(encrypted_private_key,@srsa)
|
467
|
+
|
468
|
+
wrapped_data_key = response['wrapped_data_key']
|
469
|
+
# Decode WDK from base64 format
|
470
|
+
wdk = Base64.strict_decode64(wrapped_data_key)
|
471
|
+
# Use private key to decrypt the wrapped data key
|
472
|
+
dk = private_key.private_decrypt(wdk,OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
473
|
+
|
474
|
+
@key['raw'] = dk
|
475
|
+
@key['uses'] = 0
|
476
|
+
else
|
477
|
+
# Raise the error if response is not 200
|
478
|
+
raise RuntimeError, "HTTPError Response: Expected 201, got #{response.code}"
|
479
|
+
end
|
481
480
|
end
|
482
|
-
end
|
483
481
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
482
|
+
# If the key object exists, create a new decryptor
|
483
|
+
# with the initialization vector from the header and
|
484
|
+
# the decrypted key (which is either new from the
|
485
|
+
# server or cached from the previous decryption). in
|
486
|
+
# either case, increment the key usage
|
489
487
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
488
|
+
if @key.present?
|
489
|
+
@algo = Algo.new.get_algo(@key['algorithm'])
|
490
|
+
@key['dec'] = Algo.new.decryptor(@algo, @key['raw'], iv)
|
491
|
+
@key['uses'] += 1
|
492
|
+
end
|
494
493
|
end
|
495
494
|
end
|
496
495
|
end
|
497
|
-
end
|
498
496
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
497
|
+
# if the object has a key and a decryptor, then decrypt whatever
|
498
|
+
# data is in the buffer, less any data that needs to be saved to
|
499
|
+
# serve as the tag.
|
500
|
+
plain_text = ''
|
501
|
+
if @key.present? and @key.key?("dec")
|
502
|
+
size = @data.length - @algo[:tag_length]
|
503
|
+
if size > 0
|
504
|
+
plain_text = @key['dec'].update(@data[0..size-1])
|
505
|
+
@data = @data[size..-1]
|
506
|
+
end
|
507
|
+
return plain_text
|
510
508
|
end
|
511
|
-
return plain_text
|
512
|
-
end
|
513
509
|
|
514
|
-
end
|
510
|
+
end
|
515
511
|
|
516
|
-
def end
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
512
|
+
def end
|
513
|
+
raise RuntimeError, 'Decryption is not Started' if !@decryption_started
|
514
|
+
# The update function always maintains tag-size bytes in
|
515
|
+
# the buffer because this function provides no data parameter.
|
516
|
+
# by the time the caller calls this function, all data must
|
517
|
+
# have already been input to the decryption object.
|
522
518
|
|
523
|
-
|
519
|
+
sz = @data.length - @algo[:tag_length]
|
524
520
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
521
|
+
raise RuntimeError, 'Invalid Tag!' if sz < 0
|
522
|
+
if sz == 0
|
523
|
+
@key['dec'].auth_tag = @data
|
524
|
+
begin
|
525
|
+
pt = @key['dec'].final
|
526
|
+
# Delete the decryptor context
|
527
|
+
@key.delete('dec')
|
528
|
+
# Return the decrypted plain data
|
529
|
+
@decryption_started = false
|
530
|
+
return pt
|
531
|
+
rescue Exception => e
|
532
|
+
print 'Invalid cipher data or tag!'
|
533
|
+
return ''
|
534
|
+
end
|
538
535
|
end
|
539
536
|
end
|
540
|
-
end
|
541
537
|
|
542
538
|
def close
|
543
539
|
raise RuntimeError, 'Decryption currently running' if @decryption_started
|
data/security_client.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ["vinay.ymca@gmail.com"]
|
10
10
|
|
11
11
|
spec.summary = %q{Ubiq Security ruby client}
|
12
|
-
|
12
|
+
spec.homepage = "https://github.com/vinaymehta/security_client"
|
13
13
|
# Specify which files should be added to the gem when it is released.
|
14
14
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: security_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vinaymehta
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -70,7 +70,7 @@ files:
|
|
70
70
|
- lib/security_client.rb
|
71
71
|
- lib/security_client/version.rb
|
72
72
|
- security_client.gemspec
|
73
|
-
homepage:
|
73
|
+
homepage: https://github.com/vinaymehta/security_client
|
74
74
|
licenses: []
|
75
75
|
metadata: {}
|
76
76
|
post_install_message:
|