passphrase 1.0.0 → 1.1.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +1 -1
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +12 -0
- data/README.md +58 -44
- data/lib/passphrase.rb +0 -4
- data/lib/passphrase/CLI.rb +30 -4
- data/lib/passphrase/default.rb +1 -1
- data/lib/passphrase/diceware_method.rb +3 -0
- data/lib/passphrase/diceware_random.rb +1 -1
- data/lib/passphrase/passphrase.rb +15 -17
- data/lib/passphrase/passphrase_string.rb +60 -0
- data/lib/passphrase/version.rb +1 -1
- data/spec/passphrase/cli_spec.rb +34 -2
- data/spec/passphrase/diceware_method_spec.rb +9 -1
- data/spec/passphrase/diceware_random_spec.rb +6 -6
- data/spec/passphrase/passphrase_spec.rb +70 -53
- data/spec/passphrase/passphrase_string_spec.rb +55 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f0c06ef87ec317d113d67632f44873e4c0b6c7f
|
4
|
+
data.tar.gz: c144b9ec21879b8717a5c3c3a0f5dc13be273542
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2711c0224023e76b00f2f1fea8358be40ee078edaa1891c3623f9803398cf4d2868261db067e0b9d0e072679cdb385e9b4809934325c06686de5d747c3110f1
|
7
|
+
data.tar.gz: a425a87a7134ffc3efa2262f208f278e0f941a33ea97f1eb35558fa8a68cba03661e139856e5d72a402ff49c6d50cd4ecf9eef950326b54270f8f7383160c07b
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
�^�殗�I���K�VXV:J��9���l��>�'s�c^(�m����;�*C��A��1�����[r��^ ��I������{Tϓ��D�ֹ~R*�tGi�u �^nj���?K1b${p��z�݊1v���ʪeŀ�D�V�c���>��1L��N�W�C}��:1 ��+)������
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
All notable changes to this project will be documented in this file. This
|
3
3
|
project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## Version 1.1.0 - 2015-04-14
|
6
|
+
### Added
|
7
|
+
- Added SHA256 checksum calculation to rake_helper.rb.
|
8
|
+
- Added `:use_random_org` key to Passphrase#inspect.
|
9
|
+
- Added `--passwordize|-p` option to convert a passphrase into a password.
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
- Made `number_of_words` an attribute of the Passphrase class.
|
13
|
+
- Generate a passphrase when a Passphrase object is instantiated.
|
14
|
+
- The passphrase attribute is now an instance of PassphraseString which
|
15
|
+
encapsulates the `#to_password` method.
|
16
|
+
|
5
17
|
## Version 1.0.0 - 2015-04-06
|
6
18
|
This release is a complete re-write. None of the code from the previous
|
7
19
|
release was retained.
|
data/README.md
CHANGED
@@ -7,12 +7,12 @@ the command-line, run
|
|
7
7
|
|
8
8
|
or programmatically,
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
```ruby
|
11
|
+
require "passphrase"
|
12
|
+
p = Passphrase::Passphrase.new(number_of_words: 4)
|
13
|
+
passphrase = p.passphrase
|
14
|
+
```
|
15
|
+
Passphrase also has the capability to generate passwords (indirectly).
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
The Passphrase command-line tool and library can be installed with
|
@@ -41,7 +41,9 @@ this case `sqlite3`, which is not signed, from the verification process.
|
|
41
41
|
Usage: passphrase [options]
|
42
42
|
-n, --num-words=NUM Number of words in passphrase 3..10
|
43
43
|
(default: 5)
|
44
|
-
-
|
44
|
+
-p, --[no-]passwordize Add one cap, one num, and one special char
|
45
|
+
(default: --no-passwordize)
|
46
|
+
-r, --[no-]random-org Use RANDOM.ORG to generate random numbers
|
45
47
|
(default: --no-random-org)
|
46
48
|
-h, --help Show this message
|
47
49
|
-v, --version Show version
|
@@ -52,30 +54,41 @@ this case `sqlite3`, which is not signed, from the verification process.
|
|
52
54
|
$ passphrase -n 4
|
53
55
|
apaisado vermouth seemag ebelik
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# generate a passphrase with default options
|
60
|
-
p = Passphrase::Passphrase.new
|
61
|
-
p.generate
|
62
|
-
puts "passphrase: #{p}"
|
63
|
-
puts "passphrase internals: #{p.inspect}"
|
57
|
+
$ passphrase -n 3 -p
|
58
|
+
pothvati alewives escatima
|
59
|
+
pothvati_alewiNes_e&cat5ma
|
64
60
|
|
65
|
-
|
66
|
-
options = { number_of_words: 4, use_random_org: true }
|
67
|
-
p = Passphrase::Passphrase.new(options)
|
68
|
-
passphrase1 = p.generate.passphrase
|
69
|
-
passphrase2 = p.generate.passphrase
|
70
|
-
passphrase3 = p.generate.passphrase
|
61
|
+
### Ruby library
|
71
62
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
63
|
+
```ruby
|
64
|
+
require "passphrase"
|
65
|
+
|
66
|
+
# generate a passphrase with default options
|
67
|
+
p = Passphrase::Passphrase.new
|
68
|
+
puts "passphrase: #{p}"
|
69
|
+
puts "passphrase internals: #{p.inspect}"
|
70
|
+
|
71
|
+
# generate three four-word passphrases using RANDOM.ORG
|
72
|
+
options = { number_of_words: 4, use_random_org: true }
|
73
|
+
p = Passphrase::Passphrase.new(options)
|
74
|
+
passphrase1 = p.generate.passphrase
|
75
|
+
passphrase2 = p.generate.passphrase
|
76
|
+
passphrase3 = p.generate.passphrase
|
77
|
+
|
78
|
+
# generate an array of six-word passphrases
|
79
|
+
passphrase_array = Array.new(100)
|
80
|
+
Passphrase::Passphrase.new(number_of_words: 6) do |p|
|
81
|
+
passphrase_array.map! { |e| p.generate.passphrase }
|
82
|
+
end
|
83
|
+
|
84
|
+
# generate a password
|
85
|
+
p = Passphrase::Passphrase.new(number_of_words: 3)
|
86
|
+
passphrase = p.passphrase
|
87
|
+
password = passphrase.to_password
|
88
|
+
```
|
77
89
|
|
78
90
|
## Background
|
91
|
+
### Diceware Method
|
79
92
|
Passphrase implements the [Diceware
|
80
93
|
Method](http://world.std.com/~reinhold/diceware.html) which constructs a
|
81
94
|
passphrase by randomly selecting words from a predefined list of more-or-less
|
@@ -113,11 +126,11 @@ SQLite 3 database file).
|
|
113
126
|
|
114
127
|
Except in the original Diceware list, all words are lower case, comprised of
|
115
128
|
characters from the ascii set `[a-z]`. The original Diceware list includes
|
116
|
-
some "words" with numerical and punctuation characters.
|
117
|
-
than once in this set of wordlists.
|
129
|
+
some "words" with numerical and punctuation characters. To the best of my
|
130
|
+
knowledge, no word appears more than once in this set of wordlists.
|
118
131
|
|
119
132
|
The _dice_ in Diceware refers to the act of rolling a die to achieve
|
120
|
-
randomness. A sequence of five consecutive rolls has 7776 (6<sup>5</sup>)
|
133
|
+
randomness. A sequence of five consecutive rolls has 7776 (or 6<sup>5</sup>)
|
121
134
|
possible outcomes. Each combination maps to one line in a given wordlist, and
|
122
135
|
each line, in turn, is composed of between 1 and 15 words (depending on the
|
123
136
|
language, the average being 5).
|
@@ -128,8 +141,8 @@ Therefore, to select a word, Passphrase
|
|
128
141
|
2. randomly selects one of 7776 lines from the corresponding wordlist
|
129
142
|
3. randomly selects one word from the corresponding line
|
130
143
|
|
131
|
-
This leads to roughly 10<sup>28</sup> possible five-word
|
132
|
-
example.
|
144
|
+
This leads to roughly 2<sup>66</sup> (or 10<sup>28</sup>) possible five-word
|
145
|
+
passphrases, for example.
|
133
146
|
|
134
147
|
Passphrase simulates the rolls of a die by using random numbers from one of
|
135
148
|
two sources. The default is to use the standard SecureRandom class. You also
|
@@ -137,8 +150,16 @@ have the option of requesting random numbers from
|
|
137
150
|
[RANDOM.ORG](http://www.random.org). However, because RANDOM.ORG depends on
|
138
151
|
network access, it is susceptible to network problems, and is also slower.
|
139
152
|
|
153
|
+
### Passwords
|
154
|
+
A typical passphrase will not satisfy password policies that require the use
|
155
|
+
of upper case letters, numbers, and special characters (~, !, and the like).
|
156
|
+
Therefore, Passphrase gives you the option to replace three randomly selected
|
157
|
+
characters in a generated passphrase with one random upper case letter
|
158
|
+
`[A-Z]`, one random integer `[0-9]`, and one random special character. Spaces
|
159
|
+
are not usually allowed either, so they are replaced with underscores as part
|
160
|
+
of this process.
|
161
|
+
|
140
162
|
## Contributing to Passphrase
|
141
|
-
### Getting started
|
142
163
|
After forking the [repository on
|
143
164
|
GitHub](https://github.com/esumbar/passphrase), go to your local copy of the
|
144
165
|
repository and execute `bundle install` to ensure that all development gems
|
@@ -154,21 +175,14 @@ bin/passphrase`. You can also experiment with the library in irb. For example,
|
|
154
175
|
|
155
176
|
$ irb -Ilib -rpassphrase
|
156
177
|
>> p = Passphrase::Passphrase.new(number_of_words: 3)
|
157
|
-
=> {:passphrase=>"", :number_of_words=>
|
158
|
-
>> p.generate
|
159
|
-
=> {:passphrase=>"bolt flanella ininaen", :number_of_words=>3,...}
|
178
|
+
=> {:passphrase=>"jazzy vannier viscount", :number_of_words=>3, ... }
|
160
179
|
>> p.passphrase
|
161
|
-
=> "
|
180
|
+
=> "jazzy vannier viscount"
|
181
|
+
>> p.passphrase.to_password
|
182
|
+
=> "jazz{_2annier_vBscount"
|
162
183
|
|
163
184
|
Run the tests with `rake spec`.
|
164
185
|
|
165
|
-
### Future enhancements
|
166
|
-
In order to make passphrases more acceptable as passwords, a feature could be
|
167
|
-
added to the library to upcase a random letter, replace a random alphabetic
|
168
|
-
character with a numeric character (if one does not already exist), and mix in
|
169
|
-
a special character, like "`$`" or "`%`" etc. The command-line option could be
|
170
|
-
called `--passwordize|-p`.
|
171
|
-
|
172
186
|
## Changelog
|
173
187
|
See {file:CHANGELOG.md} for a list of changes.
|
174
188
|
|
data/lib/passphrase.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
require "passphrase/default"
|
2
|
-
require "passphrase/diceware_method"
|
3
|
-
require "passphrase/diceware_random"
|
4
|
-
require "passphrase/language_query"
|
5
2
|
require "passphrase/passphrase"
|
6
3
|
require "passphrase/version"
|
7
|
-
require "passphrase/word_query"
|
8
4
|
|
9
5
|
module Passphrase
|
10
6
|
autoload(:CLI, "passphrase/CLI")
|
data/lib/passphrase/CLI.rb
CHANGED
@@ -14,20 +14,44 @@ module Passphrase
|
|
14
14
|
# given by ARGV (may be empty)
|
15
15
|
# @return [void]
|
16
16
|
def self.parse(args)
|
17
|
-
|
17
|
+
# Gotcha:
|
18
|
+
# The contents of an instance variable can be changed outside the class
|
19
|
+
# through a reader attribute. For example,
|
20
|
+
# class A
|
21
|
+
# attr_reader :x
|
22
|
+
# def initialize
|
23
|
+
# @x = { a: 1, b: 2 }
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# a = A.new
|
27
|
+
# p a.x #=> {:a=>1, :b=>2}
|
28
|
+
# x = a.x
|
29
|
+
# x[:b] = 99
|
30
|
+
# p a.x #=> {:a=>1, :b=>99}
|
31
|
+
# Therefore, need to clone the default hash otherwise parsing could
|
32
|
+
# alter the hash's contents. Although this causes no harm in the
|
33
|
+
# command-line tool, it causes havoc in the test suite.
|
34
|
+
options = Default.options.clone
|
18
35
|
|
19
36
|
default_number_of_words = Default.options[:number_of_words]
|
37
|
+
default_passwordize = Default.options[:passwordize] ? "--passwordize" : "--no-passwordize"
|
20
38
|
default_random_org = Default.options[:use_random_org] ? "--random-org" : "--no-random-org"
|
39
|
+
default_number_range = Default.number_range
|
21
40
|
|
22
41
|
parser = OptionParser.new do |opts|
|
23
42
|
opts.banner = "Usage: passphrase [options]"
|
24
43
|
opts.on(:REQUIRED, "-n NUM", "--num-words=NUM", Integer,
|
25
|
-
"Number of words in passphrase #{
|
44
|
+
"Number of words in passphrase #{default_number_range}",
|
26
45
|
"(default: #{default_number_of_words})") do |n|
|
27
46
|
options[:number_of_words] = n
|
28
47
|
end
|
48
|
+
opts.on(:NONE, "-p", "--[no-]passwordize",
|
49
|
+
"Add one cap, one num, and one special char",
|
50
|
+
"(default: #{default_passwordize})") do |p|
|
51
|
+
options[:passwordize] = p
|
52
|
+
end
|
29
53
|
opts.on(:NONE, "-r", "--[no-]random-org",
|
30
|
-
"Use
|
54
|
+
"Use RANDOM.ORG to generate random numbers",
|
31
55
|
"(default: #{default_random_org})") do |r|
|
32
56
|
options[:use_random_org] = r
|
33
57
|
end
|
@@ -44,7 +68,9 @@ module Passphrase
|
|
44
68
|
begin
|
45
69
|
parser.parse!(args)
|
46
70
|
validate_number_of_words(options)
|
47
|
-
|
71
|
+
passphrase = Passphrase.new(options).passphrase
|
72
|
+
puts passphrase
|
73
|
+
puts passphrase.to_password if options[:passwordize]
|
48
74
|
rescue OptionParser::InvalidOption => e
|
49
75
|
handle_error(e)
|
50
76
|
rescue OptionParser::MissingArgument => e
|
data/lib/passphrase/default.rb
CHANGED
@@ -19,7 +19,7 @@ module Passphrase
|
|
19
19
|
|
20
20
|
# @param use_random_org [Boolean] a flag that triggers the use of
|
21
21
|
# RANDOM.ORG for generating random numbers
|
22
|
-
def initialize(use_random_org
|
22
|
+
def initialize(use_random_org)
|
23
23
|
@random_org_uri = "https://www.random.org"
|
24
24
|
use_random_org ? setup_remote_generator : setup_local_generator
|
25
25
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require "passphrase/diceware_method"
|
2
|
+
require "passphrase/passphrase_string"
|
3
|
+
|
1
4
|
module Passphrase
|
2
5
|
# This is the main class of the Passphrase library for generating
|
3
6
|
# passphrases. It's initialized with a two-element hash that specifies the
|
@@ -12,14 +15,15 @@ module Passphrase
|
|
12
15
|
# @return [String] the passphrase string
|
13
16
|
attr_reader :passphrase
|
14
17
|
|
18
|
+
# @return [Integer] the number of words in the passphrase
|
19
|
+
attr_reader :number_of_words
|
20
|
+
|
15
21
|
# @param options [Hash] characteristics for a new Passphrase object
|
16
22
|
# @yieldparam obj [self] the Passphrase object
|
17
23
|
def initialize(options={})
|
18
24
|
@options = Default.options.merge(options)
|
19
|
-
@
|
20
|
-
|
21
|
-
@die_rolls = []
|
22
|
-
@words = []
|
25
|
+
@number_of_words = @options[:number_of_words]
|
26
|
+
generate
|
23
27
|
yield self if block_given?
|
24
28
|
end
|
25
29
|
|
@@ -27,23 +31,16 @@ module Passphrase
|
|
27
31
|
# resulting arrays are accumulated into instance variables. The words
|
28
32
|
# array is formatted into a single passphrase string and stored in another
|
29
33
|
# instance variable. The method returns itself to allow method chaining.
|
30
|
-
# @return [self]
|
34
|
+
# @return [self] the Passphrase object
|
31
35
|
def generate
|
32
36
|
@languages, @die_rolls, @words = DicewareMethod.run(@options)
|
33
|
-
@passphrase = @words.join(" ")
|
37
|
+
@passphrase = PassphraseString.new(@words.join(" "), @options[:use_random_org])
|
34
38
|
self
|
35
39
|
end
|
36
40
|
|
37
|
-
# This virtual attribute accessor returns the number of words in the
|
38
|
-
# generated passphrase.
|
39
|
-
# @return [Integer] the number of words in the passphrase
|
40
|
-
def number_of_words
|
41
|
-
@words.size
|
42
|
-
end
|
43
|
-
|
44
41
|
# A predicate method that returns true if the Passphrase object is
|
45
42
|
# initialized to use RANDOM.ORG
|
46
|
-
# @return [Boolean] returns true
|
43
|
+
# @return [Boolean] returns true if RANDOM.ORG is being used
|
47
44
|
def using_random_org?
|
48
45
|
@options[:use_random_org]
|
49
46
|
end
|
@@ -51,15 +48,16 @@ module Passphrase
|
|
51
48
|
# String representation of a Passphrase object
|
52
49
|
# @return [String] the passphrase string
|
53
50
|
def to_s
|
54
|
-
|
51
|
+
passphrase
|
55
52
|
end
|
56
53
|
|
57
54
|
# Returns details for the Passphrase object as a hash.
|
58
55
|
# @return [Hash] the details of a Passphrase object
|
59
56
|
def inspect
|
60
57
|
{
|
61
|
-
passphrase:
|
62
|
-
number_of_words:
|
58
|
+
passphrase: passphrase,
|
59
|
+
number_of_words: number_of_words,
|
60
|
+
use_random_org: using_random_org?,
|
63
61
|
word_origins: word_origins
|
64
62
|
}
|
65
63
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Passphrase
|
2
|
+
# This subclass of String implements the {#passwordize} instance method for
|
3
|
+
# substituting a capital letter, number, and special character into a
|
4
|
+
# passphrase.
|
5
|
+
class PassphraseString < String
|
6
|
+
# @param passphrase [String] the passphrase as an ordinary String object
|
7
|
+
# @param use_random_org [Boolean] a flag that triggers the use of
|
8
|
+
# RANDOM.ORG for generating random numbers
|
9
|
+
def initialize(passphrase, use_random_org)
|
10
|
+
super(passphrase)
|
11
|
+
@use_random_org = use_random_org
|
12
|
+
@random = DicewareRandom.new(use_random_org)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String] a new String that is the passwordized version of the
|
16
|
+
# passphrase
|
17
|
+
def to_password
|
18
|
+
@scratch = self.gsub(/\s/, "_")
|
19
|
+
capital_index, number_index, special_index = @random.indices(3, @scratch.length)
|
20
|
+
select_and_substitute_capital_letter(capital_index)
|
21
|
+
select_and_substitute_number(number_index)
|
22
|
+
select_and_substitute_special_character(special_index)
|
23
|
+
String.new(@scratch)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def select_and_substitute_capital_letter(random_index)
|
29
|
+
# Just in case the passphrase already includes a capital letter.
|
30
|
+
return if /[A-Z]/ =~ @scratch
|
31
|
+
capital_letter = ("A".."Z").to_a.sample
|
32
|
+
substitute(capital_letter, random_index)
|
33
|
+
end
|
34
|
+
|
35
|
+
def select_and_substitute_number(random_index)
|
36
|
+
# The Diceware wordlist includes "words" that include numbers.
|
37
|
+
return if /[0-9]/ =~ @scratch
|
38
|
+
number = ("0".."9").to_a.sample
|
39
|
+
substitute(number, random_index)
|
40
|
+
end
|
41
|
+
|
42
|
+
def select_and_substitute_special_character(random_index)
|
43
|
+
# The Diceware wordlist includes "words" that include special characters.
|
44
|
+
return if /[~!#\$%\^&\*\(\)\-=\+\[\]\\\{\}:;"'<>\?\/]/ =~ @scratch
|
45
|
+
special_character = %w( ~ ! # $ % ^ & * \( \) - = + [ ] \\ { } : ; " ' < > ? / ).sample
|
46
|
+
substitute(special_character, random_index)
|
47
|
+
end
|
48
|
+
|
49
|
+
def substitute(substitution, random_index)
|
50
|
+
character = @scratch[random_index]
|
51
|
+
@scratch[random_index] = substitution and return if /[a-z]/ =~ character
|
52
|
+
next_index = random_index.succ.modulo(@scratch.length)
|
53
|
+
until next_index == random_index
|
54
|
+
character = @scratch[next_index]
|
55
|
+
@scratch[next_index] = substitution and return if /[a-z]/ =~ character
|
56
|
+
next_index = next_index.succ.modulo(@scratch.length)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/passphrase/version.rb
CHANGED
data/spec/passphrase/cli_spec.rb
CHANGED
@@ -3,6 +3,15 @@ require "optparse"
|
|
3
3
|
|
4
4
|
module Passphrase
|
5
5
|
RSpec.describe CLI, "implements the command line interface" do
|
6
|
+
before do
|
7
|
+
@saved_stdout = $stdout
|
8
|
+
$stdout = StringIO.new
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
$stdout = @saved_stdout
|
13
|
+
end
|
14
|
+
|
6
15
|
it "responds to class method parse() with one argument" do
|
7
16
|
expect(CLI).to respond_to(:parse).with(1).argument
|
8
17
|
end
|
@@ -15,12 +24,35 @@ module Passphrase
|
|
15
24
|
expect(CLI).not_to respond_to(:handle_error)
|
16
25
|
end
|
17
26
|
|
27
|
+
it "does not emit an error when option -n 3 is supplied" do
|
28
|
+
expect(CLI).not_to receive(:handle_error)
|
29
|
+
CLI.parse(["-n", "3"])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "does not emit an error when option -p is supplied" do
|
33
|
+
expect(CLI).not_to receive(:handle_error)
|
34
|
+
CLI.parse(["-p"])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "does not emit an error when option -r is supplied" do
|
38
|
+
expect(CLI).not_to receive(:handle_error)
|
39
|
+
CLI.parse(["-r"])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "exits when option -h is supplied" do
|
43
|
+
expect { CLI.parse(["-h"]) }.to raise_error(SystemExit)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "exits when option -v is supplied" do
|
47
|
+
expect { CLI.parse(["-v"]) }.to raise_error(SystemExit)
|
48
|
+
end
|
49
|
+
|
18
50
|
it "emits an error when an invalid option is supplied" do
|
19
51
|
# For some reason, :handle_error doesn't need to be stubbed
|
20
52
|
expect(CLI).to receive(:handle_error)
|
21
53
|
.with(kind_of(OptionParser::InvalidOption))
|
22
|
-
|
23
|
-
CLI.parse(
|
54
|
+
invalid_option = ["-x"]
|
55
|
+
CLI.parse(invalid_option)
|
24
56
|
end
|
25
57
|
|
26
58
|
it "emits an error when a mandatory argument is missing" do
|
@@ -50,11 +50,19 @@ module Passphrase
|
|
50
50
|
)
|
51
51
|
end
|
52
52
|
|
53
|
-
it "each array
|
53
|
+
it "each array contains :number_of_words elements" do
|
54
54
|
@result.each do |result|
|
55
55
|
expect(result.size).to eq(@number_of_words)
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
it "each array contains String elements" do
|
60
|
+
@result.each do |result|
|
61
|
+
result.each do |element|
|
62
|
+
expect(element).to be_an_instance_of(String)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
58
66
|
end
|
59
67
|
end
|
60
68
|
end
|
@@ -46,6 +46,10 @@ module Passphrase
|
|
46
46
|
it "each one in the range 0...15" do
|
47
47
|
expect(@result).to all be_between(0, 14)
|
48
48
|
end
|
49
|
+
|
50
|
+
it "produces a different result when called a second time" do
|
51
|
+
expect(@random.indices(4, 15)).not_to eq(@result)
|
52
|
+
end
|
49
53
|
end
|
50
54
|
|
51
55
|
describe "#die_rolls(6)" do
|
@@ -82,7 +86,7 @@ module Passphrase
|
|
82
86
|
|
83
87
|
context "initialized by default to use the local Ruby random number generator" do
|
84
88
|
before do
|
85
|
-
@random = DicewareRandom.new
|
89
|
+
@random = DicewareRandom.new(false)
|
86
90
|
end
|
87
91
|
|
88
92
|
include_examples "DicewareRandom object"
|
@@ -90,11 +94,7 @@ module Passphrase
|
|
90
94
|
|
91
95
|
context "initialized to use random numbers from RANDOM.ORG" do
|
92
96
|
before do
|
93
|
-
@random = DicewareRandom.new(
|
94
|
-
end
|
95
|
-
|
96
|
-
it "initially has a zero random.org request count" do
|
97
|
-
expect(DicewareRandom.random_org_requests).to eq(0)
|
97
|
+
@random = DicewareRandom.new(true)
|
98
98
|
end
|
99
99
|
|
100
100
|
it "increments the random.org request count on indices()" do
|
@@ -1,58 +1,91 @@
|
|
1
1
|
require "passphrase"
|
2
2
|
|
3
3
|
module Passphrase
|
4
|
+
RSpec.shared_examples "Passphrase object" do
|
5
|
+
it "responds to attribute reader method passphrase()" do
|
6
|
+
expect(@passphrase).to respond_to(:passphrase)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "responds to attribute reader method number_of_words()" do
|
10
|
+
expect(@passphrase).to respond_to(:number_of_words).with(0).arguments
|
11
|
+
end
|
12
|
+
|
13
|
+
it "responds to the generate() method with zero arguments" do
|
14
|
+
expect(@passphrase).to respond_to(:generate).with(0).arguments
|
15
|
+
end
|
16
|
+
|
17
|
+
it "responds to predicate method using_random_org?()" do
|
18
|
+
expect(@passphrase).to respond_to(:using_random_org?)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "responds to the to_s() method" do
|
22
|
+
expect(@passphrase).to respond_to(:to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "responds to the inspect() method with zero arguments" do
|
26
|
+
expect(@passphrase).to respond_to(:inspect).with(0).arguments
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not respond to the word_origins() method (private)" do
|
30
|
+
expect(@passphrase).not_to respond_to(:word_origins)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
4
34
|
RSpec.describe Passphrase, "for generating passphrase objects" do
|
5
35
|
it "yields itself during instantiation" do
|
6
|
-
expect { |b| Passphrase.new(
|
36
|
+
expect { |b| Passphrase.new(&b) }.to yield_with_args(Passphrase)
|
7
37
|
end
|
8
38
|
|
9
39
|
# Dependence on RANDOM.ORG only affects the DicewareRandom class.
|
10
|
-
# Therefore, only need to test the case where RANDOM.ORG is not used.
|
11
|
-
context "initialized to
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
it "responds to the generate() method with zero arguments" do
|
17
|
-
expect(@passphrase).to respond_to(:generate).with(0).arguments
|
40
|
+
# Therefore, only need to thoroughly test the case where RANDOM.ORG is not used.
|
41
|
+
context "initialized to use RANDOM.ORG" do
|
42
|
+
it "the predicate method should confirm it is using RANDOM.ORG" do
|
43
|
+
passphrase = Passphrase.new(use_random_org: true)
|
44
|
+
expect(passphrase).to be_using_random_org
|
18
45
|
end
|
46
|
+
end
|
19
47
|
|
20
|
-
|
21
|
-
|
48
|
+
context "initialized with default options" do
|
49
|
+
before do
|
50
|
+
@passphrase = Passphrase.new
|
22
51
|
end
|
23
52
|
|
24
|
-
it "
|
25
|
-
expect(@passphrase).to
|
53
|
+
it "returns a passphrase of type PassphraseString" do
|
54
|
+
expect(@passphrase.passphrase).to be_an_instance_of(PassphraseString)
|
26
55
|
end
|
27
|
-
|
28
|
-
it "
|
29
|
-
|
56
|
+
|
57
|
+
it "returns a passphrase with the default number of words" do
|
58
|
+
number_of_words = @passphrase.passphrase.split.length
|
59
|
+
expect(number_of_words).to eq(Default.options[:number_of_words])
|
30
60
|
end
|
31
61
|
|
32
|
-
it "
|
33
|
-
expect(@passphrase).
|
62
|
+
it "should not be using RANDOM.ORG" do
|
63
|
+
expect(@passphrase).not_to be_using_random_org
|
34
64
|
end
|
35
65
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
66
|
+
include_examples "Passphrase object"
|
67
|
+
end
|
39
68
|
|
40
|
-
|
41
|
-
|
69
|
+
context "initialized to generate a passphrase with 1 word" do
|
70
|
+
before do
|
71
|
+
@passphrase = Passphrase.new(number_of_words: 1)
|
42
72
|
end
|
43
73
|
|
44
|
-
it "
|
45
|
-
expect(@passphrase.passphrase).to
|
74
|
+
it "returns a passphrase of type PassphraseString" do
|
75
|
+
expect(@passphrase.passphrase).to be_an_instance_of(PassphraseString)
|
46
76
|
end
|
47
77
|
|
48
|
-
it "
|
49
|
-
|
78
|
+
it "returns a passphrase with one word" do
|
79
|
+
number_of_words = @passphrase.passphrase.split.length
|
80
|
+
expect(number_of_words).to eq(1)
|
50
81
|
end
|
51
82
|
|
52
83
|
it "should not be using RANDOM.ORG" do
|
53
84
|
expect(@passphrase).not_to be_using_random_org
|
54
85
|
end
|
55
86
|
|
87
|
+
include_examples "Passphrase object"
|
88
|
+
|
56
89
|
context "after executing the generate() method" do
|
57
90
|
before do
|
58
91
|
dwr = [["language"], ["11111"], ["passphrase"]]
|
@@ -64,12 +97,17 @@ module Passphrase
|
|
64
97
|
expect(@result).to equal(@passphrase)
|
65
98
|
end
|
66
99
|
|
67
|
-
it "
|
68
|
-
expect(@result.passphrase).to
|
100
|
+
it "returns a passphrase of type PassphraseString" do
|
101
|
+
expect(@result.passphrase).to be_an_instance_of(PassphraseString)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "contains a passphrase with one word" do
|
105
|
+
number_of_words = @result.passphrase.split.length
|
106
|
+
expect(number_of_words).to eq(1)
|
69
107
|
end
|
70
108
|
|
71
|
-
it "contains
|
72
|
-
expect(@result.
|
109
|
+
it "contains the passphrase string" do
|
110
|
+
expect(@result.passphrase).to eq("passphrase")
|
73
111
|
end
|
74
112
|
|
75
113
|
it "returns the passphrase when printed" do
|
@@ -83,6 +121,7 @@ module Passphrase
|
|
83
121
|
expect(@result.inspect).to match(
|
84
122
|
passphrase: "passphrase",
|
85
123
|
number_of_words: 1,
|
124
|
+
use_random_org: nil,
|
86
125
|
word_origins: { "passphrase" => {
|
87
126
|
language: "language",
|
88
127
|
die_rolls: "11111"
|
@@ -92,27 +131,5 @@ module Passphrase
|
|
92
131
|
end
|
93
132
|
end
|
94
133
|
end
|
95
|
-
|
96
|
-
context "initialized to use RANDOM.ORG" do
|
97
|
-
it "the predicate method should confirm it is using RANDOM.ORG" do
|
98
|
-
passphrase = Passphrase.new(number_of_words: 3, use_random_org: true)
|
99
|
-
expect(passphrase).to be_using_random_org
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
context "initialized with default options" do
|
104
|
-
before do
|
105
|
-
@passphrase = Passphrase.new
|
106
|
-
@passphrase.generate
|
107
|
-
end
|
108
|
-
|
109
|
-
it "return a passphrase with the default number of words" do
|
110
|
-
expect(@passphrase.number_of_words).to eq(Default.options[:number_of_words])
|
111
|
-
end
|
112
|
-
|
113
|
-
it "should not be using RANDOM.ORG" do
|
114
|
-
expect(@passphrase).not_to be_using_random_org
|
115
|
-
end
|
116
|
-
end
|
117
134
|
end
|
118
135
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "passphrase"
|
2
|
+
|
3
|
+
module Passphrase
|
4
|
+
RSpec.describe PassphraseString, "subclass of String class" do
|
5
|
+
context "initialized with a sample string" do
|
6
|
+
before do
|
7
|
+
@sample = "a sample passphrase string"
|
8
|
+
@passphrase_string = PassphraseString.new(@sample, false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "contains the same string as the sample string" do
|
12
|
+
expect(@passphrase_string).to eq(@sample)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "has the same length as the sample string" do
|
16
|
+
expect(@passphrase_string.length).to eq(26)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "responds to the to_password() method with zero arguments" do
|
20
|
+
expect(@passphrase_string).to respond_to(:to_password).with(0).arguments
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#to_password" do
|
24
|
+
before do
|
25
|
+
@passwordized_string = @passphrase_string.to_password
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns an ordinary String" do
|
29
|
+
expect(@passwordized_string).to be_an_instance_of(String)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns a String that is not the same object as the PassphraseString" do
|
33
|
+
expect(@passwordized_string).not_to equal(@passphrase_string)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns a String that has the same length as the PassphraseString" do
|
37
|
+
expect(@passwordized_string.length).to eq(@passphrase_string.length)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns a String that contains at least one captial letter" do
|
41
|
+
expect(@passwordized_string).to match(/[A-Z]/)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns a String that contains at least one number" do
|
45
|
+
expect(@passwordized_string).to match(/[0-9]/)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns a String that contains at least one special character" do
|
49
|
+
special_character = /[~!#\$%\^&\*\(\)\-=\+\[\]\\\{\}:;"'<>\?\/]/
|
50
|
+
expect(@passwordized_string).to match(special_character)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passphrase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edmund Sumbar
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
aoKZhUfRmAZDUgzW4PMtMxO34fQDbDYevBzMcxmkHNDs87ROMOuPLWJGLcKpgGwJ
|
31
31
|
TxzUtBfb7E+Qa/wyo0Ws7A==
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2015-04-
|
33
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: sqlite3
|
@@ -139,6 +139,7 @@ files:
|
|
139
139
|
- lib/passphrase/diceware_random.rb
|
140
140
|
- lib/passphrase/language_query.rb
|
141
141
|
- lib/passphrase/passphrase.rb
|
142
|
+
- lib/passphrase/passphrase_string.rb
|
142
143
|
- lib/passphrase/version.rb
|
143
144
|
- lib/passphrase/word_query.rb
|
144
145
|
- lib/passphrase/wordlist/words.sqlite3
|
@@ -147,6 +148,7 @@ files:
|
|
147
148
|
- spec/passphrase/diceware_random_spec.rb
|
148
149
|
- spec/passphrase/language_query_spec.rb
|
149
150
|
- spec/passphrase/passphrase_spec.rb
|
151
|
+
- spec/passphrase/passphrase_string_spec.rb
|
150
152
|
- spec/passphrase/word_query_spec.rb
|
151
153
|
- spec/spec_helper.rb
|
152
154
|
homepage: https://github.com/esumbar/passphrase
|
metadata.gz.sig
CHANGED
Binary file
|