motp 1.0.2

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.
@@ -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