tanker-core 2.4.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|