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

com.google.crypto.tink.jwt.JwtHmacKeyManager Maven / Gradle / Ivy

// Copyright 2020 Google LLC
//
// 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.google.crypto.tink.jwt;

import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug;
import static java.nio.charset.StandardCharsets.US_ASCII;

import com.google.crypto.tink.AccessesPartialKey;
import com.google.crypto.tink.KeyManager;
import com.google.crypto.tink.KeyTemplate;
import com.google.crypto.tink.Mac;
import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.config.internal.TinkFipsUtil;
import com.google.crypto.tink.internal.KeyManagerRegistry;
import com.google.crypto.tink.internal.LegacyKeyManagerImpl;
import com.google.crypto.tink.internal.MutableKeyCreationRegistry;
import com.google.crypto.tink.internal.MutableParametersRegistry;
import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
import com.google.crypto.tink.internal.PrimitiveConstructor;
import com.google.crypto.tink.mac.HmacKey;
import com.google.crypto.tink.mac.HmacParameters;
import com.google.crypto.tink.proto.KeyData.KeyMaterialType;
import com.google.crypto.tink.subtle.PrfMac;
import com.google.crypto.tink.util.SecretBytes;
import com.google.errorprone.annotations.Immutable;
import com.google.gson.JsonObject;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * This key manager generates new {@code JwtHmacKey} keys and produces new instances of {@link
 * JwtHmac}.
 */
public final class JwtHmacKeyManager {
  @Immutable
  private static final class JwtHmac implements JwtMac {
    @SuppressWarnings("Immutable") // Mac objects obtained from PrfMac.create are immutable.
    private final Mac mac;

    private final String algorithm;
    private final JwtHmacKey jwtHmacKey;

    private JwtHmac(Mac plainMac, JwtHmacKey jwtHmacKey) {
      this.algorithm = jwtHmacKey.getParameters().getAlgorithm().getStandardName();
      this.mac = plainMac;
      this.jwtHmacKey = jwtHmacKey;
    }

    @Override
    public String computeMacAndEncode(RawJwt rawJwt) throws GeneralSecurityException {
      String unsignedCompact =
          JwtFormat.createUnsignedCompact(algorithm, jwtHmacKey.getKid(), rawJwt);
      return JwtFormat.createSignedCompact(
          unsignedCompact, mac.computeMac(unsignedCompact.getBytes(US_ASCII)));
    }

    @Override
    public VerifiedJwt verifyMacAndDecode(String compact, JwtValidator validator)
        throws GeneralSecurityException {
      JwtFormat.Parts parts = JwtFormat.splitSignedCompact(compact);
      mac.verifyMac(parts.signatureOrMac, parts.unsignedCompact.getBytes(US_ASCII));
      JsonObject parsedHeader = JsonUtil.parseJson(parts.header);
      JwtFormat.validateHeader(
          parsedHeader,
          jwtHmacKey.getParameters().getAlgorithm().getStandardName(),
          jwtHmacKey.getKid(),
          jwtHmacKey.getParameters().allowKidAbsent());
      RawJwt token = RawJwt.fromJsonPayload(JwtFormat.getTypeHeader(parsedHeader), parts.payload);
      return validator.validate(token);
    }
  }

  private static void validate(JwtHmacParameters parameters) throws GeneralSecurityException {
    int minKeySize = Integer.MAX_VALUE;
    if (parameters.getAlgorithm().equals(JwtHmacParameters.Algorithm.HS256)) {
      minKeySize = 32;
    }
    if (parameters.getAlgorithm().equals(JwtHmacParameters.Algorithm.HS384)) {
      minKeySize = 48;
    }
    if (parameters.getAlgorithm().equals(JwtHmacParameters.Algorithm.HS512)) {
      minKeySize = 64;
    }
    if (parameters.getKeySizeBytes() < minKeySize) {
      throw new GeneralSecurityException("Key size must be at least " + minKeySize);
    }
  }

  private static int getTagLength(JwtHmacParameters.Algorithm algorithm)
      throws GeneralSecurityException {
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS256)) {
      return 32;
    }
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS384)) {
      return 48;
    }
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS512)) {
      return 64;
    }
    throw new GeneralSecurityException("Unsupported algorithm: " + algorithm);
  }

  private static HmacParameters.HashType getHmacHashType(JwtHmacParameters.Algorithm algorithm)
      throws GeneralSecurityException {
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS256)) {
      return HmacParameters.HashType.SHA256;
    }
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS384)) {
      return HmacParameters.HashType.SHA384;
    }
    if (algorithm.equals(JwtHmacParameters.Algorithm.HS512)) {
      return HmacParameters.HashType.SHA512;
    }
    throw new GeneralSecurityException("Unsupported algorithm: " + algorithm);
  }

  private static final KeyManager legacyKeyManager =
      LegacyKeyManagerImpl.create(
          "type.googleapis.com/google.crypto.tink.JwtHmacKey",
          Void.class,
          KeyMaterialType.SYMMETRIC,
          com.google.crypto.tink.proto.JwtHmacKey.parser());

  @AccessesPartialKey
  private static JwtMac createFullJwtHmac(JwtHmacKey key) throws GeneralSecurityException {
    validate(key.getParameters());
    HmacKey hmacKey =
        HmacKey.builder()
            .setParameters(
                HmacParameters.builder()
                    .setKeySizeBytes(key.getParameters().getKeySizeBytes())
                    .setHashType(getHmacHashType(key.getParameters().getAlgorithm()))
                    .setTagSizeBytes(getTagLength(key.getParameters().getAlgorithm()))
                    .build())
            .setKeyBytes(key.getKeyBytes())
            .build();
    return new JwtHmac(PrfMac.create(hmacKey), key);
  }

  private static final PrimitiveConstructor PRIMITIVE_CONSTRUCTOR =
      PrimitiveConstructor.create(
          JwtHmacKeyManager::createFullJwtHmac, JwtHmacKey.class, JwtMac.class);

  static String getKeyType() {
    return "type.googleapis.com/google.crypto.tink.JwtHmacKey";
  }

  @AccessesPartialKey
  private static JwtHmacKey createKey(JwtHmacParameters parameters, @Nullable Integer idRequirement)
      throws GeneralSecurityException {
    validate(parameters);
    JwtHmacKey.Builder builder =
        JwtHmacKey.builder()
            .setParameters(parameters)
            .setKeyBytes(SecretBytes.randomBytes(parameters.getKeySizeBytes()));
    if (parameters.hasIdRequirement()) {
      if (idRequirement == null) {
        throw new GeneralSecurityException(
            "Cannot create key without ID requirement with parameters with ID requirement");
      }
      builder.setIdRequirement(idRequirement);
    }
    return builder.build();
  }

  private static final MutableKeyCreationRegistry.KeyCreator KEY_CREATOR =
      JwtHmacKeyManager::createKey;

  /**
   * List of default templates to generate tokens with algorithms "HS256", "HS384" or "HS512". Use
   * the template with the "_RAW" suffix if you want to generate tokens without a "kid" header.
   */
  private static Map namedParameters() throws GeneralSecurityException {
        Map result = new HashMap<>();
        result.put(
            "JWT_HS256_RAW",
            JwtHmacParameters.builder()
                .setKeySizeBytes(32)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS256)
                .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                .build());
        result.put(
            "JWT_HS256",
            JwtHmacParameters.builder()
                .setKeySizeBytes(32)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS256)
                .setKidStrategy(JwtHmacParameters.KidStrategy.BASE64_ENCODED_KEY_ID)
                .build());
        result.put(
            "JWT_HS384_RAW",
            JwtHmacParameters.builder()
                .setKeySizeBytes(48)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS384)
                .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                .build());
        result.put(
            "JWT_HS384",
            JwtHmacParameters.builder()
                .setKeySizeBytes(48)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS384)
                .setKidStrategy(JwtHmacParameters.KidStrategy.BASE64_ENCODED_KEY_ID)
                .build());
        result.put(
            "JWT_HS512_RAW",
            JwtHmacParameters.builder()
                .setKeySizeBytes(64)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS512)
                .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                .build());
        result.put(
            "JWT_HS512",
            JwtHmacParameters.builder()
                .setKeySizeBytes(64)
                .setAlgorithm(JwtHmacParameters.Algorithm.HS512)
                .setKidStrategy(JwtHmacParameters.KidStrategy.BASE64_ENCODED_KEY_ID)
                .build());
        return Collections.unmodifiableMap(result);
  }

  public TinkFipsUtil.AlgorithmFipsCompatibility fipsStatus() {
    return FIPS;
  }

  private static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS =
      TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;

  public static void register(boolean newKeyAllowed) throws GeneralSecurityException {
    if (!FIPS.isCompatible()) {
      throw new GeneralSecurityException(
          "Can not use HMAC in FIPS-mode, as BoringCrypto module is not available.");
    }
    JwtHmacProtoSerialization.register();
    MutableKeyCreationRegistry.globalInstance().add(KEY_CREATOR, JwtHmacParameters.class);
    MutablePrimitiveRegistry.globalInstance().registerPrimitiveConstructor(PRIMITIVE_CONSTRUCTOR);
    MutableParametersRegistry.globalInstance().putAll(namedParameters());
    KeyManagerRegistry.globalInstance()
        .registerKeyManagerWithFipsCompatibility(legacyKeyManager, FIPS, newKeyAllowed);
  }

  /** Returns a {@link KeyTemplate} that generates new instances of HS256 256-bit keys. */
  public static final KeyTemplate hs256Template() {
    return exceptionIsBug(
        () ->
            KeyTemplate.createFrom(
                JwtHmacParameters.builder()
                    .setKeySizeBytes(32)
                    .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                    .setAlgorithm(JwtHmacParameters.Algorithm.HS256)
                    .build()));
  }

  /** Returns a {@link KeyTemplate} that generates new instances of HS384 384-bit keys. */
  public static final KeyTemplate hs384Template() {
    return exceptionIsBug(
        () ->
            KeyTemplate.createFrom(
                JwtHmacParameters.builder()
                    .setKeySizeBytes(48)
                    .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                    .setAlgorithm(JwtHmacParameters.Algorithm.HS384)
                    .build()));
  }

  /** Returns a {@link KeyTemplate} that generates new instances of HS512 512-bit keys. */
  public static final KeyTemplate hs512Template() {
    return exceptionIsBug(
        () ->
            KeyTemplate.createFrom(
                JwtHmacParameters.builder()
                    .setKeySizeBytes(64)
                    .setKidStrategy(JwtHmacParameters.KidStrategy.IGNORED)
                    .setAlgorithm(JwtHmacParameters.Algorithm.HS512)
                    .build()));
  }

  private JwtHmacKeyManager() {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy