onetime-totp 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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/onetime.rb +130 -0
  3. metadata +59 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc3fd60991cff4658e421a6ae1c94d8976f8473442c7d1e480f242647cc2639f
4
+ data.tar.gz: 41467f12ee65a75c97e2ba5074f1d3fd8708a1c36a640a27f84a9fc1e1c97079
5
+ SHA512:
6
+ metadata.gz: 5f8e414491afa3ba4b0183ddc6b3e981aa6b2cbc73db1ee2b7a694033596be35d8a97c47e348ac2efd923ea2a6da8478ba9fec9057aeb65929c33372c5e8bbed
7
+ data.tar.gz: 9569c0a21c52a6b9618805bcc8a9326c9f3e0ae9a90c0f34ad04c83283b56607042cbb9fa7ce80af669acfaacf5b35d8c23355df3c41a0687af33a4a76d38c79
@@ -0,0 +1,130 @@
1
+ ################################################################################
2
+ # #
3
+ # OneTime password generator - 2020 fboccacini@gmail.com <Fabio Boccacini> #
4
+ # v1.0 #
5
+ # #
6
+ ################################################################################
7
+
8
+ require 'openssl'
9
+ require 'base64'
10
+ require 'base32'
11
+ require 'json'
12
+ require 'net/https'
13
+
14
+ class OneTime
15
+
16
+ attr_accessor :step, :encryption, :length
17
+ attr_reader :password, :secret, :generation_time
18
+
19
+ def initialize(secret, length: 6, step: 30, encryption: 'SHA512')
20
+
21
+ @secret = secret
22
+ @length = length
23
+ @step = step
24
+ @encryption = encryption
25
+ self.generate
26
+
27
+ end
28
+
29
+ def generate(time: Time.now,
30
+ length: self.length,
31
+ step: self.step,
32
+ encryption: self.encryption,
33
+ reset: false)
34
+
35
+ begin
36
+
37
+ # If reset is true nil the generation time
38
+ @generation_time = time.clone if reset == true || @generation_time.nil?
39
+
40
+ # Binary key from time / step
41
+ rounded_step = (time.to_f - @generation_time.to_f).floor / step
42
+ key = ['%0.16x' % (rounded_step).to_s(16).hex].pack('H*')
43
+
44
+ # Convert secret's non base32 char to base32 and back to string
45
+ data = Base32.decode(Base32.encode(self.secret))
46
+
47
+ # Get initial HMAC hash
48
+ hash = OpenSSL::HMAC.digest(encryption.downcase, data, key)
49
+
50
+ # Dynamic truncation:
51
+ # Get the last for bit of the last byte of the hash
52
+ offset = hash[hash.length - 1].ord & 0xf
53
+
54
+ # Get the for bytes pointed by the offset
55
+ hash = hash[offset .. offset + 3].bytes
56
+
57
+ # Get new hash
58
+ truncated_hash = ((hash[0] & 0x7f) << 24) |
59
+ ((hash[1] & 0xff) << 16) |
60
+ ((hash[2] & 0xff) << 8) |
61
+ (hash[3] & 0xff)
62
+
63
+ # Get actual password
64
+ @password = "%0.#{length}d" % (truncated_hash % (10 ** length));
65
+
66
+ return self.password
67
+
68
+ rescue Exception => e
69
+ puts e.message
70
+ # puts e.backtrace.join("\n")
71
+ return nil
72
+ end
73
+
74
+ end
75
+
76
+ def verify(password, time: Time.now,
77
+ length: self.length,
78
+ step: self.step,
79
+ encryption: self.encryption)
80
+
81
+ # verify a password correctness for a given time
82
+ begin
83
+
84
+ return self.generate(time: time,
85
+ length: length,
86
+ step: step,
87
+ encryption: encryption) == password
88
+
89
+ rescue Exception => e
90
+ puts e.message
91
+ # puts e.backtrace.join("\n")
92
+ return false
93
+ end
94
+
95
+ end
96
+
97
+ def call_service(url, user, payload, content_type: 'application/json', accept: '*/*')
98
+
99
+ begin
100
+
101
+ self.generate if self.password.nil?
102
+
103
+ # Base64 encode authentication for the header
104
+ auth = Base64.strict_encode64("#{user}:#{self.password}".encode("ASCII"))
105
+
106
+ # Prep uri and headers
107
+ uri = URI.parse(url)
108
+ headers = {
109
+ 'Authorization' => "Basic #{auth}",
110
+ 'Content-Type' => content_type,
111
+ 'Accept' => accept
112
+ }
113
+
114
+ # Create request
115
+ request = Net::HTTP.new(uri.host, uri.port)
116
+ request.use_ssl = true
117
+
118
+ # Send request
119
+ return request.post(uri.path, payload.to_json, headers)
120
+ rescue Exception => e
121
+ puts e.message
122
+ # puts e.backtrace.join("\n")
123
+ return nil
124
+ end
125
+
126
+ end
127
+
128
+
129
+
130
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: onetime-totp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Fabio Boccacini
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base32
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ description: |-
28
+ A TOTP generator, that includes validation, generation and send methods.
29
+ See https://github.com/fboccacini/onetime-totp for reference and usage.
30
+ email: fboccacini@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/onetime.rb
36
+ homepage: https://github.com/fboccacini/onetime-totp
37
+ licenses:
38
+ - MIT
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.0.3
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Time-based One Time Password (TOTP) generator.
59
+ test_files: []