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 3
Page 3 of 6

Reference implementation

The reference implementation offers a simple example of how an OTP framework can be applied to an existing client/service collaboration with minimal coding, and extended to use alternate sequence algorithms and stricter client-identifier validation. You can download the RI source code anytime.

Life without OTP

Before securing a client/service pair with an OTP scheme, interactions might flow fairly simply: the client obtains a handle to a known service and issues requests. Our sample service here provides four end points (which are of limited use but adequate for demonstration purposes):

  • echo returns the set of parameters provided by the client back to the client.
  • replace writes data into a specified file resource. It creates the file if it doesn't already exist and replaces the file if it does.
  • append appends data into a specified file resource. It creates the file if it doesn't already exist and appends to the file if it does.
  • get obtains the contents of a specified file resource.

The client sends a sequence of clearly defined requests to a service. The requests pass or fail on the merits of completing the related work, and a response is returned, as illustrated in Figure 1.

Figure 1. "Legacy" client/service interaction (Click to enlarge.)

The class model shown in Figure 2 implements the service flow shown in Figure 1.

Figure 2. Class model for the legacy service flow (Click to enlarge.)

In Figure 2's class model:

  • TestDriver simulates the action of the service client. It creates usable instances of SampleClient and SampleService and triggers a workflow on the SampleClient.
  • SampleClient is an example service-client component that issues a series of requests to an instance of SampleService (which is not secured through an OTP scheme).
  • SampleService is a simply modeled notional service that provides trivial echo, file-manipulation, and file-retrieval service end points.
  • ServiceException extends the base Java Exception, providing a single wrapper for all lower-level exceptions and meaningful exception codes and messages.

The core of the SampleClient is implemented in Listing 1.

Listing 1. SampleClient's core implementation

public void doSomeWork(SampleService someService) {
  try {
   setup(someService);
   sendRequests(someService);
   cleanup(someService);
  }
  catch(ServiceException ex) {
   SimpleLogger.error(getClass().getName() + ": " + ex);
  }
 }

 protected void sendRequests(SampleService someService) throws ServiceException {
  String scratch = "";

  scratch = someService.processRequest( "echo", "Hello,", "JavaWorld" );
  SimpleLogger.info("echo: " + scratch);
  someService.processRequest( "replace", "/tmp/JavaWorld", "Added this note on .... " + new Date());
  someService.processRequest( "append", "/tmp/JavaWorld", "Added another note on .... " + new Date());
  scratch = someService.processRequest( "get", "/tmp/JavaWorld");
  SimpleLogger.info(" \"get\" retrieved the following:\n" + scratch);

 }

Launching the TestDriver class produces the output in Listing 2, which includes some process logging and service-resident file content.

Listing 2. Testing sample client/service

SampleService echoing: Hello,...
echo: Hello, JavaWorld

 SampleService replacing: /tmp/JavaWorld
 SampleService appending: /tmp/JavaWorld
 SampleService getting: /tmp/JavaWorld
 "get" retrieved the following:

 Added this note on .... Sun Feb 22 11:05:07 EST 2009
 Added another note on .... Sun Feb 22 11:05:07 EST 2009

Listing 3 shows the main SampleService code.

Listing 3. SampleService

package com.opensolutionspace.appl;

import java.io.*;
import com.opensolutionspace.common.*;
import com.opensolutionspace.otp.lib.OtpService;

/**
* 02/01/2009 - Simply modeled notional service ...
* provides trivial "echo", and file creation and retrieval service points
* @author lji
*
*/
public class SampleService {

 protected String serviceName;

 public SampleService() {
  init();
 }

 protected void init() {
  serviceName = Utility.shortClassName(this);
 }


 public String processRequest(String request, String... args )
     throws ServiceException {

  String rs = "";
  try {
   if("get".equalsIgnoreCase(request) ) {
    rs = get(args);
   }
   else if("append".equalsIgnoreCase(request) ) {
    append(args);
   }
   else if("replace".equalsIgnoreCase(request) ) {
    replace(args);
   }
   else if("echo".equalsIgnoreCase(request) ) {
    rs = echo(args);
   }
   else {
    throw new ServiceException(ServiceException.Code.INVALID_SERVICE);
   }
  }
  catch(Exception ex) {
   throw new ServiceException(ServiceException.Code.PLATFORM_EXCEPTION, ex);
  }

  return rs;
 }

 /////////////////////////////////////////////////
 // Specific service point Implementation methods
 // echo(), get(), put(), replace()
 /**
 * echo back the provided args ...
 * @param args
 * @return
 */
 protected String echo(String...args) {
  SimpleLogger.info(" " + serviceName + " echoing: " + args[0].toString() + "..." );
  StringBuffer buffer = new StringBuffer(args.length * 25);

  for(String arg : args) {
   if(buffer.length() > 0 )
    buffer.append(" ");
   buffer.append(arg);
  }
  buffer.append("\n");
  return buffer.toString();
 }

 /**
 * return the contents of a given file
 * @param args
 * @return
 */
 protected String get(String...args)throws Exception {
  SimpleLogger.info(" " + serviceName + " getting: " + args[0].toString() );
  String file = args[0];
  return Utility.fileContents(file);
 }

 /**
 * create, or append to the file if it already exists.
 * @param args
 * @throws Exception
 */
 protected void append(String...args) throws Exception {
  put(true, args);
 }

 /**
 * create, or append to the file if it already exists.
 * @param args
 * @throws Exception
 */
 protected void replace(String...args) throws Exception {
  put(false, args);
 }

 /**
 * Write content to a file
 * @param fileFlag
 * @param args
 * @throws Exception
 */
 private void put(boolean fileFlag, String...args) throws Exception {
  String operation = "appending";
  if(fileFlag == false )
   operation = "replacing";
  SimpleLogger.info(" " + serviceName + " " + operation + ": " + args[0].toString() );
  String file = args[0];
  String log = args[1];
  Utility.writeToFile(file, log, fileFlag);

 }

 public String getServiceName() {
  return serviceName;
 }

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