frasier 0.5.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.
@@ -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'�
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/ruby
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 2.1.0
6
+ - rbx-2
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
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require "pp"
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'frasier'
6
+
7
+ options = Frasier::CLI.parse(ARGV)
8
+ Frasier::CLI.new(options)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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