alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/* $Id: OTPAuthenticator.java,v 1.1.1.1 2002/10/02 18:42:55 wastl Exp $ */
import net.wastl.webmail.server.*;
import net.wastl.webmail.exceptions.*;
import net.wastl.webmail.config.ConfigScheme;

import org.webengruven.javaOTP.*;
import org.webengruven.webmail.auth.*;

import java.util.*;
import java.security.NoSuchAlgorithmException;

/**
 * OTPAuthenticator.java -- this class is an Authenticator for
 * Webmail/Java that uses One Time Passwords for authentication.  Using the
 * new challenge/response authentication framework, this class will provide
 * authentication based on RFC1938 one time passes.
 *
 * @author Devin Kowatch
 * @version $Revision: 1.1.1.1 $
 * 
 * Copyright (C) 2000 Devin Kowatch
 */
/* This program is free software; you can redistribute it and/or
 * modify it under the terms of the Lesser GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Lesser GNU General Public License for more details.
 * 
 * You should have received a copy of the Lesser GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

public class OTPAuthenticator extends OTPAuthenticatorIface {
    /** The version of OTPAuthenticator */
    public final String     VERSION = "1.2";
    /** The default starting sequence for an OTP */
    public final int        START_SEQ = 499;
    /** The default hash algorithm for an OTP */
    public final String     DFLT_HASH = "MD5";

    private final int       CACHE_ACTIVE_ST = 0x01;
    private final int       CACHE_NEW_ST    = 0x02;

    /** Default Constructor */
    public OTPAuthenticator() {
        super();
        key = "OTP";
        cache = new Hashtable();
    }

    /** Get the AuthDisplayMngr for this class */
    public AuthDisplayMngr getAuthDisplayMngr() {
        if (disp_mngr == null) {
            disp_mngr = new OTPAuthDisplayMngr(this);
        }

        return disp_mngr;
    }

    /** Get this class' version 
     * @return A version string for this class
     */
    public String getVersion() {
        return VERSION;
    }

    /**
     * (Re-)Initilize this authenticator.
     */
    public void init(Storage store) {
    }

    /** Register this authenticator with the system.
     */
    public void register(ConfigScheme store) {
        store.configAddChoice("AUTH", key, "Simple OTP Authentication. The"
         + " server admin must create the account,"
         + " users can change passwords");
    }

    /** 
     * Get a new challenge for changing the password. This will be
     * displayed on the screen when the user tries to change their
     * password.
     * @param udata The users data, this may be needed when re-keying the
     * password.
     */
    public String getNewChallenge(UserData udata) throws WebMailException {
        OTPState        [] states = new OTPState[2];
        OTPState        st, new_st; 
        String          chal = null;
        Random          rand = new Random();
        OTPServer       server = null;
        String          pData = null;
        boolean         newAccount = false;

        getFromCache(udata.getLogin(), CACHE_ACTIVE_ST | CACHE_NEW_ST, states);
        // for convience
        st = states[0];
        new_st = states[1];

        if (st == null) {
            // Not in the cache, lets see if they have logged in before
            pData = udata.getPasswordData();
            if (pData != null && pData.length() > 0) {
                st = new OTPState(pData);
            }
            else {
                // later code expects st to be set 
                st = new OTPState("","","",DFLT_HASH);
                newAccount = true;
            }
        }

        try {
            // setup the new seed and seq.
            if (new_st == null) {

                // Get a random value between (0, 10000], and pad to 4 chars.
                int             randVal = rand.nextInt() % 9999;
                StringBuffer    newSeed = new StringBuffer(10);

                newSeed.append(udata.getDomain().substring(0, 2));

                if (randVal < 0) randVal = -randVal;
                if (randVal > 999) {       // 4 digits already, no pad
                }
                else if (randVal > 99) {   // 3 digits only, pad 1
                    newSeed.append("0");
                }
                    else if (randVal > 9) {    // 2 digits only, pad 2
                    newSeed.append("00");
                }
                else {                     // 1 digit only, pad 3
                    newSeed.append("000");
                }
                newSeed.append(String.valueOf(randVal)); 

                new_st = new OTPState(st);
                new_st.seed = newSeed.toString();
                new_st.sequence = String.valueOf(START_SEQ);
            }

            // if this is a new account don't use an instance of OTPServer
            if (newAccount) { 
                chal=OTPServer.getNewChallenge(new_st.seed,START_SEQ,DFLT_HASH);
            }
            else {
                server = new OTPServer(st);
                chal = server.getNewChallenge(new_st.seed, START_SEQ);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new WebMailException("bad hash algorithm for new OTP.");
        }

        // for new accounts, st is a dummy.  As such it should not be
        // stored in the cache where it will cause problems by being used
        if (newAccount) {
            st = null;
        }

        // keep track of the new OTPState for changePassword()
        putIntoCache(udata.getLogin(), st, new_st);

        return chal;
    }

    /** Authenticate the user. */
    public void authenticatePostUserData(UserData ud,String dom,String pass)
     throws InvalidPasswordException
    {
        String          login = ud.getLogin();
        OTPCacheNode    n = (OTPCacheNode)cache.get(login);
        OTPState        st;
        OTPServer       server;
        String          pData;
        
        try {
            st = getFromCache(login, CACHE_ACTIVE_ST);
            if (st == null) {
                pData = ud.getPasswordData();
                if (pData == null || pData.length() == 0) {
                    throw new InvalidPasswordException("no password data");
                }
                st = new OTPState(pData);
            }

            server = new OTPServer(st);

            if (! server.checkOTP(pass)) 
                throw new InvalidPasswordException("bad password");
            
            /* Update the password data so next time we need a new pass */
            st = server.getState();
            ud.setPasswordData(st.getInfoString());
        }
        catch (OTPFormatException e) {
            throw new InvalidPasswordException("bad OTP format");
        }
        catch (NoSuchAlgorithmException e) {
            throw new InvalidPasswordException("bad hash");
        }
        /*
        catch (Exception e) {
            throw new InvalidPasswordException("WTF?!?!?");
        }
        */

        // Done with n.active_st
        removeFromCache(login, CACHE_ACTIVE_ST);
    }

    /** Change the OTP Stream */
    public void changePassword(UserData ud, String newpass, String vrfy) 
     throws InvalidPasswordException
    {
        String          login = ud.getLogin();
        OTPState        st = null;
        OTPServer       server = null;
        String          pData = null;;

        st = getFromCache(login, CACHE_NEW_ST);
        if (st == null)
            throw new InvalidPasswordException(
                "Don't know what challenge the new password is for."); 

        if (! newpass.equalsIgnoreCase(vrfy))
            throw new InvalidPasswordException(
                "the password and verify don't match");

        st.otp = newpass;


        try {
            pData = ud.getPasswordData();

            if (pData != null && pData.length() > 0) {
                // Get the old OTP first, so that we can compare.
                server = new OTPServer(new OTPState(pData) );

                if (! server.reinitOTP(st, true)) 
                    throw new InvalidPasswordException(
                        "The new OTP stream is the same as the old one.");
            }
            else {
                // We need to verify the format of the OTP
                server = new OTPServer(st);
            }
        }
        catch (OTPFormatException e) {
            throw new InvalidPasswordException("bad format in OTP");
        }
        catch (NoSuchAlgorithmException e) {
            throw new InvalidPasswordException("Bad hash name");
        }

        // We do this, so that the Hex rep. is stored.
        ud.setPasswordData(server.getState().getInfoString());

        // remove this from the cache. (kill both so that the active state
        // is not stale
        removeFromCache(login, CACHE_NEW_ST | CACHE_ACTIVE_ST);
    }


    /** Get the challenge for this authentication.  This will get passed some 
     * user data and should return the approriate challenge string for that 
     * user.
     */
    public String getChallenge(UserData ud) throws WebMailException {
        OTPServer       server;
        OTPState        st = null;
        String          login = ud.getLogin();
        String          chal = null;
        String          pData = null;

        st = getFromCache(login, CACHE_ACTIVE_ST);

        if (st == null) {
            pData = ud.getPasswordData();
            if (pData == null || pData.length() == 0) {
                // No password data has been set.
                throw new WebMailException("No password data available");
            }
            st = new OTPState(pData);
        }

        try {
            server = new OTPServer(st);
            chal = server.getChallenge();
        } 
        catch (OTPFormatException e) {
            e.printStackTrace();
            // this should never happen.
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            // this either
        }

        // final sanity check
        if (chal == null) {
            throw new WebMailException("failed to get the next challenge");
        }

        // I'm expecting that we will be using this again soon.
        putIntoCache(login, st, null);

        return chal;
    }        

    /*----------------------- Private Functions -------------------------*/

    private void removeFromCache(String key, int type) {
        OTPCacheNode    node;

        node = (OTPCacheNode)cache.get(key);
        if (node == null) {
            node = new OTPCacheNode();
        }

        if ( (type & CACHE_ACTIVE_ST) != 0) {
            node.active_st = null;
        }

        if ( (type & CACHE_NEW_ST) != 0) {
            node.new_st = null;
        }

        if (node.new_st == null && node.active_st == null) {
            cache.remove(key);
        }
        else {
            cache.put(key, node);
        }
    }

    private void putIntoCache(String key, OTPState ast, OTPState nst) {
        OTPCacheNode    node;

        node = (OTPCacheNode)cache.get(key);
        if (node == null) {
            node = new OTPCacheNode(ast, nst);
        }
        else {
            if (ast != null) {
                node.active_st = ast;
            }

            if (nst != null) {
                node.new_st = nst;
            }
        }

        cache.put(key, node);
    }

    private void getFromCache(String key, int type, OTPState []sts) {
        OTPCacheNode    node;
        int             i = 0;

        node = (OTPCacheNode)cache.get(key);

        if (node != null) {
            if ( (type & CACHE_ACTIVE_ST) != 0) {
                sts[i++] = node.active_st;
            }

            if ( (type & CACHE_NEW_ST) != 0) {
                sts[i] = node.new_st;
            }
        }
    }    

    private OTPState getFromCache(String key, int type) {
        OTPCacheNode    node;
        OTPState        rtn;

        node = (OTPCacheNode)cache.get(key);
        rtn = null;

        if (node != null) {
            if ( (type & CACHE_ACTIVE_ST) != 0) {
                rtn = node.active_st;
            }
            else if ( (type & CACHE_NEW_ST) != 0) {
                rtn = node.new_st;
            }
        }      

        return rtn;
    }

    private Hashtable           cache;
    private OTPAuthDisplayMngr  disp_mngr;
} // OTPAuthenticator
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.