yubioath 0.0.1 → 0.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
- data/.ruby-version +1 -1
- data/exe/yubioath +4 -0
- data/lib/card.rb +19 -8
- data/lib/cli.rb +51 -0
- data/lib/yubioath.rb +63 -2
- data/lib/yubioath/calculate.rb +39 -0
- data/lib/yubioath/calculate_all.rb +41 -0
- data/lib/yubioath/delete.rb +20 -0
- data/lib/yubioath/instructions.rb +7 -0
- data/lib/yubioath/list.rb +20 -0
- data/lib/yubioath/put.rb +30 -0
- data/lib/yubioath/reset.rb +12 -0
- data/lib/yubioath/response.rb +13 -0
- data/lib/yubioath/select.rb +24 -0
- data/yubioath.gemspec +6 -4
- metadata +33 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cafd4ebf2444b9e960b25d326c7357d7d31583f
|
4
|
+
data.tar.gz: f2a66945a9c2355e24dd1717b58cf57c522473e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90d0ee58ddb0904823589b38a142905f5321dfcd00bd0b5692e5d16aa9e778373e0391935fff8d497201031e0aa18562b709cec0e3c55282c9a11df076d90ac5
|
7
|
+
data.tar.gz: 4b447d74069a87c974425b6920c3b52d15e3cb4b8096355b906f849e655955266edad2cd22119e1b1c10a263828143e1c4d927c76ca28ea6528cfa67ecc0c99a
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2
|
1
|
+
2.2.0
|
data/exe/yubioath
ADDED
data/lib/card.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
require 'smartcard'
|
2
|
+
require 'yubioath'
|
2
3
|
|
3
4
|
class Card
|
4
|
-
def initialize
|
5
|
-
|
5
|
+
def initialize(name:)
|
6
|
+
@name = name
|
7
|
+
end
|
8
|
+
|
9
|
+
def yubioath
|
10
|
+
tap do |card|
|
11
|
+
yield YubiOATH.new(card)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def tap(&block)
|
18
|
+
Context.tap do |context|
|
6
19
|
begin
|
7
|
-
|
8
|
-
|
9
|
-
card = context.card(name)
|
10
|
-
yield card
|
20
|
+
card = context.card(@name)
|
21
|
+
block.call(card)
|
11
22
|
ensure
|
12
23
|
card.disconnect unless card.nil?
|
13
24
|
end
|
@@ -15,9 +26,9 @@ class Card
|
|
15
26
|
end
|
16
27
|
|
17
28
|
class Context
|
18
|
-
def
|
29
|
+
def self.tap(&block)
|
19
30
|
context = Smartcard::PCSC::Context.new
|
20
|
-
|
31
|
+
block.call(context)
|
21
32
|
ensure
|
22
33
|
context.release
|
23
34
|
end
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'card'
|
3
|
+
|
4
|
+
class CLI < Thor
|
5
|
+
package_name 'yubioath'
|
6
|
+
|
7
|
+
desc 'list', 'list current OTP tokens'
|
8
|
+
def list
|
9
|
+
card.yubioath do |yubioath|
|
10
|
+
all = yubioath.calculate_all(timestamp: Time.now)
|
11
|
+
|
12
|
+
STDOUT.puts
|
13
|
+
STDOUT.puts 'YubiOATH Tokens:'
|
14
|
+
STDOUT.puts '----------------'
|
15
|
+
|
16
|
+
all[:codes].each_with_index do |token, index|
|
17
|
+
STDOUT.puts "#{index+1}. #{token.name}: #{token.code}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'token NAME', 'get the current OTP value for NAME'
|
23
|
+
def token(name)
|
24
|
+
card.yubioath do |yubioath|
|
25
|
+
token = yubioath.calculate(name: name, timestamp: Time.now)
|
26
|
+
STDOUT.print token.code
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'add NAME SECRET', 'add a new OTP secret'
|
31
|
+
def add(name, secret)
|
32
|
+
card.yubioath do |yubioath|
|
33
|
+
response = yubioath.put(name: name, secret: secret, algorithm: 'SHA256', type: 'totp', digits: 6)
|
34
|
+
throw unless response.success?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'delete NAME', 'remove the OTP token called NAME'
|
39
|
+
def delete(name)
|
40
|
+
card.yubioath do |yubioath|
|
41
|
+
response = yubioath.send(name: name)
|
42
|
+
throw unless response.success?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def card
|
49
|
+
@card ||= Card.new(name: 'Yubico Yubikey NEO OTP+CCID')
|
50
|
+
end
|
51
|
+
end
|
data/lib/yubioath.rb
CHANGED
@@ -1,4 +1,65 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'bindata'
|
2
|
+
require 'yubioath/instructions'
|
3
|
+
require 'yubioath/response'
|
4
|
+
|
5
|
+
class YubiOATH
|
3
6
|
AID = [0xA0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
|
7
|
+
|
8
|
+
def initialize(card)
|
9
|
+
@card = card
|
10
|
+
select(AID)
|
11
|
+
end
|
12
|
+
|
13
|
+
def calculate(name:, timestamp:)
|
14
|
+
data = Calculate::Request::Data.new(name: name, timestamp: timestamp.to_i / 30)
|
15
|
+
request = Calculate::Request.new(data: data.to_binary_s)
|
16
|
+
response = Response.read(@card.transmit(request.to_binary_s))
|
17
|
+
throw unless response.success?
|
18
|
+
Calculate::Response.read(response.data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def calculate_all(timestamp:)
|
22
|
+
data = CalculateAll::Request::Data.new(timestamp: timestamp.to_i / 30)
|
23
|
+
request = CalculateAll::Request.new(data: data.to_binary_s)
|
24
|
+
response = Response.read(@card.transmit(request.to_binary_s))
|
25
|
+
throw unless response.success?
|
26
|
+
CalculateAll::Response.read(response.data)
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(name:)
|
30
|
+
data = Delete::Request::Data.new(name: name)
|
31
|
+
request = Delete::Request.new(data: data.to_binary_s)
|
32
|
+
Response.read(@card.transmit(request.to_binary_s))
|
33
|
+
end
|
34
|
+
|
35
|
+
def list
|
36
|
+
request = List::Request.new.to_binary_s
|
37
|
+
response = Response.read(@card.transmit(request))
|
38
|
+
throw unless response.success?
|
39
|
+
List::Response.read(response.data)
|
40
|
+
end
|
41
|
+
|
42
|
+
def put(name:, secret:, algorithm:, type:, digits:)
|
43
|
+
data = Put::Request::Data.new(
|
44
|
+
name: name,
|
45
|
+
key_algorithm: (Put::ALGORITHMS[algorithm] | Put::TYPES[type]),
|
46
|
+
digits: digits,
|
47
|
+
secret: secret,
|
48
|
+
)
|
49
|
+
request = Put::Request.new(data: data.to_binary_s)
|
50
|
+
Response.read(@card.transmit(request.to_binary_s))
|
51
|
+
end
|
52
|
+
|
53
|
+
def reset
|
54
|
+
Response.read(@card.transmit(Reset::Request.new.to_binary_s))
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def select(aid)
|
60
|
+
request = Select::Request.new(aid: aid).to_binary_s
|
61
|
+
response = Response.read(@card.transmit(request))
|
62
|
+
throw unless response.success?
|
63
|
+
Select::Response.read(response.data)
|
64
|
+
end
|
4
65
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class Calculate
|
5
|
+
class Request < BinData::Record
|
6
|
+
uint8 :cla, value: 0x00
|
7
|
+
uint8 :ins, value: 0xA2
|
8
|
+
uint8 :p1, value: 0x00
|
9
|
+
uint8 :p2, value: 0x01
|
10
|
+
uint8 :data_length, value: -> { data.length }
|
11
|
+
string :data
|
12
|
+
|
13
|
+
class Data < BinData::Record
|
14
|
+
uint8 :name_tag, value: 0x71
|
15
|
+
uint8 :name_length, value: -> { name.length }
|
16
|
+
string :name
|
17
|
+
|
18
|
+
uint8 :challenge_tag, value: 0x74
|
19
|
+
uint8 :challenge_length, value: 8
|
20
|
+
uint64be :timestamp
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Response < BinData::Record
|
25
|
+
class Code < BinData::Record
|
26
|
+
uint8 :digits
|
27
|
+
uint32be :response
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
(response % 10**digits).to_s.rjust(digits, '0')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
uint8 :response_tag
|
35
|
+
uint8 :response_length
|
36
|
+
code :code, read_length: :response_length
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class CalculateAll
|
5
|
+
class Request < BinData::Record
|
6
|
+
uint8 :cla, value: 0x00
|
7
|
+
uint8 :ins, value: 0xA4
|
8
|
+
uint8 :p1, value: 0x00
|
9
|
+
uint8 :p2, value: 0x01
|
10
|
+
uint8 :data_length, value: -> { data.length }
|
11
|
+
string :data
|
12
|
+
|
13
|
+
class Data < BinData::Record
|
14
|
+
uint8 :challenge_tag, value: 0x74
|
15
|
+
uint8 :challenge_length, value: 8
|
16
|
+
uint64be :timestamp
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Response < BinData::Record
|
21
|
+
class Code < BinData::Record
|
22
|
+
uint8 :digits
|
23
|
+
uint32be :response
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
(response % 10**digits).to_s.rjust(digits, '0')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
array :codes, read_until: :eof do
|
31
|
+
uint8 :name_tag
|
32
|
+
uint8 :name_length
|
33
|
+
string :name, read_length: :name_length
|
34
|
+
|
35
|
+
uint8 :response_tag
|
36
|
+
uint8 :response_length
|
37
|
+
code :code, read_length: :response_length
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class Delete
|
5
|
+
class Request < BinData::Record
|
6
|
+
uint8 :cla, value: 0x00
|
7
|
+
uint8 :ins, value: 0x02
|
8
|
+
uint8 :p1, value: 0x00
|
9
|
+
uint8 :p2, value: 0x00
|
10
|
+
uint8 :data_length, value: -> { data.length }
|
11
|
+
string :data
|
12
|
+
|
13
|
+
class Data < BinData::Record
|
14
|
+
uint8 :name_tag, value: 0x71
|
15
|
+
uint8 :name_length, value: -> { name.length }
|
16
|
+
string :name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class List
|
5
|
+
class Request < BinData::Record
|
6
|
+
uint8 :cla, value: 0x00
|
7
|
+
uint8 :ins, value: 0xA1
|
8
|
+
uint8 :p1, value: 0x00
|
9
|
+
uint8 :p2, value: 0x00
|
10
|
+
end
|
11
|
+
|
12
|
+
class Response < BinData::Record
|
13
|
+
array :codes, read_until: :eof do
|
14
|
+
uint8 :name_tag
|
15
|
+
uint8 :name_length
|
16
|
+
string :name, read_length: :name_length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/yubioath/put.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class Put
|
5
|
+
ALGORITHMS = {'SHA1' => 0x01, 'SHA256' => 0x02}
|
6
|
+
TYPES = {'hotp' => 0x10, 'totp' => 0x20}
|
7
|
+
|
8
|
+
class Request < BinData::Record
|
9
|
+
uint8 :cla, value: 0x00
|
10
|
+
uint8 :ins, value: 0x01
|
11
|
+
uint8 :p1, value: 0x00
|
12
|
+
uint8 :p2, value: 0x00
|
13
|
+
uint8 :data_length, value: -> { data.length }
|
14
|
+
string :data
|
15
|
+
|
16
|
+
class Data < BinData::Record
|
17
|
+
uint8 :name_tag, value: 0x71
|
18
|
+
uint8 :name_length, value: -> { name.length }
|
19
|
+
string :name
|
20
|
+
|
21
|
+
uint8 :key_tag, value: 0x73
|
22
|
+
uint8 :key_length, value: -> { secret.length + 2 }
|
23
|
+
uint8 :key_algorithm
|
24
|
+
|
25
|
+
uint8 :digits
|
26
|
+
string :secret
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class Response < BinData::Record
|
5
|
+
count_bytes_remaining :data_length
|
6
|
+
string :data, read_length: -> { data_length - 2 }
|
7
|
+
array :success, type: :uint8, initial_length: 2
|
8
|
+
|
9
|
+
def success?
|
10
|
+
success == [0x90, 0x00]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
class YubiOATH
|
4
|
+
class Select
|
5
|
+
class Request < BinData::Record
|
6
|
+
uint8 :cla, value: 0x00
|
7
|
+
uint8 :ins, value: 0xA4
|
8
|
+
uint8 :p1, value: 0x04
|
9
|
+
uint8 :p2, value: 0x00
|
10
|
+
uint8 :aid_length, value: -> { aid.length }
|
11
|
+
array :aid, type: :uint8
|
12
|
+
end
|
13
|
+
|
14
|
+
class Response < BinData::Record
|
15
|
+
uint8 :version_tag, assert: 0x79
|
16
|
+
uint8 :version_length
|
17
|
+
array :version, type: :uint8, initial_length: :version_length
|
18
|
+
|
19
|
+
uint8 :name_tag, assert: 0x71
|
20
|
+
uint8 :name_length
|
21
|
+
array :name, type: :uint8, initial_length: :name_length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/yubioath.gemspec
CHANGED
@@ -4,20 +4,22 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'yubioath'
|
7
|
-
spec.version = '0.0
|
7
|
+
spec.version = '0.1.0'
|
8
8
|
spec.authors = ['James Ottaway']
|
9
|
-
spec.email = ['
|
9
|
+
spec.email = ['yubioath@james.ottaway.io']
|
10
10
|
spec.summary = 'Securely manage your 2FA tokens using your Yubikey NEO'
|
11
11
|
spec.homepage = 'https://github.com/jamesottaway/yubioath'
|
12
12
|
spec.license = 'MIT'
|
13
13
|
|
14
14
|
spec.files = `git ls-files -z`.split("\x0")
|
15
|
-
spec.
|
15
|
+
spec.bindir = "exe"
|
16
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
16
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
18
|
spec.require_paths = ['lib']
|
18
19
|
|
20
|
+
spec.add_dependency 'thor', '~> 0.19.1'
|
19
21
|
spec.add_dependency 'bindata', '~> 2.1'
|
20
|
-
spec.add_dependency 'smartcard', '~> 0.5'
|
22
|
+
spec.add_dependency 'smartcard', '~> 0.5.5'
|
21
23
|
|
22
24
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
23
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yubioath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Ottaway
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.19.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.19.1
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bindata
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -30,14 +44,14 @@ dependencies:
|
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 0.5.5
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: 0.5.5
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,8 +82,9 @@ dependencies:
|
|
68
82
|
version: '10.0'
|
69
83
|
description:
|
70
84
|
email:
|
71
|
-
-
|
72
|
-
executables:
|
85
|
+
- yubioath@james.ottaway.io
|
86
|
+
executables:
|
87
|
+
- yubioath
|
73
88
|
extensions: []
|
74
89
|
extra_rdoc_files: []
|
75
90
|
files:
|
@@ -80,8 +95,19 @@ files:
|
|
80
95
|
- LICENSE.txt
|
81
96
|
- README.md
|
82
97
|
- Rakefile
|
98
|
+
- exe/yubioath
|
83
99
|
- lib/card.rb
|
100
|
+
- lib/cli.rb
|
84
101
|
- lib/yubioath.rb
|
102
|
+
- lib/yubioath/calculate.rb
|
103
|
+
- lib/yubioath/calculate_all.rb
|
104
|
+
- lib/yubioath/delete.rb
|
105
|
+
- lib/yubioath/instructions.rb
|
106
|
+
- lib/yubioath/list.rb
|
107
|
+
- lib/yubioath/put.rb
|
108
|
+
- lib/yubioath/reset.rb
|
109
|
+
- lib/yubioath/response.rb
|
110
|
+
- lib/yubioath/select.rb
|
85
111
|
- yubioath.gemspec
|
86
112
|
homepage: https://github.com/jamesottaway/yubioath
|
87
113
|
licenses:
|