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
@@ -0,0 +1,116 @@
|
|
1
|
+
# Uncommented constants are DESFire comments.
|
2
|
+
class Connection
|
3
|
+
# This file holds the constants used for data transmission and commands.
|
4
|
+
#
|
5
|
+
# In another language, we would have spread the constants accross multiple
|
6
|
+
# classes (for the card, chip and reader constants) and then imported those
|
7
|
+
# constants. But in Ruby doing so might cause the constants to silently
|
8
|
+
# collide. Putting them in the same class enables warnings in case of constant
|
9
|
+
# redefinition.
|
10
|
+
|
11
|
+
# ============================================================================
|
12
|
+
# DESFire (card) related constants
|
13
|
+
# ============================================================================
|
14
|
+
|
15
|
+
# fixed fields values
|
16
|
+
|
17
|
+
# CLA field for DESFire commands.
|
18
|
+
CARD_CLA = 0x90
|
19
|
+
|
20
|
+
# SW1 field for DESFire answers.
|
21
|
+
SW1 = 0x91
|
22
|
+
|
23
|
+
# Hash from DESFire response codes name (symbols) to the codes (integers).
|
24
|
+
STATUS = {
|
25
|
+
:OPERATION_OK => 0x00,
|
26
|
+
:ADDITIONAL_FRAME => 0xAF,
|
27
|
+
:NO_CHANGES => 0x0C,
|
28
|
+
:ILLEGAL_COMMAND_CODE => 0x1C,
|
29
|
+
:INTEGRITY_ERROR => 0x1E,
|
30
|
+
:NO_SUCH_KEY => 0x40,
|
31
|
+
:LENGTH_ERROR => 0x7E,
|
32
|
+
:PERMISSION_DENIED => 0x9D,
|
33
|
+
:PARAMETER_ERROR => 0x9E,
|
34
|
+
:APPLICATION_NOT_FOUND => 0xA0,
|
35
|
+
:APPLICATION_INTEGRITY_ERROR => 0xA1,
|
36
|
+
:AUTHENTICATION_ERROR => 0xAE,
|
37
|
+
:BOUNDARY_ERROR => 0xBE,
|
38
|
+
:COMMAND_ABORTED => 0xCA,
|
39
|
+
:DUPLICATE_ERROR => 0xDE,
|
40
|
+
:FILE_NOT_FOUND => 0xF0,
|
41
|
+
:OUT_OF_EEPROM_ERROR => 0x0E,
|
42
|
+
}
|
43
|
+
|
44
|
+
CHANGE_KEY = 0xC4
|
45
|
+
CREATE_APPLICATION = 0xCA
|
46
|
+
CREATE_STD_DATA_FILE = 0xCD
|
47
|
+
GET_CIPHERED_NONCE = 0x0A
|
48
|
+
SELECT_APPLICATION = 0x5A
|
49
|
+
SEND_MORE_DATA = 0xAF
|
50
|
+
FORMAT_PICC = 0xFC
|
51
|
+
GET_APP_IDS = 0x6A
|
52
|
+
GET_FILE_IDS = 0x6F
|
53
|
+
DELETE_APP = 0xDA
|
54
|
+
DELETE_FILE = 0xDF
|
55
|
+
READ_DATA = 0xBD
|
56
|
+
WRITE_DATA = 0x3D
|
57
|
+
GET_KEY_SETTINGS = 0x45
|
58
|
+
GET_FILE_SETTINGS = 0xF5
|
59
|
+
GET_VERSION = 0x60
|
60
|
+
CHANGE_KEY_SETTINGS = 0x54
|
61
|
+
|
62
|
+
# Padding beginning marker when reading a whole file with encrypted
|
63
|
+
# communication.
|
64
|
+
WHOLE_FILE_PAD_MARKER = "\x80"
|
65
|
+
|
66
|
+
# ============================================================================
|
67
|
+
# PN532 (chip) related constants
|
68
|
+
# ============================================================================
|
69
|
+
|
70
|
+
# Communication direction: send data to the chip.
|
71
|
+
TO_CHIP = 0xD4
|
72
|
+
|
73
|
+
# Communicaiton direction: receive data from the chip.
|
74
|
+
FROM_CHIP = 0xD5
|
75
|
+
|
76
|
+
# PN532 command: poll for tags.
|
77
|
+
LIST_PASSIVE_TARGET = 0x4A
|
78
|
+
|
79
|
+
# PN532 command: send data to/from the chip.
|
80
|
+
DATA_EXCHANGE = 0x40
|
81
|
+
|
82
|
+
# PN532 response code (prefix).
|
83
|
+
LIST_RESPONSE = 0x4B
|
84
|
+
|
85
|
+
# PN532 data response codes (array) (prefix).
|
86
|
+
DATA_RESPONSE = [0x41, 0x00]
|
87
|
+
|
88
|
+
# PN532 success response codes (array) (suffix).
|
89
|
+
SUCCESS_SUFFIX = [0x90, 0x00]
|
90
|
+
|
91
|
+
# ============================================================================
|
92
|
+
# ACR112 (reader) releated constants
|
93
|
+
# ============================================================================
|
94
|
+
|
95
|
+
# ACR112: class field for pseudo-APDU commands.
|
96
|
+
READER_CLA = 0xFF
|
97
|
+
|
98
|
+
# ACR112 pseudo-APDU commands: send data.
|
99
|
+
DIRECT_TRANSMIT = 0x00
|
100
|
+
|
101
|
+
# ACR112 pseudo-APDU commands: retrieve an answer.
|
102
|
+
GET_RESPONSE = 0xC0
|
103
|
+
|
104
|
+
# ACR112 response code (SW1 field).
|
105
|
+
SUCCESS = 0x61
|
106
|
+
|
107
|
+
# ACR112 response code (SW1 field).
|
108
|
+
ERROR = 0x63
|
109
|
+
|
110
|
+
# ACR112 response codes (SW2 field for SW1=ERROR).
|
111
|
+
OPERATION_FAILED = 0x00
|
112
|
+
|
113
|
+
# ACR112 response codes (SW2 field for SW1=ERROR).
|
114
|
+
NO_ANSWER = 0x01
|
115
|
+
|
116
|
+
end
|
data/lib/conn_init.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
class Connection
|
2
|
+
# This files contains logic related to the initialization and termination of the
|
3
|
+
# connection.
|
4
|
+
|
5
|
+
# Connect to a card trough a reader.
|
6
|
+
def initialize()
|
7
|
+
super({}) # no options
|
8
|
+
|
9
|
+
@context = PCSC::Context.new
|
10
|
+
|
11
|
+
# find readers
|
12
|
+
begin
|
13
|
+
reader_names = @context.readers
|
14
|
+
rescue Exception => e
|
15
|
+
puts "No smartcard readers were detected."
|
16
|
+
disconnect
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
# select a single reader
|
21
|
+
if reader_names.length == 1
|
22
|
+
@reader_name = reader_names.first
|
23
|
+
else
|
24
|
+
puts "Multiple readers available, please select one by number."
|
25
|
+
@reader_name = nil
|
26
|
+
while @reader_name == nil
|
27
|
+
reader_names.each_with_index do |r,i|
|
28
|
+
puts "#{i}) #{r.strip}"
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
@reader_name = reader_names[gets.strip.to_i]
|
32
|
+
rescue
|
33
|
+
puts "Invalid selection."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# The following two lines are lifted from the supermethod, since they were
|
39
|
+
# the only thing of interest there, due to idiosyncraties of the ACR122.
|
40
|
+
|
41
|
+
# The card object is used to transmit data, tough we don't need to use it
|
42
|
+
# directly, as data transmission is wrapped by the exchange_apdu(apdu)
|
43
|
+
# function.
|
44
|
+
|
45
|
+
@card = @context.card(@reader_name, :shared)
|
46
|
+
|
47
|
+
# the Answer To Reset for the card
|
48
|
+
@atr = @card.info[:atr]
|
49
|
+
|
50
|
+
# Wait until a card is inserted in the reader. Normally the super() call
|
51
|
+
# takes care of this, but the ACR122 is a broken beast which always
|
52
|
+
# indicates that a card is present, even if it isn't the case.
|
53
|
+
|
54
|
+
msg_displayed = false
|
55
|
+
while true
|
56
|
+
begin
|
57
|
+
poll
|
58
|
+
break
|
59
|
+
rescue Smartcard::PCSC::Exception => e
|
60
|
+
status = e.pcsc_status_code
|
61
|
+
if status == Smartcard::PCSC::FFILib::Status[:comm_data_lost]
|
62
|
+
puts "No cards detected, please insert your card." if !msg_displayed
|
63
|
+
msg_displayed = true
|
64
|
+
else
|
65
|
+
puts "error: #{e.message}"
|
66
|
+
disconnect
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Search for a card. This succeeds if a card is detected, else it throws a
|
74
|
+
# <tt>Smartcard::PCSC::Exception</tt> with +pcsc_status_code+ field set to the
|
75
|
+
# <tt>:comm_data_lost</tt> status after some time.
|
76
|
+
def poll(max_tags = 1, baud = 0)
|
77
|
+
response = send_chip_cmd LIST_PASSIVE_TARGET, [max_tags, baud]
|
78
|
+
throw :protocol_error unless response[0] == LIST_RESPONSE
|
79
|
+
end
|
80
|
+
|
81
|
+
# Release all resources associated with the card.
|
82
|
+
def disconnect
|
83
|
+
# :unpower is necessary if we want to relaunch the program without unpluging
|
84
|
+
# the reader.
|
85
|
+
unless @card.nil?
|
86
|
+
@card.disconnect(:unpower)
|
87
|
+
@card = nil
|
88
|
+
end
|
89
|
+
super
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'crypto'
|
2
|
+
|
3
|
+
class Connection
|
4
|
+
# This file holds the implementation of the communication logic between the
|
5
|
+
# computer and the card. There are three "hardware" levels we need to go
|
6
|
+
# trought in order to send data to the card: the reader, the chip and the card
|
7
|
+
# itself. However the whole picture is a little more complex and looks like
|
8
|
+
# this:
|
9
|
+
#
|
10
|
+
# +---------------------------------------------------------------+
|
11
|
+
# | card_cmd -> card_apdu -> chip_cmd -> chip_apdu -> reader_apdu |
|
12
|
+
# +---------------------------------------------------------------+
|
13
|
+
#
|
14
|
+
# To each of the arrows in the diagram above correspond a method named
|
15
|
+
# send_<thing_before_the_arrow>. The function transforms the thing before the
|
16
|
+
# arrow into the thing after the arrow and passes it along the
|
17
|
+
# chain. Afterwards it gets a response for the thing after the arrow and
|
18
|
+
# transforms it into a response for the thing before the arrow, which is then
|
19
|
+
# returned.
|
20
|
+
#
|
21
|
+
# Transforming the responses usually consists of stripping some response codes
|
22
|
+
# from the output.
|
23
|
+
|
24
|
+
include Crypto
|
25
|
+
|
26
|
+
# Thrown when the contact with the card is lost.
|
27
|
+
CARD_CONTACT_LOST_ERROR =
|
28
|
+
'Contact with the tag was lost, please verify that the card is in the ' \
|
29
|
+
'reader, then relaunch the application.'
|
30
|
+
|
31
|
+
# Send a chip APDU trough the reader (by wrapping the chip APDU inside a
|
32
|
+
# reader pseudo-APDU) and return the chip response. This takes care of the
|
33
|
+
# reader response codes.
|
34
|
+
def send_chip_apdu(chip_apdu)
|
35
|
+
nbytes = chip_apdu.length
|
36
|
+
pseudo_apdu = [READER_CLA, DIRECT_TRANSMIT, 0x00, 0x00, nbytes, *chip_apdu]
|
37
|
+
reader_response = exchange_apdu(pseudo_apdu)
|
38
|
+
|
39
|
+
# check the reader response
|
40
|
+
case reader_response[0]
|
41
|
+
when ERROR
|
42
|
+
throw :operation_failed if reader_response[1] == OPERATION_FAILED
|
43
|
+
throw :no_answer if reader_response[1] == NO_ANSWER
|
44
|
+
throw :unknown_error
|
45
|
+
when SUCCESS
|
46
|
+
# fetch the actual card/chip response
|
47
|
+
fetch_len = reader_response[1]
|
48
|
+
pseudo_apdu = [READER_CLA, GET_RESPONSE, 0x00, 0x00, fetch_len]
|
49
|
+
exchange_apdu(pseudo_apdu)
|
50
|
+
else
|
51
|
+
throw :protocol_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Send a chip command and return the command response (not comprising the chip
|
56
|
+
# response codes).
|
57
|
+
#
|
58
|
+
# There are two useful commands: +DATA_EXCHANGE+ (used in send_car_apdu()) and
|
59
|
+
# +LIST_PASSIVE_TARGET+ (used by <tt>poll()</tt>).
|
60
|
+
def send_chip_cmd(cmd, args)
|
61
|
+
chip_apdu = [TO_CHIP, cmd, *args]
|
62
|
+
response = send_chip_apdu(chip_apdu)
|
63
|
+
len = response.length
|
64
|
+
throw :protocol_error unless
|
65
|
+
response[0] == FROM_CHIP &&
|
66
|
+
response[len-2..len-1] == SUCCESS_SUFFIX
|
67
|
+
return response[1..len-3]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Send a card APDU to the card and return the card's answer. This takes care
|
71
|
+
# of chip command DATA_EXCHANGE return codes.
|
72
|
+
def send_card_apdu(card_apdu, tag_no=1)
|
73
|
+
response = send_chip_cmd(DATA_EXCHANGE, [tag_no, *card_apdu])
|
74
|
+
raise CARD_CONTACT_LOST_ERROR unless response[0..1] == DATA_RESPONSE
|
75
|
+
return response[2..response.length-1]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Send a command to the card and return an array whose first element is the
|
79
|
+
# command response (not comprising the card response codes) and the second is
|
80
|
+
# a successful status (needed to know if additional frames are available).
|
81
|
+
def send_card_cmd(cmd, args=[])
|
82
|
+
apdu = args.length == 0 \
|
83
|
+
? [CARD_CLA, cmd, 0x00, 0x00, 0x00]
|
84
|
+
: [CARD_CLA, cmd, 0x00, 0x00, args.length, *args, 0x00]
|
85
|
+
response = send_card_apdu(apdu)
|
86
|
+
len = response.length
|
87
|
+
throw :protocol_error unless response[len-2] == SW1
|
88
|
+
status = response[len-1]
|
89
|
+
throw STATUS.key(status), true unless
|
90
|
+
status == STATUS[:OPERATION_OK] ||
|
91
|
+
status == STATUS[:ADDITIONAL_FRAME]
|
92
|
+
|
93
|
+
return [len < 3 ? [] : response[0..len-3], status]
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/lib/connection.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'smartcard'
|
2
|
+
|
3
|
+
# An instance of the class Connection represents a connection to a DESFire tag
|
4
|
+
# trough an ACR122 reader outfitted with PN532 chip. In our passworld vault
|
5
|
+
# program there is only one instance of this class.
|
6
|
+
#
|
7
|
+
# The class extends the class Smartcard::Iso::PcscTransport from which it
|
8
|
+
# inherits most notably the method <tt>exchange_apdu(apdu)</tt> which is used to
|
9
|
+
# send APDUs (Application Protocol Data Unit) to the reader.
|
10
|
+
#
|
11
|
+
# We found out that object-oriented decomposition didn't work well to model the
|
12
|
+
# mechanisms of data transmission to the card, as it is more functional in
|
13
|
+
# nature. We elected to model inside this single class. To improve readability,
|
14
|
+
# the class is split inside multiple files that cover different concerns. The
|
15
|
+
# files are described below in the <tt>connection.rb</tt> file.
|
16
|
+
class Connection < Smartcard::Iso::PcscTransport ; end
|
17
|
+
|
18
|
+
# Initialization and termination of the connection.
|
19
|
+
require_relative 'conn_init'
|
20
|
+
|
21
|
+
# Constants used for data transmission and commands.
|
22
|
+
require_relative 'conn_constants'
|
23
|
+
|
24
|
+
# Tranmission of APDUs at various levels (reader, chip, card).
|
25
|
+
require_relative 'conn_transmit'
|
26
|
+
|
27
|
+
# Functions wrapping commands to be sent to the card.
|
28
|
+
require_relative 'conn_cmds'
|
data/lib/console_ui.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'clipboard'
|
2
|
+
require 'highline/import'
|
3
|
+
require 'smartcard'
|
4
|
+
|
5
|
+
require_relative 'vault'
|
6
|
+
|
7
|
+
class ConsoleUI
|
8
|
+
|
9
|
+
# Displayed when running the console program.
|
10
|
+
HEADER = "
|
11
|
+
_____ _
|
12
|
+
| _ |___ ___ ___ _ _ _ ___ ___ _| |
|
13
|
+
| __| .'|_ -|_ -| | | | . | _| . |
|
14
|
+
|__| |__,|___|___|_____|___|_| |___|
|
15
|
+
|
16
|
+
|
17
|
+
_____ _ _
|
18
|
+
| | |___ _ _| | |_
|
19
|
+
| | | .'| | | | _|
|
20
|
+
\\___/|__,|___|_|_|
|
21
|
+
|
22
|
+
|
23
|
+
(Type \"help\" to display available commands.)"
|
24
|
+
|
25
|
+
# Time after which the the console program forgets the vault master password
|
26
|
+
# and the associated key.
|
27
|
+
TIMEOUT = 300
|
28
|
+
|
29
|
+
# The console program entry point.
|
30
|
+
def self.run
|
31
|
+
ui = ConsoleUI.new()
|
32
|
+
ui.loop()
|
33
|
+
rescue Smartcard::PCSC::Exception => e
|
34
|
+
case e.pcsc_status
|
35
|
+
when :reader_unavailable
|
36
|
+
puts "\nThe reader was disconnected or is otherwise unavailable. " \
|
37
|
+
"You can't disconnect the card during the execution of the program, " \
|
38
|
+
"even if you reconnect it before executing a command."
|
39
|
+
else
|
40
|
+
puts "\n#{e.message()}"
|
41
|
+
end
|
42
|
+
rescue Exception => e
|
43
|
+
puts "\n#{e.message()}"
|
44
|
+
ensure
|
45
|
+
ui.leave() if ui
|
46
|
+
end
|
47
|
+
|
48
|
+
# Loop on user input (commands).
|
49
|
+
def loop
|
50
|
+
puts HEADER
|
51
|
+
@continue = true
|
52
|
+
while @continue
|
53
|
+
if catch :timeout do
|
54
|
+
command = request('> ')
|
55
|
+
execute(command.strip)
|
56
|
+
false
|
57
|
+
end then
|
58
|
+
@vault.deauthenticate()
|
59
|
+
puts 'The prompt timed out, please re-do the last command.'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Exit the console program.
|
65
|
+
def leave
|
66
|
+
@vault.destroy()
|
67
|
+
end
|
68
|
+
|
69
|
+
private ######################################################################
|
70
|
+
|
71
|
+
# Initialize the UI by creating a new +Vault+ object.
|
72
|
+
def initialize
|
73
|
+
@vault = Vault.new()
|
74
|
+
end
|
75
|
+
|
76
|
+
# Display the help of the console program.
|
77
|
+
def help()
|
78
|
+
puts ''
|
79
|
+
puts '-- user commands --'
|
80
|
+
puts 'list : list the names of registered credentials'
|
81
|
+
puts 'display <name> : display a registered credential'
|
82
|
+
puts 'clip <name> : same as "display", but copies the password'
|
83
|
+
puts ' to the clipboard instead of displaying it'
|
84
|
+
puts 'add <name> : register a new credential'
|
85
|
+
puts 'remove <name> : remove a registered credential'
|
86
|
+
puts 'edit <name> : change a registered credential'
|
87
|
+
puts 'quit : exit the program'
|
88
|
+
puts 'help : display this help message'
|
89
|
+
puts ''
|
90
|
+
puts '-- administration commands --'
|
91
|
+
puts 'reset : reinitialize the vault'
|
92
|
+
puts 'erase : erase the vault from the tag'
|
93
|
+
puts ''
|
94
|
+
end
|
95
|
+
|
96
|
+
# Ask the user for input, and throws <tt>:timeout</tt> (with value +true+) if
|
97
|
+
# the user is inactive for more than +TIMEOUT+ seconds.
|
98
|
+
def request(prompt, &block)
|
99
|
+
time = Time.now.to_i
|
100
|
+
answer = ask("\n#{prompt}", &block)
|
101
|
+
time = Time.now.to_i - time
|
102
|
+
throw :timeout, true if time > TIMEOUT
|
103
|
+
answer
|
104
|
+
end
|
105
|
+
|
106
|
+
# Prompt the user for a password. The password won't show on the screen (like
|
107
|
+
# in the Unix login prompt).
|
108
|
+
def enter_pass(prompt)
|
109
|
+
request(prompt) do |p| p.echo = false end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Prompt for the tag's master password. Allows for a special case if the
|
113
|
+
# password is left empty.
|
114
|
+
def enter_tag_pass
|
115
|
+
master_prompt = 'Enter the tag master password (leave empty for default): '
|
116
|
+
pass = enter_pass(master_prompt)
|
117
|
+
pass = :default if pass == ""
|
118
|
+
return pass
|
119
|
+
end
|
120
|
+
|
121
|
+
# If not authenticated with the vault, prompt for the vault pass and perform
|
122
|
+
# the authentication.
|
123
|
+
def check_vault_pass
|
124
|
+
return true if @vault.authenticated?()
|
125
|
+
pass = enter_pass(
|
126
|
+
"Enter the vault master password \n" \
|
127
|
+
"(you will need to re-enter it after 5 minutes of inactivity): ")
|
128
|
+
@vault.authenticate(pass)
|
129
|
+
return true
|
130
|
+
rescue => e
|
131
|
+
return handle_auth_error(e)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Re-raise the exception if it is not <tt>Vault::AUTHENTICATION_ERROR</tt>,
|
135
|
+
# else display the approrpiate message and return false.
|
136
|
+
def handle_auth_error(exception)
|
137
|
+
raise exception unless exception.message == Vault::AUTHENTICATION_ERROR
|
138
|
+
puts "\n#{exception.message}"
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
|
142
|
+
# Execute a command entered by the user.
|
143
|
+
def execute(command)
|
144
|
+
# The regex will set $1 to the credential name.
|
145
|
+
name_regex = '(?<name>[[:graph:]]*)'
|
146
|
+
case command
|
147
|
+
when 'erase' ; erase()
|
148
|
+
when 'create', 'reset' ; reset()
|
149
|
+
when 'help' ; help()
|
150
|
+
when 'quit' ; @continue = false
|
151
|
+
when 'list' ; list()
|
152
|
+
when /display #{name_regex}/ ; display($1)
|
153
|
+
when /clip #{name_regex}/ ; clip($1)
|
154
|
+
when /add #{name_regex}/ ; add($1)
|
155
|
+
when /remove #{name_regex}/ ; remove($1)
|
156
|
+
when /edit #{name_regex}/ ; edit($1)
|
157
|
+
else ; unknown(command)
|
158
|
+
end
|
159
|
+
rescue Exception => e
|
160
|
+
raise e unless e.message == Vault::UNKNOWN_NAME_ERROR
|
161
|
+
puts "\n#{e.message}"
|
162
|
+
end
|
163
|
+
|
164
|
+
# See <tt>help()</tt> for a description.
|
165
|
+
def erase()
|
166
|
+
tag_pass = enter_tag_pass()
|
167
|
+
@vault.erase(tag_pass)
|
168
|
+
rescue => e
|
169
|
+
return handle_auth_error(e)
|
170
|
+
end
|
171
|
+
|
172
|
+
# See <tt>help()</tt> for a description.
|
173
|
+
def reset()
|
174
|
+
tag_pass = enter_tag_pass()
|
175
|
+
return unless tag_pass
|
176
|
+
vault_pass = enter_pass('Enter the new vault pass: ')
|
177
|
+
@vault.reset(tag_pass, vault_pass)
|
178
|
+
rescue => e
|
179
|
+
return handle_auth_error(e)
|
180
|
+
end
|
181
|
+
|
182
|
+
# See <tt>help()</tt> for a description.
|
183
|
+
def list()
|
184
|
+
return unless check_vault_pass()
|
185
|
+
puts "\nList of available credentials:"
|
186
|
+
@vault.credentials_names().each { |name| puts "- #{name}" }
|
187
|
+
end
|
188
|
+
|
189
|
+
# See <tt>help()</tt> for a description.
|
190
|
+
def display(name)
|
191
|
+
return unless check_vault_pass()
|
192
|
+
login, pass = @vault.credential(name)
|
193
|
+
puts ""
|
194
|
+
puts "login: #{login}"
|
195
|
+
puts "pass: #{pass}"
|
196
|
+
end
|
197
|
+
|
198
|
+
# See <tt>help()</tt> for a description.
|
199
|
+
def clip(name)
|
200
|
+
return unless check_vault_pass()
|
201
|
+
login, pass = @vault.credential(name)
|
202
|
+
puts ""
|
203
|
+
puts "login name: #{login}"
|
204
|
+
Clipboard.copy(pass)
|
205
|
+
end
|
206
|
+
|
207
|
+
# See <tt>help()</tt> for a description.
|
208
|
+
def add(name)
|
209
|
+
return unless check_vault_pass()
|
210
|
+
login = request('Enter a login: ')
|
211
|
+
pass = enter_pass('Enter a password: ')
|
212
|
+
@vault.add(name, login, pass)
|
213
|
+
rescue Vault::CredentialError => e
|
214
|
+
puts "\n#{e.message}"
|
215
|
+
end
|
216
|
+
|
217
|
+
# See <tt>help()</tt> for a description.
|
218
|
+
def remove(name)
|
219
|
+
return unless check_vault_pass()
|
220
|
+
@vault.remove(name)
|
221
|
+
end
|
222
|
+
|
223
|
+
# See <tt>help()</tt> for a description.
|
224
|
+
def edit(name)
|
225
|
+
return unless check_vault_pass()
|
226
|
+
login = request('Enter a login: ')
|
227
|
+
pass = enter_pass('Enter a new password: ')
|
228
|
+
@vault.edit(name, login, pass)
|
229
|
+
rescue Vault::CredentialError => e
|
230
|
+
puts "\n#{e.message}"
|
231
|
+
end
|
232
|
+
|
233
|
+
# Called when an unknown command is entered.
|
234
|
+
def unknown(command)
|
235
|
+
puts "\nUnknown command: #{command}"
|
236
|
+
puts 'Type "help" to display available commands.'
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# # entry point
|
241
|
+
# if __FILE__ == $PROGRAM_NAME
|
242
|
+
# ConsoleUI.run
|
243
|
+
# end
|