frasier 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -0
- data/.gitignore +18 -0
- data/.travis.yml +6 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +31 -0
- data/bin/frasier +8 -0
- data/certs/pjaspers.pem +19 -0
- data/checksum/frasier-0.5.1.gem.sha512 +1 -0
- data/frasier.gemspec +26 -0
- data/lib/frasier.rb +20 -0
- data/lib/frasier/book.rb +22 -0
- data/lib/frasier/cli.rb +108 -0
- data/lib/frasier/dice_list.rb +59 -0
- data/lib/frasier/generator.rb +44 -0
- data/lib/frasier/library.rb +40 -0
- data/lib/frasier/version.rb +3 -0
- data/test/book_test.rb +23 -0
- data/test/dice_list_test.rb +44 -0
- data/test/files/my_man_jeeves +7295 -0
- data/test/files/small_sample +7 -0
- data/test/generator_test.rb +29 -0
- data/test/helper.rb +11 -0
- data/test/library_test.rb +37 -0
- metadata +144 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
��#��pv�άV�;,����K���x��w&'�m;���ѿTļ��c=_az�o�r�@�>pS��9c7:�̑���`AE����k����B�'y��VS�d2���a����N�EG����ik���>�k�(�F��W��&yHC�+���`x'�
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in frasier.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem 'pry', '~> 0.9.12.6'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
# Adding rake for Travis.
|
12
|
+
gem 'rake'
|
13
|
+
gem 'minitest', '~> 5.3.2'
|
14
|
+
gem 'mocha', '~> 1.0.0'
|
15
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 pjaspers
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/pjaspers/frasier.svg?branch=master)](https://travis-ci.org/pjaspers/frasier)
|
2
|
+
|
3
|
+
## What does it do?
|
4
|
+
|
5
|
+
Generates phrases using words out of a book. Using high tech computering.
|
6
|
+
|
7
|
+
Those phrases can be used as passphrases, you can set how many words and which books, so you can claim to have passphrase inspired by Great Literature.
|
8
|
+
|
9
|
+
For example using 'The Great Gatsby' it generates easy to remember* phrases like:
|
10
|
+
|
11
|
+
- `gloved modelling pool daylight longer`
|
12
|
+
- `wrong flirtation great bembergs happier`
|
13
|
+
- `obscurity flat quarter nodding libel`
|
14
|
+
|
15
|
+
`gem install frasier` and you're good to go.
|
16
|
+
|
17
|
+
* Note: not actually easy to remember.
|
18
|
+
|
19
|
+
## Isn't this Diceware™?
|
20
|
+
|
21
|
+
Inspired by [Diceware™](http://world.std.com/~reinhold/diceware.html), except instead of being cryptogaphically sound, this uses not a carefully created word list but uses text from books.
|
22
|
+
|
23
|
+
```
|
24
|
+
Diceware™ is a method for picking passphrases that uses dice to select
|
25
|
+
words at random from a special list called the Diceware Word
|
26
|
+
List. Each word in the list is preceded by a five digit number. All
|
27
|
+
the digits are between one and six, allowing you to use the outcomes
|
28
|
+
of five dice rolls to select one unique word from the list.
|
29
|
+
```
|
30
|
+
|
31
|
+
It uses a simple `rand` to simulate the dice roll, the original [Diceware™](http://world.std.com/~reinhold/diceware.html) recommends against this.
|
32
|
+
|
33
|
+
Having said all that, it's safer then creating "th3Ult1mat2Pa$$word" for each service you encounter.
|
34
|
+
|
35
|
+
|
36
|
+
## How do I get books?
|
37
|
+
|
38
|
+
[Project Gutenberg](http://www.gutenberg.org) has some classics which can be freely downloaded as plain text, after that save them in `~/.config/frasier/` and you're good to go.
|
39
|
+
|
40
|
+
## What's with the name?
|
41
|
+
|
42
|
+
You're on a need to know basis.
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
```
|
47
|
+
Usage: frasier [options]
|
48
|
+
-n, --number [NUMBER] Generate passphrase with <n> words
|
49
|
+
-l, --list-books List available books
|
50
|
+
-b, --book [NAME] Specify book to generate from
|
51
|
+
-i, --info Show entropy info
|
52
|
+
-h, --help Show this message
|
53
|
+
--version Show version
|
54
|
+
```
|
55
|
+
|
56
|
+
## Signed gem
|
57
|
+
|
58
|
+
`frasier` is cryptographically signed. To be sure the gem you install hasn’t been tampered with:
|
59
|
+
|
60
|
+
1. Download certificate https://raw.github.com/pjaspers/frasier/certs/pjaspers.pem
|
61
|
+
2. Add `gem cert –add pjaspers.pem`
|
62
|
+
3. gem install frasier -P HighSecurity
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Do some awesome computering
|
69
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
def abort_with_error(msg)
|
4
|
+
abort "\n\e[31mError:\e[0m #{msg}"
|
5
|
+
end
|
6
|
+
|
7
|
+
task :test do
|
8
|
+
$LOAD_PATH.unshift('lib', 'test')
|
9
|
+
Dir.glob('./test/**/*_test.rb') { |f| require f }
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
task :console do
|
15
|
+
require 'pry'
|
16
|
+
require './lib/frasier'
|
17
|
+
ARGV.clear
|
18
|
+
Pry.start Frasier
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
task :checksum do
|
23
|
+
require 'digest/sha2'
|
24
|
+
abort_with_error "Set 'GEM' with name-0.x.x to calculate checksum" unless ENV["GEM"]
|
25
|
+
name = ENV["GEM"]
|
26
|
+
built_gem_path = 'pkg/frasier-0.5.1.gem'
|
27
|
+
checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
|
28
|
+
checksum_path = 'checksum/frasier-0.5.1.gem.sha512'
|
29
|
+
File.open(checksum_path, 'w' ) {|f| f.write(checksum) }
|
30
|
+
puts "Wrote checksum to #{checksum_path}"
|
31
|
+
end
|
data/bin/frasier
ADDED
data/certs/pjaspers.pem
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDKDCCAhCgAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQ0wCwYDVQQDDARwaWV0
|
3
|
+
MRUwEwYKCZImiZPyLGQBGRYFamFzcGUxEjAQBgoJkiaJk/IsZAEZFgJyczAeFw0x
|
4
|
+
NDA0MDcyMTQyMDNaFw0xNTA0MDcyMTQyMDNaMDoxDTALBgNVBAMMBHBpZXQxFTAT
|
5
|
+
BgoJkiaJk/IsZAEZFgVqYXNwZTESMBAGCgmSJomT8ixkARkWAnJzMIIBIjANBgkq
|
6
|
+
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+SQ5IzhMaUM/jrUBAP5l0/Z5ttLQb2lT
|
7
|
+
hotS1Yraz3hclbGhBhDcQilW11GSftUbWhaiMQwNXqaEN9ctmzE5JukOdwhtNSLq
|
8
|
+
IDw+TEEXbg/VImlFhryg9vunffPeOBUlnxdY484DOushNN50H/LA+jAoFST3+oZq
|
9
|
+
CXuA3yFi8ZTPqhHrEn1bkycUfLX+TOm07ZfCPly9fAVYhOYaVaPeYkHAO83+Ic63
|
10
|
+
9YGqD1sFeZ7hfSxqfYEBxpiM0+qztqHCVNrehkzX2oUmY06eVpSu2mzLYmcCD58f
|
11
|
+
inVDRBe/3ygkACxZCA7pjIuqHOpKxSbYMrgEkrAtpA7lkIMaveMY9QIDAQABozkw
|
12
|
+
NzAJBgNVHRMEAjAAMB0GA1UdDgQWBBSSJbKSj/62BYq/1utxyqsShRum8jALBgNV
|
13
|
+
HQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQADggEBAJkG60vFt/GBuMvTdMtFCw4jJiRo
|
14
|
+
8n6dMQrL2kZdD0ZNeidLfTMzhflv+9ZrWfcFWzfqaGgKJckBd2wlfQiR78ts9zom
|
15
|
+
bX9B9n/Ad32womLnb0aeTuEHX6Vy4qs4zl+VnXy4g1NQ2lfcnksUsjBM8v8GYTjy
|
16
|
+
ssiOQnzrWxNyjR01uY5g/ON/IlRKNpihe7Qze1oGWdopZP6Lwfhir5ShbDETCKQv
|
17
|
+
CuJWUOdBRsHbSraLW8n/vc/fZGqVzDAghs26bxmwicw+4/VgO2soYka81t8pUuG6
|
18
|
+
vLWWrtmIv8+cmMgvltP5AR5j2oYHYPPA9YFXuO9t1JVqJiNHm9JNRseEoBc=
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1 @@
|
|
1
|
+
1a4ea27f78e2adfdfd6805e971af94a43f6e9f94aa7ccb6bca9ebef783e2f449115f9ace8b5615ade181d50d43ae2f3e81cc29a6addcae743f87f571b8bdc2dd
|
data/frasier.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'frasier/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "frasier"
|
8
|
+
spec.version = Frasier::VERSION
|
9
|
+
spec.authors = ["pjaspers"]
|
10
|
+
spec.email = ["piet@jaspe.rs"]
|
11
|
+
spec.description = %q{Passphrase generator using plain text books}
|
12
|
+
spec.summary = %q{Inspired by Diceware™, except instead of being cryptogaphically sound, this uses not a carefully created word list but uses text from books.}
|
13
|
+
spec.homepage = "https://github.com/pjaspers/frasier"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.cert_chain = ['certs/pjaspers.pem']
|
22
|
+
spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
end
|
data/lib/frasier.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "frasier/version"
|
2
|
+
require "frasier/library"
|
3
|
+
require "frasier/book"
|
4
|
+
require "frasier/dice_list"
|
5
|
+
require "frasier/generator"
|
6
|
+
require "frasier/cli"
|
7
|
+
|
8
|
+
module Frasier
|
9
|
+
|
10
|
+
# At Cornell University, they have an incredible piece of scientific
|
11
|
+
# equipment known as the "tunneling electron microscope."
|
12
|
+
#
|
13
|
+
# Now, this microscope is so powerful that by firing electrons, you
|
14
|
+
# can actually see images of the atom, the infinite decimally minute
|
15
|
+
# building block of our universe.
|
16
|
+
#
|
17
|
+
# Roger, if I were using that microscope right now, I still wouldn't
|
18
|
+
# be able to locate my interest in your problem.
|
19
|
+
|
20
|
+
end
|
data/lib/frasier/book.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Frasier
|
2
|
+
|
3
|
+
class Book
|
4
|
+
attr_accessor :title, :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
raise Errno::ENOENT unless File.exist?(path || "")
|
8
|
+
@title = title_from_path(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def dice_word_list
|
13
|
+
DiceList.new(File.open(path)).word_list
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def title_from_path(path)
|
19
|
+
File.basename(path).split("_").map(&:capitalize).join(" ")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/frasier/cli.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "optparse"
|
3
|
+
|
4
|
+
module Frasier
|
5
|
+
|
6
|
+
class CLI
|
7
|
+
def self.parse(args)
|
8
|
+
options = OpenStruct.new
|
9
|
+
options.number_of_words = 5
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: frasier [options]"
|
13
|
+
|
14
|
+
opts.on("-n", "--number [NUMBER]", Integer, "Generate passphrase with <n> words") do |n|
|
15
|
+
options.number_of_words = n.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("-l", "--list-books", "List available books") do |list|
|
19
|
+
books = Library.new.books
|
20
|
+
longest_title = books.map(&:title).max.length
|
21
|
+
puts books.map{|b| " %s - %s" % [b.title.ljust(longest_title), File.basename(b.path)]}
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-b", "--book [NAME]", String, "Specify book to generate from") do |book|
|
26
|
+
lib = Library.new
|
27
|
+
unless lib.book_with_name(book)
|
28
|
+
puts "I don't know that book"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
options.book = book
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-i", "--info", "Show entropy info") do |info|
|
35
|
+
options.info = info
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
39
|
+
puts opts
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on_tail("--version", "Show version") do
|
44
|
+
puts Frasier::VERSION
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if Library.new.books.empty?
|
50
|
+
puts <<BLURB
|
51
|
+
|
52
|
+
Please install some books into ~/.config/frasier/
|
53
|
+
Example:
|
54
|
+
curl -L http://www.gutenberg.org/ebooks/8164.txt.utf-8 -o ~/.config/frasier/my_man_jeeves
|
55
|
+
BLURB
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.parse!(args)
|
60
|
+
options
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(options)
|
64
|
+
library = Library.new
|
65
|
+
@book = library.book_with_name(options.book) if options.book
|
66
|
+
@book = library.random_book unless @book
|
67
|
+
number_of_words = options.number_of_words
|
68
|
+
|
69
|
+
@generator = Generator.new(@book.dice_word_list, number_of_words)
|
70
|
+
print_passphrase(options.info)
|
71
|
+
end
|
72
|
+
|
73
|
+
def print_passphrase(info = true)
|
74
|
+
phrase = @generator.passphrase
|
75
|
+
# Try to copy it
|
76
|
+
copy(phrase) if copy_command
|
77
|
+
if info
|
78
|
+
number_of_guesses = 100000
|
79
|
+
duration_in_years = (@generator.duration_to_guess(number_of_guesses)/60.0/60.0/24.0/360.0).round(2)
|
80
|
+
puts "From: #{@book.title}"
|
81
|
+
puts "Bits of entropy: #{@generator.bits_of_entropy}"
|
82
|
+
puts "At 100 000 guesses/s, it would take %s years to guess" % duration_in_years
|
83
|
+
puts ""
|
84
|
+
end
|
85
|
+
puts red(phrase)
|
86
|
+
end
|
87
|
+
|
88
|
+
def copy_command
|
89
|
+
os = RbConfig::CONFIG['host_os']
|
90
|
+
return 'pbcopy' if os =~ /mac|darwin/
|
91
|
+
return 'xclip -selection clipboard' if os =~ /linux|bsd|cygwin/
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def copy(value)
|
96
|
+
return unless copy_command
|
97
|
+
begin
|
98
|
+
IO.popen(copy_command,"w") {|cc| cc.write(value)}
|
99
|
+
value
|
100
|
+
rescue Errno::ENOENT
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def red(s)
|
105
|
+
"\e[31m#{s}\e[0m"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require "set"
|
3
|
+
module Frasier
|
4
|
+
|
5
|
+
class DiceList
|
6
|
+
|
7
|
+
def initialize(io)
|
8
|
+
if io.respond_to? :each_line
|
9
|
+
@io = io
|
10
|
+
else
|
11
|
+
raise ArgumentError, "#{io} does not respond to `each_line`"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# [Full list of requirements](http://world.std.com/%7Ereinhold/dicewarekit.html)
|
16
|
+
#
|
17
|
+
# Not following all the rules since most of the books don't seem to have
|
18
|
+
# enough unique words to make a sizeable list. We'd need at least 7776 words,
|
19
|
+
# but since you can use multiple sources that's not a hard rule.
|
20
|
+
#
|
21
|
+
def valid_words
|
22
|
+
return @valid_words if @valid_words && @valid_words.length > 0
|
23
|
+
|
24
|
+
@valid_words = Set.new
|
25
|
+
@io.each_line do |l|
|
26
|
+
# Funny story, in place methods are faster.
|
27
|
+
l.gsub!(/[^[:alnum:]^[:blank:]]/, "")
|
28
|
+
l.downcase!
|
29
|
+
l.strip!
|
30
|
+
# Only 'short' words
|
31
|
+
l.split(" ").reject{|w| w.length < 3 || w.length > 10}.each do |w|
|
32
|
+
@valid_words.add(w)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@valid_words
|
36
|
+
end
|
37
|
+
|
38
|
+
def word_list
|
39
|
+
return @word_list if @word_list
|
40
|
+
|
41
|
+
# The coolest line in this whole source.
|
42
|
+
#
|
43
|
+
# Get all permuations with `1,2,3,4,5,6` (e.g. from '11111' to '66666')
|
44
|
+
# only take as many as we need.
|
45
|
+
indexes = (1..6).to_a.repeated_permutation(5).map(&:join).first(valid_words.length)
|
46
|
+
|
47
|
+
# Zip the permutations with the words
|
48
|
+
# So this creates a hash like:
|
49
|
+
#
|
50
|
+
# {'11111': 'first',
|
51
|
+
# ...
|
52
|
+
# '66665': 'another',
|
53
|
+
# '66666': 'last'}
|
54
|
+
|
55
|
+
@word_list = Hash[indexes.zip(valid_words)]
|
56
|
+
@word_list
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|