spass 0.0.2 → 0.0.3
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/Gemfile.lock +1 -1
- data/History.md +8 -0
- data/README.md +24 -15
- data/bin/spass +22 -29
- data/lib/spass.rb +22 -21
- data/spass.gemspec +1 -1
- data/spec/spass_spec.rb +22 -19
- metadata +4 -4
data/Gemfile.lock
CHANGED
data/History.md
CHANGED
data/README.md
CHANGED
@@ -10,8 +10,9 @@ computer to guess.
|
|
10
10
|
Prerequisites
|
11
11
|
-------------
|
12
12
|
|
13
|
-
|
14
|
-
`/usr/share/dict/words`
|
13
|
+
spass assumes you have a dictionary file to use as its source of words. By
|
14
|
+
default, `/usr/share/dict/words` is used, but you can pass your own dictionary
|
15
|
+
using the `-f/--file` option.
|
15
16
|
|
16
17
|
|
17
18
|
Installation
|
@@ -25,30 +26,38 @@ spass is distributed as a gem, so just do:
|
|
25
26
|
Usage
|
26
27
|
-----
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
When run without arguments, spass generates 10 passphrases, each 4 words long,
|
30
|
+
using words up to 8 characters long. You can configure this behavior by passing
|
31
|
+
command-line arguments; run `spass -h` to see full usage notes.
|
30
32
|
|
31
|
-
|
32
|
-
hive frighten
|
33
|
+
Examples:
|
33
34
|
|
34
|
-
$ spass
|
35
|
-
|
35
|
+
$ spass -w 3 # Generate 3-word phrases
|
36
|
+
$ spass -w 5 -n 20 # Generate twenty 5-word phrases
|
37
|
+
$ spass -c 6 # Limit words to 6 characters
|
38
|
+
$ spass -d # Append a digit to each word
|
39
|
+
$ spass -f mywords.txt # Get words from mywords.txt (one per line)
|
36
40
|
|
37
|
-
$ spass 32
|
38
|
-
munificent icebound raymond clorets
|
39
41
|
|
40
|
-
Passphrases are guaranteed to be at least as long as the given number, but
|
41
|
-
may be longer. By default, the words are taken from `/usr/share/dict/words`;
|
42
|
-
if you want to use words from a different dictionary, pass a second argument:
|
43
42
|
|
44
|
-
|
43
|
+
Disclaimer
|
44
|
+
----------
|
45
|
+
|
46
|
+
spass makes absolutely no guarantee that its output results in secure
|
47
|
+
passwords. If you use spass to protect your bank account or your porn
|
48
|
+
collection, I cannot be held responsible if you lose your money or marriage.
|
49
|
+
[Check your password strength](http://rumkin.com/tools/password/passchk.php)
|
50
|
+
before using it for anything important. Passwords generated with spass are
|
51
|
+
inherently vulnerable to a dictionary attack; the only thing that can make
|
52
|
+
them resistant to such attacks is their length, so always use at least three
|
53
|
+
(preferably more) words.
|
45
54
|
|
46
55
|
|
47
56
|
Future plans
|
48
57
|
------------
|
49
58
|
|
50
59
|
- Optional inclusion of uppercase and symbols
|
51
|
-
- Avoid word repetition
|
60
|
+
- Avoid possible word repetition
|
52
61
|
|
53
62
|
|
54
63
|
MIT License
|
data/bin/spass
CHANGED
@@ -5,45 +5,37 @@ require 'optparse'
|
|
5
5
|
|
6
6
|
HELP = <<EOF
|
7
7
|
|
8
|
-
This script generates
|
9
|
-
(
|
10
|
-
(/usr/share/dict/words by default). Only lowercase letters a-z will be included
|
11
|
-
in the output, making passphrases relatively easy to type (though usually
|
12
|
-
nonsensical).
|
8
|
+
This script generates passphrases using random words from the given dictionary
|
9
|
+
file (default: /usr/share/dict/words). It was inspired by http://xkcd.com/936/
|
13
10
|
|
14
11
|
Examples:
|
15
12
|
|
16
|
-
|
17
|
-
|
13
|
+
$ spass -w 3 # Generate 3-word phrases
|
14
|
+
$ spass -w 5 -n 20 # Generate twenty 5-word phrases
|
15
|
+
$ spass -c 6 # Limit words to 6 characters
|
16
|
+
$ spass -d # Append a digit to each word
|
17
|
+
$ spass -f mywords.txt # Get words from mywords.txt (one per line)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
most purposes. A 24-character passphrase clocks in at about 90 bits of entropy,
|
24
|
-
suitable for most high-security applications like root passwords or financial
|
25
|
-
records. You might want to use a password checker to verify the strength of
|
26
|
-
your passphrase after generating one that you like:
|
27
|
-
|
28
|
-
http://rumkin.com/tools/password/passchk.php
|
29
|
-
|
30
|
-
Inspired by http://xkcd.com/936/
|
19
|
+
Passphrases generated from dictionary words are inherently vulnerable to
|
20
|
+
dictionary attacks; using more and/or longer words strengthens them against
|
21
|
+
such attacks. The default settings are intended to yield relatively strong,
|
22
|
+
easy-to-remember passphrases.
|
31
23
|
|
32
24
|
EOF
|
33
25
|
|
34
26
|
options = {
|
35
|
-
:length => 24,
|
36
27
|
:number => 10,
|
37
28
|
:dict => '/usr/share/dict/words',
|
38
29
|
:digits => false,
|
39
|
-
:chars =>
|
30
|
+
:chars => 8,
|
31
|
+
:words => 4,
|
40
32
|
}
|
41
33
|
|
42
34
|
optparse = OptionParser.new do |opts|
|
43
|
-
opts.on('-
|
44
|
-
"Ensure passphrases are at least NUM
|
45
|
-
"Default: #{options[:
|
46
|
-
options[:
|
35
|
+
opts.on('-w', '--words [NUM]', Integer,
|
36
|
+
"Ensure passphrases are at least NUM words long.",
|
37
|
+
"Default: #{options[:words]}") do |words|
|
38
|
+
options[:words] = words
|
47
39
|
end
|
48
40
|
|
49
41
|
opts.on('-n', '--number [NUM]', Integer,
|
@@ -59,14 +51,14 @@ optparse = OptionParser.new do |opts|
|
|
59
51
|
end
|
60
52
|
|
61
53
|
opts.on('-d', '--digits',
|
62
|
-
"
|
63
|
-
"Default:
|
54
|
+
"Append a random digit from 0-9 to each word.",
|
55
|
+
"Default: #{options[:digits]}") do |digits|
|
64
56
|
options[:digits] = digits
|
65
57
|
end
|
66
58
|
|
67
59
|
opts.on('-c', '--chars NUM',
|
68
60
|
"Limit words to NUM characters in length.",
|
69
|
-
"Default:
|
61
|
+
"Default: #{options[:chars]}") do |chars|
|
70
62
|
options[:chars] = chars
|
71
63
|
end
|
72
64
|
|
@@ -86,7 +78,8 @@ rescue OptionParser::InvalidOption => e
|
|
86
78
|
end
|
87
79
|
|
88
80
|
sp = Generator.new(options[:dict], options)
|
81
|
+
puts "Read #{sp.dict.count} words up to #{sp.chars} characters long from #{sp.file}"
|
89
82
|
options[:number].times do
|
90
|
-
puts sp.generate(options[:
|
83
|
+
puts sp.generate(options[:words], :digits=>options[:digits])
|
91
84
|
end
|
92
85
|
|
data/lib/spass.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
|
2
2
|
class Generator
|
3
|
-
|
4
|
-
#
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
3
|
+
attr_reader :dict, :chars, :allowed, :file
|
4
|
+
# Create a Generator using the given word dictionary
|
5
|
+
def initialize(file='/usr/share/dict/words', options={})
|
6
|
+
@file = file
|
7
|
+
@allowed = options[:allowed] || /^[a-z]+$/
|
8
|
+
@chars = (options[:chars] || 10).to_i
|
9
|
+
@dict = read_dict
|
8
10
|
end
|
9
11
|
|
10
12
|
# Read a word dictionary from the given file, and return an array
|
11
13
|
# of elements that match the allowed regex. Raise an exception if
|
12
14
|
# the given dictionary file cannot be found.
|
13
|
-
def read_dict
|
14
|
-
|
15
|
-
|
16
|
-
if !File.file?(path)
|
17
|
-
raise RuntimeError, "Cannot find dict file: #{path}"
|
15
|
+
def read_dict
|
16
|
+
if !File.file?(@file)
|
17
|
+
raise RuntimeError, "Cannot find dict file: #{@file}"
|
18
18
|
end
|
19
19
|
dict = []
|
20
|
-
IO.readlines(
|
20
|
+
IO.readlines(@file).each do |line|
|
21
21
|
line.strip!
|
22
|
-
if line.length <= chars && line =~ allowed
|
22
|
+
if line.length <= @chars && line =~ @allowed
|
23
23
|
dict << line
|
24
24
|
end
|
25
25
|
end
|
@@ -31,19 +31,20 @@ class Generator
|
|
31
31
|
@dict[rand(@dict.count)]
|
32
32
|
end
|
33
33
|
|
34
|
-
# Return a random number from
|
34
|
+
# Return a random number from 0 to 9
|
35
35
|
def random_number
|
36
|
-
|
36
|
+
rand(10)
|
37
37
|
end
|
38
38
|
|
39
|
-
# Generate a passphrase
|
40
|
-
def generate(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
# Generate a passphrase with the given number of words
|
40
|
+
def generate(num_words, options={})
|
41
|
+
words = []
|
42
|
+
num_words.times do
|
43
|
+
word = random_word
|
44
|
+
word += random_number.to_s if options[:digits]
|
45
|
+
words << word
|
45
46
|
end
|
46
|
-
return
|
47
|
+
return words.join(' ')
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
data/spass.gemspec
CHANGED
data/spec/spass_spec.rb
CHANGED
@@ -3,31 +3,35 @@ require 'spass'
|
|
3
3
|
TEST_DICT = File.expand_path('../data/ten_words', __FILE__)
|
4
4
|
|
5
5
|
describe Generator do
|
6
|
-
before(:
|
6
|
+
before(:each) do
|
7
7
|
@sp = Generator.new(TEST_DICT)
|
8
8
|
end
|
9
9
|
|
10
10
|
|
11
|
-
describe "#
|
11
|
+
describe "#initialize" do
|
12
12
|
it "returns an array of lines from the given file" do
|
13
|
-
@sp.
|
13
|
+
@sp = Generator.new(TEST_DICT)
|
14
|
+
@sp.dict.should == [
|
14
15
|
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']
|
15
16
|
end
|
16
17
|
|
17
18
|
it "only returns words matching a regex" do
|
18
|
-
@sp.
|
19
|
-
@sp.
|
20
|
-
@sp.
|
19
|
+
@sp = Generator.new(TEST_DICT, :allowed=>/^t/)
|
20
|
+
@sp.dict.should == ['two', 'three', 'ten']
|
21
|
+
@sp = Generator.new(TEST_DICT, :allowed=>/^f/)
|
22
|
+
@sp.dict.should == ['four', 'five']
|
23
|
+
@sp = Generator.new(TEST_DICT, :allowed=>/e$/)
|
24
|
+
@sp.dict.should == ['one', 'three', 'five', 'nine']
|
21
25
|
end
|
22
26
|
|
23
27
|
it "only returns words with N chars or fewer" do
|
24
|
-
@sp.
|
28
|
+
@sp = Generator.new(TEST_DICT, :chars=>3)
|
29
|
+
@sp.dict.should == ['one', 'two', 'six', 'ten']
|
25
30
|
end
|
26
31
|
|
27
32
|
it "raises an exception when the file does not exist" do
|
28
|
-
path = File.expand_path('non/existent/path')
|
29
33
|
lambda do
|
30
|
-
|
34
|
+
Generator.new(File.expand_path('non/existent/path'))
|
31
35
|
end.should raise_error(RuntimeError, /Cannot find dict file/)
|
32
36
|
end
|
33
37
|
end
|
@@ -43,28 +47,27 @@ describe Generator do
|
|
43
47
|
|
44
48
|
|
45
49
|
describe "#random_number" do
|
46
|
-
it "returns a number between
|
47
|
-
|
50
|
+
it "returns a number between 0 and 9" do
|
51
|
+
100.times do
|
48
52
|
num = @sp.random_number
|
49
|
-
num.should be >=
|
50
|
-
num.should be <=
|
53
|
+
num.should be >= 0
|
54
|
+
num.should be <= 9
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
55
59
|
|
56
60
|
describe "#generate" do
|
57
|
-
it "
|
58
|
-
[
|
59
|
-
|
60
|
-
|
61
|
-
end
|
61
|
+
it "has the given number of words" do
|
62
|
+
[1, 2, 3, 4, 5, 6].each do |words|
|
63
|
+
phrase = @sp.generate(words)
|
64
|
+
phrase.split.count.should == words
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
65
68
|
it "includes digits when :digits is true" do
|
66
69
|
10.times do
|
67
|
-
@sp.generate(24, :digits=>true).should =~ /^([a-z]+
|
70
|
+
@sp.generate(24, :digits=>true).should =~ /^([a-z]+[0-9] ?)+$/
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
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: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
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-11-
|
18
|
+
date: 2011-11-02 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|