spass 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/History.md +19 -0
- data/README.md +1 -2
- data/bin/spass +61 -21
- data/lib/spass.rb +31 -25
- data/spass.gemspec +1 -1
- data/spec/spass_spec.rb +31 -28
- metadata +6 -4
data/.rspec
ADDED
data/History.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History
|
2
|
+
=======
|
3
|
+
|
4
|
+
0.0.2
|
5
|
+
-----
|
6
|
+
|
7
|
+
- Added command-line options
|
8
|
+
- Multiple-phrase generation
|
9
|
+
- Optional inclusion of digits
|
10
|
+
- Restricted word length
|
11
|
+
|
12
|
+
|
13
|
+
0.0.1
|
14
|
+
-----
|
15
|
+
|
16
|
+
- Initial release
|
17
|
+
- Single phrase with random lowercase words
|
18
|
+
|
19
|
+
|
data/README.md
CHANGED
@@ -47,11 +47,10 @@ if you want to use words from a different dictionary, pass a second argument:
|
|
47
47
|
Future plans
|
48
48
|
------------
|
49
49
|
|
50
|
-
- Optional inclusion of uppercase
|
50
|
+
- Optional inclusion of uppercase and symbols
|
51
51
|
- Avoid word repetition
|
52
52
|
|
53
53
|
|
54
|
-
|
55
54
|
MIT License
|
56
55
|
-----------
|
57
56
|
|
data/bin/spass
CHANGED
@@ -1,13 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'spass'
|
3
|
+
require 'spass'
|
4
|
+
require 'optparse'
|
4
5
|
|
5
|
-
|
6
|
-
spass: Generate a passphrase with lowercase words
|
7
|
-
|
8
|
-
Usage:
|
9
|
-
|
10
|
-
$ spass <length> [path_to_dict]
|
6
|
+
HELP = <<EOF
|
11
7
|
|
12
8
|
This script generates a plain-English passphrase of at least the given length
|
13
9
|
(in characters), using random words from the given dictionary file
|
@@ -17,15 +13,12 @@ nonsensical).
|
|
17
13
|
|
18
14
|
Examples:
|
19
15
|
|
20
|
-
$ spass 12
|
16
|
+
$ spass -l 12
|
21
17
|
hive frighten
|
22
18
|
|
23
|
-
$ spass 24
|
19
|
+
$ spass -l 24
|
24
20
|
moppet castigator harvesters
|
25
21
|
|
26
|
-
$ spass 32
|
27
|
-
munificent icebound raymond clorets
|
28
|
-
|
29
22
|
A 32-character passphrase has about 120 bits of entropy, which is overkill for
|
30
23
|
most purposes. A 24-character passphrase clocks in at about 90 bits of entropy,
|
31
24
|
suitable for most high-security applications like root passwords or financial
|
@@ -35,18 +28,65 @@ your passphrase after generating one that you like:
|
|
35
28
|
http://rumkin.com/tools/password/passchk.php
|
36
29
|
|
37
30
|
Inspired by http://xkcd.com/936/
|
31
|
+
|
38
32
|
EOF
|
39
33
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
34
|
+
options = {
|
35
|
+
:length => 24,
|
36
|
+
:number => 10,
|
37
|
+
:dict => '/usr/share/dict/words',
|
38
|
+
:digits => false,
|
39
|
+
:chars => 10,
|
40
|
+
}
|
41
|
+
|
42
|
+
optparse = OptionParser.new do |opts|
|
43
|
+
opts.on('-l', '--length [NUM]', Integer,
|
44
|
+
"Ensure passphrases are at least NUM characters.",
|
45
|
+
"Default: #{options[:length]}") do |len|
|
46
|
+
options[:length] = len
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on('-n', '--number [NUM]', Integer,
|
50
|
+
"Generate NUM passphrases.",
|
51
|
+
"Default: #{options[:number]}") do |num|
|
52
|
+
options[:number] = num
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-f', '--file [FILE]', String,
|
56
|
+
"Read words from a FILE having one word per line.",
|
57
|
+
"Default: #{options[:dict]}") do |dict|
|
58
|
+
options[:dict] = dict
|
48
59
|
end
|
49
|
-
|
60
|
+
|
61
|
+
opts.on('-d', '--digits',
|
62
|
+
"Include random numbers from 1-99.",
|
63
|
+
"Default: False") do |digits|
|
64
|
+
options[:digits] = digits
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-c', '--chars NUM',
|
68
|
+
"Limit words to NUM characters in length.",
|
69
|
+
"Default: 10") do |chars|
|
70
|
+
options[:chars] = chars
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on_tail('-h', '--help', 'Display the help page') do
|
74
|
+
puts opts
|
75
|
+
puts HELP
|
76
|
+
exit 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
optparse.parse!
|
82
|
+
rescue OptionParser::InvalidOption => e
|
83
|
+
puts optparse
|
84
|
+
puts "*** #{e}"
|
85
|
+
exit 1
|
50
86
|
end
|
51
87
|
|
88
|
+
sp = Generator.new(options[:dict], options)
|
89
|
+
options[:number].times do
|
90
|
+
puts sp.generate(options[:length], :digits=>options[:digits])
|
91
|
+
end
|
52
92
|
|
data/lib/spass.rb
CHANGED
@@ -1,41 +1,47 @@
|
|
1
1
|
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(dict_path='/usr/share/dict/words')
|
6
|
-
@dict_path =
|
7
|
-
|
8
|
-
raise RuntimeError, "Cannot find dict file: #{@dict_path}"
|
9
|
-
end
|
10
|
-
@dict_lines = `wc -l #{@dict_path}`.split.first.to_i
|
2
|
+
class Generator
|
3
|
+
# Create a Generator using the given word dictionary, returning
|
4
|
+
# only words that match the given regular expression
|
5
|
+
def initialize(dict_path='/usr/share/dict/words', options={})
|
6
|
+
@dict_path = dict_path
|
7
|
+
@dict = read_dict(dict_path, options)
|
11
8
|
end
|
12
9
|
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
10
|
+
# Read a word dictionary from the given file, and return an array
|
11
|
+
# of elements that match the allowed regex. Raise an exception if
|
12
|
+
# the given dictionary file cannot be found.
|
13
|
+
def read_dict(path, options={})
|
14
|
+
allowed = options[:allowed] || /^[a-z]+$/
|
15
|
+
chars = (options[:chars] || 10).to_i
|
16
|
+
if !File.file?(path)
|
17
|
+
raise RuntimeError, "Cannot find dict file: #{path}"
|
18
|
+
end
|
19
|
+
dict = []
|
20
|
+
IO.readlines(path).each do |line|
|
21
|
+
line.strip!
|
22
|
+
if line.length <= chars && line =~ allowed
|
23
|
+
dict << line
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return dict
|
17
27
|
end
|
18
28
|
|
19
|
-
# Return a random word from the dictionary
|
29
|
+
# Return a random word from the dictionary
|
20
30
|
def random_word
|
21
|
-
|
22
|
-
`#{cmd}`.chomp.downcase
|
31
|
+
@dict[rand(@dict.count)]
|
23
32
|
end
|
24
33
|
|
25
|
-
# Return a random
|
26
|
-
def
|
27
|
-
|
28
|
-
while word =~ /[^a-z]/
|
29
|
-
word = random_word
|
30
|
-
end
|
31
|
-
return word
|
34
|
+
# Return a random number from 1 to 99
|
35
|
+
def random_number
|
36
|
+
1 + rand(99)
|
32
37
|
end
|
33
38
|
|
34
39
|
# Generate a passphrase of at least the given length in characters
|
35
|
-
def generate(length)
|
40
|
+
def generate(length, options={})
|
36
41
|
phrase = ''
|
37
42
|
while phrase.length < length + 1 # to account for trailing space
|
38
|
-
phrase +=
|
43
|
+
phrase += "#{random_word} "
|
44
|
+
phrase += "#{random_number} " if options[:digits]
|
39
45
|
end
|
40
46
|
return phrase.chomp
|
41
47
|
end
|
data/spass.gemspec
CHANGED
data/spec/spass_spec.rb
CHANGED
@@ -2,39 +2,33 @@ require 'spass'
|
|
2
2
|
|
3
3
|
TEST_DICT = File.expand_path('../data/ten_words', __FILE__)
|
4
4
|
|
5
|
-
describe
|
6
|
-
before(:
|
7
|
-
@sp =
|
5
|
+
describe Generator do
|
6
|
+
before(:all) do
|
7
|
+
@sp = Generator.new(TEST_DICT)
|
8
8
|
end
|
9
9
|
|
10
10
|
|
11
|
-
describe "#
|
12
|
-
it "
|
13
|
-
|
14
|
-
|
15
|
-
@sp.dict_path.should == path
|
11
|
+
describe "#read_dict" do
|
12
|
+
it "returns an array of lines from the given file" do
|
13
|
+
@sp.read_dict(TEST_DICT).should == [
|
14
|
+
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']
|
16
15
|
end
|
17
16
|
|
18
|
-
it "
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end.should raise_error(RuntimeError, /Cannot find dict file/)
|
17
|
+
it "only returns words matching a regex" do
|
18
|
+
@sp.read_dict(TEST_DICT, :allowed=>/^t/).should == ['two', 'three', 'ten']
|
19
|
+
@sp.read_dict(TEST_DICT, :allowed=>/^f/).should == ['four', 'five']
|
20
|
+
@sp.read_dict(TEST_DICT, :allowed=>/e$/).should == ['one', 'three', 'five', 'nine']
|
23
21
|
end
|
24
|
-
end
|
25
|
-
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
100.times do
|
30
|
-
@sp.random_line.should be >= 1
|
31
|
-
end
|
23
|
+
it "only returns words with N chars or fewer" do
|
24
|
+
@sp.read_dict(TEST_DICT, :chars=>3).should == ['one', 'two', 'six', 'ten']
|
32
25
|
end
|
33
26
|
|
34
|
-
it "
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
it "raises an exception when the file does not exist" do
|
28
|
+
path = File.expand_path('non/existent/path')
|
29
|
+
lambda do
|
30
|
+
@sp.read_dict(path)
|
31
|
+
end.should raise_error(RuntimeError, /Cannot find dict file/)
|
38
32
|
end
|
39
33
|
end
|
40
34
|
|
@@ -48,14 +42,17 @@ describe SPass do
|
|
48
42
|
end
|
49
43
|
|
50
44
|
|
51
|
-
describe "#
|
52
|
-
it "
|
53
|
-
|
54
|
-
@sp.
|
45
|
+
describe "#random_number" do
|
46
|
+
it "returns a number between 1 and 999" do
|
47
|
+
1000.times do
|
48
|
+
num = @sp.random_number
|
49
|
+
num.should be >= 1
|
50
|
+
num.should be <= 99
|
55
51
|
end
|
56
52
|
end
|
57
53
|
end
|
58
54
|
|
55
|
+
|
59
56
|
describe "#generate" do
|
60
57
|
it "is at least the given length" do
|
61
58
|
[10, 15, 20, 25, 30, 35, 40].each do |len|
|
@@ -64,6 +61,12 @@ describe SPass do
|
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
64
|
+
|
65
|
+
it "includes digits when :digits is true" do
|
66
|
+
10.times do
|
67
|
+
@sp.generate(24, :digits=>true).should =~ /^([a-z]+ [0-9]+ ?)+$/
|
68
|
+
end
|
69
|
+
end
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Eric Pierce
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-11-01 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -58,8 +58,10 @@ extra_rdoc_files: []
|
|
58
58
|
|
59
59
|
files:
|
60
60
|
- .gitignore
|
61
|
+
- .rspec
|
61
62
|
- Gemfile
|
62
63
|
- Gemfile.lock
|
64
|
+
- History.md
|
63
65
|
- README.md
|
64
66
|
- bin/spass
|
65
67
|
- lib/spass.rb
|