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

com.impossibl.postgres.protocol.sasl.scram.ScramMechanisms Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017, OnGres.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */


package com.impossibl.postgres.protocol.sasl.scram;


import com.impossibl.postgres.protocol.sasl.scram.stringprep.StringPreparation;
import com.impossibl.postgres.protocol.sasl.scram.util.CryptoUtil;

import static com.impossibl.postgres.protocol.sasl.scram.util.Preconditions.checkNotNull;
import static com.impossibl.postgres.protocol.sasl.scram.util.Preconditions.gt0;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

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

/**
 * SCRAM Mechanisms supported by this library.
 * At least, SCRAM-SHA-1 and SCRAM-SHA-256 are provided, since both the hash and the HMAC implementations
 * are provided by the Java JDK version 6 or greater.
 *
 * {@link MessageDigest}: "Every implementation of the Java platform is required to support the
 * following standard MessageDigest algorithms: MD5, SHA-1, SHA-256".
 *
 * {@link Mac}: "Every implementation of the Java platform is required to support the following
 * standard Mac algorithms: HmacMD5, HmacSHA1, HmacSHA256".
 *
 * @see 
 *      SASL SCRAM Family Mechanisms
 */
public enum ScramMechanisms implements ScramMechanism {
  SCRAM_SHA_1("SHA-1", "SHA-1", 160, "HmacSHA1", false),
  SCRAM_SHA_1_PLUS("SHA-1", "SHA-1", 160, "HmacSHA1", true),
  SCRAM_SHA_256("SHA-256", "SHA-256", 256, "HmacSHA256", false),
  SCRAM_SHA_256_PLUS("SHA-256", "SHA-256", 256, "HmacSHA256", true);

  private static final String NAME_PREFIX = "SCRAM-";
  private static final String CHANNEL_BINDING_SUFFIX = "-PLUS";
  private static final String PBKDF2_ALGORITHM_PREFIX = "PBKDF2With";
  private static final Map BY_NAME_MAPPING = valuesAsMap();

  private final String mechanismName;
  private final String hashAlgorithmName;
  private final int keyLength;
  private final String hmacAlgorithmName;
  private final boolean channelBinding;

  ScramMechanisms(String name, String hashAlgorithmName, int keyLength,
                  String hmacAlgorithmName, boolean channelBinding) {
    this.mechanismName = NAME_PREFIX + checkNotNull(name, "name") + (channelBinding ? CHANNEL_BINDING_SUFFIX : "");
    this.hashAlgorithmName = checkNotNull(hashAlgorithmName, "hashAlgorithmName");
    this.keyLength = gt0(keyLength, "keyLength");
    this.hmacAlgorithmName = checkNotNull(hmacAlgorithmName, "hmacAlgorithmName");
    this.channelBinding = channelBinding;
  }

  /**
   * Method that returns the name of the hash algorithm.
   * It is protected since should be of no interest for direct users.
   * The instance is supposed to provide abstractions over the algorithm names,
   * and are not meant to be directly exposed.
   * @return The name of the hash algorithm
   */
  protected String getHashAlgorithmName() {
    return hashAlgorithmName;
  }

  /**
   * Method that returns the name of the HMAC algorithm.
   * It is protected since should be of no interest for direct users.
   * The instance is supposed to provide abstractions over the algorithm names,
   * and are not meant to be directly exposed.
   * @return The name of the HMAC algorithm
   */
  protected String getHmacAlgorithmName() {
    return hmacAlgorithmName;
  }

  @Override
  public String getName() {
    return mechanismName;
  }

  @Override
  public boolean requiresChannelBinding() {
    return channelBinding;
  }

  @Override
  public int algorithmKeyLength() {
    return keyLength;
  }

  @Override
  public byte[] digest(byte[] message) {
    try {
      return MessageDigest.getInstance(hashAlgorithmName).digest(message);
    }
    catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("Algorithm " + hashAlgorithmName + " not present in current JVM");
    }
  }

  @Override
  public byte[] hmac(byte[] key, byte[] message) {
    try {
      return CryptoUtil.hmac(new SecretKeySpec(key, hmacAlgorithmName), Mac.getInstance(hmacAlgorithmName), message);
    }
    catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("MAC Algorithm " + hmacAlgorithmName + " not present in current JVM");
    }
  }

  @Override
  public byte[] saltedPassword(StringPreparation stringPreparation, String password, byte[] salt, int iterations) {
    String keyFactoryAlgorithmName = PBKDF2_ALGORITHM_PREFIX + hmacAlgorithmName;
    char[] normalizedString = stringPreparation.normalize(password).toCharArray();
    try {
      return CryptoUtil.hi(
          SecretKeyFactory.getInstance(keyFactoryAlgorithmName),
          algorithmKeyLength(),
          normalizedString,
          salt,
          iterations
      );
    }
    catch (NoSuchAlgorithmException e) {
      throw new RuntimeException("PBKDF Algorithm " + keyFactoryAlgorithmName + " not present in current JVM");
    }
  }

  /**
   * Gets a SCRAM mechanism, given its standard IANA name.
   * @param name The standard IANA full name of the mechanism.
   * @return An Optional instance that contains the ScramMechanism if it was found, or empty otherwise.
   */
  public static ScramMechanisms byName(String name) {
    checkNotNull(name, "name");

    return BY_NAME_MAPPING.get(name);
  }

  private static Map valuesAsMap() {
    Map mapScramMechanisms = new HashMap<>(values().length);
    for (ScramMechanisms scramMechanisms : values()) {
      mapScramMechanisms.put(scramMechanisms.getName(), scramMechanisms);
    }
    return mapScramMechanisms;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy