motp 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in motp.gemspec
4
+ gemspec
@@ -0,0 +1,66 @@
1
+ MOTP
2
+ ====
3
+
4
+ This RubyGem implements the [Mobile-OTP standard](http://motp.sourceforge.net/) in Ruby,
5
+ allowing you to write Ruby (and Rails) powered servers and client implementations. This
6
+ enables you, for example, to implement strong two-factor authentication into your web
7
+ application, where your users use their mobile phones as a remote token.
8
+
9
+ Installation
10
+ ------------
11
+
12
+ gem install motp
13
+
14
+ Server Implementation
15
+ ---------------------
16
+
17
+ For each user, come up with a secret and allow them to specify their PIN. Store both.
18
+ When they use your system, ask them for the One-Time Pad. They can get this by using
19
+ their mobile phone (which they've already configured with the secret) and entering their
20
+ PIN.
21
+
22
+ require 'motp'
23
+
24
+ MOTP::check(secret, pin, otp)
25
+
26
+ Returns true if the otp is valid for the specified secret and PIN, false otherwise.
27
+
28
+ OTPs are based on the UTC clock and are valid (by default) for three minutes before and
29
+ three minutes after they were requested, in order to accomodate the time taken to type
30
+ in the OTP, and possible variations in the accuracy of the system clocks on the server
31
+ and the client (mobile phone) devices.
32
+
33
+ Optional parameters (appended to the end):
34
+
35
+ * :time => specifies the time for which the OTP must be valid; defaults to "now"
36
+ * :max_period => specifies the number of seconds of leeway, ahead and behind, during which an OTP is considered valid - defaults to 3 * 60 (3 minutes)
37
+
38
+ Client Implementation
39
+ ---------------------
40
+
41
+ If you want to write a mobile MOTP client in Ruby, go ahead! The MOTP RubyGem supports
42
+ this too.
43
+
44
+ require 'motp'
45
+
46
+ MOTP::otp(secret, pin)
47
+
48
+ Returns the OTP for the current time. As with the server implementation, you can pass an
49
+ optional :time parameter to specify the time for wich you want to generate, but you
50
+ shouldn't ever need to do this unless you *know* the device clock to be incorrect.
51
+
52
+ License
53
+ -------
54
+
55
+ This program is free software: you can redistribute it and/or modify
56
+ it under the terms of the GNU General Public License as published by
57
+ the Free Software Foundation, either version 3 of the License, or
58
+ (at your option) any later version.
59
+
60
+ This program is distributed in the hope that it will be useful,
61
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
62
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63
+ GNU General Public License for more details.
64
+
65
+ You should have received a copy of the GNU General Public License
66
+ along with this program. If not, see http://www.gnu.org/licenses/.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,14 @@
1
+ require 'digest/md5'
2
+
3
+ module Motp
4
+ def self.otp(secret, pin, options = {})
5
+ options = {:time => Time::now}.merge(options)
6
+ Digest::MD5::hexdigest("#{options[:time].utc.tv_sec.to_s[0...-1]}#{secret}#{pin}")[0,6]
7
+ end
8
+
9
+ def self.check(secret, pin, otp, options = {})
10
+ options = {:time => Time::now, :max_period => (3 * 60)}.merge(options)
11
+ range = ((options[:time] - options[:max_period])..(options[:time] + options[:max_period]))
12
+ return range.any?{|time| otp == self.otp(secret, pin, :time => time)}
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Motp
2
+ VERSION = "1.0.2"
3
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "motp/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "motp"
7
+ s.version = Motp::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dan Q"]
10
+ s.email = ["dan@scatmania.org"]
11
+ s.homepage = "http://www.scatmania.org/projects/motp"
12
+ s.summary = %q{Methods for implementing a Mobile One-Time Pad client or server in Ruby.}
13
+ s.description = %q{Methods for implementing a Mobile One-Time Pad client or server in Ruby.}
14
+
15
+ s.rubyforge_project = "motp"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,58 @@
1
+ require 'test/unit'
2
+ require 'lib/motp'
3
+
4
+ class MOTP_Test < Test::Unit::TestCase
5
+ def setup
6
+ @secret = 'secret12345'
7
+ @pin = '1234'
8
+ @valid_otp_regex = /^[\da-f]{6}$/
9
+ end
10
+
11
+ # def teardown
12
+ # end
13
+
14
+ def test_otp_matches_regex
15
+ otp_now = MOTP::otp(@secret, @pin)
16
+ assert(otp_now =~ @valid_otp_regex)
17
+ end
18
+
19
+ def test_now
20
+ otp_now = MOTP::otp(@secret, @pin)
21
+ assert(MOTP::check(@secret, @pin, otp_now))
22
+ end
23
+
24
+ def test_2min_ago
25
+ otp = MOTP::otp(@secret, @pin, :time => Time::now - (2*60))
26
+ assert(MOTP::check(@secret, @pin, otp))
27
+ end
28
+
29
+ def test_4min_ago_fails
30
+ otp = MOTP::otp(@secret, @pin, :time => Time::now - (4*60))
31
+ assert(!MOTP::check(@secret, @pin, otp))
32
+ end
33
+
34
+ def test_2min_ahead
35
+ otp = MOTP::otp(@secret, @pin, :time => Time::now + (2*60))
36
+ assert(MOTP::check(@secret, @pin, otp))
37
+ end
38
+
39
+ def test_4min_ahead_fails
40
+ otp = MOTP::otp(@secret, @pin, :time => Time::now + (4*60))
41
+ assert(!MOTP::check(@secret, @pin, otp))
42
+ end
43
+
44
+ def test_various_random_otps_fail
45
+ ['', '123456', 'abcdef', 'alpha' 'letmein!'].each do |otp|
46
+ assert(!MOTP::check(@secret, @pin, otp))
47
+ end
48
+ end
49
+
50
+ def test_specific_known_good_case
51
+ time = Time::utc(2011, 1, 20, 14, 28, 23)
52
+ secret, pin = 'abcd1234', '1234'
53
+ valid_otp = '6f8af8'
54
+ invalid_otp = '5d60af'
55
+ assert(MOTP::check(secret, pin, valid_otp, :time => time))
56
+ assert(!MOTP::check(secret, pin, invalid_otp, :time => time))
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motp
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 2
10
+ version: 1.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Dan Q
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-30 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Methods for implementing a Mobile One-Time Pad client or server in Ruby.
22
+ email:
23
+ - dan@scatmania.org
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.md
34
+ - Rakefile
35
+ - lib/motp.rb
36
+ - lib/motp/version.rb
37
+ - motp.gemspec
38
+ - test/motp_test.rb
39
+ homepage: http://www.scatmania.org/projects/motp
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project: motp
68
+ rubygems_version: 1.8.11
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Methods for implementing a Mobile One-Time Pad client or server in Ruby.
72
+ test_files:
73
+ - test/motp_test.rb