package org.multibit.mbm.auth.hmac; import com.google.common.base.Optional; import com.yammer.dropwizard.auth.AuthenticationException; import com.yammer.dropwizard.auth.Authenticator; import org.multibit.mbm.db.dao.UserDao; import org.multibit.mbm.core.model.User; import org.springframework.stereotype.Component; import javax.annotation.Resource; /** * <p>Authenticator to provide the following to application:</p> * <ul> * <li>Verifies the provided credentials are valid</li> * </ul> * * @since 0.0.1 */ @Component public class HmacServerAuthenticator implements Authenticator<HmacServerCredentials, User> { @Resource(name="hibernateUserDao") private UserDao userDao; @Override public Optional<User> authenticate(HmacServerCredentials credentials) throws AuthenticationException { // Get the User referred to by the API key Optional<User> user = userDao.getByApiKey(credentials.getApiKey()); if (!user.isPresent()) { return Optional.absent(); } // Check that their authorities match their credentials if (!user.get().hasAllAuthorities(credentials.getRequiredAuthorities())) { return Optional.absent(); } // Locate their secret key String secretKey = user.get().getSecretKey(); String computedSignature = new String( HmacUtils.computeSignature( credentials.getAlgorithm(), credentials.getCanonicalRepresentation().getBytes(), secretKey.getBytes())); // Avoid timing attacks by verifying every byte every time if (isEqual(computedSignature.getBytes(), credentials.getDigest().getBytes())) { return user; } return Optional.absent(); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } /** * Performs a byte array comparison with a constant time * * @param a A byte array * @param b Another byte array * @return True if the byte array have equal contents */ public static boolean isEqual(byte[] a, byte[] b) { if (a.length != b.length) { return false; } int result = 0; for (int i = 0; i < a.length; i++) { result |= a[i] ^ b[i]; } return result == 0; } }