yubikey 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +70 -0
- data/lib/yubikey.rb +4 -4
- data/lib/yubikey/otp.rb +24 -14
- data/lib/yubikey/otp_verify.rb +63 -11
- data/spec/hex_spec.rb +14 -15
- data/spec/spec_helper.rb +3 -8
- metadata +29 -56
- data/README.rdoc +0 -49
- data/spec/spec.opts +0 -1
- data/spec/yubikey_modhex_spec.rb +0 -31
- data/spec/yubikey_otp_spec.rb +0 -28
- data/spec/yubikey_otp_verify_spec.rb +0 -38
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# yubikey
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/rainforestapp/yubikey.png?branch=master)](https://travis-ci.org/rainforestapp/yubikey)
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
A library to verify, decode, decrypt and parse [Yubikey](http://www.yubico.com/home/index/) one-time passwords.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### OTP Decryption
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
key = 'ecde18dbe76fbd0c33330f1c354871db'
|
15
|
+
otp = 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
|
16
|
+
token = Yubikey::OTP.new(otp, key)
|
17
|
+
|
18
|
+
p "Device public id: #{token.public_id}" #=> 'dteffuje'
|
19
|
+
p "Device secret id: #{token.secret_id}" #=> '8792ebfe26cc'
|
20
|
+
p "Device insertions: #{token.insert_counter}" #=> 19
|
21
|
+
p "Session activation counter: #{token.session_counter}" #=> 17
|
22
|
+
p "Session timestamp: #{token.timestamp}" #=> 49712
|
23
|
+
p "OTP random data: #{token.random_number}" #=> 40904
|
24
|
+
```
|
25
|
+
|
26
|
+
### OTP Verification
|
27
|
+
Use your own `api_key` and `api_id`, which you can get at [yubico.com](https://upgrade.yubico.com/getapikey/).
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
begin
|
31
|
+
otp = Yubikey::OTP::Verify.new(:api_id => 1234,
|
32
|
+
:api_key => 'NiSwGZBQ0gTbwXbRGWAf4kM5xXg=',
|
33
|
+
:otp => 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh')
|
34
|
+
|
35
|
+
if otp.valid?
|
36
|
+
p 'valid OTP'
|
37
|
+
elsif otp.replayed?
|
38
|
+
p 'replayed OTP'
|
39
|
+
end
|
40
|
+
rescue Yubikey::OTP::InvalidOTPError
|
41
|
+
p 'invalid OTP'
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Install
|
46
|
+
|
47
|
+
Yubikey is available as a gem, to install it just install the gem:
|
48
|
+
|
49
|
+
gem install yubikey
|
50
|
+
|
51
|
+
If you're using Bundler, add the gem to Gemfile.
|
52
|
+
|
53
|
+
gem 'yubikey'
|
54
|
+
|
55
|
+
Then run bundle install.
|
56
|
+
|
57
|
+
## Copyright
|
58
|
+
|
59
|
+
### Ruby library
|
60
|
+
Written by [Jonathan Rudenberg](https://github.com/titanous) <jon335@gmail.com>
|
61
|
+
Copyright (c) 2009 Jonathan Rudenberg
|
62
|
+
The MIT License. See LICENSE.
|
63
|
+
|
64
|
+
### Contributors
|
65
|
+
- Carl Byström
|
66
|
+
- Erik Ruwalder
|
67
|
+
- [Russell Smith](https://github.com/ukd1)
|
68
|
+
- [Chris Lundquist](https://github.com/ChrisLundquist)
|
69
|
+
- [Maarten van Grootel](https://github.com/maartenvg)
|
70
|
+
- [Chris Benedict](https://github.com/chrisbdaemon)
|
data/lib/yubikey.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$LOAD_PATH.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
|
3
3
|
|
4
4
|
require 'net/https'
|
5
|
-
require '
|
5
|
+
require 'openssl'
|
6
6
|
|
7
7
|
module Yubikey; end;
|
8
8
|
|
9
9
|
require 'yubikey/hex'
|
10
10
|
require 'yubikey/modhex'
|
11
11
|
require 'yubikey/otp'
|
12
|
-
require 'yubikey/otp_verify'
|
12
|
+
require 'yubikey/otp_verify'
|
data/lib/yubikey/otp.rb
CHANGED
@@ -15,31 +15,41 @@ class Yubikey::OTP
|
|
15
15
|
attr_reader :session_counter
|
16
16
|
# random integer used as padding and extra random noise
|
17
17
|
attr_reader :random_number
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
|
20
20
|
# Decode/decrypt a Yubikey one-time password
|
21
21
|
#
|
22
22
|
# [+otp+] ModHex encoded Yubikey OTP (at least 32 characters)
|
23
23
|
# [+key+] 32-character hex AES key
|
24
|
-
def initialize(otp, key)
|
24
|
+
def initialize(otp, key)
|
25
|
+
|
26
|
+
# Get the public ID first
|
27
|
+
@public_id = otp[0, 12]
|
28
|
+
|
29
|
+
# Strip prefix so otp will decode (following from yubico-c library)
|
30
|
+
otp = otp[-32,32] if otp.length > 32
|
31
|
+
|
25
32
|
raise InvalidOTPError, 'OTP must be at least 32 characters of modhex' unless otp.modhex? && otp.length >= 32
|
26
33
|
raise InvalidKeyError, 'Key must be 32 hex characters' unless key.hex? && key.length == 32
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
|
35
|
+
|
30
36
|
@token = Yubikey::ModHex.decode(otp[-32,32])
|
31
37
|
@aes_key = key.to_bin
|
32
|
-
|
33
|
-
|
34
|
-
|
38
|
+
|
39
|
+
decrypter = OpenSSL::Cipher.new('AES-128-ECB').decrypt
|
40
|
+
decrypter.key = @aes_key
|
41
|
+
decrypter.padding = 0
|
42
|
+
|
43
|
+
@token = decrypter.update(@token) + decrypter.final
|
44
|
+
|
35
45
|
raise BadCRCError unless crc_valid?
|
36
|
-
|
46
|
+
|
37
47
|
@secret_id, @insert_counter, @timestamp, @timestamp_lo, @session_counter, @random_number, @crc = @token.unpack('H12vvCCvv')
|
38
48
|
@timestamp += @timestamp_lo * 65536
|
39
49
|
end
|
40
|
-
|
50
|
+
|
41
51
|
private
|
42
|
-
|
52
|
+
|
43
53
|
def crc_valid?
|
44
54
|
crc = 0xffff
|
45
55
|
@token.each_byte do |b|
|
@@ -52,9 +62,9 @@ class Yubikey::OTP
|
|
52
62
|
end
|
53
63
|
crc == 0xf0b8
|
54
64
|
end
|
55
|
-
|
65
|
+
|
56
66
|
# :stopdoc:
|
57
67
|
class InvalidOTPError < StandardError; end
|
58
68
|
class InvalidKeyError < StandardError; end
|
59
69
|
class BadCRCError < StandardError; end
|
60
|
-
end # Yubikey::OTP
|
70
|
+
end # Yubikey::OTP
|
data/lib/yubikey/otp_verify.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'securerandom'
|
3
|
+
|
1
4
|
module Yubikey
|
2
5
|
|
3
|
-
API_URL = 'https://api.yubico.com/wsapi/'
|
4
|
-
|
5
|
-
API_KEY = 'e928a7d3076516a8c8c879f42c3ea0388f3b19f'
|
6
|
-
|
6
|
+
API_URL = 'https://api.yubico.com/wsapi/2.0/'
|
7
|
+
|
7
8
|
class OTP::Verify
|
8
|
-
|
9
9
|
# The raw status from the Yubico server
|
10
10
|
attr_reader :status
|
11
|
-
|
12
|
-
def initialize(
|
13
|
-
|
11
|
+
|
12
|
+
def initialize(args)
|
13
|
+
raise(ArgumentError, "Must supply API ID") if args[:api_id].nil?
|
14
|
+
raise(ArgumentError, "Must supply API Key") if args[:api_key].nil?
|
15
|
+
raise(ArgumentError, "Must supply OTP") if args[:otp].nil?
|
16
|
+
|
17
|
+
@api_key = args[:api_key]
|
18
|
+
@api_id = args[:api_id]
|
19
|
+
|
20
|
+
@url = args[:url] || API_URL
|
21
|
+
@nonce = args[:nonce] || OTP::Verify.generate_nonce(32)
|
22
|
+
|
23
|
+
verify(args)
|
14
24
|
end
|
15
25
|
|
16
26
|
def valid?
|
@@ -23,8 +33,10 @@ module Yubikey
|
|
23
33
|
|
24
34
|
private
|
25
35
|
|
26
|
-
def verify(
|
27
|
-
|
36
|
+
def verify(args)
|
37
|
+
query = "id=#{@api_id}&otp=#{args[:otp]}&nonce=#{@nonce}"
|
38
|
+
|
39
|
+
uri = URI.parse(@url) + 'verify'
|
28
40
|
uri.query = query
|
29
41
|
|
30
42
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -33,12 +45,52 @@ module Yubikey
|
|
33
45
|
|
34
46
|
req = Net::HTTP::Get.new(uri.request_uri)
|
35
47
|
result = http.request(req).body
|
36
|
-
|
48
|
+
|
37
49
|
@status = result[/status=(.*)$/,1].strip
|
38
50
|
|
39
51
|
if @status == 'BAD_OTP' || @status == 'BACKEND_ERROR'
|
40
52
|
raise OTP::InvalidOTPError, "Received error: #{@status}"
|
41
53
|
end
|
54
|
+
|
55
|
+
if ! verify_response(result)
|
56
|
+
@status = 'BAD_RESPONSE'
|
57
|
+
return
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def verify_response(result)
|
62
|
+
|
63
|
+
signature = result[/^h=(.+)$/, 1].strip
|
64
|
+
returned_nonce = result[/nonce=(.+)$/, 1]
|
65
|
+
returned_nonce.strip! unless returned_nonce.nil?
|
66
|
+
|
67
|
+
if @nonce != returned_nonce
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
|
71
|
+
generated_signature = OTP::Verify.generate_hmac(result, @api_key)
|
72
|
+
|
73
|
+
return signature == generated_signature
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def self.generate_nonce(length)
|
78
|
+
return SecureRandom.hex length/2
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def self.generate_hmac(response, api_key)
|
83
|
+
response_params = response.split(' ')
|
84
|
+
response_params.reject! do |p|
|
85
|
+
p =~ /^h=(.+)$/
|
86
|
+
end
|
87
|
+
|
88
|
+
response_string = response_params.sort.join('&')
|
89
|
+
response_string.strip!
|
90
|
+
|
91
|
+
hmac = OpenSSL::HMAC.digest('sha1', Base64.decode64(api_key), response_string)
|
92
|
+
|
93
|
+
return Base64.encode64(hmac).strip
|
42
94
|
end
|
43
95
|
end # OTP::Verify
|
44
96
|
end # Yubikey
|
data/spec/hex_spec.rb
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# encoding: US-ASCII
|
3
2
|
describe 'hex' do
|
4
|
-
it '
|
3
|
+
it 'encodes binary to hex' do
|
5
4
|
"i\266H\034\213\253\242\266\016\217\"\027\233X\315V".to_hex.
|
6
5
|
should == '69b6481c8baba2b60e8f22179b58cd56'
|
7
|
-
|
6
|
+
|
8
7
|
"\354\336\030\333\347o\275\f33\017\0345Hq\333".to_hex.
|
9
8
|
should == 'ecde18dbe76fbd0c33330f1c354871db'
|
10
9
|
end
|
11
|
-
|
12
|
-
it '
|
10
|
+
|
11
|
+
it 'decodes hex to binary' do
|
13
12
|
'69b6481c8baba2b60e8f22179b58cd56'.to_bin.
|
14
13
|
should == "i\266H\034\213\253\242\266\016\217\"\027\233X\315V"
|
15
|
-
|
14
|
+
|
16
15
|
'ecde18dbe76fbd0c33330f1c354871db'.to_bin.
|
17
16
|
should == "\354\336\030\333\347o\275\f33\017\0345Hq\333"
|
18
17
|
end
|
19
|
-
|
20
|
-
it '
|
21
|
-
'ecde18dbe76fbd0c33330f1c354871db'.hex?.should
|
22
|
-
'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'.modhex?.should
|
23
|
-
|
24
|
-
'foobar'.hex?.should
|
25
|
-
'test'.modhex?.should
|
18
|
+
|
19
|
+
it 'detects if a string is hex' do
|
20
|
+
'ecde18dbe76fbd0c33330f1c354871db'.hex?.should be_true
|
21
|
+
'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'.modhex?.should be_true
|
22
|
+
|
23
|
+
'foobar'.hex?.should be_false
|
24
|
+
'test'.modhex?.should be_false
|
26
25
|
end
|
27
|
-
end
|
26
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
rescue LoadError
|
4
|
-
require 'rubygems'
|
5
|
-
gem 'rspec'
|
6
|
-
require 'spec'
|
7
|
-
end
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
8
3
|
|
9
|
-
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
10
5
|
require 'yubikey'
|
metadata
CHANGED
@@ -1,37 +1,24 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: yubikey
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.3.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
|
-
authors:
|
7
|
+
authors:
|
7
8
|
- Jonathan Rudenberg
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
default_executable:
|
14
|
-
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: crypt19
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: "0"
|
24
|
-
version:
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
25
14
|
description: A library to verify, decode, decrypt and parse Yubikey one-time passwords.
|
26
15
|
email: jon335@gmail.com
|
27
16
|
executables: []
|
28
|
-
|
29
17
|
extensions: []
|
30
|
-
|
31
|
-
extra_rdoc_files:
|
18
|
+
extra_rdoc_files:
|
32
19
|
- LICENSE
|
33
|
-
- README.
|
34
|
-
files:
|
20
|
+
- README.md
|
21
|
+
files:
|
35
22
|
- examples/otp.rb
|
36
23
|
- lib/yubikey.rb
|
37
24
|
- lib/yubikey/hex.rb
|
@@ -39,49 +26,35 @@ files:
|
|
39
26
|
- lib/yubikey/otp.rb
|
40
27
|
- lib/yubikey/otp_verify.rb
|
41
28
|
- spec/hex_spec.rb
|
42
|
-
- spec/spec.opts
|
43
29
|
- spec/spec_helper.rb
|
44
|
-
- spec/yubikey_modhex_spec.rb
|
45
|
-
- spec/yubikey_otp_spec.rb
|
46
|
-
- spec/yubikey_otp_verify_spec.rb
|
47
30
|
- LICENSE
|
48
|
-
- README.
|
49
|
-
|
50
|
-
homepage: http://github.com/titanous/yubikey
|
31
|
+
- README.md
|
32
|
+
homepage: https://github.com/titanous/yubikey
|
51
33
|
licenses: []
|
52
|
-
|
53
34
|
post_install_message:
|
54
|
-
rdoc_options:
|
55
|
-
- --charset=UTF-8
|
35
|
+
rdoc_options:
|
56
36
|
- --title
|
57
37
|
- yubikey
|
58
38
|
- --main
|
59
39
|
- README.rdoc
|
60
|
-
require_paths:
|
40
|
+
require_paths:
|
61
41
|
- lib
|
62
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
74
54
|
requirements: []
|
75
|
-
|
76
55
|
rubyforge_project: yubikey
|
77
|
-
rubygems_version: 1.
|
56
|
+
rubygems_version: 1.8.25
|
78
57
|
signing_key:
|
79
58
|
specification_version: 3
|
80
|
-
summary:
|
81
|
-
test_files:
|
82
|
-
- spec/hex_spec.rb
|
83
|
-
- spec/spec_helper.rb
|
84
|
-
- spec/yubikey_modhex_spec.rb
|
85
|
-
- spec/yubikey_otp_spec.rb
|
86
|
-
- spec/yubikey_otp_verify_spec.rb
|
87
|
-
- examples/otp.rb
|
59
|
+
summary: Yubikey library for Ruby
|
60
|
+
test_files: []
|
data/README.rdoc
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
= yubikey
|
2
|
-
|
3
|
-
== Description
|
4
|
-
|
5
|
-
A library to verify, decode, decrypt and parse Yubikey[http://www.yubico.com/home/index/] one-time passwords.
|
6
|
-
|
7
|
-
== Usage
|
8
|
-
|
9
|
-
=== OTP Decryption
|
10
|
-
|
11
|
-
key = 'ecde18dbe76fbd0c33330f1c354871db'
|
12
|
-
otp = 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
|
13
|
-
token = Yubikey::OTP.new(otp, key)
|
14
|
-
|
15
|
-
p "Device public id: #{token.public_id}" #=> 'dteffuje'
|
16
|
-
p "Device secret id: #{token.secret_id}" #=> '8792ebfe26cc'
|
17
|
-
p "Device insertions: #{token.insert_counter}" #=> 19
|
18
|
-
p "Session activation counter: #{token.session_counter}" #=> 17
|
19
|
-
p "Session timestamp: #{token.timestamp}" #=> 49712
|
20
|
-
p "OTP random data: #{token.random_number}" #=> 40904
|
21
|
-
|
22
|
-
=== OTP Verification
|
23
|
-
|
24
|
-
begin
|
25
|
-
otp = Yubikey::OTP::Verify.new('dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh')
|
26
|
-
|
27
|
-
if otp.valid?
|
28
|
-
p 'valid OTP'
|
29
|
-
elsif otp.replayed?
|
30
|
-
p 'replayed OTP'
|
31
|
-
end
|
32
|
-
rescue Yubikey::OTP::InvalidOTPError
|
33
|
-
p 'invalid OTP'
|
34
|
-
end
|
35
|
-
|
36
|
-
== Install
|
37
|
-
|
38
|
-
sudo gem install yubikey
|
39
|
-
|
40
|
-
== Copyright
|
41
|
-
|
42
|
-
=== Ruby library
|
43
|
-
Written by Jonathan Rudenberg <jon335@gmail.com>
|
44
|
-
Copyright (c) 2009 Jonathan Rudenberg
|
45
|
-
The MIT License. See LICENSE.
|
46
|
-
|
47
|
-
=== Contributors
|
48
|
-
Carl Byström
|
49
|
-
Erik Ruwalder
|
data/spec/spec.opts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--colour
|
data/spec/yubikey_modhex_spec.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe 'Yubikey::Modhex' do
|
4
|
-
it 'should decode modhex' do
|
5
|
-
Yubikey::ModHex.decode('hknhfjbrjnlnldnhcujvddbikngjrtgh').should == "i\266H\034\213\253\242\266\016\217\"\027\233X\315V"
|
6
|
-
Yubikey::ModHex.decode('urtubjtnuihvntcreeeecvbregfjibtn').should == "\354\336\030\333\347o\275\f33\017\0345Hq\333"
|
7
|
-
|
8
|
-
Yubikey::ModHex.decode('dteffuje').should == "-4N\203"
|
9
|
-
|
10
|
-
Yubikey::ModHex.decode('ifhgieif').should == 'test'
|
11
|
-
Yubikey::ModHex.decode('hhhvhvhdhbid').should == 'foobar'
|
12
|
-
|
13
|
-
Yubikey::ModHex.decode('cc').should == "\000"
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'should raise if modhex string length uneven' do
|
17
|
-
lambda { Yubikey::ModHex.decode('ifh') }.should raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'should encode modhex' do
|
21
|
-
Yubikey::ModHex.encode("i\266H\034\213\253\242\266\016\217\"\027\233X\315V").should == 'hknhfjbrjnlnldnhcujvddbikngjrtgh'
|
22
|
-
Yubikey::ModHex.encode("\354\336\030\333\347o\275\f33\017\0345Hq\333").should == 'urtubjtnuihvntcreeeecvbregfjibtn'
|
23
|
-
|
24
|
-
Yubikey::ModHex.encode("-4N\203").should == 'dteffuje'
|
25
|
-
|
26
|
-
Yubikey::ModHex.encode('test').should == 'ifhgieif'
|
27
|
-
Yubikey::ModHex.encode('foobar').should == 'hhhvhvhdhbid'
|
28
|
-
|
29
|
-
Yubikey::ModHex.encode("\000").should == 'cc'
|
30
|
-
end
|
31
|
-
end
|
data/spec/yubikey_otp_spec.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe 'Yubikey::OTP' do
|
4
|
-
it 'should parse a otp' do
|
5
|
-
token = Yubikey::OTP.new('dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh', 'ecde18dbe76fbd0c33330f1c354871db')
|
6
|
-
|
7
|
-
token.public_id.should == 'dteffuje'
|
8
|
-
token.secret_id.should == '8792ebfe26cc'
|
9
|
-
token.insert_counter.should == 19
|
10
|
-
token.session_counter.should == 17
|
11
|
-
token.timestamp.should == 49712
|
12
|
-
token.random_number.should == 40904
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should raise if key or otp invalid' do
|
16
|
-
otp = 'hknhfjbrjnlnldnhcujvddbikngjrtgh'
|
17
|
-
key = 'ecde18dbe76fbd0c33330f1c354871db'
|
18
|
-
|
19
|
-
lambda { Yubikey::OTP.new(key, key) }.should raise_error(Yubikey::OTP::InvalidOTPError)
|
20
|
-
lambda { Yubikey::OTP.new(otp, otp) }.should raise_error(Yubikey::OTP::InvalidKeyError)
|
21
|
-
|
22
|
-
lambda { Yubikey::OTP.new(otp[0,31], key) }.should raise_error(Yubikey::OTP::InvalidOTPError)
|
23
|
-
lambda { Yubikey::OTP.new(otp, key[0,31]) }.should raise_error(Yubikey::OTP::InvalidKeyError)
|
24
|
-
|
25
|
-
lambda { Yubikey::OTP.new(otp[1,31]+'d', key) }.should raise_error(Yubikey::OTP::BadCRCError)
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
describe 'Yubikey::OTP::Verify' do
|
4
|
-
|
5
|
-
before do
|
6
|
-
@otp = 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
|
7
|
-
|
8
|
-
@mock_http = mock('http')
|
9
|
-
@mock_http_get = mock('http_get')
|
10
|
-
|
11
|
-
Net::HTTP.should_receive(:new).with('api.yubico.com', 443).and_return(@mock_http)
|
12
|
-
@mock_http.should_receive(:use_ssl=).and_return(nil)
|
13
|
-
@mock_http.should_receive(:verify_mode=).and_return(nil)
|
14
|
-
@mock_http.should_receive(:request).with(@mock_http_get).and_return(@mock_http_get)
|
15
|
-
Net::HTTP::Get.should_receive(:new).with(/otp=#{@otp}/).and_return(@mock_http_get)
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'should verify a valid OTP' do
|
19
|
-
@mock_http_get.should_receive(:body).and_return('status=OK')
|
20
|
-
otp = Yubikey::OTP::Verify.new(@otp)
|
21
|
-
otp.valid?.should == true
|
22
|
-
otp.replayed?.should == false
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should verify a replayed OTP' do
|
26
|
-
@mock_http_get.should_receive(:body).and_return('status=REPLAYED_OTP')
|
27
|
-
otp = Yubikey::OTP::Verify.new(@otp)
|
28
|
-
otp.valid?.should == false
|
29
|
-
otp.replayed?.should == true
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should raise on invalid OTP' do
|
33
|
-
@mock_http_get.should_receive(:body).and_return('status=BAD_OTP')
|
34
|
-
lambda { otp = Yubikey::OTP::Verify.new(@otp) }.should raise_error(Yubikey::OTP::InvalidOTPError)
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
end
|