2016-02-03 10:20:22 +00:00
/ *
* Copyright 2016 Red Hat , Inc . and / or its affiliates
* and other contributors as indicated by the @author tags .
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2015-08-06 00:39:47 +00:00
package org.keycloak.models ;
2015-09-18 17:02:36 +00:00
import org.jboss.logging.Logger ;
2015-08-06 00:39:47 +00:00
import org.keycloak.models.utils.Base32 ;
import org.keycloak.models.utils.HmacOTP ;
2015-12-03 07:23:46 +00:00
import java.io.Serializable ;
2015-09-18 17:02:36 +00:00
import java.io.UnsupportedEncodingException ;
import java.net.URLEncoder ;
2015-08-06 00:39:47 +00:00
import java.util.HashMap ;
2017-12-15 13:25:21 +00:00
import java.util.LinkedList ;
import java.util.List ;
2015-08-06 00:39:47 +00:00
import java.util.Map ;
/ * *
* @author < a href = " mailto:bill@burkecentral.com " > Bill Burke < / a >
* @version $Revision : 1 $
* /
2015-12-03 07:23:46 +00:00
public class OTPPolicy implements Serializable {
2015-08-06 00:39:47 +00:00
2015-09-18 17:02:36 +00:00
protected static final Logger logger = Logger . getLogger ( OTPPolicy . class ) ;
2015-08-06 00:39:47 +00:00
protected String type ;
protected String algorithm ;
protected int initialCounter ;
protected int digits ;
protected int lookAheadWindow ;
2015-08-06 20:54:59 +00:00
protected int period ;
2015-08-06 00:39:47 +00:00
private static final Map < String , String > algToKeyUriAlg = new HashMap < > ( ) ;
2017-12-15 13:25:21 +00:00
private static final OtpApp [ ] allApplications = new OtpApp [ ] { new FreeOTP ( ) , new GoogleAuthenticator ( ) } ;
2015-08-06 00:39:47 +00:00
static {
algToKeyUriAlg . put ( HmacOTP . HMAC_SHA1 , " SHA1 " ) ;
algToKeyUriAlg . put ( HmacOTP . HMAC_SHA256 , " SHA256 " ) ;
algToKeyUriAlg . put ( HmacOTP . HMAC_SHA512 , " SHA512 " ) ;
}
public OTPPolicy ( ) {
}
2015-08-06 20:54:59 +00:00
public OTPPolicy ( String type , String algorithm , int initialCounter , int digits , int lookAheadWindow , int period ) {
2015-08-06 00:39:47 +00:00
this . type = type ;
this . algorithm = algorithm ;
this . initialCounter = initialCounter ;
this . digits = digits ;
this . lookAheadWindow = lookAheadWindow ;
2015-08-06 20:54:59 +00:00
this . period = period ;
2015-08-06 00:39:47 +00:00
}
2015-08-06 20:54:59 +00:00
public static OTPPolicy DEFAULT_POLICY = new OTPPolicy ( UserCredentialModel . TOTP , HmacOTP . HMAC_SHA1 , 0 , 6 , 1 , 30 ) ;
2015-08-06 00:39:47 +00:00
2018-01-24 15:54:34 +00:00
public String getAlgorithmKey ( ) {
return algToKeyUriAlg . containsKey ( algorithm ) ? algToKeyUriAlg . get ( algorithm ) : algorithm ;
}
2015-08-06 00:39:47 +00:00
public String getType ( ) {
return type ;
}
public void setType ( String type ) {
this . type = type ;
}
public String getAlgorithm ( ) {
return algorithm ;
}
public void setAlgorithm ( String algorithm ) {
this . algorithm = algorithm ;
}
public int getInitialCounter ( ) {
return initialCounter ;
}
public void setInitialCounter ( int initialCounter ) {
this . initialCounter = initialCounter ;
}
public int getDigits ( ) {
return digits ;
}
public void setDigits ( int digits ) {
this . digits = digits ;
}
public int getLookAheadWindow ( ) {
return lookAheadWindow ;
}
public void setLookAheadWindow ( int lookAheadWindow ) {
this . lookAheadWindow = lookAheadWindow ;
}
2015-08-06 20:54:59 +00:00
public int getPeriod ( ) {
return period ;
}
public void setPeriod ( int period ) {
this . period = period ;
}
2016-05-20 11:59:19 +00:00
/ * *
* Constructs the < code > otpauth : //</code> URI based on the <a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">Key-Uri-Format</a>.
* @param realm
* @param user
* @param secret
* @return the < code > otpauth : //</code> URI
* /
2015-09-18 17:02:36 +00:00
public String getKeyURI ( RealmModel realm , UserModel user , String secret ) {
2016-05-20 11:59:19 +00:00
2016-01-29 10:00:28 +00:00
try {
2016-05-20 11:59:19 +00:00
2016-01-29 10:00:28 +00:00
String displayName = realm . getDisplayName ( ) ! = null & & ! realm . getDisplayName ( ) . isEmpty ( ) ? realm . getDisplayName ( ) : realm . getName ( ) ;
2015-08-06 00:39:47 +00:00
2016-05-20 11:59:19 +00:00
String accountName = URLEncoder . encode ( user . getUsername ( ) , " UTF-8 " ) ;
String issuerName = URLEncoder . encode ( displayName , " UTF-8 " ) . replaceAll ( " \\ + " , " %20 " ) ;
2016-01-29 10:00:28 +00:00
2016-05-20 11:59:19 +00:00
/ *
* The issuerName component in the label is usually shown in a authenticator app , such as
* Google Authenticator or FreeOTP , as a hint for the user to which system an username
* belongs to .
* /
String label = issuerName + " : " + accountName ;
String parameters = " secret= " + Base32 . encode ( secret . getBytes ( ) ) //
+ " &digits= " + digits //
+ " &algorithm= " + algToKeyUriAlg . get ( algorithm ) //
+ " &issuer= " + issuerName ;
2015-08-06 00:39:47 +00:00
2016-01-29 10:00:28 +00:00
if ( type . equals ( UserCredentialModel . HOTP ) ) {
2016-05-20 11:59:19 +00:00
parameters + = " &counter= " + initialCounter ;
} else if ( type . equals ( UserCredentialModel . TOTP ) ) {
parameters + = " &period= " + period ;
2016-01-29 10:00:28 +00:00
}
2016-05-20 11:59:19 +00:00
return " otpauth:// " + type + " / " + label + " ? " + parameters ;
2016-01-29 10:00:28 +00:00
} catch ( UnsupportedEncodingException e ) {
throw new RuntimeException ( e ) ;
}
2015-08-06 00:39:47 +00:00
}
2017-12-15 13:25:21 +00:00
public List < String > getSupportedApplications ( ) {
List < String > applications = new LinkedList < > ( ) ;
for ( OtpApp a : allApplications ) {
if ( a . supports ( this ) ) {
applications . add ( a . getName ( ) ) ;
}
}
return applications ;
}
public interface OtpApp {
String getName ( ) ;
boolean supports ( OTPPolicy policy ) ;
}
public static class GoogleAuthenticator implements OtpApp {
@Override
public String getName ( ) {
return " Google Authenticator " ;
}
@Override
public boolean supports ( OTPPolicy policy ) {
if ( policy . digits ! = 6 ) {
return false ;
}
if ( ! policy . getAlgorithm ( ) . equals ( " HmacSHA1 " ) ) {
return false ;
}
if ( policy . getType ( ) . equals ( " totp " ) & & policy . getPeriod ( ) ! = 30 ) {
return false ;
}
return true ;
}
}
public static class FreeOTP implements OtpApp {
@Override
public String getName ( ) {
return " FreeOTP " ;
}
@Override
public boolean supports ( OTPPolicy policy ) {
return true ;
}
}
2015-08-06 00:39:47 +00:00
}