• 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 static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug;
20 
21 import com.google.crypto.tink.Parameters;
22 import com.google.crypto.tink.aead.AesCtrHmacAeadParameters;
23 import com.google.crypto.tink.aead.AesGcmParameters;
24 import com.google.crypto.tink.aead.XChaCha20Poly1305Parameters;
25 import com.google.crypto.tink.daead.AesSivParameters;
26 import com.google.crypto.tink.util.Bytes;
27 import com.google.errorprone.annotations.CanIgnoreReturnValue;
28 import com.google.errorprone.annotations.Immutable;
29 import java.security.GeneralSecurityException;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.Objects;
33 import java.util.Set;
34 import javax.annotation.Nullable;
35 
36 /**
37  * Parameters for an ECIES primitive with HKDF and AEAD encryption.
38  *
39  * <p>This API follows loosely ECIES ISO 18033-2 standard (Elliptic Curve Integrated Encryption
40  * Scheme, see http://www.shoup.net/iso/std6.pdf), but with some differences:
41  *
42  * <ul>
43  *   <li>use of HKDF key derivation function (instead of KDF1 and KDF2) enabling the use of optional
44  *       parameters to the key derivation function, which strenghten the overall security and allow
45  *       for binding the key material to application-specific information (see RFC 5869)
46  *   <li>use of modern AEAD/Deterministic AEAD schemes rather than "manual composition" of symmetric
47  *       encryption with message authentication codes (as in DEM1, DEM2, and DEM3 schemes of ISO
48  *       18033-2)
49  * </ul>
50  */
51 public final class EciesParameters extends HybridParameters {
listAcceptedDemParameters()52   private static Set<Parameters> listAcceptedDemParameters() throws GeneralSecurityException {
53     HashSet<Parameters> acceptedDemParameters = new HashSet<>();
54     // AES128_GCM_RAW
55     acceptedDemParameters.add(
56         AesGcmParameters.builder()
57             .setIvSizeBytes(12)
58             .setKeySizeBytes(16)
59             .setTagSizeBytes(16)
60             .setVariant(AesGcmParameters.Variant.NO_PREFIX)
61             .build());
62     // AES256_GCM_RAW
63     acceptedDemParameters.add(
64         AesGcmParameters.builder()
65             .setIvSizeBytes(12)
66             .setKeySizeBytes(32)
67             .setTagSizeBytes(16)
68             .setVariant(AesGcmParameters.Variant.NO_PREFIX)
69             .build());
70     // AES128_CTR_HMAC_SHA256_RAW
71     acceptedDemParameters.add(
72         AesCtrHmacAeadParameters.builder()
73             .setAesKeySizeBytes(16)
74             .setHmacKeySizeBytes(32)
75             .setTagSizeBytes(16)
76             .setIvSizeBytes(16)
77             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
78             .setVariant(AesCtrHmacAeadParameters.Variant.NO_PREFIX)
79             .build());
80     // AES256_CTR_HMAC_SHA256_RAW
81     acceptedDemParameters.add(
82         AesCtrHmacAeadParameters.builder()
83             .setAesKeySizeBytes(32)
84             .setHmacKeySizeBytes(32)
85             .setTagSizeBytes(32)
86             .setIvSizeBytes(16)
87             .setHashType(AesCtrHmacAeadParameters.HashType.SHA256)
88             .setVariant(AesCtrHmacAeadParameters.Variant.NO_PREFIX)
89             .build());
90     // XCHACHA20_POLY1305_RAW
91     acceptedDemParameters.add(XChaCha20Poly1305Parameters.create());
92     // AES256_SIV_RAW
93     acceptedDemParameters.add(
94         AesSivParameters.builder()
95             .setKeySizeBytes(64)
96             .setVariant(AesSivParameters.Variant.NO_PREFIX)
97             .build());
98     return Collections.unmodifiableSet(acceptedDemParameters);
99   }
100 
101   private static final Set<Parameters> acceptedDemParameters =
102       exceptionIsBug(() -> listAcceptedDemParameters());
103 
104   /** Description of the output prefix prepended to the ciphertext. */
105   @Immutable
106   public static final class Variant {
107     /** Leading 0x01-byte followed by 4-byte key id (big endian format). */
108     public static final Variant TINK = new Variant("TINK");
109 
110     /** Leading 0x00-byte followed by 4-byte key id (big endian format). */
111     public static final Variant CRUNCHY = new Variant("CRUNCHY");
112 
113     /** Empty prefix. */
114     public static final Variant NO_PREFIX = new Variant("NO_PREFIX");
115 
116     private final String name;
117 
Variant(String name)118     private Variant(String name) {
119       this.name = name;
120     }
121 
122     @Override
toString()123     public String toString() {
124       return name;
125     }
126   }
127 
128   /** The elliptic curve type used for the KEM. */
129   @Immutable
130   public static final class CurveType {
131     public static final CurveType NIST_P256 = new CurveType("NIST_P256");
132     public static final CurveType NIST_P384 = new CurveType("NIST_P384");
133     public static final CurveType NIST_P521 = new CurveType("NIST_P521");
134     public static final CurveType X25519 = new CurveType("X25519");
135 
136     private final String name;
137 
CurveType(String name)138     private CurveType(String name) {
139       this.name = name;
140     }
141 
142     @Override
toString()143     public String toString() {
144       return name;
145     }
146   }
147 
148   /** The Hash algorithm used for the KEM. */
149   @Immutable
150   public static final class HashType {
151     public static final HashType SHA1 = new HashType("SHA1");
152     public static final HashType SHA224 = new HashType("SHA224");
153     public static final HashType SHA256 = new HashType("SHA256");
154     public static final HashType SHA384 = new HashType("SHA384");
155     public static final HashType SHA512 = new HashType("SHA512");
156 
157     private final String name;
158 
HashType(String name)159     private HashType(String name) {
160       this.name = name;
161     }
162 
163     @Override
toString()164     public String toString() {
165       return name;
166     }
167   }
168 
169   /** The Elliptic Curve Point Format. */
170   @Immutable
171   public static final class PointFormat {
172     public static final PointFormat COMPRESSED = new PointFormat("COMPRESSED");
173     public static final PointFormat UNCOMPRESSED = new PointFormat("UNCOMPRESSED");
174 
175     /**
176      * Like {@code UNCOMPRESSED}, but without the \x04 prefix. Crunchy uses this format. DO NOT USE
177      * unless you are a Crunchy user moving to Tink.
178      */
179     public static final PointFormat LEGACY_UNCOMPRESSED = new PointFormat("LEGACY_UNCOMPRESSED");
180 
181     private final String name;
182 
PointFormat(String name)183     private PointFormat(String name) {
184       this.name = name;
185     }
186 
187     @Override
toString()188     public String toString() {
189       return name;
190     }
191   }
192 
193   /** Builds a new {@link EciesParameters} instance. */
194   public static final class Builder {
195     private CurveType curveType = null;
196     private HashType hashType = null;
197     private PointFormat nistCurvePointFormat = null;
198     private Parameters demParameters = null;
199     private Variant variant = Variant.NO_PREFIX;
200     @Nullable private Bytes salt = null;
201 
Builder()202     private Builder() {}
203 
204     @CanIgnoreReturnValue
setCurveType(CurveType curveType)205     public Builder setCurveType(CurveType curveType) {
206       this.curveType = curveType;
207       return this;
208     }
209 
210     @CanIgnoreReturnValue
setHashType(HashType hashType)211     public Builder setHashType(HashType hashType) {
212       this.hashType = hashType;
213       return this;
214     }
215 
216     @CanIgnoreReturnValue
setNistCurvePointFormat(PointFormat pointFormat)217     public Builder setNistCurvePointFormat(PointFormat pointFormat) {
218       this.nistCurvePointFormat = pointFormat;
219       return this;
220     }
221 
222     /**
223      * Current implementation only accepts certain NO_PREFIX instances of AesGcmParameters,
224      * AesCtrHmacAeadParameters, XChaCha20Poly1305Parameters or AesSivParameters.
225      */
226     @CanIgnoreReturnValue
setDemParameters(Parameters demParameters)227     public Builder setDemParameters(Parameters demParameters) throws GeneralSecurityException {
228       if (!acceptedDemParameters.contains(demParameters)) {
229         throw new GeneralSecurityException(
230             "Invalid DEM parameters "
231                 + demParameters
232                 + "; only AES128_GCM_RAW, AES256_GCM_RAW, AES128_CTR_HMAC_SHA256_RAW,"
233                 + " AES256_CTR_HMAC_SHA256_RAW XCHACHA20_POLY1305_RAW and AES256_SIV_RAW are"
234                 + " currently supported.");
235       }
236       this.demParameters = demParameters;
237       return this;
238     }
239 
240     @CanIgnoreReturnValue
setVariant(Variant variant)241     public Builder setVariant(Variant variant) {
242       this.variant = variant;
243       return this;
244     }
245 
246     /** Defaults to null if not set. */
247     @CanIgnoreReturnValue
setSalt(Bytes salt)248     public Builder setSalt(Bytes salt) {
249       if (salt.size() == 0) {
250         this.salt = null;
251         return this;
252       }
253 
254       this.salt = salt;
255       return this;
256     }
257 
build()258     public EciesParameters build() throws GeneralSecurityException {
259       if (curveType == null) {
260         throw new GeneralSecurityException("Elliptic curve type is not set");
261       }
262       if (hashType == null) {
263         throw new GeneralSecurityException("Hash type is not set");
264       }
265       if (demParameters == null) {
266         throw new GeneralSecurityException("DEM parameters are not set");
267       }
268       if (variant == null) {
269         throw new GeneralSecurityException("Variant is not set");
270       }
271 
272       if (curveType != CurveType.X25519 && nistCurvePointFormat == null) {
273         throw new GeneralSecurityException("Point format is not set");
274       }
275       if (curveType == CurveType.X25519 && nistCurvePointFormat != null) {
276         throw new GeneralSecurityException("For Curve25519 point format must not be set");
277       }
278       return new EciesParameters(
279           curveType, hashType, nistCurvePointFormat, demParameters, variant, salt);
280     }
281   }
282 
283   private final CurveType curveType;
284   private final HashType hashType;
285   @Nullable private final PointFormat nistCurvePointFormat;
286   private final Variant variant;
287   private final Parameters demParameters;
288   @Nullable private final Bytes salt;
289 
EciesParameters( CurveType curveType, HashType hashType, @Nullable PointFormat pointFormat, Parameters demParameters, Variant variant, Bytes salt)290   private EciesParameters(
291       CurveType curveType,
292       HashType hashType,
293       @Nullable PointFormat pointFormat,
294       Parameters demParameters,
295       Variant variant,
296       Bytes salt) {
297     this.curveType = curveType;
298     this.hashType = hashType;
299     this.nistCurvePointFormat = pointFormat;
300     this.demParameters = demParameters;
301     this.variant = variant;
302     this.salt = salt;
303   }
304 
builder()305   public static Builder builder() {
306     return new Builder();
307   }
308 
getCurveType()309   public CurveType getCurveType() {
310     return curveType;
311   }
312 
getHashType()313   public HashType getHashType() {
314     return hashType;
315   }
316 
317   @Nullable
getNistCurvePointFormat()318   public PointFormat getNistCurvePointFormat() {
319     return nistCurvePointFormat;
320   }
321 
getDemParameters()322   public Parameters getDemParameters() {
323     return demParameters;
324   }
325 
getVariant()326   public Variant getVariant() {
327     return variant;
328   }
329 
330   /**
331    * Gets the salt value, which defaults to null if not set.
332    *
333    * <p>This class does not store an RFC compliant default value and the converion must be done in
334    * the implementation (meaning that a null salt must be converted to a string of zeros that is of
335    * the length of the hash function output, as per RFC 5869).
336    */
337   @Nullable
getSalt()338   public Bytes getSalt() {
339     return salt;
340   }
341 
342   @Override
hasIdRequirement()343   public boolean hasIdRequirement() {
344     return variant != Variant.NO_PREFIX;
345   }
346 
347   @Override
equals(Object o)348   public boolean equals(Object o) {
349     if (!(o instanceof EciesParameters)) {
350       return false;
351     }
352     EciesParameters that = (EciesParameters) o;
353     return Objects.equals(that.getCurveType(), getCurveType())
354         && Objects.equals(that.getHashType(), getHashType())
355         && Objects.equals(that.getNistCurvePointFormat(), getNistCurvePointFormat())
356         && Objects.equals(that.getDemParameters(), getDemParameters())
357         && Objects.equals(that.getVariant(), getVariant())
358         && Objects.equals(that.getSalt(), getSalt());
359   }
360 
361   @Override
hashCode()362   public int hashCode() {
363     return Objects.hash(
364         EciesParameters.class,
365         curveType,
366         hashType,
367         nistCurvePointFormat,
368         demParameters,
369         variant,
370         salt);
371   }
372 
373   @Override
toString()374   public String toString() {
375     return String.format(
376         "EciesParameters(curveType=%s, hashType=%s, pointFormat=%s, demParameters=%s, variant=%s,"
377             + " salt=%s)",
378         curveType, hashType, nistCurvePointFormat, demParameters, variant, salt);
379   }
380 }
381