All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.predic8.membrane.core.interceptor.authentication.session.totp.OtpProvider Maven / Gradle / Ivy

There is a newer version: 5.7.3
Show newest version
/*
 * Copyright 2010 Google Inc. All Rights Reserved.
 *
 * 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.
 */

package com.predic8.membrane.core.interceptor.authentication.session.totp;

import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.predic8.membrane.core.interceptor.authentication.session.totp.PasscodeGenerator.Signer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class containing implementation of HOTP/TOTP. Generates OTP codes for one or
 * more accounts.
 *
 * Source: http://code.google.com/p/google-authenticator
 * License: ASL 2.0
 *
 * @author Steve Weis ([email protected])
 * @author Cem Paya ([email protected])
 */
public class OtpProvider {
	private static Logger log = LoggerFactory.getLogger(OtpProvider.class.getName());

	private static final int PIN_LENGTH = 6; // HOTP or TOTP

	public String getNextCode(String secret, long time) {
		return computePin(secret, mTotpCounter.getValueAtTime(time / 1000));
	}

	public OtpProvider() {
		this(DEFAULT_INTERVAL);
	}

	public OtpProvider(int interval) {
		mTotpCounter = new TotpCounter(interval);
	}

	private static byte[] decodeKey(String secret) {
		return Base32String.decode(secret);
	}

	static Signer getSigningOracle(String secret) {
		try {
			byte[] keyBytes = decodeKey(secret);
			final Mac mac = Mac.getInstance("HMACSHA1");
			mac.init(new SecretKeySpec(keyBytes, ""));

			// Create a signer object out of the standard Java MAC
			// implementation.
			return new Signer() {
				@Override
				public byte[] sign(byte[] data) {
					return mac.doFinal(data);
				}
			};
		} catch (NoSuchAlgorithmException error) {
			log.error("", error);
		} catch (InvalidKeyException error) {
			log.error("", error);
		}

		return null;
	}

	/**
	 * Computes the one-time PIN given the secret key.
	 *
	 * @param secret
	 *            the secret key
	 * @param otp_state
	 *            current token state (counter or time-interval)
	 * @param challenge
	 *            optional challenge bytes to include when computing passcode.
	 * @return the PIN
	 */
	private String computePin(String secret, long otp_state) {
		if (secret == null || secret.length() == 0) {
			throw new RuntimeException("Null or empty secret");
		}

		try {
			Signer signer = getSigningOracle(secret);
			PasscodeGenerator pcg = new PasscodeGenerator(signer, PIN_LENGTH);

			return pcg.generateResponseCode(otp_state);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException("Crypto failure", e);
		}
	}

	/** Default passcode timeout period (in seconds) */
	public static final int DEFAULT_INTERVAL = 30;

	/** Counter for time-based OTPs (TOTP). */
	private final TotpCounter mTotpCounter;

	public boolean verifyCode(String secret, long time, String code, int window) {
		long t = mTotpCounter.getValueAtTime(time / 1000);
		for (int i = -window; i <= window; i++)
			if (code.equals(computePin(secret, t + i)))
				return true;
		return false;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy