passphrase 0.0.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/Gemfile +14 -0
- data/Gemfile.lock +16 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +56 -0
- data/Rakefile +33 -0
- data/VERSION +1 -0
- data/bin/passphrase +13 -0
- data/lib/passphrase/data.rb +1632 -0
- data/lib/passphrase/generator.rb +52 -0
- data/lib/passphrase/options.rb +60 -0
- data/lib/passphrase/random.rb +41 -0
- data/lib/passphrase/version.rb +14 -0
- data/lib/passphrase/wordlist.rb +30 -0
- data/test/test_options.rb +33 -0
- data/test/test_wordlist.rb +17 -0
- metadata +152 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'options')
|
4
|
+
require File.join(File.dirname(__FILE__), 'wordlist')
|
5
|
+
require File.join(File.dirname(__FILE__), 'random')
|
6
|
+
|
7
|
+
module Passphrase
|
8
|
+
class Generator
|
9
|
+
|
10
|
+
attr_reader :num_words
|
11
|
+
attr_reader :odd_char
|
12
|
+
attr_reader :phrase
|
13
|
+
attr_reader :unmixed_phrase
|
14
|
+
attr_reader :words
|
15
|
+
|
16
|
+
def initialize(argv)
|
17
|
+
@options = Options.new(argv)
|
18
|
+
@num_words = @options.num_words
|
19
|
+
@words = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
word_list = WordList.create
|
24
|
+
list_selector = Random.new(@num_words, 0, word_list.length - 1).rand_array
|
25
|
+
@num_words.times do |iword|
|
26
|
+
word_hash = Random.new(5, 1, 6).rand_array.join.to_sym
|
27
|
+
@words << word_list[list_selector[iword]][word_hash]
|
28
|
+
end
|
29
|
+
@phrase = @unmixed_phrase = @words.join(' ')
|
30
|
+
mix_odd_char if @options.mix
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def mix_odd_char
|
36
|
+
odd =
|
37
|
+
%w( ~ & + : ? 4 ) +
|
38
|
+
%w( ! * [ ; / 5 ) +
|
39
|
+
%w( # \( ] " 0 6 ) +
|
40
|
+
%w( $ \) \\ ' 1 7 ) +
|
41
|
+
%w( % - { < 2 8 ) +
|
42
|
+
%w( ^ = } > 3 9 )
|
43
|
+
odd_char_index = Random.new(1, 0, odd.length - 1).rand_array.shift
|
44
|
+
word_index = Random.new(1, 0, @num_words - 1).rand_array.shift
|
45
|
+
word_length = @words[word_index].length
|
46
|
+
char_index = Random.new(1, 0, word_length - 1).rand_array.shift unless word_length.zero?
|
47
|
+
char_index ||= 0
|
48
|
+
@words[word_index][char_index] = @odd_char = odd[odd_char_index]
|
49
|
+
@phrase = @words.join(' ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require File.join(File.dirname(__FILE__), 'version')
|
5
|
+
|
6
|
+
module Passphrase
|
7
|
+
class Options
|
8
|
+
|
9
|
+
NUM_WORDS_RANGE = (3..10)
|
10
|
+
DEFAULT_NUM_WORDS = 5
|
11
|
+
|
12
|
+
attr_reader :num_words
|
13
|
+
attr_reader :mix
|
14
|
+
|
15
|
+
def initialize(argv)
|
16
|
+
@num_words = DEFAULT_NUM_WORDS
|
17
|
+
@mix = true
|
18
|
+
parse(argv)
|
19
|
+
validate
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse(argv)
|
25
|
+
OptionParser.new do |opts|
|
26
|
+
opts.banner = "Usage: passphrase [options]"
|
27
|
+
opts.separator "Options:"
|
28
|
+
opts.on("-n", "--num-words NUMBER", Integer,
|
29
|
+
"Desired number of words (#{NUM_WORDS_RANGE.to_s}), default #{DEFAULT_NUM_WORDS}") do |num|
|
30
|
+
@num_words = num
|
31
|
+
end
|
32
|
+
opts.on("-x", "--[no-]mix", "Mix in odd character, default mix") do |m|
|
33
|
+
@mix = m
|
34
|
+
end
|
35
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
36
|
+
puts opts
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
opts.on_tail("-v", "--version", "Show version") do
|
40
|
+
puts "#{$PROGRAM_NAME}, version #{Passphrase::Version::STRING}"
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
opts.parse!(argv)
|
46
|
+
rescue OptionParser::ParseError => e
|
47
|
+
STDERR.puts e.message, "\n", opts
|
48
|
+
exit(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate
|
54
|
+
unless NUM_WORDS_RANGE.include?(@num_words)
|
55
|
+
STDERR.puts "Number of words out of range: allowed #{NUM_WORD_RANGE.to_s}: specified #{@num_words}"
|
56
|
+
exit(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module Passphrase
|
6
|
+
class Random
|
7
|
+
|
8
|
+
RANDOM_ORG = "www.random.org"
|
9
|
+
|
10
|
+
attr_reader :num
|
11
|
+
attr_reader :min
|
12
|
+
attr_reader :max
|
13
|
+
attr_reader :via_random_org
|
14
|
+
attr_reader :rand_array
|
15
|
+
|
16
|
+
def initialize(num, min, max)
|
17
|
+
@num, @min, @max = num, min, max
|
18
|
+
@rand_array = []
|
19
|
+
generate_rand_array
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def generate_rand_array
|
25
|
+
query = "/integers/?col=1&base=10&format=plain&rnd=new" +
|
26
|
+
"&num=#{@num}&min=#{@min}&max=#{@max}"
|
27
|
+
site = Net::HTTP.new(RANDOM_ORG)
|
28
|
+
response, data = site.get(query)
|
29
|
+
raise unless response.code == "200"
|
30
|
+
@rand_array = data.split.collect {|num| num.to_i}
|
31
|
+
@via_random_org = true
|
32
|
+
rescue
|
33
|
+
max = @max - @min + 1
|
34
|
+
offset = @min
|
35
|
+
@num.times do
|
36
|
+
@rand_array << (rand(max) + offset)
|
37
|
+
end
|
38
|
+
@via_random_org = false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zlib'
|
4
|
+
require 'base64'
|
5
|
+
require File.join(File.dirname(__FILE__), 'data')
|
6
|
+
|
7
|
+
module Passphrase
|
8
|
+
module WordList
|
9
|
+
|
10
|
+
def self.create
|
11
|
+
decoded_wordlists
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.decoded_wordlists
|
17
|
+
Data::ENCODED.collect {|encoded_list| self.decode(encoded_list) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.decode(encoded_list)
|
21
|
+
decoded_list = {}
|
22
|
+
lines = Zlib::Inflate.inflate(Base64.decode64(encoded_list)).split(/\n/)
|
23
|
+
lines.grep(/^\d{5}/).each do |line|
|
24
|
+
key, value = line.chomp.split
|
25
|
+
decoded_list[key.to_sym] = value
|
26
|
+
end
|
27
|
+
decoded_list
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'options'
|
5
|
+
|
6
|
+
class TestOptions < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_000_empty_args
|
9
|
+
opts = Passphrase::Options.new([])
|
10
|
+
assert_equal Passphrase::Options::DEFAULT_NUM_WORDS, opts.num_words
|
11
|
+
assert opts.mix
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_001_specify_num_words
|
15
|
+
num = Passphrase::Options::DEFAULT_NUM_WORDS - 1
|
16
|
+
opts = Passphrase::Options.new(%W{--num-words #{num}})
|
17
|
+
assert_equal num, opts.num_words
|
18
|
+
assert opts.mix
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_002_specify_mix
|
22
|
+
opts = Passphrase::Options.new(%W{--no-mix})
|
23
|
+
assert_equal Passphrase::Options::DEFAULT_NUM_WORDS, opts.num_words
|
24
|
+
assert !opts.mix
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_003_specify_num_words_and_mix
|
28
|
+
num = Passphrase::Options::DEFAULT_NUM_WORDS - 1
|
29
|
+
opts = Passphrase::Options.new(%W{--num-words #{num} --no-mix})
|
30
|
+
assert_equal num, opts.num_words
|
31
|
+
assert !opts.mix
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'wordlist'
|
5
|
+
|
6
|
+
class TestOptions < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@wordlist = Passphrase::WordList.create
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_000_sample_words
|
13
|
+
assert_equal 2, @wordlist.length
|
14
|
+
assert_equal "embalm", @wordlist[0][:'24356']
|
15
|
+
assert_equal "potato", @wordlist[1][:'46132']
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: passphrase
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Edmund Sumbar
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-01-15 00:00:00 -07:00
|
14
|
+
default_executable: passphrase
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: bundler
|
18
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.0
|
24
|
+
requirement: *id001
|
25
|
+
prerelease: false
|
26
|
+
type: :development
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
29
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.5.2
|
35
|
+
requirement: *id002
|
36
|
+
prerelease: false
|
37
|
+
type: :development
|
38
|
+
description: |+
|
39
|
+
= passphrase
|
40
|
+
|
41
|
+
Passphrase is a command line tool written in Ruby for generating a passphrase
|
42
|
+
using the {Diceware(TM) method}[http://world.std.com/~reinhold/diceware.html].
|
43
|
+
|
44
|
+
The act of rolling dice is simulated by requesting random numbers from
|
45
|
+
{www.random.org}[http://www.random.org]. If a network error occurs or a request
|
46
|
+
fails, the program gracefully degenerates to using Ruby's global +rand+ method,
|
47
|
+
which is based on the Mersenne Twister algorithm.
|
48
|
+
|
49
|
+
Words in the generated passphrase are selected from either the Diceware list or
|
50
|
+
the alternate list edited by Alan Beale, referred to as the _Beale_ list. The
|
51
|
+
choice of list for each word is made randomly. Both lists are embedded into the
|
52
|
+
code as compressed, base64 encoded strings. No external files are referenced.
|
53
|
+
|
54
|
+
== Generating a passphrase
|
55
|
+
|
56
|
+
The command line interface is simple. You have the option to specify the number
|
57
|
+
of words in the generated passphrase within the range of 3 to 10. The default
|
58
|
+
is 5. One _odd_ character is automatically mixed into one of the words
|
59
|
+
comprising the passphrase. You can forgo mixing if desired.
|
60
|
+
|
61
|
+
Usage: passphrase [options]
|
62
|
+
Options:
|
63
|
+
-n, --num-words NUMBER Desired number of words (3..10), default 5
|
64
|
+
-x, --[no-]mix Mix in odd character, default mix
|
65
|
+
-h, --help Show this message
|
66
|
+
-v, --version Show version
|
67
|
+
|
68
|
+
== Examples
|
69
|
+
|
70
|
+
$ passphrase --num-words 3
|
71
|
+
unmixed phrase => shot elm mild
|
72
|
+
odd character => !
|
73
|
+
passphrase => shot e!m mild
|
74
|
+
|
75
|
+
$ passphrase --num-words 4 --no-mix
|
76
|
+
passphrase => humus smooth persia mz
|
77
|
+
|
78
|
+
If you don't like a generated passphrase, repeat the command.
|
79
|
+
|
80
|
+
== Contributing to passphrase
|
81
|
+
|
82
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
83
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
84
|
+
* Fork the project
|
85
|
+
* Start a feature/bugfix branch
|
86
|
+
* Commit and push until you are happy with your contribution
|
87
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
88
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
89
|
+
|
90
|
+
== Copyright
|
91
|
+
|
92
|
+
Copyright (c) 2011 Edmund Sumbar. See LICENSE.txt for
|
93
|
+
further details.
|
94
|
+
|
95
|
+
email: esumbar@gmail.com
|
96
|
+
executables:
|
97
|
+
- passphrase
|
98
|
+
extensions: []
|
99
|
+
|
100
|
+
extra_rdoc_files:
|
101
|
+
- LICENSE.txt
|
102
|
+
- README.rdoc
|
103
|
+
files:
|
104
|
+
- Gemfile
|
105
|
+
- Gemfile.lock
|
106
|
+
- LICENSE.txt
|
107
|
+
- README.rdoc
|
108
|
+
- Rakefile
|
109
|
+
- VERSION
|
110
|
+
- bin/passphrase
|
111
|
+
- lib/passphrase/data.rb
|
112
|
+
- lib/passphrase/generator.rb
|
113
|
+
- lib/passphrase/options.rb
|
114
|
+
- lib/passphrase/random.rb
|
115
|
+
- lib/passphrase/version.rb
|
116
|
+
- lib/passphrase/wordlist.rb
|
117
|
+
- test/test_options.rb
|
118
|
+
- test/test_wordlist.rb
|
119
|
+
has_rdoc: true
|
120
|
+
homepage: http://github.com/esumbar/passphrase
|
121
|
+
licenses: []
|
122
|
+
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
hash: 2
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
version: "0"
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: "0"
|
143
|
+
requirements: []
|
144
|
+
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 1.4.1
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: Generate a passphrase using the Diceware method.
|
150
|
+
test_files:
|
151
|
+
- test/test_options.rb
|
152
|
+
- test/test_wordlist.rb
|