passvault 0.1.2
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.
- data/.gitignore +3 -0
- data/Laurent_Vansteenberghe.zip +0 -0
- data/README.md +65 -0
- data/bin/passvault +3 -0
- data/bin/passvault-gui +3 -0
- data/lib/bytes_manip.rb +47 -0
- data/lib/conn_cmds.rb +337 -0
- data/lib/conn_constants.rb +116 -0
- data/lib/conn_init.rb +92 -0
- data/lib/conn_transmit.rb +96 -0
- data/lib/connection.rb +28 -0
- data/lib/console_ui.rb +243 -0
- data/lib/crypto.rb +146 -0
- data/lib/file_access.rb +103 -0
- data/lib/gui.rb +270 -0
- data/lib/key_settings.rb +124 -0
- data/lib/rights.rb +6 -0
- data/lib/vault.rb +357 -0
- data/passvault.gemspec +30 -0
- data/pres/secu.pdf +0 -0
- data/pres/secu.pptx +0 -0
- metadata +136 -0
data/lib/key_settings.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Rights
|
2
|
+
|
3
|
+
# Used in communication settings in place of a key, to indicate that an
|
4
|
+
# authentication with the key being changed should be performed in order to
|
5
|
+
# change a key.
|
6
|
+
CHANGED_KEY = 0xE
|
7
|
+
|
8
|
+
# Used in communication settings in place of a key, to indicate that a key
|
9
|
+
# cannot be changed.
|
10
|
+
IMMUTABLE = 0xF
|
11
|
+
|
12
|
+
# The key settings determine the level of authentication needed to perform
|
13
|
+
# operations on the tag (DESFire) top-level, or on an application.
|
14
|
+
class KeySettings
|
15
|
+
|
16
|
+
# Is the application/tag master key modifiable? Takes values 0 or 1.
|
17
|
+
attr_reader :master_change
|
18
|
+
|
19
|
+
# Can the commands (+GetFileIDs+, +GetFileSettings+, +GetKeySettings+) /
|
20
|
+
# (+GetApplicationIDs+, +GetKeySettings+) be performed without application/tag
|
21
|
+
# master key authentication? Takes values 0 or 1.
|
22
|
+
attr_reader :auth_get
|
23
|
+
|
24
|
+
# Can files/applications be created or deleted / created without
|
25
|
+
# application/tag master key authentication? Takes values 0 or 1.
|
26
|
+
attr_reader :auth_create
|
27
|
+
|
28
|
+
# Can the key settings for this application/tag be changed again? Takes values
|
29
|
+
# 0 or 1.
|
30
|
+
attr_reader :settings_change
|
31
|
+
|
32
|
+
# Only defined for application keys settings. Defines the key needed to change
|
33
|
+
# application keys. It can be an application key number between 0x0 and 0xE
|
34
|
+
# (inclusive, 0x0 being the application master key) or one of the constants
|
35
|
+
# +CHANGED_KEY+ (authenticated with the key that will be changed) or
|
36
|
+
# +IMMUTABLE+ (keys cannot be changed) (constants defined in the +Rights+
|
37
|
+
# module). Takes a value +key+ such that (0x0 <= +key+ <= 0xF).
|
38
|
+
attr_reader :change_key
|
39
|
+
|
40
|
+
# The constructor takes a hash of parameters, which correspond to the class
|
41
|
+
# attributes.
|
42
|
+
def initialize(hash={})
|
43
|
+
@master_change = hash[:master_change] || 1
|
44
|
+
@auth_get = hash[:auth_get] || 1
|
45
|
+
@auth_create = hash[:auth_create] || 0
|
46
|
+
@settings_change = hash[:settings_change] || 1
|
47
|
+
@change_key = hash[:change_key] || 0x0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Creates a +KeySettings+ object from a byte representing the key settings.
|
51
|
+
def self.from_byte(byte)
|
52
|
+
KeySettings.new(
|
53
|
+
:master_change => (byte & 0x1) % 2,
|
54
|
+
:auth_get => (byte & 0x2) % 2,
|
55
|
+
:auth_create => (byte & 0x4) % 2,
|
56
|
+
:settings_change => (byte & 0x8) % 2,
|
57
|
+
:change_key => byte >> 4)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns a byte representing the key settings, fit to be used as a
|
61
|
+
# command parameter.
|
62
|
+
def to_byte
|
63
|
+
1 * @master_change +
|
64
|
+
2 * @auth_get +
|
65
|
+
4 * @auth_create +
|
66
|
+
8 * @settings_change +
|
67
|
+
16 * @change_key
|
68
|
+
end
|
69
|
+
|
70
|
+
# Can key +changing_key+ of application +aid+ be changed by authenticating
|
71
|
+
# with key +change_key+?
|
72
|
+
def can_change_key?(aid, changing_key, change_key)
|
73
|
+
if aid == 0
|
74
|
+
changing_key == 0 && change_key == 0
|
75
|
+
elsif @change_key == IMMUTABLE
|
76
|
+
false
|
77
|
+
elsif @change_key == CHANGED_KEY
|
78
|
+
change_key == changing_key
|
79
|
+
elsif @change_key == changing_key
|
80
|
+
change_key == 0
|
81
|
+
else
|
82
|
+
change_key == @change_key
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Can we perform the application-level +GetFileIDs+, +GetFileSettings+ and
|
87
|
+
# +GetKeySettings+ commands if authenticated with the given key on the given
|
88
|
+
# application?
|
89
|
+
def can_get?(key_no)
|
90
|
+
@auth_get == 1 or key_no == 0
|
91
|
+
end
|
92
|
+
|
93
|
+
# Can we perform the top-level +GetApplicationIDs+ and
|
94
|
+
# +GetKeySettings+) commands if authenticated with the given key?
|
95
|
+
def can_get0?(aid, key_no)
|
96
|
+
aid == 0 and (@auth_get == 1 or key_no == 0)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Can we create or delete a file if authenticated with the given key?
|
100
|
+
def can_edit_file?(key_no)
|
101
|
+
@auth_create == 1 or key_no == 0
|
102
|
+
end
|
103
|
+
|
104
|
+
# Can we create an application if authenticated with the given key on the
|
105
|
+
# given application?
|
106
|
+
def can_create_app?(aid, key_no)
|
107
|
+
aid == 0 and (@auth_create = 1 or key_no = 0)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Can we delete an application if authenticated with the given key on the given
|
111
|
+
# application?
|
112
|
+
def can_delete_app?(aid, key_no)
|
113
|
+
aid == 0 and key_no == 0
|
114
|
+
end
|
115
|
+
|
116
|
+
# Can we change this key settings on the tag's currently selected application
|
117
|
+
# if authenticated with the given key?
|
118
|
+
def can_change_key_settings?(key_no)
|
119
|
+
@settings_change and key_no == 0
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
data/lib/rights.rb
ADDED
data/lib/vault.rb
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
require_relative 'connection'
|
2
|
+
require_relative 'crypto'
|
3
|
+
require_relative 'rights'
|
4
|
+
|
5
|
+
# The Vault class represents a vault stored on a tag. It implements all
|
6
|
+
# operations that can be performed on the vault.
|
7
|
+
#
|
8
|
+
# A vault stores credentials as <tt>[name, login, pass, hmac]</tt>
|
9
|
+
# quadruplets. When we say "a credential" we refer to this quadruplet. The name
|
10
|
+
# is ciphered with AES. The later three elements are ciphered together using
|
11
|
+
# AES. The three first elements are padded with zeroes to attain their mandated
|
12
|
+
# length (see constants). The HMAC is computed on unpadded (name || login ||
|
13
|
+
# pass). The three keys are derives from the vault password using PKBDF2,
|
14
|
+
# altough they use different salt. All the salt include the tag UID, and the
|
15
|
+
# salt for the ciphering of the last three elements include the credential name.
|
16
|
+
#
|
17
|
+
# For a credential, 112 bytes of data are stored of memory. This is a multiple
|
18
|
+
# of 8, so no padding is required in AES.
|
19
|
+
#
|
20
|
+
# When a vault object is initialized, it scans the tag to get a map from
|
21
|
+
# credential names to the file where the credential is stored. It also builds
|
22
|
+
# lists of unused applications ids and of unused file ids on existing
|
23
|
+
# applications.
|
24
|
+
class Vault
|
25
|
+
include Crypto
|
26
|
+
include Rights
|
27
|
+
|
28
|
+
# This is the default tag master key, and the initial key for new
|
29
|
+
# applications.
|
30
|
+
ZERO_KEY = Array.new(16, 0).pack('C*')
|
31
|
+
|
32
|
+
# These are the access rights we use when creating a new file.
|
33
|
+
FILE_RIGHTS = FileAccess.new(
|
34
|
+
read: 0, write: 0, rw: 0, change: 0, com_set: CS_CIPHERED)
|
35
|
+
|
36
|
+
# These are the key settings for the tag master key.
|
37
|
+
# This is set when creating the vault.
|
38
|
+
TAG_KEY_SETTINGS = KeySettings.new(change_key: 0x0,
|
39
|
+
master_change: 1, auth_get: 1, auth_create: 1, settings_change: 1)
|
40
|
+
|
41
|
+
# These are the key settings for new applications.
|
42
|
+
VAULT_KEY_SETTINGS = KeySettings.new(
|
43
|
+
master_change: 1, auth_get: 0, auth_create: 0, settings_change: 0)
|
44
|
+
|
45
|
+
# Number of files that a DESFire application can hold.
|
46
|
+
NB_FILES = 15
|
47
|
+
|
48
|
+
# The length of the name of a credential in the tag's memory. This is the
|
49
|
+
# maximum length for a name, and a padding of nul characters is added to reach
|
50
|
+
# this length if needed.
|
51
|
+
NAME_LEN = 16
|
52
|
+
|
53
|
+
# The length of the login in a credential in the tag's memory. This is the
|
54
|
+
# maximum length for a login, and a padding of nul characters is added to
|
55
|
+
# reach this length if needed.
|
56
|
+
LOGIN_LEN = 32
|
57
|
+
|
58
|
+
# The length of the password in a credential in the tag's memory. This is the
|
59
|
+
# maximum length for a password, and a padding of nul characters is added to
|
60
|
+
# reach this length if needed.
|
61
|
+
PASS_LEN = 32
|
62
|
+
|
63
|
+
# The total length on a credential in the tag's memory.
|
64
|
+
TOTAL_LEN = NAME_LEN + LOGIN_LEN + PASS_LEN + HMAC_LEN
|
65
|
+
|
66
|
+
# Raised if the tag contents changed while the program was running.
|
67
|
+
TAG_CHANGED_ERROR =
|
68
|
+
'Tag content changed while the program was running.'
|
69
|
+
|
70
|
+
# Raised if the tag memory becomes full.
|
71
|
+
TAG_FULL_ERROR =
|
72
|
+
'Tag full, remove some credentials to be able to add other.'
|
73
|
+
|
74
|
+
# Raised when we are supplied an unknown credential name.
|
75
|
+
UNKNOWN_NAME_ERROR =
|
76
|
+
'The credential name is unknown. Use "list" to view existing names.'
|
77
|
+
|
78
|
+
# Raised if we are supplied an incorrect vault or tag password.
|
79
|
+
AUTHENTICATION_ERROR =
|
80
|
+
'The supplied password is incorrect, please try again.'
|
81
|
+
|
82
|
+
# Raised if we detect that the tag memory has been tempered with.
|
83
|
+
TEMPER_ERROR =
|
84
|
+
'The card has been tempered with (invalid signature).'
|
85
|
+
|
86
|
+
# Text used for credential errors where the name is already used.
|
87
|
+
NAME_IN_USE_TEXT =
|
88
|
+
'Credential name already in use, choose another one.'
|
89
|
+
|
90
|
+
# Thrown when the user supplies an invalid credential (name, login or pass too
|
91
|
+
# long, or empty).
|
92
|
+
class CredentialError < StandardError ; end
|
93
|
+
|
94
|
+
public #######################################################################
|
95
|
+
|
96
|
+
# Erase the vault from the tag and restore it to a pristine state
|
97
|
+
# (memory wiped, tag master key set to +ZERO_KEY+).
|
98
|
+
def erase(tag_pass)
|
99
|
+
deauthenticate()
|
100
|
+
tag_key = derive_tag_key(tag_pass)
|
101
|
+
authentication { @conn.select_app_auth(0, tag_key) }
|
102
|
+
@conn.format()
|
103
|
+
@conn.change_key(0, tag_key, ZERO_KEY)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Reset the vault: erases the tag and creates a new vault on it using the
|
107
|
+
# given vault master password.
|
108
|
+
def reset(tag_pass, vault_pass)
|
109
|
+
deauthenticate()
|
110
|
+
tag_key = derive_tag_key(tag_pass)
|
111
|
+
vault_key = derive_key(vault_pass, @uid, 16)
|
112
|
+
authentication { @conn.select_app_auth(0, tag_key) }
|
113
|
+
@conn.format()
|
114
|
+
@conn.change_key_settings(TAG_KEY_SETTINGS)
|
115
|
+
ensure_free_file([], [1], vault_key) # create application 1
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a list of credentials names.
|
119
|
+
def credentials_names
|
120
|
+
return @locations.each_key()
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the <tt>[login, pass]</tt> from the named credential.
|
124
|
+
def credential(name)
|
125
|
+
_ ,_ ,content = get_location(name)
|
126
|
+
triplet_aes = content.byteslice(NAME_LEN, LOGIN_LEN + PASS_LEN + HMAC_LEN)
|
127
|
+
triplet = aes_decipher(triplet_aes, derive_aes_key(name))
|
128
|
+
|
129
|
+
login = triplet.byteslice(0, LOGIN_LEN).tr("\x00", '')
|
130
|
+
pass = triplet.byteslice(LOGIN_LEN, PASS_LEN).tr("\x00", '')
|
131
|
+
hmac = triplet.byteslice(LOGIN_LEN + PASS_LEN, HMAC_LEN)
|
132
|
+
|
133
|
+
raise TEMPER_ERROR if hmac != hmac(name + login + pass, derive_aes_key('hmac'))
|
134
|
+
return [login, pass]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Adds a new credential to the vault.
|
138
|
+
def add(name, login, pass)
|
139
|
+
ensure_free_file(@free_files, @free_apps, @key)
|
140
|
+
aid, fid = @free_files.first
|
141
|
+
raise CredentialError.new(NAME_IN_USE_TEXT) unless @locations[name] == nil
|
142
|
+
write_credentials(name, login, pass, aid, fid)
|
143
|
+
# Do this only after that potential exception have been thrown.
|
144
|
+
@locations[name] = @free_files.shift
|
145
|
+
end
|
146
|
+
|
147
|
+
# Replace the login and password for the named credential.
|
148
|
+
def edit(name, login, pass)
|
149
|
+
aid, fid = get_location(name)
|
150
|
+
write_credentials(name, login, pass, aid, fid)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Remove the named credential from the vault.
|
154
|
+
def remove(name)
|
155
|
+
aid, fid, _ = get_location(name)
|
156
|
+
@free_files << [aid, fid]
|
157
|
+
@locations.delete(name)
|
158
|
+
@conn.select_app_auth(aid, @key)
|
159
|
+
# We do not remove the file, else the memory would be lost, instead
|
160
|
+
# we overwrite the previous credential with zeroes.
|
161
|
+
rand1 = Random.new(Random.new_seed).bytes(32)
|
162
|
+
rand2 = Random.new(Random.new_seed).bytes(32)
|
163
|
+
write_credentials('', rand1, rand2, aid, fid, true)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Set the password and derive the key, then scans the card for available
|
167
|
+
# credentials (see <tt>scan()</tt>).
|
168
|
+
def authenticate(pass)
|
169
|
+
key = derive_desfire_key(pass, @uid)
|
170
|
+
authentication { @conn.select_app_auth(1, key) }
|
171
|
+
@pass = pass
|
172
|
+
@key = key
|
173
|
+
@conn.select_app(0)
|
174
|
+
scan()
|
175
|
+
end
|
176
|
+
|
177
|
+
# Forget all that is known about the card password, key and content.
|
178
|
+
def deauthenticate
|
179
|
+
@pass = nil
|
180
|
+
@key = nil
|
181
|
+
unscan()
|
182
|
+
end
|
183
|
+
|
184
|
+
# Indicate if we know the key to access the card.
|
185
|
+
def authenticated?
|
186
|
+
return @pass != nil
|
187
|
+
end
|
188
|
+
|
189
|
+
# "Destroy" the +Vault+ object by releasing the connection to the tag. Leaves
|
190
|
+
# the object in an unusable state.
|
191
|
+
def destroy
|
192
|
+
deauthenticate()
|
193
|
+
@conn.disconnect
|
194
|
+
end
|
195
|
+
|
196
|
+
private ######################################################################
|
197
|
+
|
198
|
+
# Creates the +Vault+ object by establishing a connection to the tag,
|
199
|
+
# retrieving its UID, and setting some default values.
|
200
|
+
def initialize
|
201
|
+
@conn = Connection.new
|
202
|
+
@uid = @conn.get_uid().pack('C*')
|
203
|
+
@continue = true
|
204
|
+
@pass = nil
|
205
|
+
@key = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
# Try to run some authentication code. In case of failure, catch the throwed
|
209
|
+
# symbol and convert it into an exception.
|
210
|
+
def authentication(&block)
|
211
|
+
raise AUTHENTICATION_ERROR if
|
212
|
+
catch(:AUTHENTICATION_ERROR) do
|
213
|
+
raise AUTHENTICATION_ERROR if
|
214
|
+
catch(:authentication_failed) { block.call() ; false }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# * Sets <tt>@locations</tt> to a map from credential names to <tt>[aid,
|
219
|
+
# fid]</tt> pair.
|
220
|
+
#
|
221
|
+
# * Sets <tt>@free_files</tt> to a list of <tt>[aid, fid]</tt>
|
222
|
+
# pairs representing unused files.
|
223
|
+
#
|
224
|
+
# * Sets <tt>@free_apps</tt> to a list of unused applications ids.
|
225
|
+
def scan
|
226
|
+
@locations = {}
|
227
|
+
@free_files = []
|
228
|
+
@free_apps = (1..28).to_a
|
229
|
+
@conn.get_app_ids().each do |aid|
|
230
|
+
@free_apps.delete(aid)
|
231
|
+
add_free_files(aid)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Adds the free files in the application +aid+ to @free_files.
|
236
|
+
def add_free_files(aid)
|
237
|
+
@conn.select_app_auth(aid, @key)
|
238
|
+
@free_files += Array.new(NB_FILES, aid).zip(1..NB_FILES)
|
239
|
+
@conn.get_file_ids.each { |fid| check_free_file(aid, fid) }
|
240
|
+
end
|
241
|
+
|
242
|
+
# If the file <tt>[aid, fid]</tt> isn't free, remove it from the list of free
|
243
|
+
# files and update <tt>@locations</tt> according to the file's content.
|
244
|
+
def check_free_file(aid, fid)
|
245
|
+
content = @conn.read_whole_file(fid)
|
246
|
+
name = get_name(content)
|
247
|
+
if name != '' # if not an erased file
|
248
|
+
@free_files.delete([aid, fid])
|
249
|
+
@locations[name] = [aid, fid]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Get the name of a credential from the stored credential.
|
254
|
+
def get_name(content)
|
255
|
+
name_ciphered = content.byteslice(0, NAME_LEN)
|
256
|
+
name_deciphered = aes_decipher(name_ciphered, derive_aes_key(''))
|
257
|
+
return name_deciphered.tr("\x00", '')
|
258
|
+
end
|
259
|
+
|
260
|
+
# Erases all informations registered by <tt>scan()</tt>.
|
261
|
+
def unscan
|
262
|
+
@locations = nil
|
263
|
+
@free_files = nil
|
264
|
+
@free_apps = nil
|
265
|
+
end
|
266
|
+
|
267
|
+
# Derives the tag master key from the tag password. The password may have the
|
268
|
+
# special value <tt>:default</tt>, in which case +ZERO_KEY+ is used.
|
269
|
+
def derive_tag_key(tag_pass)
|
270
|
+
return ZERO_KEY if tag_pass == :default
|
271
|
+
return derive_desfire_key(tag_pass, @uid)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Returns the memory location where the named credential is stored and the
|
275
|
+
# encrypted credential stored there as an <tt>[aid, fid, content]</tt>
|
276
|
+
# array. Also authenticates ourselves with the returned aid, and ensures that
|
277
|
+
# the tag content did not change since we last scanned the tag.
|
278
|
+
def get_location(name)
|
279
|
+
raise UNKNOWN_NAME_ERROR if @locations[name] == nil
|
280
|
+
aid, fid = @locations[name]
|
281
|
+
@conn.select_app_auth(aid, @key)
|
282
|
+
content = @conn.read_whole_file(fid)
|
283
|
+
tag_name = get_name(content)
|
284
|
+
raise TAG_CHANGED_ERROR if name != tag_name
|
285
|
+
return [aid, fid, content]
|
286
|
+
end
|
287
|
+
|
288
|
+
# Derives the AES key used to store a credential from the credential name.
|
289
|
+
# The credential name is used as salt in addition to the tag's UID. The key is
|
290
|
+
# derived from the vault master password.
|
291
|
+
def derive_aes_key(name)
|
292
|
+
return derive_key(@pass, @uid + name, 32)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Ensure there is an unused <tt>[aid, fid]</tt> pair in <tt>@free_files</tt>
|
296
|
+
# available to add a credential to the vault (this does not actually create
|
297
|
+
# the file, but might create an application). Note that this does *not* ensure
|
298
|
+
# that there is memory left on the vault, as this can only be detected by
|
299
|
+
# writing to the vault.
|
300
|
+
def ensure_free_file(free_files, free_apps, vault_key)
|
301
|
+
return unless free_files.empty?
|
302
|
+
raise TAG_FULL_ERROR if free_apps.empty?
|
303
|
+
|
304
|
+
new_app = free_apps.shift
|
305
|
+
@conn.select_app(0)
|
306
|
+
@conn.create_app(new_app, 1, VAULT_KEY_SETTINGS)
|
307
|
+
@conn.select_app_auth(new_app, ZERO_KEY)
|
308
|
+
@conn.change_key(0, ZERO_KEY, vault_key)
|
309
|
+
free_files += Array.new(NB_FILES, new_app).zip(1..NB_FILES)
|
310
|
+
end
|
311
|
+
|
312
|
+
# Write the supplied credential to the supplied location on the tag. This
|
313
|
+
# does not modify any class variable.
|
314
|
+
def write_credentials(name, login, pass, aid, fid, bypass_check=false)
|
315
|
+
check_credential_sanity(name, login, pass) unless bypass_check
|
316
|
+
@conn.select_app_auth(aid, @key)
|
317
|
+
|
318
|
+
padded_name = name.ljust(NAME_LEN, "\x00")
|
319
|
+
padded_login = login.ljust(LOGIN_LEN, "\x00")
|
320
|
+
padded_pass = pass.ljust(PASS_LEN, "\x00")
|
321
|
+
hmac_data = hmac(name + login + pass, derive_aes_key('hmac'))
|
322
|
+
triplet = padded_login + padded_pass + hmac_data
|
323
|
+
ciph_name = aes_encipher(padded_name, derive_aes_key(''))
|
324
|
+
ciph_triplet = aes_encipher(triplet, derive_aes_key(name))
|
325
|
+
content = ciph_name + ciph_triplet
|
326
|
+
|
327
|
+
raise TAG_FULL_ERROR if
|
328
|
+
catch :OUT_OF_EEPROM_ERROR do
|
329
|
+
# If the file already exiting (e.g. when editing it), there is no need
|
330
|
+
# to create the file.
|
331
|
+
catch :DUPLICATE_ERROR do
|
332
|
+
@conn.create_file(fid, FILE_RIGHTS, TOTAL_LEN)
|
333
|
+
end
|
334
|
+
false
|
335
|
+
end
|
336
|
+
@conn.write_file(fid, 0, TOTAL_LEN, content)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Check if the supplied name, login and password are neither too long nor too
|
340
|
+
# short.
|
341
|
+
def check_credential_sanity(name, login, pass)
|
342
|
+
if name.length > NAME_LEN
|
343
|
+
raise CredentialError.new("Name too long (max #{NAME_LEN} chars.)")
|
344
|
+
elsif login.length > LOGIN_LEN
|
345
|
+
raise CredentialError.new("Login too long (max #{LOGIN_LEN} chars.)")
|
346
|
+
elsif pass.length > PASS_LEN
|
347
|
+
raise CredentialError.new("Pass too long (max #{PASS_LEN} chars.)")
|
348
|
+
elsif name.length == 0
|
349
|
+
raise CredentialError.new('Empty name')
|
350
|
+
elsif pass.length == 0
|
351
|
+
raise CredentialError.new('Empty pass')
|
352
|
+
elsif login.length == 0
|
353
|
+
raise CredentialError.new('Empty login')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
end
|