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

The implementation of HashSequencer and its base class -- BaseSequencer -- is worth reviewing, because it's where the core of the Lamport algorithm is implemented. Listing 5 shows the code for these classes.

Listing 5. HashSequencer and BaseSequencer

package com.opensolutionspace.otp.lib;

import java.util.*;
import java.security.*;
import java.io.UnsupportedEncodingException;
import java.math.*;


/**
* The is the only concrete implementor of PassKeySequencer
* in the com.opensolutionspace.otp.lib package.
* Others can be found in the com.opensolutionspace.otp.poc package.
* Sequence is based on starting with a reasonably unique seed
* value and making each successor value the MD5-hash value of its
* predecessor ...
*
* @author lji
* 02/07/2009
*/
public class HashSequencer extends BaseSequencer implements PassKeySequencer {


 protected String genSeed() {
  return this.getClass().getName() + new Date().getTime();
 }

 public String encode(String value) {
   String rval = "";
   try {
     rval = genMD5(value);
   }
   catch (NoSuchAlgorithmException ex) {
     System.err.println("encode():" + ex);
     return rval;
   }
   catch (UnsupportedEncodingException ex) {
     System.err.println("encode():" + ex);
     return rval;
   }
   return rval;
 }


 protected String genMD5(String value)
    throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(value.getBytes(), 0, value.length());
    return "" + new BigInteger(1, md.digest()).toString(16);
 }

 /*
 NOTE: BaseSequence provides
 implementation of nextValue() & genValues(...) methods
 */


}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package com.opensolutionspace.otp.lib;

import java.util.*;


/**
* 02/02/2009
* Convenience base Sequencer Class - carries data structures and
* sequence value generation and maintenance logic that apply to all/most PassKeySequencer implementors.
* Concrete classes that implement PassKeySequencer would be well served
* by extending this base class and modifying/extending behavior as needed.
*
* @author lji
*/
public abstract class BaseSequencer {

 protected int maxValues = PassKeySequencer.DEFUALT_MAX_VALUES;
 protected Stack<String> passwordLifo;


 protected abstract String genSeed();
 public abstract String encode (String value);


 protected String nextValue(String value) {
   return encode(value);
 }


 public BaseSequencer() {
   passwordLifo = new Stack<String>();
 }

 public BaseSequencer(int n) {
   maxValues = n;
   passwordLifo = new Stack<String>();
 }


 public String nextValue() {
   return passwordLifo.pop();
 }

 public void genValues(int n) {
   passwordLifo.removeAllElements();
   String value = genSeed();
   for (int i=0; i < n ; i++) {
     value = nextValue(value);
     passwordLifo.add(value);
   }
 }

 public void genValues() {
   genValues(maxValues);
 }


}

The SimpleSequencer class, shown in Listing 6, represents an alternative sequence algorithm.

Listing 6. SimpleSequencer

package com.opensolutionspace.otp.poc;

import java.util.*;

import com.opensolutionspace.otp.lib.BaseSequencer;
import com.opensolutionspace.otp.lib.PassKeySequencer;

/**
 * 02/10/2009
 *
 * This sequencer produces a sequence of numbers that starts with
 * "18" and goes onto generate a series of numbers that follows
 * this logic - add 3 - if the resulting numbers contain a 3,
 * replace it with a 4.
 * @author lji
 */
public class SimpleSequencer
    extends BaseSequencer implements PassKeySequencer {


    public String encode(String value) {

        /**
        * Change to add 3, if result contains "3"
        * change that digit to "4" ...
        */
        int ival = this.seqToInt(value);
        ival = ival += 3;
        String sval = "" + ival;

        return sval.replaceAll("3", "4");
    }


    protected String genSeed() {
        return "18";
    }


    private int seqToInt(String seq) {
        int ival;

        try {
            ival = Integer.parseInt(seq);
        }
        catch(NumberFormatException ex) {
            ival = 90890707;
        }

        return ival;
    }

}

Notice that the required coding here is limited to two methods: genSeed(), which is an abstract method defined in its superclass, and encode(), a method defined in the PassKeySequencer interface that implements the sequence algorithm.

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