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

com.google.crypto.tink.hybrid.HpkePrivateKey Maven / Gradle / Ivy

Go to download

Tink is a small cryptographic library that provides a safe, simple, agile and fast way to accomplish some common cryptographic tasks.

The newest version!
// Copyright 2023 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.hybrid;

import com.google.crypto.tink.AccessesPartialKey;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.Key;
import com.google.crypto.tink.internal.BigIntegerEncoding;
import com.google.crypto.tink.internal.EllipticCurvesUtil;
import com.google.crypto.tink.subtle.EllipticCurves;
import com.google.crypto.tink.subtle.X25519;
import com.google.crypto.tink.util.SecretBytes;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.RestrictedApi;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/** Representation of the decryption function for an HPKE hybrid encryption primitive. */
@Immutable
public final class HpkePrivateKey extends HybridPrivateKey {
  private final HpkePublicKey publicKey;
  private final SecretBytes privateKeyBytes;

  private HpkePrivateKey(HpkePublicKey publicKey, SecretBytes privateKeyBytes) {
    this.publicKey = publicKey;
    this.privateKeyBytes = privateKeyBytes;
  }

  private static void validatePrivateKeyByteLength(
      HpkeParameters.KemId kemId, SecretBytes privateKeyBytes) throws GeneralSecurityException {
    // Key lengths from 'Nsk' column in https://www.rfc-editor.org/rfc/rfc9180.html#table-2.
    int keyLengthInBytes = privateKeyBytes.size();
    String parameterizedErrorMessage =
        "Encoded private key byte length for " + kemId + " must be %d, not " + keyLengthInBytes;
    if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
      if (keyLengthInBytes != 32) {
        throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 32));
      }
      return;
    }
    if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
      if (keyLengthInBytes != 48) {
        throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 48));
      }
      return;
    }
    if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
      if (keyLengthInBytes != 66) {
        throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 66));
      }
      return;
    }
    if (kemId == HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256) {
      if (keyLengthInBytes != 32) {
        throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 32));
      }
      return;
    }
    throw new GeneralSecurityException("Unable to validate private key length for " + kemId);
  }

  private static boolean isNistKem(HpkeParameters.KemId kemId) {
    return kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256
        || kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384
        || kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512;
  }

  private static ECParameterSpec getNistCurveParams(HpkeParameters.KemId kemId) {
    if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
      return EllipticCurves.getNistP256Params();
    }
    if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
      return EllipticCurves.getNistP384Params();
    }
    if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
      return EllipticCurves.getNistP521Params();
    }
    throw new IllegalArgumentException("Unable to determine NIST curve params for " + kemId);
  }

  /**
   * Confirms that the private key encoded as {@code privateKeyBytes} corresponds to the public key
   * encoded as {@code publicKeyBytes} given the HPKE {@code kemId}.
   */
  private static void validateKeyPair(
      HpkeParameters.KemId kemId, byte[] publicKeyBytes, byte[] privateKeyBytes)
      throws GeneralSecurityException {
    if (isNistKem(kemId)) {
      ECParameterSpec spec = getNistCurveParams(kemId);
      BigInteger order = spec.getOrder();
      BigInteger privateKey = BigIntegerEncoding.fromUnsignedBigEndianBytes(privateKeyBytes);
      if ((privateKey.signum() <= 0) || (privateKey.compareTo(order) >= 0)) {
        throw new GeneralSecurityException("Invalid private key.");
      }
      ECPoint expectedPoint = EllipticCurvesUtil.multiplyByGenerator(privateKey, spec);
      ECPoint publicPoint =
          EllipticCurves.pointDecode(
              spec.getCurve(), EllipticCurves.PointFormatType.UNCOMPRESSED, publicKeyBytes);
      if (!expectedPoint.equals(publicPoint)) {
        throw new GeneralSecurityException("Invalid private key for public key.");
      }
      return;
    }
    if (kemId == HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256) {
      byte[] expectedPublicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
      if (!Arrays.equals(expectedPublicKeyBytes, publicKeyBytes)) {
        throw new GeneralSecurityException("Invalid private key for public key.");
      }
      return;
    }
    throw new IllegalArgumentException("Unable to validate key pair for " + kemId);
  }

  /**
   * Creates a new HPKE private key.
   *
   * @param publicKey Corresponding HPKE public key for this private key
   * @param privateKeyBytes Private key encoded according to
   *     https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.2
   */
  @AccessesPartialKey
  @RestrictedApi(
      explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
      link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
      allowedOnPath = ".*Test\\.java",
      allowlistAnnotations = {AccessesPartialKey.class})
  public static HpkePrivateKey create(HpkePublicKey publicKey, SecretBytes privateKeyBytes)
      throws GeneralSecurityException {
    if (publicKey == null) {
      throw new GeneralSecurityException(
          "HPKE private key cannot be constructed without an HPKE public key");
    }
    if (privateKeyBytes == null) {
      throw new GeneralSecurityException("HPKE private key cannot be constructed without secret");
    }
    validatePrivateKeyByteLength(publicKey.getParameters().getKemId(), privateKeyBytes);
    validateKeyPair(
        publicKey.getParameters().getKemId(),
        publicKey.getPublicKeyBytes().toByteArray(),
        privateKeyBytes.toByteArray(InsecureSecretKeyAccess.get()));
    return new HpkePrivateKey(publicKey, privateKeyBytes);
  }

  @RestrictedApi(
      explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
      link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
      allowedOnPath = ".*Test\\.java",
      allowlistAnnotations = {AccessesPartialKey.class})
  public SecretBytes getPrivateKeyBytes() {
    return privateKeyBytes;
  }

  @Override
  public HpkeParameters getParameters() {
    return publicKey.getParameters();
  }

  @Override
  public HpkePublicKey getPublicKey() {
    return publicKey;
  }

  @Override
  public boolean equalsKey(Key o) {
    if (!(o instanceof HpkePrivateKey)) {
      return false;
    }
    HpkePrivateKey other = (HpkePrivateKey) o;
    return publicKey.equalsKey(other.publicKey)
        && privateKeyBytes.equalsSecretBytes(other.privateKeyBytes);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy