• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.hybrid;
18 
19 import com.google.crypto.tink.AccessesPartialKey;
20 import com.google.crypto.tink.Key;
21 import com.google.crypto.tink.internal.EllipticCurvesUtil;
22 import com.google.crypto.tink.internal.OutputPrefixUtil;
23 import com.google.crypto.tink.subtle.EllipticCurves;
24 import com.google.crypto.tink.util.Bytes;
25 import com.google.errorprone.annotations.Immutable;
26 import com.google.errorprone.annotations.RestrictedApi;
27 import java.security.GeneralSecurityException;
28 import java.security.spec.ECPoint;
29 import java.security.spec.EllipticCurve;
30 import java.util.Objects;
31 import javax.annotation.Nullable;
32 
33 /** Representation of the encryption function for an HPKE hybrid encryption primitive. */
34 @Immutable
35 public final class HpkePublicKey extends HybridPublicKey {
36   private final HpkeParameters parameters;
37   private final Bytes publicKeyBytes;
38   private final Bytes outputPrefix;
39   @Nullable private final Integer idRequirement;
40 
HpkePublicKey( HpkeParameters parameters, Bytes publicKeyBytes, Bytes outputPrefix, @Nullable Integer idRequirement)41   private HpkePublicKey(
42       HpkeParameters parameters,
43       Bytes publicKeyBytes,
44       Bytes outputPrefix,
45       @Nullable Integer idRequirement) {
46     this.parameters = parameters;
47     this.publicKeyBytes = publicKeyBytes;
48     this.outputPrefix = outputPrefix;
49     this.idRequirement = idRequirement;
50   }
51 
validateIdRequirement( HpkeParameters.Variant variant, @Nullable Integer idRequirement)52   private static void validateIdRequirement(
53       HpkeParameters.Variant variant, @Nullable Integer idRequirement)
54       throws GeneralSecurityException {
55     if (!variant.equals(HpkeParameters.Variant.NO_PREFIX) && idRequirement == null) {
56       throw new GeneralSecurityException(
57           "'idRequirement' must be non-null for " + variant + " variant.");
58     }
59     if (variant.equals(HpkeParameters.Variant.NO_PREFIX) && idRequirement != null) {
60       throw new GeneralSecurityException("'idRequirement' must be null for NO_PREFIX variant.");
61     }
62   }
63 
validatePublicKeyByteLength(HpkeParameters.KemId kemId, Bytes publicKeyBytes)64   private static void validatePublicKeyByteLength(HpkeParameters.KemId kemId, Bytes publicKeyBytes)
65       throws GeneralSecurityException {
66     // Key lengths from 'Npk' column in https://www.rfc-editor.org/rfc/rfc9180.html#table-2.
67     int keyLengthInBytes = publicKeyBytes.size();
68     String parameterizedErrorMessage =
69         "Encoded public key byte length for " + kemId + " must be %d, not " + keyLengthInBytes;
70     if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
71       if (keyLengthInBytes != 65) {
72         throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 65));
73       }
74       return;
75     }
76     if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
77       if (keyLengthInBytes != 97) {
78         throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 97));
79       }
80       return;
81     }
82     if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
83       if (keyLengthInBytes != 133) {
84         throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 133));
85       }
86       return;
87     }
88     if (kemId == HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256) {
89       if (keyLengthInBytes != 32) {
90         throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 32));
91       }
92       return;
93     }
94     throw new GeneralSecurityException("Unable to validate public key length for " + kemId);
95   }
96 
isNistKem(HpkeParameters.KemId kemId)97   private static boolean isNistKem(HpkeParameters.KemId kemId) {
98     return kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256
99         || kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384
100         || kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512;
101   }
102 
getNistCurve(HpkeParameters.KemId kemId)103   private static EllipticCurve getNistCurve(HpkeParameters.KemId kemId) {
104     if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
105       return EllipticCurves.getNistP256Params().getCurve();
106     }
107     if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
108       return EllipticCurves.getNistP384Params().getCurve();
109     }
110     if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
111       return EllipticCurves.getNistP521Params().getCurve();
112     }
113     throw new IllegalArgumentException("Unable to determine NIST curve type for " + kemId);
114   }
115 
validatePublicKeyOnCurve(HpkeParameters.KemId kemId, Bytes publicKeyBytes)116   private static void validatePublicKeyOnCurve(HpkeParameters.KemId kemId, Bytes publicKeyBytes)
117       throws GeneralSecurityException {
118     if (!isNistKem(kemId)) {
119       return;
120     }
121     EllipticCurve curve = getNistCurve(kemId);
122     ECPoint point =
123         EllipticCurves.pointDecode(
124             curve, EllipticCurves.PointFormatType.UNCOMPRESSED, publicKeyBytes.toByteArray());
125     EllipticCurvesUtil.checkPointOnCurve(point, curve);
126   }
127 
128   /**
129    * Validate public key according to https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.4.
130    *
131    * <p>Specifically, validate public key lengths and NIST KEM public key values according to
132    * Section 5.6.2.3.4 of https://doi.org/10.6028/nist.sp.800-56ar3.
133    */
validatePublicKey(HpkeParameters.KemId kemId, Bytes publicKeyBytes)134   private static void validatePublicKey(HpkeParameters.KemId kemId, Bytes publicKeyBytes)
135       throws GeneralSecurityException {
136     validatePublicKeyByteLength(kemId, publicKeyBytes);
137     validatePublicKeyOnCurve(kemId, publicKeyBytes);
138   }
139 
createOutputPrefix( HpkeParameters.Variant variant, @Nullable Integer idRequirement)140   private static Bytes createOutputPrefix(
141       HpkeParameters.Variant variant, @Nullable Integer idRequirement) {
142     if (variant == HpkeParameters.Variant.NO_PREFIX) {
143       return OutputPrefixUtil.EMPTY_PREFIX;
144     }
145     if (idRequirement == null) {
146       throw new IllegalStateException(
147           "idRequirement must be non-null for HpkeParameters.Variant " + variant);
148     }
149     if (variant == HpkeParameters.Variant.CRUNCHY) {
150       return OutputPrefixUtil.getLegacyOutputPrefix(idRequirement);
151     }
152     if (variant == HpkeParameters.Variant.TINK) {
153       return OutputPrefixUtil.getTinkOutputPrefix(idRequirement);
154     }
155     throw new IllegalStateException("Unknown HpkeParameters.Variant: " + variant);
156   }
157 
158   /**
159    * Creates a new HPKE public key.
160    *
161    * @param parameters HPKE parameters for the public key
162    * @param publicKeyBytes Public key encoded according to
163    *     https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.1
164    * @param idRequirement Key id requirement, which must be null for {@code NO_PREFIX} variant and
165    *     non-null for all other variants
166    */
167   @RestrictedApi(
168       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
169       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
170       allowedOnPath = ".*Test\\.java",
171       allowlistAnnotations = {AccessesPartialKey.class})
create( HpkeParameters parameters, Bytes publicKeyBytes, @Nullable Integer idRequirement)172   public static HpkePublicKey create(
173       HpkeParameters parameters, Bytes publicKeyBytes, @Nullable Integer idRequirement)
174       throws GeneralSecurityException {
175     validateIdRequirement(parameters.getVariant(), idRequirement);
176     validatePublicKey(parameters.getKemId(), publicKeyBytes);
177     Bytes prefix = createOutputPrefix(parameters.getVariant(), idRequirement);
178     return new HpkePublicKey(parameters, publicKeyBytes, prefix, idRequirement);
179   }
180 
181   @RestrictedApi(
182       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
183       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
184       allowedOnPath = ".*Test\\.java",
185       allowlistAnnotations = {AccessesPartialKey.class})
getPublicKeyBytes()186   public Bytes getPublicKeyBytes() {
187     return publicKeyBytes;
188   }
189 
190   @Override
getOutputPrefix()191   public Bytes getOutputPrefix() {
192     return outputPrefix;
193   }
194 
195   @Override
getParameters()196   public HpkeParameters getParameters() {
197     return parameters;
198   }
199 
200   @Override
201   @Nullable
getIdRequirementOrNull()202   public Integer getIdRequirementOrNull() {
203     return idRequirement;
204   }
205 
206   @Override
equalsKey(Key o)207   public boolean equalsKey(Key o) {
208     if (!(o instanceof HpkePublicKey)) {
209       return false;
210     }
211     HpkePublicKey other = (HpkePublicKey) o;
212     // Since outputPrefix is a function of parameters, we can ignore it here.
213     return parameters.equals(other.parameters)
214         && publicKeyBytes.equals(other.publicKeyBytes)
215         && Objects.equals(idRequirement, other.idRequirement);
216   }
217 }
218