• 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 /** Represents the encryption function for an ECIES hybrid encryption primitive. */
34 @Immutable
35 public final class EciesPublicKey extends HybridPublicKey {
36   private final EciesParameters parameters;
37 
38   /** Exactly one of nistPublicPoint and x25519PublicPointBytes is non-null. */
39   @SuppressWarnings("Immutable") // ECPoint is immutable
40   @Nullable
41   private final ECPoint nistPublicPoint;
42 
43   /** Exactly one of nistPublicPoint and x25519PublicPointBytes is non-null. */
44   @Nullable private final Bytes x25519PublicPointBytes;
45 
46   private final Bytes outputPrefix;
47   @Nullable private final Integer idRequirement;
48 
EciesPublicKey( EciesParameters parameters, @Nullable ECPoint nistPublicPoint, @Nullable Bytes x25519PublicPointBytes, Bytes outputPrefix, @Nullable Integer idRequirement)49   private EciesPublicKey(
50       EciesParameters parameters,
51       @Nullable ECPoint nistPublicPoint,
52       @Nullable Bytes x25519PublicPointBytes,
53       Bytes outputPrefix,
54       @Nullable Integer idRequirement) {
55     this.parameters = parameters;
56     this.nistPublicPoint = nistPublicPoint;
57     this.x25519PublicPointBytes = x25519PublicPointBytes;
58     this.outputPrefix = outputPrefix;
59     this.idRequirement = idRequirement;
60   }
61 
validateIdRequirement( EciesParameters.Variant variant, @Nullable Integer idRequirement)62   private static void validateIdRequirement(
63       EciesParameters.Variant variant, @Nullable Integer idRequirement)
64       throws GeneralSecurityException {
65     if (!variant.equals(EciesParameters.Variant.NO_PREFIX) && idRequirement == null) {
66       throw new GeneralSecurityException(
67           "'idRequirement' must be non-null for " + variant + " variant.");
68     }
69     if (variant.equals(EciesParameters.Variant.NO_PREFIX) && idRequirement != null) {
70       throw new GeneralSecurityException("'idRequirement' must be null for NO_PREFIX variant.");
71     }
72   }
73 
getParameterSpecNistCurve(EciesParameters.CurveType curveType)74   private static EllipticCurve getParameterSpecNistCurve(EciesParameters.CurveType curveType) {
75     if (curveType == EciesParameters.CurveType.NIST_P256) {
76       return EllipticCurves.getNistP256Params().getCurve();
77     }
78     if (curveType == EciesParameters.CurveType.NIST_P384) {
79       return EllipticCurves.getNistP384Params().getCurve();
80     }
81     if (curveType == EciesParameters.CurveType.NIST_P521) {
82       return EllipticCurves.getNistP521Params().getCurve();
83     }
84     throw new IllegalArgumentException("Unable to determine NIST curve type for " + curveType);
85   }
86 
createOutputPrefix( EciesParameters.Variant variant, @Nullable Integer idRequirement)87   private static Bytes createOutputPrefix(
88       EciesParameters.Variant variant, @Nullable Integer idRequirement) {
89     if (variant == EciesParameters.Variant.NO_PREFIX) {
90       return OutputPrefixUtil.EMPTY_PREFIX;
91     }
92     if (idRequirement == null) {
93       throw new IllegalStateException(
94           "idRequirement must be non-null for EciesParameters.Variant: " + variant);
95     }
96     if (variant == EciesParameters.Variant.CRUNCHY) {
97       return OutputPrefixUtil.getLegacyOutputPrefix(idRequirement);
98     }
99     if (variant == EciesParameters.Variant.TINK) {
100       return OutputPrefixUtil.getTinkOutputPrefix(idRequirement);
101     }
102     throw new IllegalStateException("Unknown EciesParameters.Variant: " + variant);
103   }
104 
105   /**
106    * Creates a new ECIES public key using Curve25519.
107    *
108    * @param parameters ECIES parameters for the public key
109    * @param publicPointBytes public point coordinates in bytes.
110    * @param idRequirement key id requirement, which must be null for {@code NO_PREFIX} variant and
111    *     non-null for all other variants
112    */
113   @RestrictedApi(
114       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
115       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
116       allowedOnPath = ".*Test\\.java",
117       allowlistAnnotations = {AccessesPartialKey.class})
createForCurveX25519( EciesParameters parameters, Bytes publicPointBytes, @Nullable Integer idRequirement)118   public static EciesPublicKey createForCurveX25519(
119       EciesParameters parameters, Bytes publicPointBytes, @Nullable Integer idRequirement)
120       throws GeneralSecurityException {
121     if (!parameters.getCurveType().equals(EciesParameters.CurveType.X25519)) {
122       throw new GeneralSecurityException(
123           "createForCurveX25519 may only be called with parameters for curve X25519");
124     }
125     validateIdRequirement(parameters.getVariant(), idRequirement);
126     if (publicPointBytes.size() != 32) {
127       throw new GeneralSecurityException(
128           "Encoded public point byte length for X25519 curve must be 32");
129     }
130 
131     Bytes prefix = createOutputPrefix(parameters.getVariant(), idRequirement);
132 
133     return new EciesPublicKey(parameters, null, publicPointBytes, prefix, idRequirement);
134   }
135 
136   /**
137    * Creates a new ECIES public key using a NIST Curve.
138    *
139    * @param parameters ECIES parameters for the public key
140    * @param publicPoint public point as a {@code ECPoint}.
141    * @param idRequirement key id requirement, which must be null for {@code NO_PREFIX} variant and
142    *     non-null for all other variants
143    */
createForNistCurve( EciesParameters parameters, ECPoint publicPoint, @Nullable Integer idRequirement)144   public static EciesPublicKey createForNistCurve(
145       EciesParameters parameters, ECPoint publicPoint, @Nullable Integer idRequirement)
146       throws GeneralSecurityException {
147     if (parameters.getCurveType().equals(EciesParameters.CurveType.X25519)) {
148       throw new GeneralSecurityException(
149           "createForNistCurve may only be called with parameters for NIST curve");
150     }
151     validateIdRequirement(parameters.getVariant(), idRequirement);
152     EllipticCurvesUtil.checkPointOnCurve(
153         publicPoint, getParameterSpecNistCurve(parameters.getCurveType()));
154 
155     Bytes prefix = createOutputPrefix(parameters.getVariant(), idRequirement);
156 
157     return new EciesPublicKey(parameters, publicPoint, null, prefix, idRequirement);
158   }
159 
160   /**
161    * Returns the underlying public point if the curve is a NIST curve.
162    *
163    * <p>Returns null if the curve used for this key is not a NIST curve.
164    */
165   @RestrictedApi(
166       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
167       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
168       allowedOnPath = ".*Test\\.java",
169       allowlistAnnotations = {AccessesPartialKey.class})
170   @Nullable
getNistCurvePoint()171   public ECPoint getNistCurvePoint() {
172     return nistPublicPoint;
173   }
174 
175   /**
176    * Returns the underlying public point as EC Point in case the curve is a NIST curve.
177    *
178    * <p>Returns null for X25519 curves.
179    */
180   @RestrictedApi(
181       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
182       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
183       allowedOnPath = ".*Test\\.java",
184       allowlistAnnotations = {AccessesPartialKey.class})
185   @Nullable
getX25519CurvePointBytes()186   public Bytes getX25519CurvePointBytes() {
187     return x25519PublicPointBytes;
188   }
189 
190   @Override
getOutputPrefix()191   public Bytes getOutputPrefix() {
192     return outputPrefix;
193   }
194 
195   @Override
getParameters()196   public EciesParameters 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 EciesPublicKey)) {
209       return false;
210     }
211     EciesPublicKey other = (EciesPublicKey) o;
212     // Since outputPrefix is a function of parameters, we can ignore it here.
213     return parameters.equals(other.parameters)
214         && Objects.equals(x25519PublicPointBytes, other.x25519PublicPointBytes)
215         && Objects.equals(nistPublicPoint, other.nistPublicPoint)
216         && Objects.equals(idRequirement, other.idRequirement);
217   }
218 }
219