keychain 0.2.1
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/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:
|