vacman_controller 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a82c1e1da792a6d845ed3fa39e2ad0a7f24dc022
4
+ data.tar.gz: df608d69585f838e1d1591fc2c4ba1c6496e61ed
5
+ SHA512:
6
+ metadata.gz: f6348405cfdec6d7cdb5450625d9b8bdb5f08c034f0a93d7f27884f03f91560152b8e16d92195abfb157f896ce654dc3fef6463ff0e0d77184552cc206c20e29
7
+ data.tar.gz: 3a023343b469676f06956f1e14ec7dfc63301e397ea66ac50e827199dd97b293b65e73640607ef23ea51cb9c1350b88c2eb244f6bb4e0246e21287d816f7460b
@@ -0,0 +1,24 @@
1
+ require 'mkmf'
2
+
3
+ def find_vacman_controller
4
+ Dir.glob('/opt/vasco/VACMAN_Controller-*').sort.reverse.first
5
+ end
6
+
7
+ VACMAN_CONTROLLER = ENV['VACMAN_PATH'] || find_vacman_controller
8
+
9
+ if VACMAN_CONTROLLER
10
+ puts "Using VACMAN controller from #{VACMAN_CONTROLLER}"
11
+ else
12
+ puts "No VASCO Vacman controller found in /opt/vasco"
13
+ exit 1
14
+ end
15
+
16
+ append_cflags "-I#{VACMAN_CONTROLLER}/include -Wall"
17
+ append_ldflags "-L#{VACMAN_CONTROLLER}/lib -laal2sdk -Wl,-rpath #{VACMAN_CONTROLLER}/lib"
18
+
19
+ if find_library('aal2sdk', 'AAL2DPXInit', "#{VACMAN_CONTROLLER}/lib")
20
+ create_makefile('vacman_controller/vacman_controller')
21
+ else
22
+ puts "No libaal2sdk found"
23
+ exit 1
24
+ end
@@ -0,0 +1,394 @@
1
+ #include <ruby.h>
2
+ #include <string.h>
3
+ #include <aal2sdk.h>
4
+
5
+ static VALUE e_vacmanerror; // our ruby exception type
6
+ TKernelParms KernelParms; // Kernel Params
7
+
8
+ /*
9
+ * raise an error and tell wich method failed with wich error code
10
+ */
11
+ static void vacman_raise_error(char* method, int error_code) {
12
+ aat_ascii error_message[100];
13
+ AAL2GetErrorMsg (error_code, error_message);
14
+ rb_raise(e_vacmanerror, "%s error %d: %s", method, error_code, error_message);
15
+ }
16
+
17
+
18
+ /*
19
+ * convert a ruby hash to TDigipassBlob structure
20
+ */
21
+ static void rbhash_to_digipass(VALUE token, TDigipassBlob* dpdata) {
22
+ memset(dpdata, 0, sizeof(*dpdata));
23
+
24
+ VALUE blob = rb_hash_aref(token, rb_str_new2("blob"));
25
+ VALUE serial = rb_hash_aref(token, rb_str_new2("serial"));
26
+ VALUE app_name = rb_hash_aref(token, rb_str_new2("app_name"));
27
+ VALUE flag1 = rb_hash_aref(token, rb_str_new2("flags1"));
28
+ VALUE flag2 = rb_hash_aref(token, rb_str_new2("flags2"));
29
+
30
+ strcpy(dpdata->Blob, rb_string_value_cstr(&blob));
31
+ strncpy(dpdata->Serial, rb_string_value_cstr(&serial), sizeof(dpdata->Serial));
32
+ strncpy(dpdata->AppName, rb_string_value_cstr(&app_name), sizeof(dpdata->AppName));
33
+ dpdata->DPFlags[0] = rb_fix2int(flag1);
34
+ dpdata->DPFlags[1] = rb_fix2int(flag2);
35
+ }
36
+
37
+ static void digipass_to_rbhash(TDigipassBlob* dpdata, VALUE hash) {
38
+ char buffer[256];
39
+
40
+ memset(buffer, 0, sizeof(buffer));
41
+ strncpy(buffer, dpdata->Serial, 10);
42
+ rb_hash_aset(hash, rb_str_new2("serial"), rb_str_new2(buffer));
43
+
44
+ memset(buffer, 0, sizeof(buffer));
45
+ strncpy(buffer, dpdata->AppName, 12);
46
+ rb_hash_aset(hash, rb_str_new2("app_name"), rb_str_new2(buffer));
47
+
48
+ memset(buffer, 0, sizeof(buffer));
49
+ strncpy(buffer, dpdata->Blob, 224);
50
+ rb_hash_aset(hash, rb_str_new2("blob"), rb_str_new2(buffer));
51
+
52
+ rb_hash_aset(hash, rb_str_new2("flags1"), rb_fix_new(dpdata->DPFlags[0]));
53
+ rb_hash_aset(hash, rb_str_new2("flags2"), rb_fix_new(dpdata->DPFlags[1]));
54
+ }
55
+
56
+ /*
57
+ * Get library version and return it as an hash
58
+ */
59
+ static VALUE vacman_library_version(VALUE module) {
60
+ aat_ascii version[16];
61
+ aat_int32 version_len = sizeof(version);
62
+
63
+ aat_ascii bitness[4];
64
+ aat_int32 bitness_len = sizeof(bitness);
65
+
66
+ aat_ascii type[8];
67
+ aat_int32 type_len = sizeof(type);
68
+
69
+ aat_int32 result = AAL2GetLibraryVersion(version, &version_len, bitness,
70
+ &bitness_len, type, &type_len);
71
+
72
+ if (result != 0) {
73
+ vacman_raise_error("AAL2GetLibraryVersion", result);
74
+ return Qnil;
75
+ }
76
+
77
+ VALUE hash = rb_hash_new();
78
+ rb_hash_aset(hash, rb_str_new2("version"), rb_str_new2(version));
79
+ rb_hash_aset(hash, rb_str_new2("bitness"), rb_str_new2(bitness));
80
+ rb_hash_aset(hash, rb_str_new2("type"), rb_str_new2(type));
81
+
82
+ return hash;
83
+ }
84
+
85
+
86
+ /*
87
+ * generate a password
88
+ * this will not work with all the dpx files available, it must be prepared for it
89
+ */
90
+ static VALUE vacman_generate_password(VALUE module, VALUE token) {
91
+ TDigipassBlob dpdata;
92
+
93
+ rbhash_to_digipass(token, &dpdata);
94
+
95
+ aat_ascii password[18];
96
+ memset(password, 0, sizeof(password));
97
+
98
+ aat_int32 result = AAL2GenPassword(&dpdata, &KernelParms, password, NULL);
99
+ digipass_to_rbhash(&dpdata, token);
100
+
101
+ if (result != 0) {
102
+ vacman_raise_error("AAL2GenPassword", result);
103
+ return Qnil;
104
+ }
105
+
106
+ return rb_str_new2(password);
107
+ }
108
+
109
+
110
+ /*
111
+ * Properties names and IDs registry
112
+ */
113
+ struct token_property {
114
+ char *name;
115
+ aat_int32 id;
116
+ };
117
+ static struct token_property token_properties[] = {
118
+ {"token_model", TOKEN_MODEL },
119
+ {"token_status", TOKEN_STATUS },
120
+ {"use_count", USE_COUNT },
121
+ {"last_time_used", LAST_TIME_USED },
122
+ {"last_time_shift", LAST_TIME_SHIFT },
123
+ {"time_based_algo", TIME_BASED_ALGO },
124
+ {"event_based_algo", EVENT_BASED_ALGO },
125
+ {"pin_supported", PIN_SUPPORTED },
126
+ {"unlock_supported", UNLOCK_SUPPORTED },
127
+ {"pin_ch_on", PIN_CH_ON },
128
+ {"pin_change_enabled", PIN_CH_ON },
129
+ {"pin_len", PIN_LEN },
130
+ {"pin_length", PIN_LEN },
131
+ {"pin_min_len", PIN_MIN_LEN },
132
+ {"pin_minimum_length", PIN_MIN_LEN },
133
+ {"pin_enabled", PIN_ENABLED },
134
+ {"pin_ch_forced", PIN_CH_FORCED },
135
+ {"pin_change_forced", PIN_CH_FORCED },
136
+ {"virtual_token_type", VIRTUAL_TOKEN_TYPE },
137
+ {"virtual_token_grace_period", VIRTUAL_TOKEN_GRACE_PERIOD },
138
+ {"virtual_token_remain_use", VIRTUAL_TOKEN_REMAIN_USE },
139
+ {"last_response_type", LAST_RESPONSE_TYPE },
140
+ {"error_count", ERROR_COUNT },
141
+ {"event_value", EVENT_VALUE },
142
+ {"last_event_value", LAST_EVENT_VALUE },
143
+ {"sync_windows", SYNC_WINDOWS },
144
+ {"primary_token_enabled", PRIMARY_TOKEN_ENABLED },
145
+ {"virtual_token_supported", VIRTUAL_TOKEN_SUPPORTED },
146
+ {"virtual_token_enabled", VIRTUAL_TOKEN_ENABLED },
147
+ {"code_word", CODE_WORD },
148
+ {"auth_mode", AUTH_MODE },
149
+ {"ocra_suite", OCRA_SUITE },
150
+ {"derivation_supported", DERIVATION_SUPPORTED },
151
+ {"max_dtf_number", MAX_DTF_NUMBER },
152
+ {"response_len", RESPONSE_LEN },
153
+ {"response_length", RESPONSE_LEN },
154
+ {"response_format", RESPONSE_FORMAT },
155
+ {"response_chk", RESPONSE_CHK },
156
+ {"response_checksum", RESPONSE_CHK },
157
+ {"time_step", TIME_STEP },
158
+ {"use_3des", TRIPLE_DES_USED },
159
+ {"triple_des_used", TRIPLE_DES_USED },
160
+ };
161
+
162
+ static size_t properties_count = sizeof(token_properties)/sizeof(struct token_property);
163
+
164
+ /*
165
+ * Convert property name to property ID
166
+ */
167
+ static long vacman_get_property_id(char *property_name) {
168
+ for (int i = 0; i < properties_count; i++) {
169
+ if (strcmp(property_name, token_properties[i].name) == 0) {
170
+ return token_properties[i].id;
171
+ }
172
+ }
173
+
174
+ rb_raise(e_vacmanerror, "Invalid property name `%s'", property_name);
175
+ return 0;
176
+ }
177
+
178
+
179
+ /*
180
+ * Get token properties
181
+ */
182
+ static VALUE vacman_get_token_property(VALUE module, VALUE token, VALUE property) {
183
+ TDigipassBlob dpdata;
184
+ rbhash_to_digipass(token, &dpdata);
185
+
186
+ aat_ascii value[64];
187
+ aat_int32 property_id = vacman_get_property_id(StringValueCStr(property));
188
+ aat_int32 result = AAL2GetTokenProperty(&dpdata, &KernelParms, property_id, value);
189
+
190
+ if (result == 0) {
191
+ return rb_str_new2(value);
192
+ } else {
193
+ vacman_raise_error("AAL2GetTokenProperty", result);
194
+ return Qnil;
195
+ }
196
+ }
197
+
198
+
199
+ /*
200
+ * Set token properties
201
+ */
202
+ static VALUE vacman_set_token_property(VALUE module, VALUE token, VALUE property, VALUE rbval) {
203
+ TDigipassBlob dpdata;
204
+
205
+ aat_int32 property_id = vacman_get_property_id(StringValueCStr(property));
206
+ aat_int32 value = rb_fix2int(rbval);
207
+
208
+ rbhash_to_digipass(token, &dpdata);
209
+
210
+ aat_int32 result = AAL2SetTokenProperty(&dpdata, &KernelParms, property_id, value);
211
+
212
+ digipass_to_rbhash(&dpdata, token);
213
+
214
+ if (result == 0) {
215
+ return Qtrue;
216
+ } else {
217
+ vacman_raise_error("AAL2SetTokenProperty", result);
218
+ return Qnil;
219
+ }
220
+ }
221
+
222
+
223
+ /*
224
+ * verify password
225
+ * this is the main usecase, check the use input for authentication
226
+ */
227
+ static VALUE vacman_verify_password(VALUE module, VALUE token, VALUE password ) {
228
+ TDigipassBlob dpdata;
229
+
230
+ rbhash_to_digipass(token, &dpdata);
231
+
232
+ aat_int32 result = AAL2VerifyPassword(&dpdata, &KernelParms, rb_string_value_cstr(&password), 0);
233
+
234
+ digipass_to_rbhash(&dpdata, token);
235
+
236
+ if (result == 0)
237
+ return Qtrue;
238
+ else {
239
+ vacman_raise_error("AAL2VerifyPassword", result);
240
+ return Qnil;
241
+ }
242
+ }
243
+
244
+
245
+
246
+ /*
247
+ * Import a .DPX file containing token seeds and initialisation values.
248
+ *
249
+ * Pass the pre-shared key to validate it as the second argument. Or if
250
+ * you don't have the key, replace the DC line with one from a file you
251
+ * know the key. Yes.
252
+ */
253
+ static VALUE vacman_import(VALUE module, VALUE filename, VALUE key) {
254
+ TDPXHandle dpx_handle;
255
+ aat_int16 appl_count;
256
+ aat_ascii appl_names[13*8];
257
+ aat_int16 token_count;
258
+
259
+ aat_int32 result = AAL2DPXInit(&dpx_handle, rb_string_value_cstr(&filename), rb_string_value_cstr(&key),
260
+ &appl_count, appl_names, &token_count);
261
+
262
+ if (result != 0) {
263
+ vacman_raise_error("AAL2DPXInit", result);
264
+ return Qnil;
265
+ }
266
+
267
+ aat_ascii sw_out_serial_No[22+1];
268
+ aat_ascii sw_out_type[5+1];
269
+ aat_ascii sw_out_authmode[2+1];
270
+ TDigipassBlob dpdata;
271
+
272
+ VALUE list = rb_ary_new();
273
+
274
+ while (1) {
275
+ result = AAL2DPXGetToken(&dpx_handle,
276
+ &KernelParms,
277
+ appl_names,
278
+ sw_out_serial_No,
279
+ sw_out_type,
280
+ sw_out_authmode,
281
+ &dpdata);
282
+
283
+
284
+ if (result < 0) {
285
+ vacman_raise_error("AAL2DPXGetToken", result);
286
+ return Qnil;
287
+ }
288
+ if (result == 107) break;
289
+
290
+ VALUE hash = rb_hash_new();
291
+
292
+ digipass_to_rbhash(&dpdata, hash);
293
+
294
+ rb_ary_push(list, hash);
295
+ }
296
+
297
+ AAL2DPXClose(&dpx_handle);
298
+
299
+ return list;
300
+ }
301
+
302
+ struct kernel_property {
303
+ char *name;
304
+ aat_int32 *value;
305
+ aat_int32 deflt;
306
+ };
307
+ static struct kernel_property kernel_properties[] = {
308
+ { "ITimeWindow", &KernelParms.ITimeWindow, 30 }, // Identification Window size in time steps
309
+ { "STimeWindow", &KernelParms.STimeWindow, 24 }, // Signature Window size in secs
310
+ { "DiagLevel", &KernelParms.DiagLevel, 0 }, // Requested Diagnostic Level
311
+ { "GMTAdjust", &KernelParms.GMTAdjust, 0 }, // GMT Time adjustment to perform
312
+ { "CheckChallenge", &KernelParms.CheckChallenge, 0 }, // Verify Challenge Corrupted (mandatory for Gordian)
313
+ { "IThreshold", &KernelParms.IThreshold, 3 }, // Identification Error Threshold
314
+ { "SThreshold", &KernelParms.SThreshold, 1 }, // Signature Error Threshold
315
+ { "ChkInactDays", &KernelParms.ChkInactDays, 0 }, // Check Inactive Days
316
+ { "DeriveVector", &KernelParms.DeriveVector, 0 }, // Vector used to make Data Encryption unique
317
+ { "SyncWindow", &KernelParms.SyncWindow, 2 }, // Synchronisation Time Window (h)
318
+ { "OnLineSG", &KernelParms.OnLineSG, 2 }, // On line signature
319
+ { "EventWindow", &KernelParms.EventWindow, 100 }, // Event Window size in nbr of iterations
320
+ { "HSMSlotId", &KernelParms.HSMSlotId, 0 }, // HSM Slot id uses to store DB and Transport Key
321
+ };
322
+ static int kernel_properties_count = sizeof(kernel_properties)/sizeof(struct kernel_property);
323
+
324
+ /*
325
+ * Set kernel parameter
326
+ */
327
+ static VALUE vacman_set_kernel_param(VALUE module, VALUE paramname, VALUE rbval) {
328
+ char *name = StringValueCStr(paramname);
329
+ int value = rb_fix2int(rbval);
330
+
331
+ for (int i = 0; i < kernel_properties_count; i++) {
332
+ if (strcmp(name, kernel_properties[i].name) == 0) {
333
+ *kernel_properties[i].value = value;
334
+ return Qtrue;
335
+ }
336
+ }
337
+
338
+ rb_raise(e_vacmanerror, "Invalid kernel param %s", name);
339
+ return Qnil;
340
+ }
341
+
342
+
343
+ /*
344
+ * Get kernel parameter
345
+ */
346
+ static VALUE vacman_get_kernel_param(VALUE module, VALUE paramname) {
347
+ char *name = StringValueCStr(paramname);
348
+
349
+ for (int i = 0; i < kernel_properties_count; i++) {
350
+ if (strcmp(name, kernel_properties[i].name) == 0) {
351
+ return LONG2FIX(*kernel_properties[i].value);
352
+ }
353
+ }
354
+
355
+ rb_raise(e_vacmanerror, "Invalid kernel param %s", name);
356
+ return Qnil;
357
+ }
358
+
359
+ /*
360
+ * Init the kernel parameters, with their defaults
361
+ */
362
+ static void init_kernel_params() {
363
+ memset(&KernelParms, 0, sizeof(TKernelParms));
364
+
365
+ KernelParms.ParmCount = 19; /* Number of valid parameters in this list */
366
+
367
+ for (int i = 0; i < kernel_properties_count; i++) {
368
+ *kernel_properties[i].value = kernel_properties[i].deflt;
369
+ }
370
+ }
371
+
372
+
373
+ /*
374
+ * rubys entry point to load the extension
375
+ */
376
+ void Init_vacman_controller(void) {
377
+ VALUE vacman_module = rb_define_module("VacmanLowLevel");
378
+
379
+ e_vacmanerror = rb_define_class("VacmanError", rb_eStandardError);
380
+ init_kernel_params();
381
+
382
+ rb_define_singleton_method(vacman_module, "version", vacman_library_version, 0);
383
+
384
+ rb_define_singleton_method(vacman_module, "import", vacman_import, 2);
385
+
386
+ rb_define_singleton_method(vacman_module, "generate_password", vacman_generate_password, 1);
387
+ rb_define_singleton_method(vacman_module, "verify_password", vacman_verify_password, 2);
388
+
389
+ rb_define_singleton_method(vacman_module, "get_kernel_param", vacman_get_kernel_param, 1);
390
+ rb_define_singleton_method(vacman_module, "set_kernel_param", vacman_set_kernel_param, 2);
391
+
392
+ rb_define_singleton_method(vacman_module, "get_token_property", vacman_get_token_property, 2);
393
+ rb_define_singleton_method(vacman_module, "set_token_property", vacman_set_token_property, 3);
394
+ }
@@ -0,0 +1,141 @@
1
+ require 'vacman_controller/vacman_controller'
2
+
3
+ # Provides VACMAN Controller functionality to identify and authorize user via VASCO DIGIPASS tokens.
4
+ #
5
+ #
6
+ module VacmanController
7
+ extend self
8
+
9
+ # Import .dpx file containing the secure token-data
10
+ #
11
+ # == Parameters:
12
+ # filename::
13
+ # The path of the .dpx file to load
14
+ # key::
15
+ # The secure key to decrypt the dpx file
16
+ #
17
+ # == Returns:
18
+ # A list of hashes. Each hash contains
19
+ # serial: the serial number of the token
20
+ # blob: the blob containing some secret magic data
21
+ # app_name: the application name (the security method)
22
+ # flags1: some flags
23
+ # flags2: more flags
24
+ #
25
+ # this hash must be persisted and regained for each call of verify_password and generate_password
26
+ #
27
+ def import(filename, key)
28
+ VacmanLowLevel.import(filename, key)
29
+ end
30
+
31
+
32
+
33
+ # Generate a Password for a user. This does the same as hitting the button on your hardware token
34
+ #
35
+ # == Parameters:
36
+ # hash::
37
+ # The hash for a specific token (you get these in import)
38
+ #
39
+ # == Returns:
40
+ # The password string. The password is only valid for a period (todo: add method to change the period, currently its 30 seconds)
41
+ #
42
+ def generate_password(hash)
43
+ VacmanLowLevel.generate_password(hash)
44
+ end
45
+
46
+
47
+
48
+ # Verify a password. This is the usecase a user sends you a password generated by his token and we have to verify it.
49
+ #
50
+ # == Parameters:
51
+ # hash::
52
+ # The hash for a specific token (you get these in import)
53
+ # pw::
54
+ # The password provided by the user
55
+ #
56
+ # == Returns:
57
+ # true if the password is valid, false otherwise
58
+ #
59
+ # ATTENTION: it is very important to persist the hash afterwards!!!
60
+ #
61
+ def verify_password(hash, pw)
62
+ VacmanLowLevel.verify_password(hash, pw)
63
+ end
64
+
65
+
66
+
67
+ # Get a kernel parameter, wich is a basically a setting for vacman controller
68
+ #
69
+ # == Parameters:
70
+ # name::
71
+ # the param name. Most TKernelParms struct elements are accessible.
72
+ #
73
+ def get_kernel_param(name)
74
+ VacmanLowLevel.get_kernel_param(name)
75
+ end
76
+
77
+
78
+
79
+ # Change a kernel parameter, wich is a basically a setting for vacman controller
80
+ #
81
+ # == Parameters:
82
+ # name::
83
+ # the param name. Most TKernelParms struct elements are accessible.
84
+ # val::
85
+ # the fixnum value
86
+ #
87
+ def set_kernel_param(name, val)
88
+ VacmanLowLevel.set_kernel_param(name, val)
89
+ end
90
+
91
+
92
+
93
+ # Get a token single property
94
+ #
95
+ # == Parameters:
96
+ # hash::
97
+ # the hash for a specific token (you get these in import)
98
+ # property::
99
+ # the property names
100
+ #
101
+ # possible names:
102
+ #
103
+ # token_model
104
+ # use_count
105
+ # last_time_used
106
+ # last_time_shift
107
+ # time_based_algo
108
+ # event_based_algo
109
+ # pin_supported
110
+ # unlock_supported
111
+ # pin_change_enabled
112
+ # pin_length
113
+ # pin_minimum_length
114
+ # pin_enabled
115
+ # pin_change_forced
116
+ # virtual_token_type
117
+ # virtual_token_grace_period
118
+ # virtual_token_remain_use
119
+ # last_response_type
120
+ # error_count
121
+ # event_value
122
+ # last_event_value
123
+ # sync_windows
124
+ # primary_token_enabled
125
+ # virtual_token_supported
126
+ # virtual_token_enabled
127
+ # code_word
128
+ # auth_mode
129
+ # ocra_suite
130
+ # derivation_supported
131
+ # max_dtf_number
132
+ # response_length
133
+ # response_format
134
+ # response_checksum
135
+ # time_step
136
+ # use_3des
137
+ #
138
+ def get_token_property(hash, property)
139
+ VacmanLowLevel.get_token_property(hash, property)
140
+ end
141
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vacman_controller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcus Lankenau
8
+ - Marcello Barnaba
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-01-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake-compiler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: Authenticate user via vacman controller
29
+ email:
30
+ - marcus.lankenau@gmail.com
31
+ - marcello.barnaba@gmail.com
32
+ executables: []
33
+ extensions:
34
+ - ext/vacman_controller/extconf.rb
35
+ extra_rdoc_files: []
36
+ files:
37
+ - ext/vacman_controller/extconf.rb
38
+ - ext/vacman_controller/vacman_controller.c
39
+ - lib/vacman_controller.rb
40
+ homepage: http://github.com/ifad/vacman_controller
41
+ licenses: []
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.5.2.1
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Access to the vacman controller library
63
+ test_files: []