potp 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 572944d75dff4240e25ec32bb91202acd04ce07b2dea52e585fb765bf579076d
4
+ data.tar.gz: 633ba7f0dac49a1c66d71680a030571b32185806adf6e9e9111bc5561d937865
5
+ SHA512:
6
+ metadata.gz: 4e6b4d5efe9da1b692486e28a56fcf20f4c858a99bb7d870889c4cee21540fff2061589088ba01917333dcf83ec9d3293e4e9741d79d468fac039d5913b0bef2
7
+ data.tar.gz: 5ed1543a5214cb4be08e1b0b54d7ebff17833ed7442757bc7a5605356d52860967d136bf0ed2a0eb3629024797d65df313dd70ab198dd1b3397b412740744c5f
data/LICENSE ADDED
@@ -0,0 +1,52 @@
1
+ # BSD-2-clause license, extended by language use conditions
2
+
3
+ Copyright (C) 2024, Bertram Scharpf <software@bertram-scharpf.de>.
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 copyright
14
+ notice, this list of conditions and the following disclaimer in
15
+ the documentation and/or other materials provided with the
16
+ distribution.
17
+
18
+ * Redistributions must not contain any clauses about anticipated
19
+ harassment or discrimination, nor must they be held in a so-called
20
+ "inclusive language". As far as German language is used, the
21
+ conditions mentioned below additionally apply.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
27
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+
36
+ ## Use of the German Language
37
+
38
+ Beim Gebrauch deutscher Sprache sind Weiterentwicklungen und
39
+ -verbreitungen nur gestattet unter Einhaltung sowie abermaligen
40
+ Einforderns folgender zusätzlicher Bedingungen:
41
+
42
+ * Keine Verwendung von sogenannter „geschlechtergerechter Sprache“,
43
+ also Anfügen von weiblichen Endungen mit Binnen-I, Sternchen,
44
+ Doppelpunkt, Unterstrich oder ähnlichem, oder Konstruktionen, die
45
+ den Sachverhalt falsch wiedergeben („Radfahrende“, „Studierende“).
46
+
47
+ * Keine Verwendung der „reformierten Rechtschreibung“ von 1996,
48
+ insbesondere Doppel-S am Silbenende, „plazieren“ mit T, sowie
49
+ Großschreibung von Wendungen wie „des weiteren“.
50
+
51
+
52
+ <!-- vim:set ft=markdown : -->
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # The Plain One Time Password Library
2
+
3
+ A ruby library for generating and validating one time passwords (HOTP & TOTP)
4
+ according to
5
+ [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
6
+ and
7
+ [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238).
8
+
9
+ POTP aims to be compatible with
10
+ [Google Authenticator](https://github.com/google/google-authenticator).
11
+
12
+ The Base32 format conforms to
13
+ [RFC 4648 Base32](http://en.wikipedia.org/wiki/Base32#Base_32_Encoding_per_§6)
14
+
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ sudo gem install potp
20
+ ```
21
+
22
+ If you like to run the executable (instead of writing a one-liner for
23
+ yourself), you have to install the `appl` gem.
24
+
25
+ ```bash
26
+ sudo gem install appl
27
+ ```
28
+
29
+
30
+ ## Library Usage
31
+
32
+ ### Time based (TOTP)
33
+
34
+ ```ruby
35
+ require "potp"
36
+
37
+ totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
38
+ totp.now #=> "152201"
39
+
40
+ totp.verify "152201" #=> 1735417500 # ok, value is the timestamp
41
+ sleep 30
42
+ totp.verify "152201" #=> nil # not ok
43
+ ```
44
+
45
+ ### Counter based (HOTP)
46
+
47
+ ```ruby
48
+ hotp = POTP::HOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
49
+ hotp.at 0 #=> "178748"
50
+ hotp.at 1 #=> "584373"
51
+ hotp.at 73 #=> "309764"
52
+
53
+ # OTP verifying with a counter
54
+ hotp.verify "309764", 73 #=> 73
55
+ hotp.verify "309764", 74 #=> nil
56
+ hotp.verify "309764", 70, retries: 2 #=> nil
57
+ hotp.verify "309764", 70, retries: 3 #=> 73
58
+ ```
59
+
60
+
61
+ ### Avoiding reuse of TOTP
62
+
63
+ ```ruby
64
+ require "potp"
65
+ totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
66
+ code = totp.now #=> "054626"
67
+ last_verify = totp.verify code #=> 1735527390
68
+ totp.verify code, after: last_verify #=> nil
69
+ sleep 30
70
+ code = totp.now #=> "481150"
71
+ totp.verify code, after: last_verify #=> 1735527420
72
+ ```
73
+
74
+
75
+ ### Verifying a TOTP with drift
76
+
77
+ In case a user entered a code just after it has expired, you can allow
78
+ the token to remain valid.
79
+
80
+ ```ruby
81
+ totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
82
+ now = Time.now - 30
83
+ code = totp.at now #=> "455335"
84
+ totp.verify code #=> nil
85
+ totp.verify code, drift_behind: 27 #=> 1735530510
86
+ ```
87
+
88
+
89
+ ### Generating a Base32 secret key
90
+
91
+ Returns a 160 bit (32 character) Base32 secret.
92
+
93
+ ```ruby
94
+ require "potp/random"
95
+ POTP::Base32.random #=> "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
96
+ ```
97
+
98
+
99
+ ### Generating QR codes for provisioning mobile apps
100
+
101
+ ```ruby
102
+ require "potp"
103
+
104
+ totp = POTP::TOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
105
+ uri = totp.provisioning_uri name: "jdoe@example.net", issuer: "ACME Service"
106
+ #=> "otpauth://totp/ACME%20Service:jdoe%40example.net?secret=GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A&issuer=ACME%20Service"
107
+
108
+ hotp = POTP::HOTP.new "GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A"
109
+ uri = hotp.provisioning_uri name: "jdoe@example.net", issuer: "ACME Service", counter: 0
110
+ #=> "otpauth://hotp/ACME%20Service:jdoe%40example.net?secret=GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A&issuer=ACME%20Service&counter=0"
111
+
112
+ # Then, do something like this:
113
+ system *%w(qrencode -t xpm -s 1 -o), "qr.xpm", uri
114
+ ```
115
+
116
+
117
+ ## Executable Usage
118
+
119
+ Generates a time-based one-time password:
120
+
121
+ ```bash
122
+ potp --secret GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A
123
+ ```
124
+
125
+ Generates a counter-based one-time password:
126
+
127
+ ```bash
128
+ potp --hmac --secret GYS5L3N3E4AAYNMN562LW76TMWHQBJ4A --counter 42
129
+ ```
130
+
131
+ What you expect:
132
+
133
+ ```bash
134
+ potp --help
135
+ ```
136
+
137
+
138
+ ## Copyright
139
+
140
+ * (C) 2024 Bertram Scharpf <software@bertram-scharpf.de>
141
+ * License: [BSD-2-Clause+](./LICENSE)
142
+
data/bin/potp ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # potp -- The Plain One-Time Password Tool
5
+ #
6
+
7
+ begin
8
+ require "appl"
9
+ rescue LoadError
10
+ puts "This tool requires the `appl` gem."
11
+ exit 2
12
+ end
13
+ require "potp/foreign/supplement"
14
+ require "potp"
15
+
16
+
17
+ class POTP::Appl < Application
18
+
19
+ NAME = "potp"
20
+ VERSION = POTP::VERSION
21
+ SUMMARY = "Plain One Time Password Tool"
22
+ COPYRIGHT = "(C) 2024 Bertram Scharpf <software@bertram-scharpf.de>"
23
+ LICENSE = "BSD-2-Clause+"
24
+ AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
25
+
26
+ DESCRIPTION = <<~EOT
27
+ Generate and validate one time passwords (HOTP & TOTP)
28
+ according to [RFC 4226] and [RFC 6238].
29
+
30
+ Examples:
31
+
32
+ potp --secret p4ssword # Generates a time-based one-time password
33
+ potp --hmac --secret p4ssword --counter 42 # Generates a counter-based one-time password
34
+
35
+ EOT
36
+
37
+ attr_writer :secret, :counter, :digest
38
+ attr_bang :debug
39
+ def time! ; @mode = :time ; end
40
+ def hmac! ; @mode = :hmac ; end
41
+
42
+ define_option "t", :time!, true, "use time-based OTP according to RFC 6238"
43
+ alias_option "t", "time"
44
+
45
+ define_option "m", :hmac!, "use counter-based OTP according to RFC 4226"
46
+ alias_option "m", "hmac"
47
+
48
+ define_option "s", :secret=, "STR", "the shared secret"
49
+ alias_option "s", "secret"
50
+
51
+ define_option "d", :digest=, "ALG", "sha1", "algorithm for the digest"
52
+ alias_option "d", "digest"
53
+
54
+ define_option "c", :counter=, "NUM", 0,
55
+ "the counter for counter-based hmac OTP"
56
+ alias_option "c", "counter"
57
+
58
+ define_option "g", :debug!, "full Ruby error messages and backtrace"
59
+ alias_option "g", "debug"
60
+ define_option "h", :help, "show options"
61
+ alias_option "h", "help"
62
+ define_option "V", :version, "show version"
63
+ alias_option "V", "version"
64
+
65
+ def run
66
+ puts generate_output
67
+ rescue POTP::Base32::Invalid
68
+ raise "Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#Base_32_Encoding_per_§6"
69
+ end
70
+
71
+ private
72
+
73
+ def generate_output
74
+ @secret.notempty? or raise "You must specify a --secret. See --help."
75
+ case @mode
76
+ when :time then (POTP::TOTP.new @secret, digest: @digest).now
77
+ when :hmac then (POTP::HOTP.new @secret, digest: @digest).at @counter
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ POTP::Appl.run
84
+
@@ -0,0 +1,140 @@
1
+ #
2
+ # potp/base32.rb -- Base32 Encoding
3
+ #
4
+
5
+ require "potp/foreign/supplement"
6
+
7
+
8
+ module POTP
9
+
10
+ class Base32
11
+
12
+ ORD_A, ORD_Z, ORD_a, ORD_z, ORD_2, ORD_7 = %w(A Z a z 2 7).map { |c| c.ord }
13
+ LAP = 26
14
+ DIFF_2 = ORD_2 - LAP
15
+
16
+ class << self
17
+
18
+ def encode bytes, width: nil, equals: true
19
+ res = [""]
20
+ scan_bufs bytes do |buf,r|
21
+ chunk = []
22
+ 8.times {
23
+ chunk.unshift buf & 0x1f
24
+ buf >>= 5
25
+ }
26
+ loop do
27
+ r -= 5
28
+ break unless r > 0
29
+ chunk.pop
30
+ end
31
+ chunk = chunk.map { |c| (c + (c < LAP ? ORD_A : DIFF_2)).chr }
32
+ if equals then
33
+ chunk.push "=" until chunk.length >= 8
34
+ end
35
+ res.last << chunk.join
36
+ if width and res.last.length > width then
37
+ res.push res.last.slice! width, 8
38
+ end
39
+ end
40
+ new res.join "\n"
41
+ end
42
+
43
+ private
44
+
45
+ def scan_bufs bytes
46
+ scan_blocks bytes do |s|
47
+ buf, r = 0, 40
48
+ 5.times {
49
+ buf <<= 8
50
+ if (b = s.shift) then
51
+ buf |= b
52
+ r -= 8
53
+ end
54
+ }
55
+ yield buf, r
56
+ end
57
+ end
58
+
59
+ def scan_blocks bytes
60
+ i = 0
61
+ loop do
62
+ s = bytes.byteslice i, 5
63
+ break unless s.notempty?
64
+ yield s.unpack "C*"
65
+ i += 5
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+ class Invalid < ArgumentError ; end
73
+
74
+ attr_reader :data
75
+
76
+ def initialize data
77
+ @data = data
78
+ end
79
+
80
+ def decode encoding: nil
81
+ res = ""
82
+ each_block { |buf,n|
83
+ chunk = []
84
+ 5.times {
85
+ chunk.unshift buf & 0xff
86
+ buf >>= 8
87
+ }
88
+ until n >= 40 do
89
+ chunk.pop
90
+ n += 8
91
+ end
92
+ res << (chunk.pack "C*")
93
+ }
94
+ encoding = Encoding.default_external if encoding == :default
95
+ res.force_encoding encoding if encoding
96
+ res
97
+ end
98
+
99
+ private
100
+
101
+ def each_block
102
+ buf, n = 0, 0
103
+ each_char do |c|
104
+ c -= case c
105
+ when ORD_A..ORD_Z then ORD_A
106
+ when ORD_a..ORD_z then ORD_a
107
+ when ORD_2..ORD_7 then DIFF_2
108
+ else raise Invalid, "Character '#{c}'."
109
+ end
110
+ buf <<= 5
111
+ buf |= c
112
+ n += 5
113
+ if n == 40 then
114
+ yield buf, n
115
+ buf, n = 0, 0
116
+ end
117
+ end
118
+ if n.nonzero? then
119
+ buf <<= 40 - n
120
+ yield buf, n
121
+ end
122
+ nil
123
+ end
124
+
125
+ def each_char
126
+ done = false
127
+ @data.each_char { |c|
128
+ case c
129
+ when "=" then done = true ; next
130
+ when "\n" then next
131
+ else raise Invalid, "Material after '=': '#{c}'" if done
132
+ end
133
+ yield c.ord
134
+ }
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+
@@ -0,0 +1,21 @@
1
+ #
2
+ # potp/foreign/supplement.rb -- Addition usefull Ruby functions
3
+ #
4
+
5
+ # The purpose of this is simply to reduce dependencies.
6
+
7
+ begin
8
+ require "supplement____"
9
+ rescue LoadError
10
+ class NilClass ; def notempty? ; end ; end
11
+ class String ; def notempty? ; self unless empty? ; end ; end
12
+ class Array ; def notempty? ; self unless empty? ; end ; end
13
+ class <<Struct ; alias [] new ; end
14
+ class String
15
+ def starts_with? oth ; o = oth.to_str ; o.length if start_with? o ; end
16
+ def ends_with? oth ; o = oth.to_str ; length - o.length if end_with? o ; end
17
+ alias starts_with starts_with?
18
+ alias ends_with ends_with?
19
+ end
20
+ end
21
+
data/lib/potp/hotp.rb ADDED
@@ -0,0 +1,29 @@
1
+ #
2
+ # potp/hotp.rb -- HOTP class
3
+ #
4
+
5
+ require "potp/otp"
6
+
7
+
8
+ module POTP
9
+
10
+ class HOTP < OTP
11
+
12
+ SCHEME = "hotp"
13
+
14
+ def verify input, counter, retries: 0
15
+ while retries >= 0 do
16
+ return counter if super input, counter
17
+ counter += 1
18
+ retries -= 1
19
+ end
20
+ end
21
+
22
+ def provisioning_uri counter: 0, **kwargs
23
+ super
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
data/lib/potp/otp.rb ADDED
@@ -0,0 +1,93 @@
1
+ #
2
+ # potp/otp.rb -- OTP class
3
+ #
4
+
5
+ require "potp/foreign/supplement"
6
+ require "openssl"
7
+ require "potp/base32"
8
+
9
+
10
+ module POTP
11
+
12
+ class OTP
13
+
14
+ attr_reader :secret, :digits, :digest
15
+
16
+ DEFAULT_DIGITS = 6
17
+
18
+ def initialize secret, digits: nil, digest: nil, **kwargs
19
+ @secret = secret
20
+ @digits = digits || DEFAULT_DIGITS # Google Authenticate only supports 6 currently
21
+ @digest = digest || "sha1" # Google Authenticate only supports SHA1 currently
22
+ end
23
+
24
+ def at data
25
+ hmac = build_digest data
26
+ code = hmac[ (hmac.last & 0x0f), 4].inject do |c,e| c <<= 8 ; c |= e end
27
+ code &= 0x7fffffff
28
+ s = ""
29
+ @digits.times { code, d = code.divmod 10 ; s << d.to_s }
30
+ s.reverse!
31
+ s
32
+ end
33
+
34
+ def verify input, data
35
+ String === input or raise ArgumentError, "`otp` has to be a String"
36
+ time_constant_compare input, (at data)
37
+ end
38
+
39
+ # https://github.com/google/google-authenticator/wiki/Key-Uri-Format
40
+ # Example additional parameter: image: "https://example.com/icon.png"
41
+ def provisioning_uri name:, issuer: nil, **kwargs
42
+ label = [ issuer, name||""].map { |x| x&.tr ":", "_" }
43
+ parameters = {
44
+ **kwargs,
45
+ digits: (@digits unless @digits == DEFAULT_DIGITS),
46
+ algorithm: (@digest.upcase unless @digest.downcase == "sha1"),
47
+ issuer: issuer,
48
+ secret: @secret,
49
+ }
50
+ build_uri label, parameters
51
+ end
52
+
53
+ private
54
+
55
+ def int_to_bytestring i, padding = 8
56
+ i >= 0 or raise ArgumentError, "#int_to_bytestring requires a positive number"
57
+ result = []
58
+ while i != 0 or padding > 0 do
59
+ c = (i & 0xff).chr
60
+ result.unshift c
61
+ i >>= 8
62
+ padding -= 1
63
+ end
64
+ result.join
65
+ end
66
+
67
+ def time_constant_compare a, b
68
+ a.notempty? and b.notempty? and a == b
69
+ end
70
+
71
+ def build_digest input
72
+ d = OpenSSL::Digest.new @digest
73
+ s = (Base32.new @secret).decode
74
+ i = int_to_bytestring input.to_i
75
+ (OpenSSL::HMAC.digest d, s, i).bytes
76
+ end
77
+
78
+ def build_uri label, parameters
79
+ label.compact!
80
+ label = label.map! { |s| url_encode s }.join ":"
81
+ parameters.reject! { |_,v| v.nil? }
82
+ parameters = parameters.keys.reverse.map { |k| "#{k}=#{url_encode parameters[ k]}" }.join "&"
83
+ "otpauth://#{self.class::SCHEME}/#{label}?#{parameters}"
84
+ end
85
+
86
+ def url_encode str
87
+ str.to_s.gsub %r/([^a-zA-Z0-9_.-])/ do |c| "%%%02X" % c.ord end
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
@@ -0,0 +1,26 @@
1
+ #
2
+ # potp/random.rb -- Generate random secrets
3
+ #
4
+
5
+ require "potp/base32"
6
+ require "securerandom"
7
+
8
+
9
+ module POTP
10
+
11
+ class Base32
12
+
13
+ class <<self
14
+
15
+ # A byte length of 20 means 160 bits and results in 32 character long base32 value.
16
+ def random byte_length = 20
17
+ rand_bytes = SecureRandom.random_bytes byte_length
18
+ (encode rand_bytes).data
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
data/lib/potp/totp.rb ADDED
@@ -0,0 +1,61 @@
1
+ #
2
+ # potp/totp.rb -- TOTP class
3
+ #
4
+
5
+ require "potp/otp"
6
+
7
+
8
+ module POTP
9
+
10
+ class TOTP < OTP
11
+
12
+ SCHEME = "totp"
13
+
14
+ DEFAULT_INTERVAL = 30
15
+
16
+ attr_reader :interval
17
+
18
+ # @option options [Integer] interval (30) the time interval in seconds for OTP
19
+ # This defaults to 30 which is standard.
20
+ def initialize secret, interval: nil, **kwargs
21
+ @interval = interval || DEFAULT_INTERVAL
22
+ super secret, **kwargs
23
+ end
24
+
25
+ def at time ; super (timeint time) / @interval ; end
26
+ def now ; at Time.now ; end
27
+
28
+ def verify input, drift_ahead: nil, drift_behind: nil, after: nil, at: nil
29
+ fin = now = timeint at||Time.now
30
+ now -= drift_behind if drift_behind
31
+ fin += drift_ahead if drift_ahead
32
+ if after then
33
+ after += @interval
34
+ now = after if now < after
35
+ end
36
+ now -= now % @interval
37
+ while now < fin do
38
+ return now if super input, now
39
+ now += @interval
40
+ end
41
+ nil
42
+ end
43
+
44
+ def provisioning_uri **kwargs
45
+ super **kwargs, period: (@interval unless @interval == TOTP::DEFAULT_INTERVAL)
46
+ end
47
+
48
+ private
49
+
50
+ def timeint time
51
+ case time
52
+ when Integer then time
53
+ when Time then time.utc.to_i
54
+ else time.to_i
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
@@ -0,0 +1,11 @@
1
+ #
2
+ # potp/version.rb -- Version number
3
+ #
4
+
5
+
6
+ module POTP
7
+
8
+ VERSION = "1.0.0".freeze
9
+
10
+ end
11
+
data/lib/potp.rb ADDED
@@ -0,0 +1,11 @@
1
+ #
2
+ # potp.rb -- The whole potp package
3
+ #
4
+
5
+ require "potp/totp"
6
+ require "potp/hotp"
7
+ require "potp/version"
8
+
9
+ module POTP
10
+ end
11
+
data/potp.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ #
2
+ # potp.gemspec -- Gem Specification
3
+ #
4
+
5
+ require "./lib/potp/version"
6
+
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = "potp"
10
+ s.version = POTP::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.required_ruby_version = ">= 3.1"
13
+ s.summary = "Plain One Time Password Tool"
14
+ s.description = <<~EOT
15
+ A Ruby library for generating and verifying one time passwords,
16
+ both HOTP and TOTP, and includes QR Code provisioning.
17
+ EOT
18
+ s.license = "LicenseRef-LICENSE"
19
+ s.authors = ["Bertram Scharpf"]
20
+ s.email = ["<software@bertram-scharpf.de>"]
21
+ s.homepage = "https://github.com/BertramScharpf/ruby-potp"
22
+
23
+ s.requirements = "Just Ruby and some more if you like"
24
+ unless :full_dependecies then
25
+ s.add_dependency "supplement", "~>2", ">=2.10"
26
+ s.add_dependency "appl", "~>1"
27
+ end
28
+
29
+ s.require_paths = %w(lib)
30
+ s.extensions = %w()
31
+ s.files = Dir[ "lib/**/*.rb", "bin/*", ]
32
+ s.executables = %w(potp)
33
+ s.extra_rdoc_files = %w(LICENSE README.md potp.gemspec)
34
+ end
35
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: potp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Bertram Scharpf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |
14
+ A Ruby library for generating and verifying one time passwords,
15
+ both HOTP and TOTP, and includes QR Code provisioning.
16
+ email:
17
+ - "<software@bertram-scharpf.de>"
18
+ executables:
19
+ - potp
20
+ extensions: []
21
+ extra_rdoc_files:
22
+ - LICENSE
23
+ - README.md
24
+ - potp.gemspec
25
+ files:
26
+ - LICENSE
27
+ - README.md
28
+ - bin/potp
29
+ - lib/potp.rb
30
+ - lib/potp/base32.rb
31
+ - lib/potp/foreign/supplement.rb
32
+ - lib/potp/hotp.rb
33
+ - lib/potp/otp.rb
34
+ - lib/potp/random.rb
35
+ - lib/potp/totp.rb
36
+ - lib/potp/version.rb
37
+ - potp.gemspec
38
+ homepage: https://github.com/BertramScharpf/ruby-potp
39
+ licenses:
40
+ - LicenseRef-LICENSE
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '3.1'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements:
57
+ - Just Ruby and some more if you like
58
+ rubygems_version: 3.5.23
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Plain One Time Password Tool
62
+ test_files: []