keychain 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +34 -0
- data/bin/keychain +237 -0
- metadata +64 -0
data/README
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
keychain is a very simple command line tool for managing passwords
|
2
|
+
|
3
|
+
Features include:
|
4
|
+
|
5
|
+
- passwords are stored in the blowfish encrypted file $HOME/.keychain
|
6
|
+
- search for entries using regular expressions
|
7
|
+
- automatically generates more or less pronouncable random passwords
|
8
|
+
|
9
|
+
Usage is very simple, you either start keychain without any arguments
|
10
|
+
to enter an interactive mode or directly supply the command on the
|
11
|
+
command line. Commands are:
|
12
|
+
|
13
|
+
help - print this help
|
14
|
+
print [pattern] - print entries matching pattern (patterin is full
|
15
|
+
regular expression. In particular '.*' matches
|
16
|
+
everything, not '*'
|
17
|
+
store id user key - add an entry. If key == ?, generates random password
|
18
|
+
delete id - delete entry
|
19
|
+
rename id_new id_old - rename an entry
|
20
|
+
password - change master password
|
21
|
+
|
22
|
+
Now, although I'm using this script for managing my own passwords you
|
23
|
+
should carefully read the following disclaimer:
|
24
|
+
|
25
|
+
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY GUARANTEES TO ITS
|
26
|
+
SUITEDNESS FOR ANY PARTICULAR USE. IN PARTICULAR, I CANNOT GUARANTEE
|
27
|
+
THAT THIS SCRIPT WILL NOT ACCIDENTALLY FORGETS YOUR PASSWORDS, OR THAT
|
28
|
+
THE DATA IS ENCRYPTED IN A FASHION WHICH CANNOT BE CRACKED. THEREFORE,
|
29
|
+
KEEP YOUR $HOME/.keychain PRIVATE, AND REGULARY MAKE SURE TO STORE
|
30
|
+
YOUR PASSWORDS SOMEWHERE ELSE. ALSO MAKE SURE THAT YOU DO NOT FORGET
|
31
|
+
YOUR MASTER PASSWORD!
|
32
|
+
|
33
|
+
On the other hand, if you have ideas how to improve the security and
|
34
|
+
the cryptologic strength of the encoding, I'd be happy to know!
|
data/bin/keychain
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
# A simple keychain manager written in ruby
|
5
|
+
#
|
6
|
+
# Requires the crypt and highline gem
|
7
|
+
#
|
8
|
+
# Written by Mikio L. Braun, 2008
|
9
|
+
|
10
|
+
require 'yaml'
|
11
|
+
require 'rubygems'
|
12
|
+
require 'crypt/blowfish'
|
13
|
+
require 'highline/import'
|
14
|
+
|
15
|
+
class KeyChain
|
16
|
+
FILENAME = File.join(ENV['HOME'], '.keychain')
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@keychain = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def load
|
23
|
+
if test ?f, FILENAME
|
24
|
+
@crypt = get_crypt
|
25
|
+
s = File.open(FILENAME) do |f|
|
26
|
+
@crypt.decrypt_string(f.read)
|
27
|
+
end
|
28
|
+
begin
|
29
|
+
@keychain = YAML::load(s)
|
30
|
+
raise 'ups' unless Hash === @keychain
|
31
|
+
rescue
|
32
|
+
puts "Wrong password."
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
else
|
36
|
+
puts "Couldn't find keychain. Enter initial password!"
|
37
|
+
@crypt = get_new_crypt
|
38
|
+
save
|
39
|
+
File.chmod 0600, FILENAME
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def save
|
44
|
+
s = YAML::dump(@keychain)
|
45
|
+
File.open(FILENAME, 'w') do |f|
|
46
|
+
f.write(@crypt.encrypt_string(s))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def store(id, user, passwd)
|
51
|
+
if @keychain.member? id
|
52
|
+
if ask("overwriting \"#{id}\"? ") != 'y'
|
53
|
+
puts "Did not overwrite"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@keychain[id] = [user, passwd]
|
58
|
+
end
|
59
|
+
|
60
|
+
def print(pat=nil)
|
61
|
+
puts "id | login | password"
|
62
|
+
puts "---------------------+---------------------------+----------------------"
|
63
|
+
pat ||= /.*/
|
64
|
+
@keychain.keys.grep(pat).sort.each do |id|
|
65
|
+
printf "%-20s | %-25s | %-15s\n", id, @keychain[id][0], @keychain[id][1]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def rename(id_old, id_new)
|
70
|
+
unless @keychain.member? id_old
|
71
|
+
puts "Cannot find key \"#{id_old}\""
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
if @keychain.member? id_new
|
76
|
+
puts "New id \"#{id_new}\" already exists!"
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
@keychain[id_new] = @keychain[id_old]
|
81
|
+
@keychain.delete id_old
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete(id)
|
85
|
+
unless @keychain.member? id_old
|
86
|
+
puts "Cannot find key \"#{id_old}\""
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
@keychain.delete id
|
91
|
+
end
|
92
|
+
|
93
|
+
def password
|
94
|
+
@crypt = get_new_crypt
|
95
|
+
save
|
96
|
+
end
|
97
|
+
|
98
|
+
# generate a random password using a markov chain on
|
99
|
+
# vowels and consonants to produce pronouncable passwords.
|
100
|
+
def generate_password(l)
|
101
|
+
vow = %w(a e i o u)
|
102
|
+
con = %w(b c d f g h j k l m n p q r s t v x y z)
|
103
|
+
|
104
|
+
pw = ''
|
105
|
+
state = if rand < 0.8 then :c else :v end
|
106
|
+
for i in 0..l
|
107
|
+
case state
|
108
|
+
when :v
|
109
|
+
pw << vow[rand(vow.size)]
|
110
|
+
state = :c if rand > 0.4
|
111
|
+
when :c
|
112
|
+
pw << con[rand(con.size)]
|
113
|
+
state = :v if rand > 0.1
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return pw
|
117
|
+
end
|
118
|
+
private
|
119
|
+
def get_new_crypt
|
120
|
+
password = ask('new password> ') {|q| q.echo = false}
|
121
|
+
retyped = ask('retype password> ') {|q| q.echo = false}
|
122
|
+
if password != retyped
|
123
|
+
puts "Passwords did not match."
|
124
|
+
exit
|
125
|
+
end
|
126
|
+
Crypt::Blowfish.new password
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_crypt
|
130
|
+
password = ask('password> ') {|q| q.echo = false}
|
131
|
+
Crypt::Blowfish.new password
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
Help = <<EOS
|
136
|
+
Usage: keychain command [args]
|
137
|
+
|
138
|
+
Commands:
|
139
|
+
|
140
|
+
help - print this help
|
141
|
+
print [pattern] - print entries matching pattern (patterin is full
|
142
|
+
regular expression. In particular '.*' matches
|
143
|
+
everything, not '*'
|
144
|
+
store id user key - add an entry. If key == ?, generates random password
|
145
|
+
delete id - delete entry
|
146
|
+
rename id_new id_old - rename an entry
|
147
|
+
password - change master password
|
148
|
+
|
149
|
+
Version 0.2.1 - August 8, 2008
|
150
|
+
EOS
|
151
|
+
|
152
|
+
if ARGV.size == 0
|
153
|
+
ARGV << 'interactive'
|
154
|
+
end
|
155
|
+
|
156
|
+
def cmd(kc, args)
|
157
|
+
case args[0]
|
158
|
+
when 'print'
|
159
|
+
if args.size == 2
|
160
|
+
kc.print(Regexp.new(args[1]))
|
161
|
+
else
|
162
|
+
kc.print
|
163
|
+
end
|
164
|
+
when 'store'
|
165
|
+
if args.size < 4
|
166
|
+
puts "Format is \"add id user key\""
|
167
|
+
return
|
168
|
+
end
|
169
|
+
if args[3] == '?'
|
170
|
+
l = ask('password length? ').to_i
|
171
|
+
if l > 0
|
172
|
+
ok = false
|
173
|
+
pw = nil
|
174
|
+
until ok
|
175
|
+
pw = kc.generate_password(l)
|
176
|
+
puts "Generated password: #{pw}"
|
177
|
+
case ask('okay (ynq)? ')
|
178
|
+
when 'y'
|
179
|
+
ok = true
|
180
|
+
when 'q'
|
181
|
+
pw = nil
|
182
|
+
break
|
183
|
+
end
|
184
|
+
end
|
185
|
+
return unless pw
|
186
|
+
kc.store(args[1], args[2], pw)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
kc.store(args[1], args[2], args[3])
|
190
|
+
end
|
191
|
+
kc.save
|
192
|
+
when 'delete'
|
193
|
+
if args.size < 1
|
194
|
+
puts "Which id do you want to delete?"
|
195
|
+
end
|
196
|
+
kc.delete(args[1])
|
197
|
+
kc.save
|
198
|
+
when 'rename'
|
199
|
+
if args.size < 2
|
200
|
+
puts "Format is \"rename id_old id_new\""
|
201
|
+
end
|
202
|
+
kc.rename(args[1], args[2])
|
203
|
+
kc.save
|
204
|
+
when 'help'
|
205
|
+
puts Help
|
206
|
+
when 'password'
|
207
|
+
kc.password
|
208
|
+
when 'gen'
|
209
|
+
puts kc.generate_password(args[1].to_i)
|
210
|
+
else
|
211
|
+
puts "Didn't understand command"
|
212
|
+
return
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
kc = KeyChain.new
|
217
|
+
if ARGV[0] == 'interactive'
|
218
|
+
puts "Entering interactive mode. Enter password. Then type 'exit' or an empty line to quite."
|
219
|
+
kc.load
|
220
|
+
while true
|
221
|
+
begin
|
222
|
+
s = ask('keychain> ')
|
223
|
+
if s == 'exit' or s == ''
|
224
|
+
exit
|
225
|
+
end
|
226
|
+
cmd(kc, s.split(' '))
|
227
|
+
rescue EOFError
|
228
|
+
puts "quitting..."
|
229
|
+
exit
|
230
|
+
rescue
|
231
|
+
raise
|
232
|
+
end
|
233
|
+
end
|
234
|
+
else
|
235
|
+
kc.load unless ARGV[0] == 'help' or ARGV[0] == 'gen'
|
236
|
+
cmd(kc, ARGV)
|
237
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: keychain
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.2.1
|
7
|
+
date: 2008-08-08 00:00:00 +02:00
|
8
|
+
summary: A simple shell tool for managing passwords
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: mikiobraun@gmail.com
|
12
|
+
homepage: http://ml.cs.tu-berlin.de/~mikio
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Mikio L. Braun
|
31
|
+
files:
|
32
|
+
- bin/keychain
|
33
|
+
- README
|
34
|
+
test_files: []
|
35
|
+
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README
|
40
|
+
executables:
|
41
|
+
- keychain
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
requirements: []
|
45
|
+
|
46
|
+
dependencies:
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: crypt
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.1.4
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: highline
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.4.0
|
64
|
+
version:
|