Class: Vault
Overview
The Vault class represents a vault stored on a tag. It implements all operations that can be performed on the vault.
A vault stores credentials as [name, login, pass, hmac] quadruplets. When we say “a credential” we refer to this quadruplet. The name is ciphered with AES. The later three elements are ciphered together using AES. The three first elements are padded with zeroes to attain their mandated length (see constants). The HMAC is computed on unpadded (name || login || pass). The three keys are derives from the vault password using PKBDF2, altough they use different salt. All the salt include the tag UID, and the salt for the ciphering of the last three elements include the credential name.
For a credential, 112 bytes of data are stored of memory. This is a multiple of 8, so no padding is required in AES.
When a vault object is initialized, it scans the tag to get a map from credential names to the file where the credential is stored. It also builds lists of unused applications ids and of unused file ids on existing applications.
Defined Under Namespace
Classes: CredentialError
Constant Summary collapse
- ZERO_KEY =
This is the default tag master key, and the initial key for new applications.
Array.new(16, 0).pack('C*')
- FILE_RIGHTS =
These are the access rights we use when creating a new file.
FileAccess.new( read: 0, write: 0, rw: 0, change: 0, com_set: CS_CIPHERED)
- TAG_KEY_SETTINGS =
These are the key settings for the tag master key. This is set when creating the vault.
KeySettings.new(change_key: 0x0, master_change: 1, auth_get: 1, auth_create: 1, settings_change: 1)
- VAULT_KEY_SETTINGS =
These are the key settings for new applications.
KeySettings.new( master_change: 1, auth_get: 0, auth_create: 0, settings_change: 0)
- NB_FILES =
Number of files that a DESFire application can hold.
15- NAME_LEN =
The length of the name of a credential in the tag’s memory. This is the maximum length for a name, and a padding of nul characters is added to reach this length if needed.
16- LOGIN_LEN =
The length of the login in a credential in the tag’s memory. This is the maximum length for a login, and a padding of nul characters is added to reach this length if needed.
32- PASS_LEN =
The length of the password in a credential in the tag’s memory. This is the maximum length for a password, and a padding of nul characters is added to reach this length if needed.
32- TOTAL_LEN =
The total length on a credential in the tag’s memory.
NAME_LEN + LOGIN_LEN + PASS_LEN + HMAC_LEN
- TAG_CHANGED_ERROR =
Raised if the tag contents changed while the program was running.
'Tag content changed while the program was running.'- TAG_FULL_ERROR =
Raised if the tag memory becomes full.
'Tag full, remove some credentials to be able to add other.'- UNKNOWN_NAME_ERROR =
Raised when we are supplied an unknown credential name.
'The credential name is unknown. Use "list" to view existing names.'- AUTHENTICATION_ERROR =
Raised if we are supplied an incorrect vault or tag password.
'The supplied password is incorrect, please try again.'- TEMPER_ERROR =
Raised if we detect that the tag memory has been tempered with.
'The card has been tempered with (invalid signature).'- NAME_IN_USE_TEXT =
Text used for credential errors where the name is already used.
'Credential name already in use, choose another one.'
Constants included from Rights
Rights::CHANGED_KEY, Rights::CS_CIPHERED, Rights::CS_PLAIN, Rights::CS_PLAIN_MAC, Rights::DENY_ACCESS, Rights::FREE_ACCESS, Rights::IMMUTABLE
Constants included from Crypto
Crypto::DES_BLEN, Crypto::HMAC_LEN, Crypto::ZERO_IV
Instance Method Summary collapse
-
#add(name, login, pass) ⇒ Object
Adds a new credential to the vault.
-
#authenticate(pass) ⇒ Object
Set the password and derive the key, then scans the card for available credentials (see
scan()). -
#authenticated? ⇒ Boolean
Indicate if we know the key to access the card.
-
#credential(name) ⇒ Object
Returns the
[login, pass]from the named credential. -
#credentials_names ⇒ Object
Returns a list of credentials names.
-
#deauthenticate ⇒ Object
Forget all that is known about the card password, key and content.
-
#destroy ⇒ Object
“Destroy” the
Vaultobject by releasing the connection to the tag. -
#edit(name, login, pass) ⇒ Object
Replace the login and password for the named credential.
-
#erase(tag_pass) ⇒ Object
Erase the vault from the tag and restore it to a pristine state (memory wiped, tag master key set to
ZERO_KEY). -
#remove(name) ⇒ Object
Remove the named credential from the vault.
-
#reset(tag_pass, vault_pass) ⇒ Object
Reset the vault: erases the tag and creates a new vault on it using the given vault master password.
Methods included from Crypto
#aes_decipher, #aes_encipher, #decipher_receive, #decipher_send, #derive_desfire_key, #derive_key, #encipher_receive, #encipher_send, #hmac
Methods included from BytesManipulation
#bs_rotate, #bs_xor, #desfire_crc, #to_hex_array, #to_le_array
Instance Method Details
#add(name, login, pass) ⇒ Object
Adds a new credential to the vault.
138 139 140 141 142 143 144 145 |
# File 'lib/vault.rb', line 138 def add(name, login, pass) ensure_free_file(@free_files, @free_apps, @key) aid, fid = @free_files.first raise CredentialError.new(NAME_IN_USE_TEXT) unless @locations[name] == nil write_credentials(name, login, pass, aid, fid) # Do this only after that potential exception have been thrown. @locations[name] = @free_files.shift end |
#authenticate(pass) ⇒ Object
Set the password and derive the key, then scans the card for available credentials (see scan()).
168 169 170 171 172 173 174 175 |
# File 'lib/vault.rb', line 168 def authenticate(pass) key = derive_desfire_key(pass, @uid) authentication { @conn.select_app_auth(1, key) } @pass = pass @key = key @conn.select_app(0) scan() end |
#authenticated? ⇒ Boolean
Indicate if we know the key to access the card.
185 186 187 |
# File 'lib/vault.rb', line 185 def authenticated? return @pass != nil end |
#credential(name) ⇒ Object
Returns the [login, pass] from the named credential.
124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/vault.rb', line 124 def credential(name) _ ,_ ,content = get_location(name) triplet_aes = content.byteslice(NAME_LEN, LOGIN_LEN + PASS_LEN + HMAC_LEN) triplet = aes_decipher(triplet_aes, derive_aes_key(name)) login = triplet.byteslice(0, LOGIN_LEN).tr("\x00", '') pass = triplet.byteslice(LOGIN_LEN, PASS_LEN).tr("\x00", '') hmac = triplet.byteslice(LOGIN_LEN + PASS_LEN, HMAC_LEN) raise TEMPER_ERROR if hmac != hmac(name + login + pass, derive_aes_key('hmac')) return [login, pass] end |
#credentials_names ⇒ Object
Returns a list of credentials names.
119 120 121 |
# File 'lib/vault.rb', line 119 def credentials_names return @locations.each_key() end |
#deauthenticate ⇒ Object
Forget all that is known about the card password, key and content.
178 179 180 181 182 |
# File 'lib/vault.rb', line 178 def deauthenticate @pass = nil @key = nil unscan() end |
#destroy ⇒ Object
“Destroy” the Vault object by releasing the connection to the tag. Leaves the object in an unusable state.
191 192 193 194 |
# File 'lib/vault.rb', line 191 def destroy deauthenticate() @conn.disconnect end |
#edit(name, login, pass) ⇒ Object
Replace the login and password for the named credential.
148 149 150 151 |
# File 'lib/vault.rb', line 148 def edit(name, login, pass) aid, fid = get_location(name) write_credentials(name, login, pass, aid, fid) end |
#erase(tag_pass) ⇒ Object
Erase the vault from the tag and restore it to a pristine state (memory wiped, tag master key set to ZERO_KEY).
98 99 100 101 102 103 104 |
# File 'lib/vault.rb', line 98 def erase(tag_pass) deauthenticate() tag_key = derive_tag_key(tag_pass) authentication { @conn.select_app_auth(0, tag_key) } @conn.format() @conn.change_key(0, tag_key, ZERO_KEY) end |
#remove(name) ⇒ Object
Remove the named credential from the vault.
154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/vault.rb', line 154 def remove(name) aid, fid, _ = get_location(name) @free_files << [aid, fid] @locations.delete(name) @conn.select_app_auth(aid, @key) # We do not remove the file, else the memory would be lost, instead # we overwrite the previous credential with zeroes. rand1 = Random.new(Random.new_seed).bytes(32) rand2 = Random.new(Random.new_seed).bytes(32) write_credentials('', rand1, rand2, aid, fid, true) end |
#reset(tag_pass, vault_pass) ⇒ Object
Reset the vault: erases the tag and creates a new vault on it using the given vault master password.
108 109 110 111 112 113 114 115 116 |
# File 'lib/vault.rb', line 108 def reset(tag_pass, vault_pass) deauthenticate() tag_key = derive_tag_key(tag_pass) vault_key = derive_key(vault_pass, @uid, 16) authentication { @conn.select_app_auth(0, tag_key) } @conn.format() @conn.change_key_settings(TAG_KEY_SETTINGS) ensure_free_file([], [1], vault_key) # create application 1 end |