Lamport's one-time password algorithm (or, don't talk to complete strangers!)

A design pattern for securing client/service interactions with OTP

1 2 3 4 5 6 Page 4
Page 4 of 6

Applying OTP

Applying a OTP scheme to the SampleClient/SampleService pair makes the process diagram in Figure 3 more interesting but not much more complicated.

Figure 3. Process diagram for applying OTP scheme (Click to enlarge.)

The five class-level entities in Figure 3 are:

  • SecureSampleClient: A client component that extends the implementation of SampleClient. Its activities include:
    • Obtaining a PassKeySequencer instance
    • Asking the sequencer instance for a set or specific number of passkey values
    • Negotiating with an OTP protected service:
      • Introduction, invoking the hello() service method
      • Some series of service requests, invoking the requestService() service method
      • Indicating the end of the conversation, invoking the goodbye() service method
  • OtpService: An interface that defines a set of operations to be implemented by OTP secured services.
  • PassKeySequencerFactory: A class that encapsulates the details of constructing an implementation of PassKeySequencer. Clients of PassKeySequencer need not concern themselves with selecting a specific sequence algorithm and the details of creation/initialization.
  • PassKeySequencer: An interface that defines functionality of all implementing OTP Sequencer classes. Implementors of PassKeySequencer encapsulate a specific sequence algorithm. Operations include:
    • Providing "next" sequence value. (Recall that the sequence of values is used as a classic stack collection from last to first values.)
    • Encoding a given value with the same algorithm that was used to create each sequence value's successor
    • Generating or regenerating a new sequence of values
  • OtpAuthority: An interface that defines a client request's authentication behavior. Implementations of OtpAuthority are expected to be used directly by implementations of OtpService. Their responsibility can include passkey and client-identifier validation as well as service-request context checking, but a minimum level of validation beyond the described OTP check is not defined.

These class-level entities perform the following sequence of actions:

  1. The SecureSampleClient class asks the PassKeySequencerFactory for a PassKeySequencer instance.
  2. The PassKeySequencerFactory has a notion of which PassKeySequencer implementation to create and return.
  3. The SecureSampleClient asks the PassKeySequencer to generate N number of sequence values (or a default number of values), which are consumed with each service interaction.
  4. The OtpService receives a request from a client. Before agreeing to process a client request, the OtpService asks the OtpAuthority to validate.
  5. The OtpAuthority receives the requesting client-identifier, a passkey value, and the service name/type. It can fail the authentication request for any reason, but it is expected to validate the passkey value according to Lamport OTP rules.
  6. If a PassKeySequencer instance is not already created, the OtpAuthority asks the PassKeySequencerFactory for one.
  7. The PassKeySequencer instance is asked to validate the passkey value.

As with the original client/service, request responses and potentially thrown exceptions flow their way back to the initiating client.

Static structures

The supporting classes and interfaces shown in Figure 4 meet our general design goals.

Figure 4. Supporting classes and interfaces (Click to enlarge.)

The classes and interfaces in Figure 4 reuse our original client/service implementations through inheritance. Sequence-value generation and validation is not only decoupled from the client and OtpService implementations, but we also made it trivial to leverage multiple sequence algorithms. The authentication of the client-identifier and passkey value is also decoupled into a separate OtpAuthority component. Finally, a "factory" component -- PassKeySequenceFactory -- ensures that cooperating client/service components use the same OTP sequence algorithm.

The classes and interfaces in Figure 4 that I haven't already described are:

  • SecureSampleService: A class that extends the SampleService. It uses an instance of SimpleOtpAuthority to meet its OtpService interface obligations (following the common Adapter pattern).
  • SecureServiceException: A class extending ServiceException. It defines error codes and conditions specific to an OTP-protected service interaction.
  • BaseOtpAuthority: An abstract class that is a convenient start for extending classes that implement the OtpAuthority interface. It provides data structures and default key-management and authentication methods. Concrete classes that implement the OtpAuthority would be well served by extending BaseOtpAuthority and modifying or extending behavior as needed.
  • SimpleOtpAuthority : A class representing the only concrete implementation of OtpAuthority, fulfilling most of its obligations by extending the BaseOtpAuthority class.
  • BaseSequencer: An abstract class that is a convenient start for extending classes that implement the PassKeySequencer interface. It provides data structures and sequence-value-generation and maintenance logic that would apply to most PassKeySequencer implementations. Concrete classes that implement PassKeySequencer would be well served by extending this base class and modifying or extending behavior as needed.
  • HashSequencer: This is the only concrete implementation of PassKeySequencerin the base library (*.otp.lib) package. Others can be found in the *.otp.poc package. The sequence algorithm here is based on starting with a reasonably unique seed value and making each successor value the MD5-hash value of its predecessor.
  • SimpleSequencer: This implementation of PassKeySequencer produces a sequence of numbers that starts with 18 and follows this logic: add 3; if the resulting numbers contain a 3, replace it with a 4. (Recall that this was the algorithm used by Tom and Jerry's notional conversation.)
  • FiboSequencer: This sequencer produces a sequence of numbers that starts with 1 and generates a series of values that follows the well-known Fibonacci sequence (1, 1, 2, 3, 5, 8, 13 ...).

Listing 4 shows the significant coding details of the SecureSampleService class, which implements OtpService and extends the original service class, SampleService.

Listing 4. SecureSampleService

package com.opensolutionspace.otp.appl;

import com.opensolutionspace.appl.SampleService;
import com.opensolutionspace.common.*;
import com.opensolutionspace.otp.lib.*;
import com.opensolutionspace.otp.poc.*;

/**
 * 02/03/2009 - extends the SampleService as is.
 * Uses an instance of SimpleOtpAuthority to meet its OtpService
 * obligations ...
 * @author lji
 */
public class SecureSampleService extends SampleService implements OtpService {

    public SecureSampleService() {

    }

    SimpleOtpAuthority otpAuthority = new SimpleOtpAuthority();


    public void hello(String id, String first ) throws SecureServiceException {
      otpAuthority.hello(id, first);
    }


    public String requestService(
      String id, String password, String request, String... args )
      throws SecureServiceException {

      if(otpAuthority.authorizeRequest(id, password, request, args) != OtpService.OK) {
        throw new SecureServiceException(
          SecureServiceException.Code.AUTHORIZATION_EXCEPTION);
      }

      String rs = "";
      try {
        rs = processRequest(request, args);
      }
      catch(ServiceException se) {
        SimpleLogger.error("CATCH CLAUSE failed");
        throw new SecureServiceException(
            SecureServiceException.Code.SERVICE_EXCEPTION, se);
      }

      return rs;
    }


    public void goodbye(String id, String password ) throws SecureServiceException {
      otpAuthority.goodbye(id, password);
    }


}

Notice that SecureSampleService, as I stated previously, does little on its own beyond marshaling the service-request attributes submitted by the requesting client. An instance of OtpAuthority is used to fulfill the behavior defined by OtpService. The basic service operations are supported by the SampleService superclass.

1 2 3 4 5 6 Page 4
Page 4 of 6