• 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.signature;
18 
19 import com.google.errorprone.annotations.CanIgnoreReturnValue;
20 import com.google.errorprone.annotations.Immutable;
21 import java.math.BigInteger;
22 import java.security.GeneralSecurityException;
23 import java.security.InvalidAlgorithmParameterException;
24 import java.util.Objects;
25 import javax.annotation.Nullable;
26 
27 /**
28  * Describes the parameters of a {@link RsaSsaPssPublicKey} and {@link RsaSsaPssPrivateKey}.
29  *
30  * <p>Standard: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
31  */
32 public final class RsaSsaPssParameters extends SignatureParameters {
33   /**
34    * Describes details of the signature.
35    *
36    * <p>The usual key is used for variant "NO_PREFIX". Other variants slightly change how the
37    * signature is computed, or add a prefix to every computation depending on the key id.
38    */
39   @Immutable
40   public static final class Variant {
41     public static final Variant TINK = new Variant("TINK");
42     public static final Variant CRUNCHY = new Variant("CRUNCHY");
43     public static final Variant LEGACY = new Variant("LEGACY");
44     public static final Variant NO_PREFIX = new Variant("NO_PREFIX");
45 
46     private final String name;
47 
Variant(String name)48     private Variant(String name) {
49       this.name = name;
50     }
51 
52     @Override
toString()53     public String toString() {
54       return name;
55     }
56   }
57 
58   /** The Hash algorithm used. */
59   @Immutable
60   public static final class HashType {
61     public static final HashType SHA256 = new HashType("SHA256");
62     public static final HashType SHA384 = new HashType("SHA384");
63     public static final HashType SHA512 = new HashType("SHA512");
64 
65     private final String name;
66 
HashType(String name)67     private HashType(String name) {
68       this.name = name;
69     }
70 
71     @Override
toString()72     public String toString() {
73       return name;
74     }
75   }
76 
77   public static final BigInteger F4 = BigInteger.valueOf(65537);
78 
79   /** Builds a new RsaSsaPssParameters instance. */
80   public static final class Builder {
81     @Nullable private Integer modulusSizeBits = null;
82     @Nullable private BigInteger publicExponent = F4;
83     @Nullable private HashType sigHashType = null;
84     @Nullable private HashType mgf1HashType = null;
85     @Nullable private Integer saltLengthBytes = null;
86     private Variant variant = Variant.NO_PREFIX;
87 
Builder()88     private Builder() {}
89 
90     @CanIgnoreReturnValue
setModulusSizeBits(int modulusSizeBits)91     public Builder setModulusSizeBits(int modulusSizeBits) {
92       this.modulusSizeBits = modulusSizeBits;
93       return this;
94     }
95 
96     @CanIgnoreReturnValue
setPublicExponent(BigInteger e)97     public Builder setPublicExponent(BigInteger e) {
98       this.publicExponent = e;
99       return this;
100     }
101 
102     @CanIgnoreReturnValue
setVariant(Variant variant)103     public Builder setVariant(Variant variant) {
104       this.variant = variant;
105       return this;
106     }
107 
108     @CanIgnoreReturnValue
setSigHashType(HashType sigHashType)109     public Builder setSigHashType(HashType sigHashType) {
110       this.sigHashType = sigHashType;
111       return this;
112     }
113 
114     @CanIgnoreReturnValue
setMgf1HashType(HashType mgf1HashType)115     public Builder setMgf1HashType(HashType mgf1HashType) {
116       this.mgf1HashType = mgf1HashType;
117       return this;
118     }
119 
120     @CanIgnoreReturnValue
setSaltLengthBytes(int saltLengthBytes)121     public Builder setSaltLengthBytes(int saltLengthBytes) throws GeneralSecurityException {
122       if (saltLengthBytes < 0) {
123         throw new GeneralSecurityException(
124             String.format(
125                 "Invalid salt length in bytes %d; salt length must be positive", saltLengthBytes));
126       }
127       this.saltLengthBytes = saltLengthBytes;
128       return this;
129     }
130 
131     private static final BigInteger TWO = BigInteger.valueOf(2);
132     private static final BigInteger PUBLIC_EXPONENT_UPPER_BOUND = TWO.pow(256);
133     private static final int MIN_RSA_MODULUS_SIZE = 2048;
134 
validatePublicExponent(BigInteger publicExponent)135     private void validatePublicExponent(BigInteger publicExponent)
136         throws InvalidAlgorithmParameterException {
137       // We use the validation of the public exponent as defined in
138       // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf, B.3
139       int c = publicExponent.compareTo(F4);
140       if (c == 0) {
141         // publicExponent is F4.
142         return;
143       }
144       if (c < 0) {
145         // publicExponent is smaller than F4.
146         throw new InvalidAlgorithmParameterException("Public exponent must be at least 65537.");
147       }
148       if (publicExponent.mod(TWO).equals(BigInteger.ZERO)) {
149         // publicExponent is even. This is invalid since it is not co-prime to p-1.
150         throw new InvalidAlgorithmParameterException("Invalid public exponent");
151       }
152       if (publicExponent.compareTo(PUBLIC_EXPONENT_UPPER_BOUND) > 0) {
153         // publicExponent is larger than PUBLIC_EXPONENT_UPPER_BOUND.
154         throw new InvalidAlgorithmParameterException(
155             "Public exponent cannot be larger than 2^256.");
156       }
157     }
158 
build()159     public RsaSsaPssParameters build() throws GeneralSecurityException {
160       if (modulusSizeBits == null) {
161         throw new GeneralSecurityException("key size is not set");
162       }
163       if (publicExponent == null) {
164         throw new GeneralSecurityException("publicExponent is not set");
165       }
166       if (sigHashType == null) {
167         throw new GeneralSecurityException("signature hash type is not set");
168       }
169       if (mgf1HashType == null) {
170         throw new GeneralSecurityException("mgf1 hash type is not set");
171       }
172       if (variant == null) {
173         throw new GeneralSecurityException("variant is not set");
174       }
175       if (saltLengthBytes == null) {
176         throw new GeneralSecurityException("salt length is not set");
177       }
178       if (modulusSizeBits < MIN_RSA_MODULUS_SIZE) {
179         throw new InvalidAlgorithmParameterException(
180             String.format(
181                 "Invalid key size in bytes %d; must be at least %d bits",
182                 modulusSizeBits, MIN_RSA_MODULUS_SIZE));
183       }
184       if (sigHashType != mgf1HashType) {
185         throw new GeneralSecurityException("MGF1 hash is different from signature hash");
186       }
187       validatePublicExponent(publicExponent);
188       return new RsaSsaPssParameters(
189           modulusSizeBits, publicExponent, variant, sigHashType, mgf1HashType, saltLengthBytes);
190     }
191   }
192 
193   private final int modulusSizeBits;
194   private final BigInteger publicExponent;
195   private final Variant variant;
196   private final HashType sigHashType;
197   private final HashType mgf1HashType;
198   private final int saltLengthBytes;
199 
RsaSsaPssParameters( int modulusSizeBits, BigInteger publicExponent, Variant variant, HashType sigHashType, HashType mgf1HashType, int saltLengthBytes)200   private RsaSsaPssParameters(
201       int modulusSizeBits,
202       BigInteger publicExponent,
203       Variant variant,
204       HashType sigHashType,
205       HashType mgf1HashType,
206       int saltLengthBytes) {
207     this.modulusSizeBits = modulusSizeBits;
208     this.publicExponent = publicExponent;
209     this.variant = variant;
210     this.sigHashType = sigHashType;
211     this.mgf1HashType = mgf1HashType;
212     this.saltLengthBytes = saltLengthBytes;
213   }
214 
builder()215   public static Builder builder() {
216     return new Builder();
217   }
218 
getModulusSizeBits()219   public int getModulusSizeBits() {
220     return modulusSizeBits;
221   }
222 
getPublicExponent()223   public BigInteger getPublicExponent() {
224     return publicExponent;
225   }
226 
getVariant()227   public Variant getVariant() {
228     return variant;
229   }
230 
getSigHashType()231   public HashType getSigHashType() {
232     return sigHashType;
233   }
234 
getMgf1HashType()235   public HashType getMgf1HashType() {
236     return mgf1HashType;
237   }
238 
getSaltLengthBytes()239   public int getSaltLengthBytes() {
240     return saltLengthBytes;
241   }
242 
243   @Override
equals(Object o)244   public boolean equals(Object o) {
245     if (!(o instanceof RsaSsaPssParameters)) {
246       return false;
247     }
248     RsaSsaPssParameters that = (RsaSsaPssParameters) o;
249     return that.getModulusSizeBits() == getModulusSizeBits()
250         && Objects.equals(that.getPublicExponent(), getPublicExponent())
251         && Objects.equals(that.getVariant(), getVariant())
252         && Objects.equals(that.getSigHashType(), getSigHashType())
253         && Objects.equals(that.getMgf1HashType(), getMgf1HashType())
254         && that.getSaltLengthBytes() == getSaltLengthBytes();
255   }
256 
257   @Override
hashCode()258   public int hashCode() {
259     return Objects.hash(
260         RsaSsaPssParameters.class,
261         modulusSizeBits,
262         publicExponent,
263         variant,
264         sigHashType,
265         mgf1HashType,
266         saltLengthBytes);
267   }
268 
269   @Override
hasIdRequirement()270   public boolean hasIdRequirement() {
271     return variant != Variant.NO_PREFIX;
272   }
273 
274   @Override
toString()275   public String toString() {
276     return "RSA SSA PSS Parameters (variant: "
277         + variant
278         + ", signature hashType: "
279         + sigHashType
280         + ", mgf1 hashType: "
281         + mgf1HashType
282         + ", saltLengthBytes: "
283         + saltLengthBytes
284         + ", publicExponent: "
285         + publicExponent
286         + ", and "
287         + modulusSizeBits
288         + "-bit modulus)";
289   }
290 }
291