Class Yubico
In: lib/yubico.rb
Parent: Object

Methods

Constants

E_OK = "OK"   Returned if all is well with the request.
E_REPLAYED_OTP = "REPLAYED_OTP"   Returned if the server has seen that OTP for that key before.
E_BAD_OTP = "BAD_OTP"   Returned if the OTP is invalid or misformed.
E_NO_SUCH_CLIENT = "NO_SUCH_CLIENT"   Returned if such a client doesn‘t exist
E_POLICY_VIOLATION = "POLICY_VIOLATION"   Returned if a specified request violates the rules for the verifier. Typically this happens when you send an unsigned request but the verifier is set up to only use signed requests.
E_BAD_SIGNATURE = "BAD_SIGNATURE"   Returned if the signature on the request was invalid.
E_OPERATION_NOT_ALLOWED = "OPERATION_NOT_ALLOWED"   Returned if the operation is not allowed, or otherwise denied.
E_INCORRECT_PARAMETER = "INCORRECT_PARAMETER"   Returned if a parameter is bad or missing, respectively. (Also returns an "info" field — needs to be parsed (1) )
E_MISSING_PARAMETER = "MISSING_PARAMETER"
E_BACKEND_ERROR = "BACKEND_ERROR"   Returned on a Backend Error
E_MISSING_SECRET = "MISSING_SECRET"   Returned when the object is constructed without a shared key, on requests that require the shared key.
E_UNKNOWN_ERROR = "UNKNOWN_ERROR"   Returned on an unknown error server side.

Attributes

_id  [RW]  The requested client or verifier ID for the instance of the object.
_key  [RW]  The shared key, if required, for the client or verifier.
_res  [RW]  The response from the server.

Public Class methods

Initialization is used to create the instance variables at creation. Yubikey.new requires in the least an ID, and also takes a shared key.

[Source]

     # File lib/yubico.rb, line 125
125:   def initialize(id, key='')
126:     @_id = id
127:     @_key = key
128:   end

Public Instance methods

Unimplemented

[Source]

     # File lib/yubico.rb, line 237
237:                 def addClient
238:                 end

Requests add_key information from the server and returns all the information required to encode a Yubikey. This command will create a new key on request, so standard practice should be very secure and sure before this is called.

  if( user.created? )
    user.keyflag = yk.add_key( @_id, timestamp )
    user.keydata = yk.last_response
  end
    # =>

[Source]

     # File lib/yubico.rb, line 195
195:                 def add_key( id, nonce )
196:                   if not @_key
197:                     return E_MISSING_SECRET
198:                   end
199:                   
200:                         operation = "add_key"
201:                         mesg = "id=#{id}&nonce=#{nonce}&operation=#{operation}"
202:                         key = self.hmac( @_id, mesg, 'sha1' )
203:                         url = URI.parse("http://api.yubico.com/wsapi/add_key?operation=#{operation}&id=#{id}&nonce=#{nonce}&h=#{key}")
204:       http = Net::HTTP.new(url.host, 443)
205:       http.use_ssl = true
206:       res = http.get(url.path + "?" + url.query)
207:                         if( ( res.body ) && ( /status=([\w_]+)[\s]/.match(res.body) ) )
208:                                 @_res = res.body
209:                                 return res.body.scan(/status=([\w_]+)[\s]/).first
210:                         else
211:                                 return E_UNKNOWN_ERROR
212:                         end  
213:                 end

Unimplemented

[Source]

     # File lib/yubico.rb, line 241
241:                 def deleteClient
242:                 end

Signed request that deletes key key_id from the server, with the client ID id and passes nonce as the request nonce.

[Source]

     # File lib/yubico.rb, line 217
217:                 def delete_key( id, key_id, nonce )
218:                   if not @_key
219:                     return E_MISSING_SECRET
220:                   end
221:                   operation = "delete_key"
222:                   mesg = "key_id=#{key_id}&id=#{id}&nonce=#{nonce}&operation=#{operation}"
223:                   key = self.hmac( @_id, mesg, 'sha1' )
224:                   url = URI.parse("http://api.yubico.com/wsapi/delete_key?#{mesg}&h=#{key}")
225:                   http = Net::HTTP.new(url.host, 443)
226:       http.use_ssl = true
227:       res = http.get(url.path + "?" + url.query)
228:                   if( (res.body) && ( /status=([\w_]+)[\s]/.match(res.body) ) )
229:                     @_res = res.body
230:                     return res.body.scan(/status=([\w_]+)[\s]/).first
231:                   else
232:                     return E_UNKNOWN_ERROR
233:                   end
234:                 end

Takes the OTP, shared key, and ID, creates the RFC3339 timestamp, and returns the hash.

  hash = yubikey_object.signKey( @otp, @shared, @id )
    # => "39ccb32d95edfdbcd882f2b01809724ec640ea16"

[Source]

     # File lib/yubico.rb, line 253
253:                 def hmac( key, msg, algorithm )
254:                         OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(algorithm), key, msg)
255:                 end

Return the last available Yubikey response body, in full, for parsing.

  req = yk.verify( @token, @_id )
  res = req.last_response
    # => "h=...\nstatus=OK"

On an error, there will be many fields returned by this function you can sift out manually using regular expressions (to be implemented: last_response returns an associative array of the response.) for various variables.

  req = yk.verify( @token, @_id )
    # => "h=Z/D42NucC1nSlIVZqFPR3RO5dG4=\r\nt=2007-10-23T14:26:48Z\r\nstatus=REPLAYED_OTP\r\n\r\n"
  res = yk.last_response
  res["h"]
    # => "h=Z/D42NucC1nSlIVZqFPR3RO5dG4="
  res["t"]
    # => "2007-10-23T14:26:48Z"
  res["status"]
    # => "REPLAYED_OTP"
  res["info"]
    # => nil

[Source]

     # File lib/yubico.rb, line 177
177:     def last_response
178:       hash = @_res.scan(/h=([\w_\=\/]+)[\s]/).first
179:             timestamp = @_res.scan(/t=([\w\-:]+)[\s]/).first
180:             status = @_res.scan(/status=([a-zA-Z0-9_]+)[\s]/).first
181:             info = @_res.scan(/info=([\w\t\S\W]+)/).first
182:           
183:             return { "h" => hash, "t" => timestamp, "status" => status, "info" => info }
184:     end

Verify an Yubikey OTP Token. Returns the value of the response variable "status"

  req = yk.verify( @token, @id )
    # => STATUS_VARIABLE

Upon an error, you should use the Yubikey.last_response method for a more detailed collection of variables, including the request hash, info and further.

[Source]

     # File lib/yubico.rb, line 137
137:                 def verify( otp, id = @_id )
138:                   @_id = id if @_id.nil?
139:                         # Set up the full URL for the request, then pass it through the +URI+ gauntlet.
140:                         fullurl = "https://api.yubico.com/wsapi/verify?id="+ (@_id.to_s) +"&otp="+ otp
141:                         url = URI.parse(fullurl)
142:                         http = Net::HTTP.new(url.host, 443)
143:       http.use_ssl = true
144:       # Make the call to the server and return the full response, not just the body (to be used later)
145:       res = http.get(url.path + "?" + url.query)
146:                   # Parse and find the status within the response's body, to verify this is, in fact, what we're looking for.
147:                   if ( !(/status=([a-zA-Z0-9_]+)/.match(res.body)) )
148:                     # If it's not, let's raise an error.
149:                     raise "Response Error: "+ res.body
150:               else 
151:                 @_res = res.body
152:               end
153:                   # Finally, return the response value.
154:                   @_res.scan(/status=([a-zA-Z0-9_]+)[\s]/).first.to_s
155:                 end

[Validate]