[jboss-cvs] Picketbox SVN: r114 - in trunk: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp and 4 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Sep 21 17:09:14 EDT 2010


Author: anil.saldhana at jboss.com
Date: 2010-09-21 17:09:13 -0400 (Tue, 21 Sep 2010)
New Revision: 114

Added:
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/JBossTimeBasedOTPLoginModule.java
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/SecurityActions.java
   trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/jaas/JBossTimeBasedOTPLoginModuleUnitTestCase.java
   trunk/security-jboss-sx/jbosssx/src/test/resources/otp-users.properties
Modified:
   trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/util/TestHttpServletRequest.java
   trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTP.java
   trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTPUtil.java
Log:
SECURITY-530: totp based login module

Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/JBossTimeBasedOTPLoginModule.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/JBossTimeBasedOTPLoginModule.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/JBossTimeBasedOTPLoginModule.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -0,0 +1,286 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.auth.spi.otp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import javax.security.jacc.PolicyContext;
+import javax.security.jacc.PolicyContextException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.jboss.logging.Logger;
+import org.jboss.security.otp.TimeBasedOTP;
+import org.jboss.security.otp.TimeBasedOTPUtil;
+
+/**
+ * <p>
+ * Login Module that can be configured to validate a Time based OTP.
+ * </p>
+ * 
+ * <p>
+ * Usage:
+ * This login module needs to be configured along with one of the other JBoss login modules such
+ * as {@code org.jboss.security.auth.spi.DatabaseServerLoginModule} or
+ * {@code org.jboss.security.auth.spi.LdapLoginModule}
+ * </p>
+ * Example configuration:
+ * <p>
+ * <pre>
+ * {@code
+ * <application-policy name="otp">
+    <authentication>
+      <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
+        flag="required">
+        <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
+        <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
+      </login-module>
+      <login-module code="org.jboss.security.auth.spi.otp.JBossTimeBasedOTPLoginModule" />
+    </authentication>
+  </application-policy>
+ * }
+ * </pre>
+ * </p>
+ * 
+ * <p>
+ * Configurable Options:
+ * </p>
+ * <p>
+ * <ul>
+ * <li>algorithm:  either "HmacSHA1", "HmacSHA256" or "HmacSHA512"   [Default: "HmacSHA1"]</li>
+ * <li>numOfDigits:  Number of digits in the TOTP.  Default is 6.</li>
+ * </ul>
+ * </p>
+ * 
+ * <p>
+ * This login module requires the presence of "otp-users.properties" on the class path with the format:
+ * username=key
+ * </p>
+ * 
+ * <p>
+ * An example of otp-users.properties is:
+ * </p>
+ * <p>
+ * <pre>
+    admin=35cae61d6d51a7b3af
+   </pre>
+ * </p>
+ * 
+ * 
+ * @author Anil.Saldhana at redhat.com
+ * @since Sep 21, 2010
+ */
+public class JBossTimeBasedOTPLoginModule implements LoginModule
+{  
+   private static Logger log = Logger.getLogger( JBossTimeBasedOTPLoginModule.class );
+   private boolean trace = log.isTraceEnabled();
+
+   public static final String TOTP = "totp";
+
+   private Map<String,Object> lmSharedState = new HashMap<String,Object>();
+   private Map<String, Object> lmOptions = new HashMap<String,Object>(); 
+   private CallbackHandler callbackHandler;
+   private boolean useFirstPass;
+
+   //This is the number of digits in the totp
+   private int NUMBER_OF_DIGITS = 6;
+   
+   /**
+    * Default algorithm is HMAC_SHA1
+    */
+   private String algorithm = TimeBasedOTP.HMAC_SHA1; //Default
+
+   public void initialize( Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
+         Map<String, ?> options )
+   { 
+      this.callbackHandler = callbackHandler;
+      this.lmSharedState.putAll( sharedState );
+      this.lmOptions.putAll( options );
+
+      /* Check for password sharing options. Any non-null value for
+      password_stacking sets useFirstPass as this module has no way to
+      validate any shared password.
+       */
+      String passwordStacking = (String) options.get("password-stacking");
+      if( passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass") )
+         useFirstPass = true;
+      
+      //Option for number of digits
+      String numDigitString = (String) options.get( "numOfDigits" );
+      if( numDigitString != null && numDigitString.length() > 0 )
+         NUMBER_OF_DIGITS = Integer.parseInt( numDigitString );
+      
+      //Algorithm
+      String algorithmStr = (String) options.get( "algorithm" );
+      if( algorithmStr != null && algorithmStr != "" )
+      {
+         if( algorithmStr.equalsIgnoreCase( TimeBasedOTP.HMAC_SHA256) )
+            algorithm = TimeBasedOTP.HMAC_SHA256;
+         if( algorithmStr.equalsIgnoreCase( TimeBasedOTP.HMAC_SHA512 ))
+            algorithm = TimeBasedOTP.HMAC_SHA512;
+      }
+   }
+
+   /**
+    * @see {@code LoginModule#login()}
+    */
+   public boolean login() throws LoginException
+   {
+      String username = null;
+       
+
+      if( useFirstPass == true )
+      {
+         username = (String) lmSharedState.get("javax.security.auth.login.name");  
+      }
+      else
+      { 
+         NameCallback nc = new NameCallback("User name: ", "guest"); 
+         Callback[] callbacks = { nc };
+         try
+         {
+            callbackHandler.handle(callbacks);
+         }
+         catch ( Exception e )
+         {
+            LoginException le = new LoginException();
+            le.initCause( e );
+            throw le;
+         } 
+
+         username = nc.getName();
+      }
+      
+      //Load the otp-users.properties file
+      ClassLoader tcl = SecurityActions.getContextClassLoader();
+      InputStream is = tcl.getResourceAsStream( "otp-users.properties" );
+      
+      Properties otp = new Properties();
+      try
+      {
+         otp.load( is );
+      }
+      catch (IOException e )
+      {
+         LoginException le = new LoginException( "Unable to load the otp users properties");
+         le.initCause( e );
+         throw le;
+      }
+      
+      String seed = otp.getProperty( username );
+
+      String submittedTOTP = this.getTimeBasedOTPFromRequest();
+      if( submittedTOTP == null || submittedTOTP.length() == 0 )
+      {
+         if( trace )
+         {
+            log.trace( "Either the TOTP in request was null or was of zero length::TOTP=" + submittedTOTP );
+         }
+         throw new LoginException(); 
+      }
+  
+      try
+      {
+         boolean result =  false;
+         
+         if( algorithm.equals( TimeBasedOTP.HMAC_SHA1 ))
+         {
+            result =  TimeBasedOTPUtil.validate( submittedTOTP, seed.getBytes() , NUMBER_OF_DIGITS ); 
+         }
+         else if( algorithm.equals( TimeBasedOTP.HMAC_SHA256 ))
+         {
+            result =  TimeBasedOTPUtil.validate256( submittedTOTP, seed.getBytes() , NUMBER_OF_DIGITS ); 
+         }
+         else if( algorithm.equals( TimeBasedOTP.HMAC_SHA512 ))
+         {
+            result =  TimeBasedOTPUtil.validate512( submittedTOTP, seed.getBytes() , NUMBER_OF_DIGITS ); 
+         }
+         
+         if( result == false )
+            throw new LoginException();
+         
+         return result; 
+      }
+      catch (GeneralSecurityException e)
+      {
+         LoginException le = new LoginException();
+         le.initCause( e );
+         throw le;
+      } 
+   }
+
+   /**
+    * @see {@code LoginModule#commit()}
+    */
+   public boolean commit() throws LoginException
+   { 
+      return true;
+   }
+
+   /**
+    * @see {@code LoginModule#abort()}
+    */
+   public boolean abort() throws LoginException
+   { 
+      return true;
+   }
+
+   /**
+    * @see {@code LoginModule#logout()}
+    */
+   public boolean logout() throws LoginException
+   { 
+      return true;
+   } 
+
+   private String getTimeBasedOTPFromRequest()
+   {
+      String totp = null;
+
+      //This is JBoss AS specific mechanism 
+      String WEB_REQUEST_KEY = "javax.servlet.http.HttpServletRequest";
+
+      try
+      {
+         HttpServletRequest request = (HttpServletRequest) PolicyContext.getContext(WEB_REQUEST_KEY);
+         totp = request.getParameter( TOTP );
+      }
+      catch (PolicyContextException e)
+      {
+         if( log.isTraceEnabled() )
+         {
+            log.trace( "Error getting request::", e ); 
+         } 
+      }
+      return totp; 
+   }
+}
\ No newline at end of file

Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/SecurityActions.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/SecurityActions.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/otp/SecurityActions.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -0,0 +1,46 @@
+/*
+  * JBoss, Home of Professional Open Source
+  * Copyright 2007, JBoss Inc., and individual contributors as indicated
+  * by the @authors tag. See the copyright.txt in the distribution for a
+  * full listing of individual contributors.
+  *
+  * This is free software; you can redistribute it and/or modify it
+  * under the terms of the GNU Lesser General Public License as
+  * published by the Free Software Foundation; either version 2.1 of
+  * the License, or (at your option) any later version.
+  *
+  * This software 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 GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public
+  * License along with this software; if not, write to the Free
+  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  */
+package org.jboss.security.auth.spi.otp;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+
+/**
+ *  Privileged Blocks
+ *  @author Anil.Saldhana at redhat.com
+ *  @since  Sep 26, 2007 
+ *  @version $Revision$
+ */
+class SecurityActions
+{
+   static ClassLoader getContextClassLoader()
+   {
+      return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>()
+      { 
+         public ClassLoader run()
+         { 
+            return Thread.currentThread().getContextClassLoader();
+         }
+       });  
+   } 
+}
\ No newline at end of file

Added: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/jaas/JBossTimeBasedOTPLoginModuleUnitTestCase.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/jaas/JBossTimeBasedOTPLoginModuleUnitTestCase.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/authentication/jaas/JBossTimeBasedOTPLoginModuleUnitTestCase.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors. 
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.test.authentication.jaas;
+
+import static org.junit.Assert.fail;
+
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import javax.security.jacc.PolicyContext;
+import javax.security.jacc.PolicyContextException;
+import javax.security.jacc.PolicyContextHandler;
+
+import org.jboss.security.SimplePrincipal;
+import org.jboss.security.auth.callback.JBossCallbackHandler;
+import org.jboss.security.auth.spi.otp.JBossTimeBasedOTPLoginModule;
+import org.jboss.security.otp.TimeBasedOTP;
+import org.jboss.test.util.TestHttpServletRequest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Unit Test the {@code JBossTimeBasedOTPLoginModule}
+ * @author Anil.Saldhana at redhat.com
+ * @since Sep 21, 2010
+ */
+public class JBossTimeBasedOTPLoginModuleUnitTestCase
+{
+   static String seed = "3132333435363738393031323334353637383930";
+
+   static final String WEB_REQUEST_KEY = "javax.servlet.http.HttpServletRequest";
+   
+   @BeforeClass
+   public static void setUp() throws Exception
+   {
+      try
+      {
+         String totp =  TimeBasedOTP.generateTOTP( seed, 6 ) ; 
+         PolicyContext.registerHandler( WEB_REQUEST_KEY, getHandler(totp), true );
+      }
+      catch (GeneralSecurityException e)
+      {
+         throw new RuntimeException( e );
+      } 
+   }
+   
+   @Test
+   public void testTOTP() throws Exception
+   {
+      Principal principal = new SimplePrincipal( "anil" );
+      
+      Subject subject = new Subject();
+      CallbackHandler callbackHandler = new JBossCallbackHandler(principal, seed );
+      Map<String,Object> sharedState = new HashMap<String,Object>();
+      Map<String, Object> options = new HashMap<String,Object>();
+      
+      JBossTimeBasedOTPLoginModule jtp = new JBossTimeBasedOTPLoginModule();
+      jtp.initialize(subject, callbackHandler, sharedState, options); 
+      jtp.login();
+   }  
+   
+   @Test
+   public void testInvalidAuth() throws Exception
+   {
+      PolicyContext.registerHandler( WEB_REQUEST_KEY, getHandler( "ArbitraryDummy" ), true ); 
+      
+      Principal principal = new SimplePrincipal( "anil" );
+      
+      Subject subject = new Subject();
+      CallbackHandler callbackHandler = new JBossCallbackHandler(principal, seed );
+      Map<String,Object> sharedState = new HashMap<String,Object>();
+      Map<String, Object> options = new HashMap<String,Object>();
+      
+      JBossTimeBasedOTPLoginModule jtp = new JBossTimeBasedOTPLoginModule();
+      jtp.initialize(subject, callbackHandler, sharedState, options); 
+      try
+      {
+         jtp.login();
+         fail( "Should have failed auth" );
+      }
+      catch( LoginException le )
+      {
+         //pass
+      }
+   }
+   
+   /**
+    * Create a JACC Policy Context Handler that takes in a totp string
+    * and returns a {@code HttpServletRequest} with the totp as parameter
+    * @param totp
+    * @return
+    */
+   private static PolicyContextHandler getHandler( final String totp )
+   {
+      return new PolicyContextHandler()
+      {
+         public Object getContext(String key, Object data) throws PolicyContextException
+         { 
+            if( WEB_REQUEST_KEY.equals( key ))
+            {  
+               TestHttpServletRequest tsr = new TestHttpServletRequest();
+               tsr.setParameter( "totp", totp );
+               
+               return tsr; 
+            } 
+            return null;
+         }
+
+         public String[] getKeys() throws PolicyContextException
+         { 
+            return null;
+         }
+
+         public boolean supports(String key) throws PolicyContextException
+         {
+            if( WEB_REQUEST_KEY.equals( key ))
+               return true;
+            
+            return false;
+         }
+      }; 
+   } 
+}
\ No newline at end of file

Modified: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/util/TestHttpServletRequest.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/util/TestHttpServletRequest.java	2010-09-21 17:09:46 UTC (rev 113)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/util/TestHttpServletRequest.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -53,6 +53,9 @@
    
    private Map<String,Object> parameterMap = new HashMap<String,Object>();
    
+   public TestHttpServletRequest()
+   {   
+   }
    public TestHttpServletRequest(Principal p, String uri, String meth)
    {
       this.p = p; 
@@ -326,4 +329,10 @@
    public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException
    { 
    } 
+   
+   //Non-standard methods
+   public void setParameter( String key, Object value )
+   {
+      parameterMap.put(key, value);
+   }
 }

Added: trunk/security-jboss-sx/jbosssx/src/test/resources/otp-users.properties
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/resources/otp-users.properties	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/test/resources/otp-users.properties	2010-09-21 21:09:13 UTC (rev 114)
@@ -0,0 +1 @@
+anil=3132333435363738393031323334353637383930
\ No newline at end of file

Modified: trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTP.java
===================================================================
--- trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTP.java	2010-09-21 17:09:46 UTC (rev 113)
+++ trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTP.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -40,11 +40,11 @@
  */
 public class TimeBasedOTP
 {
-   private static final String HMAC_SHA1 = "HmacSHA1";
+   public static final String HMAC_SHA1 = "HmacSHA1";
 
-   private static final String HMAC_SHA256 = "HmacSHA256";
+   public static final String HMAC_SHA256 = "HmacSHA256";
    
-   private static final String HMAC_SHA512 = "HmacSHA512";
+   public static final String HMAC_SHA512 = "HmacSHA512";
    
    // 0 1  2   3    4     5      6       7        8
    private static final int[] DIGITS_POWER  = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; 
@@ -77,6 +77,52 @@
    }
    
    /**
+    * Generate a TOTP value using HMAC_SHA256
+    * @param key
+    * @param returnDigits
+    * @return
+    * @throws GeneralSecurityException
+    */
+   public static String generateTOTP256( String key, int returnDigits ) throws GeneralSecurityException
+   {
+      TimeZone utc = TimeZone.getTimeZone( "UTC" );
+      Calendar currentDateTime = Calendar.getInstance( utc );
+      long timeInMilis = currentDateTime.getTimeInMillis();
+       
+      String steps = "0";
+      long T = ( timeInMilis - TIME_ZERO ) /  TIME_SLICE_X ; 
+      steps = Long.toHexString( T ).toUpperCase();
+      
+      // Just get a 16 digit string
+      while(steps.length() < 16) 
+         steps = "0" + steps;
+      return TimeBasedOTP.generateTOTP256( key, steps, returnDigits); 
+   }
+   
+   /**
+    * Generate a TOTP value using HMAC_SHA512
+    * @param key
+    * @param returnDigits
+    * @return
+    * @throws GeneralSecurityException
+    */
+   public static String generateTOTP512( String key, int returnDigits ) throws GeneralSecurityException
+   {
+      TimeZone utc = TimeZone.getTimeZone( "UTC" );
+      Calendar currentDateTime = Calendar.getInstance( utc );
+      long timeInMilis = currentDateTime.getTimeInMillis();
+       
+      String steps = "0";
+      long T = ( timeInMilis - TIME_ZERO ) /  TIME_SLICE_X ; 
+      steps = Long.toHexString( T ).toUpperCase();
+      
+      // Just get a 16 digit string
+      while(steps.length() < 16) 
+         steps = "0" + steps;
+      return TimeBasedOTP.generateTOTP512( key, steps, returnDigits); 
+   }
+   
+   /**
     * This method generates an TOTP value for the given
     * set of parameters.
     *

Modified: trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTPUtil.java
===================================================================
--- trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTPUtil.java	2010-09-21 17:09:46 UTC (rev 113)
+++ trunk/security-spi/spi/src/main/java/org/jboss/security/otp/TimeBasedOTPUtil.java	2010-09-21 21:09:13 UTC (rev 114)
@@ -22,6 +22,8 @@
 package org.jboss.security.otp;
 
 import java.security.GeneralSecurityException;
+import java.util.Calendar;
+import java.util.TimeZone;
 
 /**
  * Utility class associated with the {@code TimeBasedOTP} class
@@ -30,6 +32,8 @@
  */
 public class TimeBasedOTPUtil
 {   
+   private static long TIME_INTERVAL = 30 * 1000; //30 secs
+   
    /**
    * Validate a submitted OTP string
    * @param submittedOTP OTP string to validate
@@ -39,9 +43,108 @@
    */
   public static boolean validate( String submittedOTP, byte[] secret, int numDigits ) throws GeneralSecurityException
   {
-     String generatedTOTP = TimeBasedOTP.generateTOTP( new String( secret ) , numDigits ); 
+     TimeZone utc = TimeZone.getTimeZone( "UTC" );
+     Calendar currentDateTime = Calendar.getInstance( utc );
      
-     System.out.println( "Generated[" + generatedTOTP + "]::Submitted[" + submittedOTP );
-     return generatedTOTP.equals( submittedOTP ); 
-  } 
+     String generatedTOTP = TimeBasedOTP.generateTOTP( new String( secret ) , numDigits );
+     boolean result =  generatedTOTP.equals( submittedOTP );
+     
+     if( !result )
+     {
+        //Step back time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        
+        timeInMilis -= TIME_INTERVAL;
+        
+        generatedTOTP = TimeBasedOTP.generateTOTP( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     if( !result )
+     {
+        //Step ahead time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        timeInMilis += TIME_INTERVAL;
+        generatedTOTP = TimeBasedOTP.generateTOTP( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     return result;
+  }
+  
+  /**
+   * Validate a submitted OTP string using HMAC_256
+   * @param submittedOTP OTP string to validate
+   * @param secret Shared secret 
+   * @return 
+   * @throws GeneralSecurityException
+   */
+  public static boolean validate256( String submittedOTP, byte[] secret, int numDigits ) throws GeneralSecurityException
+  {
+     TimeZone utc = TimeZone.getTimeZone( "UTC" );
+     Calendar currentDateTime = Calendar.getInstance( utc );
+     
+     String generatedTOTP = TimeBasedOTP.generateTOTP256( new String( secret ) , numDigits );
+     boolean result =  generatedTOTP.equals( submittedOTP );
+     
+     if( !result )
+     {
+        //Step back time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        timeInMilis -= TIME_INTERVAL;
+        
+        generatedTOTP = TimeBasedOTP.generateTOTP256( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     if( !result )
+     {
+        //Step ahead time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        timeInMilis += TIME_INTERVAL;
+        
+        generatedTOTP = TimeBasedOTP.generateTOTP256( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     return result;
+  }
+  
+  /**
+   * Validate a submitted OTP string using HMAC_512
+   * @param submittedOTP OTP string to validate
+   * @param secret Shared secret 
+   * @return 
+   * @throws GeneralSecurityException
+   */
+  public static boolean validate512( String submittedOTP, byte[] secret, int numDigits ) throws GeneralSecurityException
+  {
+     TimeZone utc = TimeZone.getTimeZone( "UTC" );
+     Calendar currentDateTime = Calendar.getInstance( utc );
+     
+     String generatedTOTP = TimeBasedOTP.generateTOTP512( new String( secret ) , numDigits );
+     boolean result =  generatedTOTP.equals( submittedOTP );
+     
+     if( !result )
+     {
+        //Step back time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        timeInMilis -= TIME_INTERVAL;
+        
+        generatedTOTP = TimeBasedOTP.generateTOTP512( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     if( !result )
+     {
+        //Step ahead time interval
+        long timeInMilis = currentDateTime.getTimeInMillis();
+        timeInMilis += TIME_INTERVAL;
+        
+        generatedTOTP = TimeBasedOTP.generateTOTP512( new String( secret ) , "" + timeInMilis, numDigits );
+        result =  generatedTOTP.equals( submittedOTP );
+     }
+     
+     return result;
+  }
 }
\ No newline at end of file



More information about the jboss-cvs-commits mailing list