tanker-core 2.4.0.alpha.1
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 +7 -0
- data/LICENSE +13 -0
- data/README.rst +30 -0
- data/lib/tanker-core.rb +3 -0
- data/lib/tanker/admin.rb +51 -0
- data/lib/tanker/admin/app.rb +21 -0
- data/lib/tanker/admin/c_admin.rb +29 -0
- data/lib/tanker/admin/c_admin/c_app_descriptor.rb +23 -0
- data/lib/tanker/c_tanker.rb +107 -0
- data/lib/tanker/c_tanker/c_device_info.rb +24 -0
- data/lib/tanker/c_tanker/c_event.rb +11 -0
- data/lib/tanker/c_tanker/c_future.rb +41 -0
- data/lib/tanker/c_tanker/c_log_record.rb +12 -0
- data/lib/tanker/c_tanker/c_string.rb +27 -0
- data/lib/tanker/c_tanker/c_tanker_error.rb +11 -0
- data/lib/tanker/c_tanker/c_verification.rb +68 -0
- data/lib/tanker/c_tanker/c_verification_method.rb +35 -0
- data/lib/tanker/core.rb +17 -0
- data/lib/tanker/core/attach_result.rb +12 -0
- data/lib/tanker/core/encryption.rb +109 -0
- data/lib/tanker/core/encryption_session.rb +52 -0
- data/lib/tanker/core/group.rb +17 -0
- data/lib/tanker/core/init.rb +43 -0
- data/lib/tanker/core/log_record.rb +20 -0
- data/lib/tanker/core/options.rb +33 -0
- data/lib/tanker/core/session.rb +86 -0
- data/lib/tanker/core/status.rb +9 -0
- data/lib/tanker/core/stream.rb +231 -0
- data/lib/tanker/core/verification.rb +50 -0
- data/lib/tanker/core/verification_method.rb +27 -0
- data/lib/tanker/core/version.rb +11 -0
- data/lib/tanker/error.rb +35 -0
- data/lib/tanker/sharing_options.rb +42 -0
- data/vendor/libctanker/linux64/tanker/lib/libctanker.so +0 -0
- metadata +192 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
require 'tanker/core/verification_method'
|
5
|
+
require 'tanker/c_tanker/c_string'
|
6
|
+
|
7
|
+
module Tanker
|
8
|
+
module CTanker
|
9
|
+
class CVerificationMethod < FFI::Struct
|
10
|
+
layout :version, :uint8,
|
11
|
+
:type, :uint8,
|
12
|
+
:email, :pointer
|
13
|
+
|
14
|
+
TYPE_EMAIL = 1
|
15
|
+
TYPE_PASSPHRASE = 2
|
16
|
+
TYPE_VERIFICATION_KEY = 3
|
17
|
+
TYPE_OIDC_ID_TOKEN = 4
|
18
|
+
|
19
|
+
def to_verification_method
|
20
|
+
case self[:type]
|
21
|
+
when TYPE_EMAIL
|
22
|
+
EmailVerificationMethod.new(self[:email].read_string.force_encoding(Encoding::UTF_8))
|
23
|
+
when TYPE_PASSPHRASE
|
24
|
+
PassphraseVerificationMethod.new
|
25
|
+
when TYPE_VERIFICATION_KEY
|
26
|
+
VerificationKeyVerificationMethod.new
|
27
|
+
when TYPE_OIDC_ID_TOKEN
|
28
|
+
OIDCIDTokenVerificationMethod.new
|
29
|
+
else
|
30
|
+
raise "Unknown VerificationMethod type #{self[:type]}!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/tanker/core.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
# always keep this file first, it defines the module and the class
|
5
|
+
require_relative 'core/version'
|
6
|
+
require_relative 'c_tanker'
|
7
|
+
require_relative 'error'
|
8
|
+
require_relative 'core/session'
|
9
|
+
require_relative 'core/verification'
|
10
|
+
require_relative 'core/encryption'
|
11
|
+
require_relative 'core/stream'
|
12
|
+
require_relative 'sharing_options'
|
13
|
+
require_relative 'core/group'
|
14
|
+
require_relative 'core/encryption_session'
|
15
|
+
require_relative 'core/log_record'
|
16
|
+
require_relative 'core/attach_result'
|
17
|
+
require_relative 'core/init'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tanker/c_tanker'
|
4
|
+
require_relative 'encryption_session'
|
5
|
+
|
6
|
+
module Tanker
|
7
|
+
class Core
|
8
|
+
def encrypt_data(data, encryption_options = nil)
|
9
|
+
unless data.is_a?(String)
|
10
|
+
raise TypeError, "expected data to be an ASCII-8BIT binary String, but got a #{data.class}"
|
11
|
+
end
|
12
|
+
unless data.encoding == Encoding::ASCII_8BIT
|
13
|
+
raise ArgumentError, "expected data to be an ASCII-8BIT binary String, but it was #{data.encoding} encoded"
|
14
|
+
end
|
15
|
+
|
16
|
+
encrypt_common data, encryption_options
|
17
|
+
end
|
18
|
+
|
19
|
+
def encrypt_utf8(str, encryption_options = nil)
|
20
|
+
ASSERT_UTF8.call(str)
|
21
|
+
|
22
|
+
encrypt_common str, encryption_options
|
23
|
+
end
|
24
|
+
|
25
|
+
def decrypt_data(data)
|
26
|
+
inbuf = FFI::MemoryPointer.from_string(data)
|
27
|
+
|
28
|
+
decrypted_size = CTanker.tanker_decrypted_size(inbuf, data.bytesize).get.address
|
29
|
+
outbuf = FFI::MemoryPointer.new(:char, decrypted_size)
|
30
|
+
|
31
|
+
CTanker.tanker_decrypt(@ctanker, outbuf, inbuf, data.bytesize).get
|
32
|
+
|
33
|
+
outbuf.read_string decrypted_size
|
34
|
+
end
|
35
|
+
|
36
|
+
def decrypt_utf8(data)
|
37
|
+
decrypted = decrypt_data data
|
38
|
+
decrypted.force_encoding(Encoding::UTF_8)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_resource_id(data)
|
42
|
+
unless data.is_a?(String)
|
43
|
+
raise TypeError, "expected data to be an ASCII-8BIT binary String, but got a #{data.class}"
|
44
|
+
end
|
45
|
+
unless data.encoding == Encoding::ASCII_8BIT
|
46
|
+
raise ArgumentError, "expected data to be an ASCII-8BIT binary String, but it was #{data.encoding} encoded"
|
47
|
+
end
|
48
|
+
|
49
|
+
inbuf = FFI::MemoryPointer.from_string(data)
|
50
|
+
CTanker.tanker_get_resource_id(inbuf, data.bytesize).get_string
|
51
|
+
end
|
52
|
+
|
53
|
+
def share(resource_ids, sharing_options)
|
54
|
+
unless resource_ids.is_a?(Array)
|
55
|
+
raise TypeError, "expected resource_ids to be an array of strings, but got a #{resource_ids.class}"
|
56
|
+
end
|
57
|
+
unless sharing_options.is_a?(SharingOptions)
|
58
|
+
raise TypeError, "expected sharing_options to be a SharingOptions, but got a #{sharing_options.class}"
|
59
|
+
end
|
60
|
+
|
61
|
+
cresource_ids = CTanker.new_cstring_array resource_ids
|
62
|
+
cusers = sharing_options[:recipient_public_identities]
|
63
|
+
nb_cusers = sharing_options[:nb_recipient_public_identities]
|
64
|
+
cgroups = sharing_options[:recipient_group_ids]
|
65
|
+
nb_cgroups = sharing_options[:nb_recipient_group_ids]
|
66
|
+
|
67
|
+
CTanker.tanker_share(@ctanker, cusers, nb_cusers,
|
68
|
+
cgroups, nb_cgroups,
|
69
|
+
cresource_ids, resource_ids.length).get
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_encryption_session(sharing_options = nil)
|
73
|
+
if sharing_options.nil?
|
74
|
+
cusers = nil
|
75
|
+
nb_cusers = 0
|
76
|
+
cgroups = nil
|
77
|
+
nb_cgroups = 0
|
78
|
+
else
|
79
|
+
cusers = sharing_options[:recipient_public_identities]
|
80
|
+
nb_cusers = sharing_options[:nb_recipient_public_identities]
|
81
|
+
cgroups = sharing_options[:recipient_group_ids]
|
82
|
+
nb_cgroups = sharing_options[:nb_recipient_group_ids]
|
83
|
+
end
|
84
|
+
|
85
|
+
csession = CTanker.tanker_encryption_session_open(@ctanker, cusers, nb_cusers,
|
86
|
+
cgroups, nb_cgroups).get
|
87
|
+
EncryptionSession.new(csession)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.prehash_password(str)
|
91
|
+
ASSERT_UTF8.call(str)
|
92
|
+
|
93
|
+
CTanker.tanker_prehash_password(str).get_string
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def encrypt_common(data, encryption_options = nil)
|
99
|
+
inbuf = FFI::MemoryPointer.from_string(data)
|
100
|
+
|
101
|
+
encrypted_size = CTanker.tanker_encrypted_size data.bytesize
|
102
|
+
outbuf = FFI::MemoryPointer.new(:char, encrypted_size)
|
103
|
+
|
104
|
+
CTanker.tanker_encrypt(@ctanker, outbuf, inbuf, data.bytesize, encryption_options).get
|
105
|
+
|
106
|
+
outbuf.read_string encrypted_size
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tanker/c_tanker'
|
4
|
+
require 'tanker/core/stream'
|
5
|
+
|
6
|
+
module Tanker
|
7
|
+
class Core::EncryptionSession
|
8
|
+
def initialize(csession)
|
9
|
+
@csession = csession
|
10
|
+
csession_addr = @csession.address
|
11
|
+
ObjectSpace.define_finalizer(@csession) do |_|
|
12
|
+
CTanker.tanker_encryption_session_close(FFI::Pointer.new(:void, csession_addr)).get
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def encrypt_data(data)
|
17
|
+
unless data.is_a?(String)
|
18
|
+
raise TypeError, "expected data to be an ASCII-8BIT binary String, but got a #{data.class}"
|
19
|
+
end
|
20
|
+
unless data.encoding == Encoding::ASCII_8BIT
|
21
|
+
raise ArgumentError, "expected data to be an ASCII-8BIT binary String, but it was #{data.encoding} encoded"
|
22
|
+
end
|
23
|
+
|
24
|
+
encrypt_common(data)
|
25
|
+
end
|
26
|
+
|
27
|
+
def encrypt_utf8(str)
|
28
|
+
ASSERT_UTF8.call(str)
|
29
|
+
|
30
|
+
encrypt_common str
|
31
|
+
end
|
32
|
+
|
33
|
+
def encrypt_common(data)
|
34
|
+
inbuf = FFI::MemoryPointer.from_string(data)
|
35
|
+
|
36
|
+
encrypted_size = CTanker.tanker_encryption_session_encrypted_size data.bytesize
|
37
|
+
outbuf = FFI::MemoryPointer.new(:char, encrypted_size)
|
38
|
+
|
39
|
+
CTanker.tanker_encryption_session_encrypt(@csession, outbuf, inbuf, data.bytesize).get
|
40
|
+
|
41
|
+
outbuf.read_string encrypted_size
|
42
|
+
end
|
43
|
+
|
44
|
+
def encrypt_stream(stream)
|
45
|
+
Stream.do_stream_action(stream) { |cb| CTanker.tanker_encryption_session_stream_encrypt(@csession, cb, nil) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource_id
|
49
|
+
CTanker.tanker_encryption_session_get_resource_id(@csession).get_string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tanker/c_tanker'
|
4
|
+
|
5
|
+
module Tanker
|
6
|
+
class Core
|
7
|
+
def create_group(member_identities)
|
8
|
+
cmember_identities = CTanker.new_cstring_array member_identities
|
9
|
+
CTanker.tanker_create_group(@ctanker, cmember_identities, member_identities.length).get_string
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_group_members(group_id, users_to_add:)
|
13
|
+
cidentities = CTanker.new_cstring_array users_to_add
|
14
|
+
CTanker.tanker_update_group_members(@ctanker, group_id, cidentities, users_to_add.length).get
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tanker
|
4
|
+
# Main entry point for the Tanker SDK. Can open a Tanker session.
|
5
|
+
class Core
|
6
|
+
CTanker.tanker_init
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@revoke_event_handlers = Set.new
|
10
|
+
@ctanker = CTanker.tanker_create(options).get
|
11
|
+
ctanker_addr = @ctanker.address
|
12
|
+
ObjectSpace.define_finalizer(@ctanker) do |_|
|
13
|
+
CTanker.tanker_destroy(FFI::Pointer.new(:void, ctanker_addr)).get
|
14
|
+
end
|
15
|
+
|
16
|
+
@device_revoked_handler = ->(_) { @revoke_event_handlers.each(&:call) }
|
17
|
+
CTanker.tanker_event_connect(@ctanker, CTanker::CTankerEvent::DEVICE_REVOKED, @device_revoked_handler, nil).get
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.set_log_handler(&block) # rubocop:disable Naming/AccessorMethodName
|
21
|
+
@log_handler = lambda do |clog|
|
22
|
+
block.call LogRecord.new clog[:category], clog[:level], clog[:file], clog[:line], clog[:message]
|
23
|
+
end
|
24
|
+
CTanker.tanker_set_log_handler @log_handler
|
25
|
+
end
|
26
|
+
|
27
|
+
def connect_device_revoked_handler(&block)
|
28
|
+
@revoke_event_handlers.add block
|
29
|
+
end
|
30
|
+
|
31
|
+
def disconnect_handler(&block)
|
32
|
+
@revoke_event_handlers.delete block
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
ASSERT_UTF8 = lambda do |str|
|
37
|
+
raise TypeError, "expected a String, but got a #{str.class}" unless str.is_a?(String)
|
38
|
+
|
39
|
+
encoding = str.encoding # Workaround rubocop bug
|
40
|
+
raise ArgumentError, "expected an UTF-8 String, but it was #{encoding} encoded" unless encoding == Encoding::UTF_8
|
41
|
+
end
|
42
|
+
private_constant :ASSERT_UTF8
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Tanker::Core
|
4
|
+
class LogRecord
|
5
|
+
attr_reader :category, :level, :file, :line, :message
|
6
|
+
|
7
|
+
LEVEL_DEBUG = 1
|
8
|
+
LEVEL_INFO = 2
|
9
|
+
LEVEL_WARNING = 3
|
10
|
+
LEVEL_ERROR = 4
|
11
|
+
|
12
|
+
def initialize(category, level, file, line, message)
|
13
|
+
@category = category
|
14
|
+
@level = level
|
15
|
+
@file = file
|
16
|
+
@line = line
|
17
|
+
@message = message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
require 'tanker/c_tanker/c_string'
|
5
|
+
|
6
|
+
module Tanker
|
7
|
+
# Options that can be given when opening a Tanker session
|
8
|
+
class Core::Options < FFI::Struct
|
9
|
+
layout :version, :uint8,
|
10
|
+
:app_id, :pointer,
|
11
|
+
:url, :pointer,
|
12
|
+
:writable_path, :pointer,
|
13
|
+
:sdk_type, :pointer,
|
14
|
+
:sdk_version, :pointer
|
15
|
+
|
16
|
+
SDK_TYPE = CTanker.new_cstring 'client-ruby'
|
17
|
+
SDK_VERSION = CTanker.new_cstring Core::VERSION
|
18
|
+
|
19
|
+
def initialize(app_id:, url: nil, writable_path: nil)
|
20
|
+
# Note: Instance variables are required to keep the CStrings alive
|
21
|
+
@app_id = CTanker.new_cstring app_id
|
22
|
+
@url = CTanker.new_cstring url
|
23
|
+
@writable_path = CTanker.new_cstring writable_path
|
24
|
+
|
25
|
+
self[:version] = 2
|
26
|
+
self[:app_id] = @app_id
|
27
|
+
self[:url] = @url
|
28
|
+
self[:writable_path] = @writable_path
|
29
|
+
self[:sdk_type] = SDK_TYPE
|
30
|
+
self[:sdk_version] = SDK_VERSION
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tanker/c_tanker'
|
4
|
+
require_relative 'status'
|
5
|
+
|
6
|
+
module Tanker
|
7
|
+
class Core
|
8
|
+
def start(identity)
|
9
|
+
CTanker.tanker_start(@ctanker, identity).get.address
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_verification_key
|
13
|
+
CTanker.tanker_generate_verification_key(@ctanker).get_string
|
14
|
+
end
|
15
|
+
|
16
|
+
def register_identity(verification)
|
17
|
+
cverif = CTanker::CVerification.new(verification)
|
18
|
+
CTanker.tanker_register_identity(@ctanker, cverif).get
|
19
|
+
end
|
20
|
+
|
21
|
+
def verify_identity(verification)
|
22
|
+
cverif = CTanker::CVerification.new(verification)
|
23
|
+
CTanker.tanker_verify_identity(@ctanker, cverif).get
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_verification_method(verification) # rubocop:disable Naming/AccessorMethodName (this is not a setter)
|
27
|
+
cverif = CTanker::CVerification.new(verification)
|
28
|
+
CTanker.tanker_set_verification_method(@ctanker, cverif).get
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_verification_methods # rubocop:disable Naming/AccessorMethodName
|
32
|
+
method_list_ptr = CTanker.tanker_get_verification_methods(@ctanker).get
|
33
|
+
count = method_list_ptr.get(:uint32, FFI::Pointer.size)
|
34
|
+
|
35
|
+
method_base_addr = method_list_ptr.read_pointer
|
36
|
+
method_list = count.times.map do |i|
|
37
|
+
method_ptr = method_base_addr + i * CTanker::CVerificationMethod.size
|
38
|
+
CTanker::CVerificationMethod.new(method_ptr).to_verification_method
|
39
|
+
end
|
40
|
+
CTanker.tanker_free_verification_method_list method_list_ptr
|
41
|
+
method_list
|
42
|
+
end
|
43
|
+
|
44
|
+
def device_id
|
45
|
+
CTanker.tanker_device_id(@ctanker).get_string
|
46
|
+
end
|
47
|
+
|
48
|
+
def device_list
|
49
|
+
device_list_ptr = CTanker.tanker_get_device_list(@ctanker).get
|
50
|
+
count = device_list_ptr.get(:uint32, FFI::Pointer.size)
|
51
|
+
|
52
|
+
method_base_addr = device_list_ptr.read_pointer
|
53
|
+
device_info_list = count.times.map do |i|
|
54
|
+
method_ptr = method_base_addr + i * CTanker::CDeviceInfo.size
|
55
|
+
CTanker::CDeviceInfo.new(method_ptr)
|
56
|
+
end
|
57
|
+
CTanker.tanker_free_device_list device_list_ptr
|
58
|
+
device_info_list
|
59
|
+
end
|
60
|
+
|
61
|
+
def revoke_device(device_id)
|
62
|
+
CTanker.tanker_revoke_device(@ctanker, device_id).get
|
63
|
+
end
|
64
|
+
|
65
|
+
def stop
|
66
|
+
CTanker.tanker_stop(@ctanker).get
|
67
|
+
end
|
68
|
+
|
69
|
+
def status
|
70
|
+
CTanker.tanker_status(@ctanker)
|
71
|
+
end
|
72
|
+
|
73
|
+
def attach_provisional_identity(provisional_identity)
|
74
|
+
attach_ptr = CTanker.tanker_attach_provisional_identity(@ctanker, provisional_identity).get
|
75
|
+
attach_status = attach_ptr.get(:uint8, 1)
|
76
|
+
method_ptr = attach_ptr.get_pointer(FFI::Pointer.size)
|
77
|
+
method = CTanker::CVerificationMethod.new(method_ptr).to_verification_method
|
78
|
+
AttachResult.new attach_status, method
|
79
|
+
end
|
80
|
+
|
81
|
+
def verify_provisional_identity(verification)
|
82
|
+
cverif = CTanker::CVerification.new(verification)
|
83
|
+
CTanker.tanker_verify_provisional_identity(@ctanker, cverif).get
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|