YubiRuby 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/Manifest +13 -3
- data/README +44 -0
- data/Rakefile +8 -2
- data/YubiRuby.gemspec +19 -7
- data/bin/yubikey +39 -0
- data/ext/libyubikey/libyubikey.c +26 -7
- data/lib/exceptions.rb +52 -0
- data/lib/fake_yubikey.rb +209 -0
- data/lib/hex.rb +36 -5
- data/lib/yubiruby.rb +2 -0
- data/tasks/yard_doc.rake +50 -0
- data/test/fake_yubikey_spec.rb +155 -0
- data/test/hex_spec.rb +52 -0
- data/test/modhex_spec.rb +81 -0
- data/{tests/ts_yubiruby.rb → test/path_loader.rb} +2 -2
- data/test/tc_crc16.rb +10 -0
- data/test/tc_fake_yubikey.rb +89 -0
- data/test/tc_hex.rb +29 -0
- data/test/tc_modhex.rb +36 -0
- data/test/test_helper.rb +8 -0
- metadata +84 -12
- data/tests/tc_modhex.rb +0 -13
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
v1.0.0. broken api compatibility. TestCase. fixed some bugs in (mod)hex decoding. Exceptions. FakeYubikey generator. yubikey app for personal key management.
|
2
|
+
|
3
|
+
v0.1.2. backported fixes from 1.0.0
|
4
|
+
|
5
|
+
v0.1.1. fixed some bugs in hex decoding.
|
6
|
+
|
1
7
|
v0.1.0. modhex installed as binary.
|
2
8
|
|
3
9
|
v0.0.1. First release
|
data/Manifest
CHANGED
@@ -3,8 +3,8 @@ LICENSE
|
|
3
3
|
Manifest
|
4
4
|
README
|
5
5
|
Rakefile
|
6
|
-
YubiRuby.gemspec
|
7
6
|
bin/modhex
|
7
|
+
bin/yubikey
|
8
8
|
ext/libyubikey/extconf.rb
|
9
9
|
ext/libyubikey/libyubikey.c
|
10
10
|
ext/libyubikey/ykaes.c
|
@@ -13,7 +13,17 @@ ext/libyubikey/ykhex.c
|
|
13
13
|
ext/libyubikey/ykmodhex.c
|
14
14
|
ext/libyubikey/ykparse.c
|
15
15
|
ext/libyubikey/yubikey.h
|
16
|
+
lib/exceptions.rb
|
17
|
+
lib/fake_yubikey.rb
|
16
18
|
lib/hex.rb
|
17
19
|
lib/yubiruby.rb
|
18
|
-
|
19
|
-
|
20
|
+
tasks/yard_doc.rake
|
21
|
+
test/fake_yubikey_spec.rb
|
22
|
+
test/hex_spec.rb
|
23
|
+
test/modhex_spec.rb
|
24
|
+
test/path_loader.rb
|
25
|
+
test/tc_crc16.rb
|
26
|
+
test/tc_fake_yubikey.rb
|
27
|
+
test/tc_hex.rb
|
28
|
+
test/tc_modhex.rb
|
29
|
+
test/test_helper.rb
|
data/README
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
= YubyRuby
|
2
|
+
|
3
|
+
Yubico Server wrapper in Ruby
|
4
|
+
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
$ gem install YubiRuby
|
9
|
+
|
10
|
+
== Some examples
|
11
|
+
|
12
|
+
Checking an OTP
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'YubiRuby'
|
16
|
+
|
17
|
+
key = "6df89690b5f51bd9ac912c5004781e86" #use your AES key
|
18
|
+
y = YubiRuby::Yubikey.new(key);
|
19
|
+
puts y.key
|
20
|
+
otp = gets().strip
|
21
|
+
puts y.parse(otp)
|
22
|
+
puts "Ouput: #{y}"
|
23
|
+
puts "uid: #{y.uid}"
|
24
|
+
puts "session counter: #{y.session_counter}"
|
25
|
+
puts "capslock: #{y.triggered_by_capslock?}"
|
26
|
+
puts "timestamp low/high: #{y.timestamp_low}/#{y.timestamp_high}"
|
27
|
+
puts "session use: #{y.session_use}"
|
28
|
+
puts "random: #{y.random}"
|
29
|
+
puts "crc: #{y.crc}"
|
30
|
+
puts "crc residue: #{y.crc_residue}"
|
31
|
+
puts "crc residue ok?: #{y.crc?} (#{y.crc_residue} == #{YubiRuby::Yubikey::CRC_OK_RESIDUE})"
|
32
|
+
|
33
|
+
Generating a valid yubikey
|
34
|
+
|
35
|
+
key = YubiRuby::FakeYubikey.new
|
36
|
+
key.set_random_key
|
37
|
+
key.set_random_uid
|
38
|
+
key.public_id = "vvefeeccebek"
|
39
|
+
puts "Yubikey generated"
|
40
|
+
puts "public id:\t#{key.public_id}"
|
41
|
+
puts "key:\t\t#{key.key}"
|
42
|
+
puts "uid:\t\t#{key.uid}"
|
43
|
+
puts "And now 3 otp"
|
44
|
+
3.times { puts key.otp }
|
data/Rakefile
CHANGED
@@ -3,6 +3,8 @@ require 'rubygems'
|
|
3
3
|
require 'rake'
|
4
4
|
require 'echoe'
|
5
5
|
|
6
|
+
ENV['SPEC_OPTS'] ||= '-c'
|
7
|
+
|
6
8
|
Echoe.new('YubiRuby') do |p|
|
7
9
|
p.description = <<-EOF
|
8
10
|
Yubikey integration -
|
@@ -40,8 +42,12 @@ EOF
|
|
40
42
|
p.author = "Alessio Caiazza"
|
41
43
|
p.email = "nolith@abisso.org"
|
42
44
|
p.platform = Gem::Platform::RUBY
|
43
|
-
p.ignore_pattern = ["tmp/**/*", "script/*"]
|
44
|
-
p.development_dependencies = []
|
45
|
+
p.ignore_pattern = ["tmp/**/*", "script/*", "meta/**/*"]
|
46
|
+
p.development_dependencies = ['yard', 'rspec']
|
47
|
+
p.runtime_dependencies << ['ruby-aes-normal', '~> 1.0']
|
48
|
+
p.runtime_dependencies << ['bit-struct'] #, '~> 0.13.6']
|
49
|
+
p.spec_pattern = ['test/**/*_spec.rb']
|
50
|
+
p.has_rdoc = true
|
45
51
|
end
|
46
52
|
|
47
53
|
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/YubiRuby.gemspec
CHANGED
@@ -2,12 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{YubiRuby}
|
5
|
-
s.version = "
|
5
|
+
s.version = "1.0.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Alessio Caiazza"]
|
9
|
-
s.date = %q{2010-
|
10
|
-
s.default_executable = %q{modhex}
|
9
|
+
s.date = %q{2010-03-21}
|
11
10
|
s.description = %q{Yubikey integration -
|
12
11
|
|
13
12
|
Includes Prototypes for low-level Yubikey OTP functions
|
@@ -40,24 +39,37 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
40
39
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
41
40
|
}
|
42
41
|
s.email = %q{nolith@abisso.org}
|
43
|
-
s.executables = ["modhex"]
|
42
|
+
s.executables = ["modhex", "yubikey"]
|
44
43
|
s.extensions = ["ext/libyubikey/extconf.rb"]
|
45
|
-
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "bin/modhex", "ext/libyubikey/extconf.rb", "ext/libyubikey/libyubikey.c", "ext/libyubikey/ykaes.c", "ext/libyubikey/ykcrc.c", "ext/libyubikey/ykhex.c", "ext/libyubikey/ykmodhex.c", "ext/libyubikey/ykparse.c", "ext/libyubikey/yubikey.h", "lib/hex.rb", "lib/yubiruby.rb"]
|
46
|
-
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "
|
44
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "bin/modhex", "bin/yubikey", "ext/libyubikey/extconf.rb", "ext/libyubikey/libyubikey.c", "ext/libyubikey/ykaes.c", "ext/libyubikey/ykcrc.c", "ext/libyubikey/ykhex.c", "ext/libyubikey/ykmodhex.c", "ext/libyubikey/ykparse.c", "ext/libyubikey/yubikey.h", "lib/exceptions.rb", "lib/fake_yubikey.rb", "lib/hex.rb", "lib/yubiruby.rb", "tasks/yard_doc.rake"]
|
45
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "bin/modhex", "bin/yubikey", "ext/libyubikey/extconf.rb", "ext/libyubikey/libyubikey.c", "ext/libyubikey/ykaes.c", "ext/libyubikey/ykcrc.c", "ext/libyubikey/ykhex.c", "ext/libyubikey/ykmodhex.c", "ext/libyubikey/ykparse.c", "ext/libyubikey/yubikey.h", "lib/exceptions.rb", "lib/fake_yubikey.rb", "lib/hex.rb", "lib/yubiruby.rb", "tasks/yard_doc.rake", "test/fake_yubikey_spec.rb", "test/hex_spec.rb", "test/modhex_spec.rb", "test/path_loader.rb", "test/tc_crc16.rb", "test/tc_fake_yubikey.rb", "test/tc_hex.rb", "test/tc_modhex.rb", "test/test_helper.rb", "YubiRuby.gemspec"]
|
47
46
|
s.homepage = %q{http://bitbucket.org/nolith/yubiruby}
|
48
47
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "YubiRuby", "--main", "README"]
|
49
48
|
s.require_paths = ["lib", "ext"]
|
50
49
|
s.rubyforge_project = %q{yubiruby}
|
51
|
-
s.rubygems_version = %q{1.3.
|
50
|
+
s.rubygems_version = %q{1.3.6}
|
52
51
|
s.summary = %q{Yubikey integration - Includes Prototypes for low-level Yubikey OTP functions witten by Simon Josefsson <simon@josefsson.org>. Copyright (c) 2006, 2007, 2008, 2009 Yubico AB All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.}
|
52
|
+
s.test_files = ["test/test_helper.rb"]
|
53
53
|
|
54
54
|
if s.respond_to? :specification_version then
|
55
55
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
56
|
s.specification_version = 3
|
57
57
|
|
58
58
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_runtime_dependency(%q<ruby-aes-normal>, ["~> 1.0"])
|
60
|
+
s.add_runtime_dependency(%q<bit-struct>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
59
63
|
else
|
64
|
+
s.add_dependency(%q<ruby-aes-normal>, ["~> 1.0"])
|
65
|
+
s.add_dependency(%q<bit-struct>, [">= 0"])
|
66
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
60
68
|
end
|
61
69
|
else
|
70
|
+
s.add_dependency(%q<ruby-aes-normal>, ["~> 1.0"])
|
71
|
+
s.add_dependency(%q<bit-struct>, [">= 0"])
|
72
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
62
74
|
end
|
63
75
|
end
|
data/bin/yubikey
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require 'yubiruby'
|
4
|
+
|
5
|
+
|
6
|
+
if ARGV.length > 1
|
7
|
+
msg=<<-EOF
|
8
|
+
Usage: #{$PROGRAM_NAME} [-v]
|
9
|
+
|
10
|
+
Produce an OTP data
|
11
|
+
|
12
|
+
-v for verbose output
|
13
|
+
EOF
|
14
|
+
abort msg
|
15
|
+
end
|
16
|
+
|
17
|
+
verbose = ARGV.include? "-h"
|
18
|
+
|
19
|
+
key = nil
|
20
|
+
begin
|
21
|
+
File.open(File.expand_path("~/.yubiruby")) do |f|
|
22
|
+
key = Marshal.load(f)
|
23
|
+
end
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
puts "Key not found. Generating a new one."
|
26
|
+
key = YubiRuby::FakeYubikey.new
|
27
|
+
key.set_random_key
|
28
|
+
key.set_random_uid
|
29
|
+
tmp = " "*3
|
30
|
+
0.upto(2) { |i| tmp[i] = rand(2**8)}
|
31
|
+
key.public_id = "vv" + YubiRuby::HEX.encode(tmp).modhex_encode[2..-1]
|
32
|
+
puts "Yubikey generated"
|
33
|
+
puts "public id:\t#{key.public_id}"
|
34
|
+
puts "key:\t\t#{key.key}"
|
35
|
+
puts "uid:\t\t#{key.uid}"
|
36
|
+
end
|
37
|
+
|
38
|
+
puts key.otp
|
39
|
+
File.open(File.expand_path("~/.yubiruby"), 'w+') { |f| Marshal.dump(key, f) }
|
data/ext/libyubikey/libyubikey.c
CHANGED
@@ -33,6 +33,8 @@
|
|
33
33
|
#include <stdlib.h>
|
34
34
|
#include "yubikey.h"
|
35
35
|
|
36
|
+
//#undef NDEBUG
|
37
|
+
|
36
38
|
#define MODHEX_SING_ENCODE "encode"
|
37
39
|
#define MODHEX_SING_DECODE "decode"
|
38
40
|
#define MODHEX_ENCODE "modhex_encode"
|
@@ -42,6 +44,7 @@
|
|
42
44
|
static VALUE mYubiRuby;
|
43
45
|
static VALUE mModHex;
|
44
46
|
static VALUE cYubikey;
|
47
|
+
static VALUE mCRC16;
|
45
48
|
|
46
49
|
|
47
50
|
/* ModHex */
|
@@ -92,13 +95,15 @@ static VALUE modhex_sing_decode(VALUE self, VALUE obj)
|
|
92
95
|
{
|
93
96
|
VALUE str = StringValue(obj);
|
94
97
|
int src_size = RSTRING(str)->len;
|
98
|
+
if (rb_funcall(str, rb_intern(MODHEX_VALID), 0) == Qfalse)
|
99
|
+
rb_raise(rb_eval_string("YubiRuby::NoModHexEncodedError"), "");
|
95
100
|
char *dst = (char*) malloc(((src_size/2)+1)*sizeof(char));
|
96
101
|
/* ModHex decode input string SRC of length DSTSIZE/2 into output
|
97
102
|
string DST. The output string DST is always DSTSIZE/2 large plus
|
98
103
|
the terminating zero. */
|
99
104
|
yubikey_modhex_decode(dst, RSTRING(str)->ptr, src_size);
|
100
105
|
|
101
|
-
return
|
106
|
+
return rb_str_new(dst, src_size/2);
|
102
107
|
}
|
103
108
|
|
104
109
|
/*
|
@@ -371,9 +376,13 @@ static VALUE yubikey_to_str(VALUE self)
|
|
371
376
|
|
372
377
|
VALUE ary = rb_ary_new2(YUBIKEY_BLOCK_SIZE);
|
373
378
|
for (i = 0; i < YUBIKEY_BLOCK_SIZE; i++) {
|
374
|
-
|
379
|
+
uint8_t c_num = ((uint8_t*)data->token)[i];
|
380
|
+
VALUE num = INT2FIX(c_num);
|
375
381
|
//invoke .to_s 16 on each number
|
376
|
-
|
382
|
+
VALUE hexed = rb_funcall(num,rb_intern("to_s"), 1, INT2FIX(16));
|
383
|
+
if(c_num < 16) //if num < 16 prepend a '0'
|
384
|
+
hexed = rb_funcall(hexed,rb_intern("insert"), 2, INT2FIX(0),rb_str_new2("0"));
|
385
|
+
rb_ary_store(ary, i, hexed );
|
377
386
|
}
|
378
387
|
|
379
388
|
return rb_funcall(ary, rb_intern("join"), 0);
|
@@ -393,9 +402,9 @@ static VALUE yubikey_get_uid(VALUE self)
|
|
393
402
|
}
|
394
403
|
|
395
404
|
/* call-seq:
|
396
|
-
* yubikey.
|
405
|
+
* yubikey.session_counter -> Fixnum
|
397
406
|
*
|
398
|
-
* Gets the decoded token counter field.
|
407
|
+
* Gets the decoded token session counter field.
|
399
408
|
*/
|
400
409
|
static VALUE yubikey_get_counter(VALUE self)
|
401
410
|
{
|
@@ -480,6 +489,13 @@ static VALUE yubikey_check_crc(VALUE self)
|
|
480
489
|
}
|
481
490
|
/* Yubikey END */
|
482
491
|
|
492
|
+
/* CRC 16 */
|
493
|
+
static VALUE crc16_sing_calc(VALUE self, VALUE data)
|
494
|
+
{
|
495
|
+
VALUE str = StringValue(data);
|
496
|
+
return INT2NUM(yubikey_crc16(RSTRING(str)->ptr, RSTRING(str)->len));
|
497
|
+
}
|
498
|
+
|
483
499
|
/* Implementation of Yubikey OTP functions.
|
484
500
|
* This Module may be used to interact with a Yubikey
|
485
501
|
*/
|
@@ -523,13 +539,16 @@ void Init_libyubikey() {
|
|
523
539
|
rb_define_method(cYubikey, "to_str", yubikey_to_str, 0);
|
524
540
|
rb_define_alias(cYubikey, "to_s", "to_str");
|
525
541
|
rb_define_method(cYubikey, "uid", yubikey_get_uid, 0);
|
526
|
-
rb_define_method(cYubikey, "
|
542
|
+
rb_define_method(cYubikey, "session_counter", yubikey_get_counter, 0);
|
527
543
|
rb_define_method(cYubikey, "triggered_by_capslock?", yubikey_get_triggered_by_capslock, 0);
|
528
544
|
rb_define_method(cYubikey, "timestamp_low", yubikey_get_tsl, 0);
|
529
545
|
rb_define_method(cYubikey, "timestamp_high", yubikey_get_tsh, 0);
|
530
|
-
rb_define_method(cYubikey, "
|
546
|
+
rb_define_method(cYubikey, "session_use", yubikey_get_session, 0);
|
531
547
|
rb_define_method(cYubikey, "random", yubikey_get_random, 0);
|
532
548
|
rb_define_method(cYubikey, "crc", yubikey_get_crc, 0);
|
533
549
|
rb_define_method(cYubikey, "crc_residue", yubikey_calc_crc, 0);
|
534
550
|
rb_define_method(cYubikey, "crc?", yubikey_check_crc, 0);
|
551
|
+
|
552
|
+
mCRC16 = rb_define_module_under(mYubiRuby, "CRC16");
|
553
|
+
rb_define_singleton_method(mCRC16, "calculate", crc16_sing_calc, 1);
|
535
554
|
}
|
data/lib/exceptions.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Author:: Alessio Caiazza (mailto:nolith@abisso.org)
|
2
|
+
# Copyright:: Copyright (c) 2010 Alessio Caiazza
|
3
|
+
# License:: New BSD License - http://www.opensource.org/licenses/bsd-license.php
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are
|
8
|
+
# met:
|
9
|
+
#
|
10
|
+
# * Redistributions of source code must retain the above copyright
|
11
|
+
# notice, this list of conditions and the following disclaimer.
|
12
|
+
#
|
13
|
+
# * Redistributions in binary form must reproduce the above
|
14
|
+
# copyright notice, this list of conditions and the following
|
15
|
+
# disclaimer in the documentation and/or other materials provided
|
16
|
+
# with the distribution.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
|
31
|
+
module YubiRuby
|
32
|
+
class NoHexEncodedError < Exception
|
33
|
+
def initialize
|
34
|
+
super('The string provided isn\'t hex encoded')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NoModHexEncodedError < Exception
|
39
|
+
def inizialize(msg=nil)
|
40
|
+
if msg.nil? || msg.empty?
|
41
|
+
msg = 'The string provided isn\'t modhex encoded'
|
42
|
+
end
|
43
|
+
super(msg)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class YubikeySessionCounterOverflow < Exception
|
48
|
+
def initialize
|
49
|
+
super('Session Counter Overflow')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/fake_yubikey.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# Author:: Alessio Caiazza (mailto:nolith@abisso.org)
|
2
|
+
# Copyright:: Copyright (c) 2010 Alessio Caiazza
|
3
|
+
# License:: New BSD License - http://www.opensource.org/licenses/bsd-license.php
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are
|
8
|
+
# met:
|
9
|
+
#
|
10
|
+
# * Redistributions of source code must retain the above copyright
|
11
|
+
# notice, this list of conditions and the following disclaimer.
|
12
|
+
#
|
13
|
+
# * Redistributions in binary form must reproduce the above
|
14
|
+
# copyright notice, this list of conditions and the following
|
15
|
+
# disclaimer in the documentation and/or other materials provided
|
16
|
+
# with the distribution.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'ruby-aes'
|
32
|
+
require 'bit-struct'
|
33
|
+
|
34
|
+
# 1-complement is uses to computer crc field
|
35
|
+
#
|
36
|
+
#
|
37
|
+
class Numeric
|
38
|
+
def ones_complement(bits)
|
39
|
+
self ^ ((1 << bits) - 1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module YubiRuby
|
44
|
+
#
|
45
|
+
# This class simulate a yubikey allowing you to make some tests
|
46
|
+
#
|
47
|
+
class FakeYubikey
|
48
|
+
# Maximum allowed length for a modhex encoded public id
|
49
|
+
PUBLIC_ID_MAX_LEN = 32
|
50
|
+
|
51
|
+
#
|
52
|
+
# Yubikey internal data
|
53
|
+
#
|
54
|
+
class Data < BitStruct
|
55
|
+
YUBIKEY_BLOCK_SIZE = 16
|
56
|
+
YUBIKEY_KEY_SIZE = 16
|
57
|
+
YUBIKEY_UID_SIZE = 6
|
58
|
+
SESSION_COUNTER_OVERFLOW = 0x7FFF
|
59
|
+
|
60
|
+
hex_octets :uid, YUBIKEY_UID_SIZE*8, "User ID"
|
61
|
+
unsigned :session_counter, 16, {:endian=>:little, :display_name=>"Session Counter"}
|
62
|
+
unsigned :timestamp_low, 16, {:endian=>:little, :display_name=>"Timestamp low"}
|
63
|
+
unsigned :timestamp_high, 8, "Timestamp high"
|
64
|
+
unsigned :session_use, 8, "Session Use"
|
65
|
+
unsigned :random, 16, {:endian=>:little, :display_name=>"random"}
|
66
|
+
unsigned :crc, 16, {:endian=>:little, :display_name=>"crc"}
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_accessor :key
|
71
|
+
#attr_reader :public_id
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@public_id = ""
|
75
|
+
@data = YubiRuby::FakeYubikey::Data.new
|
76
|
+
@data.session_counter = 0
|
77
|
+
init
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Sets the public id of the key.
|
82
|
+
#
|
83
|
+
# public_id must be a modhex valid string no longer than
|
84
|
+
# 32 char, if longer it will be truncated.
|
85
|
+
#
|
86
|
+
# @param [String, #read] value the new public_id
|
87
|
+
# @return [String] the new public_id
|
88
|
+
def public_id=(value)
|
89
|
+
raise NoModHexEncodedError unless value.modhex?
|
90
|
+
|
91
|
+
value = value[0, PUBLIC_ID_MAX_LEN] if value.length > PUBLIC_ID_MAX_LEN
|
92
|
+
raise NoModHexEncodedError unless value.modhex?
|
93
|
+
|
94
|
+
@public_id = value
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the value of attribute public_id.
|
98
|
+
def public_id
|
99
|
+
@public_id
|
100
|
+
end
|
101
|
+
|
102
|
+
# Simaluates the power on sequence of a YubuKey.
|
103
|
+
#
|
104
|
+
# This will increase session_counter
|
105
|
+
def init
|
106
|
+
@data.session_counter += 1
|
107
|
+
if (@data.session_counter == Data::SESSION_COUNTER_OVERFLOW+1)
|
108
|
+
raise YubikeySessionCounterOverflow.new
|
109
|
+
end
|
110
|
+
@data.session_use = 0
|
111
|
+
@data.timestamp_low = rand(2**16)
|
112
|
+
@data.timestamp_high = rand(2**8)
|
113
|
+
end
|
114
|
+
|
115
|
+
%w{session_use timestamp_low timestamp_high session_counter random crc}.each do |name|
|
116
|
+
define_method name do
|
117
|
+
@data.send name
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Generates a random key attribute
|
122
|
+
def set_random_key
|
123
|
+
@key = produce_random_bytes_in_hex Data::YUBIKEY_KEY_SIZE
|
124
|
+
end
|
125
|
+
|
126
|
+
# Generates a random uid attribute
|
127
|
+
def set_random_uid
|
128
|
+
self.uid= produce_random_bytes_in_hex(Data::YUBIKEY_UID_SIZE)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Sets the attribute uid
|
132
|
+
def uid=(value)
|
133
|
+
if YubiRuby::HEX.decode(value).size == Data::YUBIKEY_UID_SIZE
|
134
|
+
@data.uid = "#{value[0,2]}:#{value[2,2]}:#{value[4,2]}:#{value[6,2]}:#{value[8,2]}:#{value[10,2]}"
|
135
|
+
else
|
136
|
+
raise NoHexEncodedError.new
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns the value of attribute uid.
|
141
|
+
def uid
|
142
|
+
@data.uid.gsub(':','')
|
143
|
+
end
|
144
|
+
|
145
|
+
# Simulates the pression of the yubikey's button
|
146
|
+
#
|
147
|
+
# session_use will increase and if it overflows also
|
148
|
+
# session_counter will be increased
|
149
|
+
def otp
|
150
|
+
@data.random = rand(2**16)
|
151
|
+
@data.session_use += 1
|
152
|
+
@data.session_counter +=1 if @data.session_use == 0
|
153
|
+
generate_otp
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
@data.to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.generate_specific_otp_and_fake_yubikey(key, uid, session_counter,
|
161
|
+
timestamp_low, timestamp_high, session_use, random)
|
162
|
+
y_key = FakeYubikey.new
|
163
|
+
y_key.key = key
|
164
|
+
y_key.uid = uid
|
165
|
+
y_key.data.session_use = session_use
|
166
|
+
y_key.data.timestamp_low = timestamp_low
|
167
|
+
y_key.data.timestamp_high = timestamp_high
|
168
|
+
y_key.data.session_counter = session_counter
|
169
|
+
y_key.data.random = random
|
170
|
+
[y_key.generate_otp, y_key]
|
171
|
+
end
|
172
|
+
|
173
|
+
def data
|
174
|
+
@data
|
175
|
+
end
|
176
|
+
|
177
|
+
# Generates an OTP without altering any internal data.
|
178
|
+
def generate_otp
|
179
|
+
@data.crc = YubiRuby::CRC16.calculate(@data.to_s[0...-2]).ones_complement 16
|
180
|
+
data = Aes.encrypt_block(128, 'ECB', YubiRuby::HEX.decode(@key), nil,
|
181
|
+
@data.to_s).modhex_encode
|
182
|
+
"#{@public_id}#{data}"
|
183
|
+
end
|
184
|
+
|
185
|
+
#serialization support
|
186
|
+
def marshal_dump
|
187
|
+
{'key' => key, 'uid' => uid, 'session_counter' => @data.session_counter,
|
188
|
+
'public_id' => public_id }
|
189
|
+
end
|
190
|
+
|
191
|
+
#serialization support
|
192
|
+
def marshal_load(data)
|
193
|
+
@data = YubiRuby::FakeYubikey::Data.new
|
194
|
+
self.key = data['key']
|
195
|
+
self.uid = data['uid']
|
196
|
+
self.data.session_counter = data['session_counter']
|
197
|
+
self.public_id = data['public_id']
|
198
|
+
init
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
private
|
203
|
+
def produce_random_bytes_in_hex(size)
|
204
|
+
tmp = " "*size
|
205
|
+
0.upto(size-1) { |i| tmp[i] = rand(2**8)}
|
206
|
+
YubiRuby::HEX.encode(tmp)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|