human_token 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bd2c6de633f5bcebaea72ae7eac1505ac9739cf
4
+ data.tar.gz: f92d52708f9964826317111ef016158d89311ecb
5
+ SHA512:
6
+ metadata.gz: 93353224a2123151548107066765c8bd69af4d9400a3ef6276fb886f829d95eb30dcf86219fdd81e75eaf6622573d2f0ac8e8dfe8b4237940b611461f5488fa5
7
+ data.tar.gz: e5d8e28b1539d023f9a8846a565ef0d0da7267cdc464e0b65eea9d91da6ffa854983ad21c56ee8c340927a200304f2d1fbf09ab3cb7786e521ea30bd0eafd088
@@ -0,0 +1,17 @@
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
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "minitest"
4
+ gem "guard-minitest", require: false
5
+ gem "terminal-notifier-guard", require: false
6
+ gem "minitest-reporters"
7
+
8
+ gemspec
@@ -0,0 +1,15 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :bundler do
5
+ watch('Gemfile')
6
+ # Uncomment next line if your Gemfile contains the `gemspec' command.
7
+ watch(/^.+\.gemspec/)
8
+ end
9
+
10
+ guard :minitest do
11
+ # with Minitest::Unit
12
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
13
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
14
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
15
+ end
@@ -0,0 +1,3 @@
1
+ HumanToken is dedicated to the public domain by its author, Brian Hempel. No rights are reserved. No restrictions are placed on the use of HumanToken. That freedom also means, of course, that no warrenty of fitness is claimed; use HumanToken at your own risk.
2
+
3
+ Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
@@ -0,0 +1,142 @@
1
+ # HumanToken
2
+
3
+ HumanToken is a (relatively) human-friendly token generator. You can create tokens of a given cryptographic strength but without ambiguous characters.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'human_token'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```
22
+ $ gem install human_token
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ HumanToken uses Ruby's SecureRandom to generate tokens.
28
+
29
+ ```ruby
30
+ require 'human_token'
31
+
32
+ HumanToken.generate # => "2re9y4mdsh39jy4qqh3eq6tzptz"
33
+ ```
34
+
35
+ Just like SecureRandom, the default is 16 bytes of randomness. However, the token is longer than 16 characters because, well, those 16 bytes aren't binary anymore.
36
+
37
+ You can specify an alternate number of bytes of randomness.
38
+
39
+ ```ruby
40
+ # 64-bit token
41
+ HumanToken.generate(8) # => "nte4mh95kvxdde"
42
+ ```
43
+
44
+ By default, tokens contain lowercase alphanumeric characters, with the exceptions of `0` `1` `i` `l` `o` `u`. Those characters are excluded to prevent ambiguity. The `i` and `o` are not ambiguous when lowercase, but are excluded anyway so the token can be treated case-insensitively: uppercase `I` and `O` are ambiguous.
45
+
46
+ The letter `u` is excluded because, if it were included, when generating 128 bit tokens about 1 in every 340 tokens would phonetically drop the f-bomb on the viewer—or even more often (a lot more often!) if we count a "u" proceed by an "f". [Douglas Crockford](http://www.crockford.com/wrmg/base32.html) is the inspiration for excluding "u", though he didn't enumerate the math.
47
+
48
+ The default tokens are lowercase because lowercase is much easier to read.
49
+
50
+ ## Schemes
51
+
52
+ Several encoding schemes are provided.
53
+
54
+ ```ruby
55
+ HumanToken.hex # Lowercase hexadecimal (included for comparison purposes)
56
+ HumanToken.base_30 # Lowercase base 30, no 0 1 I L O U (this is the default scheme)
57
+ HumanToken.base_31 # Lowercase base 31, no 0 1 I L O
58
+ HumanToken.base_32 # Crockford's Base 32: Uppercase, no I L O U
59
+ HumanToken.base_58 # Bitcoin Base 58: Mixed case, no 0 I O l
60
+ HumanToken.new_base_60 # Tantek Çelik's "New Base 60": Mixed case and underscore, no I O l
61
+ HumanToken.base_62 # All 62 mixed case alphanumerics
62
+ ```
63
+
64
+ You can provide a custom scheme.
65
+
66
+ ```ruby
67
+ HumanToken.generate(6, characters: "aeiou")
68
+ # => "iauioieuoaeeeauiauuii"
69
+ ```
70
+
71
+ Internally, HumanToken uses [BaseX](https://github.com/brianhempel/base_x) for encoding. A custom scheme can also be specified by providing a BaseX object.
72
+
73
+ ```ruby
74
+ HumanToken.generate(32, base: BaseX.base(36))
75
+ # => "9KV0FL722DDYQ5IOFIJDB1DT0AIDUID6U0VUBCRC9IK7POQG9S"
76
+ ```
77
+
78
+ For reference, in IRB you can get a list of all the provided schemes with a sample 128 bit token in each.
79
+
80
+ ```
81
+ > HumanToken.samples
82
+ hex "2c50e3ec571d5d662580e22de852147e"
83
+ base_30 "fwkt9zvwn4ara5n6qfhbmbsvfgr"
84
+ base_31 "7577vd28g58d8s5g84sgc5c6zq"
85
+ base_32 "BSGTEN21DRYMDSG75TDEMDDMKS"
86
+ base_58 "jW9NHFsBv4b2ynHaMa68zy"
87
+ new_base_60 "Fh7HDfRZ_pGwbUMtVF2ttv"
88
+ base_62 "x8SuXOLhXSjAx1jGzSCvzB"
89
+
90
+ # You can also ask for sample tokens of a given size
91
+ > HumanToken.samples(4)
92
+ hex "0a91b59b"
93
+ base_30 "cx85466"
94
+ base_31 "un7hxe6"
95
+ base_32 "N9YXAZ3"
96
+ base_58 "pJKN11"
97
+ new_base_60 "wJ5cc_"
98
+ base_62 "DugJc6"
99
+ ```
100
+
101
+ ## Other Tidbits
102
+
103
+ If you ask for 128 bits (16 bytes) of randomness, you are actually getting _at least_ 128 bits. Why?
104
+
105
+ Consider the default generator. There are thirty "numerals" used in the default scheme (the 36 alphanumerics, minus 0, 1, I, O, L, and U). Each "numeral" in base 30 encodes about 4.9 bits of information. A token of length 26 can encdoe 127.6 bits of information. That's not enough for 128 bits of randomness. A token of length 27 can encode 132.5 bits of information. Why waste the extra space by encoding only 128 bits in a string that can encode 132.5? Therefore, HumanToken encodes 132.5 bits of randomness.
106
+
107
+ However, if you don't want to explain to your colleagues why your tokens have "132 bit security" instead of a standard number like 128, then you can ask for exactly 128 bits. Your tokens will be the same length, but the first character will appear less random.
108
+
109
+ ```ruby
110
+ HumanToken.generate(exact_bytes: 16)
111
+ # => "29spx4xsse3cqgr7da7nrasxfc2"
112
+
113
+ # First character will always be a 2 or 3 in this case
114
+ > 10.times { p HumanToken.generate(exact_bytes: 16) }
115
+ "2va3np2rhc8phqcwfmyy8mm62b6"
116
+ "2nhdf3jxz87qcfx4w2j8gna3f8k"
117
+ "25x5j7eh7k8vfnm372sbayd3brh"
118
+ "2wtxf5mrcw7aaw2hwk8ybpqex4r"
119
+ "2at5knh6aw38a8xaxcr262gnxs4"
120
+ "2bn7a7fx2gq6g8t5cg9yff6qeh8"
121
+ "23cy29jsbmj4w3xj35f6dygrvqp"
122
+ "2575jtkajbtyhx44gksdykjcfsq"
123
+ "29ny2f956jzytxwbt56y2vsea8t"
124
+ "2yrptgvaq6hyywanssgqm6ca6jb"
125
+ ```
126
+
127
+ ## License
128
+
129
+ Public Domain; no rights reserved.
130
+
131
+ No restrictions are placed on the use of HumanToken. That freedom also means, of course, that no warrenty of fitness is claimed; use HumanToken at your own risk.
132
+
133
+ Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
134
+
135
+
136
+ ## Contributing
137
+
138
+ 1. Fork it ( http://github.com/brianhempel/human_token/fork )
139
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
140
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
141
+ 4. Push to the branch (`git push origin my-new-feature`)
142
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+
5
+ desc "Run the tests"
6
+ task :test do
7
+ Dir.glob(File.expand_path("../test/*.rb", __FILE__)).each do |file|
8
+ require file
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'human_token/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "human_token"
8
+ spec.version = HumanToken::VERSION
9
+ spec.authors = ["Brian Hempel"]
10
+ spec.email = ["plasticchicken@gmail.com"]
11
+ spec.summary = %q{Tokens for humans: no ambiguous characters! Highly configurable.}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/brianhempel/human_token"
14
+ spec.license = "Public Domain"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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.add_dependency "base_x", "~> 0.8.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+ end
@@ -0,0 +1,60 @@
1
+ require 'human_token/version'
2
+ require 'securerandom'
3
+ require 'base_x'
4
+
5
+ module HumanToken
6
+ DEFAULTS = { bytes: 16, base: BaseX::Base30L }
7
+ BASES = [
8
+ [:hex, BaseX::Base16L],
9
+ [:base_30, BaseX::Base30L],
10
+ [:base_31, BaseX::Base31L],
11
+ [:base_32, BaseX::CrockfordBase32],
12
+ [:base_58, BaseX::BitcoinBase58],
13
+ [:new_base_60, BaseX::NewBase60],
14
+ [:base_62, BaseX::Base62DUL],
15
+ ]
16
+
17
+ def self.generate(*args)
18
+ options = normalize_generate_options(args, DEFAULTS)
19
+ base, bytes, exact_bytes = options[:base], options[:bytes], options[:exact_bytes]
20
+
21
+ base = BaseX.new(options[:characters]) if options[:characters]
22
+
23
+ if exact_bytes
24
+ base.encode(SecureRandom.random_bytes(exact_bytes))
25
+ else
26
+ length = (Math.log(256)/Math.log(base.base)*bytes).ceil
27
+ token = base.encode(SecureRandom.random_bytes(bytes + 1))
28
+ token[-length..-1]
29
+ end
30
+ end
31
+
32
+ BASES.each do |name, base|
33
+ define_singleton_method(name) do |*args|
34
+ options = normalize_generate_options(args, base: base)
35
+ generate(options)
36
+ end
37
+ end
38
+
39
+ def self.samples(*args)
40
+ STDERR.puts samples_string(*args)
41
+ end
42
+
43
+ class << self
44
+ private
45
+
46
+ def normalize_generate_options(args, defaults)
47
+ defaults.dup.tap do |options|
48
+ options[:bytes] = args.first if args.first.is_a?(Integer)
49
+ args.grep(Hash).each {|hash| options.merge!(hash)}
50
+ end
51
+ end
52
+
53
+ def samples_string(*args)
54
+ longest_name_length = BASES.sort_by {|name, base| -name.size}.first[0].size
55
+ BASES.map do |method_name, _|
56
+ "%-#{longest_name_length}s %s" % [method_name, self.send(method_name, *args).inspect]
57
+ end.join("\n")
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module HumanToken
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,80 @@
1
+ require 'bundler/setup'
2
+ Bundler.require(:test)
3
+
4
+ require 'human_token'
5
+ require 'minitest/autorun'
6
+ require 'minitest/reporters'
7
+ MiniTest::Reporters.use! Minitest::Reporters::DefaultReporter.new
8
+
9
+ class TestHumanToken < Minitest::Test
10
+ def test_generate
11
+ token = HumanToken.generate
12
+ assert_equal 27, token.length
13
+ end
14
+
15
+ def test_generate_with_bytes_given
16
+ token = HumanToken.generate(8)
17
+ assert_equal 14, token.length
18
+ end
19
+
20
+ def test_generate_with_exact_bytes_given
21
+ token = HumanToken.generate(exact_bytes: 8)
22
+ assert_equal 14, token.length
23
+ end
24
+
25
+ def test_generate_with_different_base
26
+ token = HumanToken.generate(base: BaseX::Base58)
27
+ assert_equal 22, token.length
28
+ end
29
+
30
+ def test_floating_point_math_works
31
+ token = HumanToken.generate(5, base: BaseX.base(32))
32
+ assert_equal 8, token.length
33
+ end
34
+
35
+ def test_custom_characters
36
+ token = HumanToken.generate(6, characters: "asdfjkl;")
37
+ assert_equal 16, token.length
38
+ end
39
+
40
+ def test_base_30
41
+ token = HumanToken.base_30
42
+ assert_equal 27, token.length
43
+ token = HumanToken.base_30(exact_bytes: 32)
44
+ assert_equal 53, token.length
45
+ end
46
+
47
+ def test_base_31
48
+ token = HumanToken.base_31
49
+ assert_equal 26, token.length
50
+ end
51
+
52
+ def test_base_32
53
+ token = HumanToken.base_32
54
+ assert_equal 26, token.length
55
+ end
56
+
57
+ def test_base_58
58
+ token = HumanToken.base_58
59
+ assert_equal 22, token.length
60
+ end
61
+
62
+ def test_new_base_60
63
+ token = HumanToken.new_base_60
64
+ assert_equal 22, token.length
65
+ end
66
+
67
+ def test_base_62
68
+ token = HumanToken.base_62
69
+ assert_equal 22, token.length
70
+ end
71
+
72
+ def test_hex
73
+ token = HumanToken.hex
74
+ assert_equal 32, token.length
75
+ end
76
+
77
+ def test_samples
78
+ HumanToken.send(:samples_string) # doesn't error
79
+ end
80
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: human_token
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Hempel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base_x
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: 'Tokens for humans: no ambiguous characters! Highly configurable.'
56
+ email:
57
+ - plasticchicken@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Gemfile
65
+ - Guardfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - human_token.gemspec
70
+ - lib/human_token.rb
71
+ - lib/human_token/version.rb
72
+ - test/test_human_token.rb
73
+ homepage: https://github.com/brianhempel/human_token
74
+ licenses:
75
+ - Public Domain
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: 'Tokens for humans: no ambiguous characters! Highly configurable.'
97
+ test_files:
98
+ - test/test_human_token.rb