passvault 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|