• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 Google Inc.
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.subtle;
18 
19 import com.google.crypto.tink.AccessesPartialKey;
20 import com.google.crypto.tink.InsecureSecretKeyAccess;
21 import com.google.crypto.tink.PublicKeySign;
22 import com.google.crypto.tink.config.internal.TinkFipsUtil;
23 import com.google.crypto.tink.signature.RsaSsaPssParameters;
24 import com.google.crypto.tink.signature.RsaSsaPssPrivateKey;
25 import com.google.crypto.tink.signature.RsaSsaPssPublicKey;
26 import com.google.crypto.tink.signature.internal.RsaSsaPssSignConscrypt;
27 import com.google.crypto.tink.subtle.Enums.HashType;
28 import com.google.crypto.tink.util.SecretBigInteger;
29 import com.google.errorprone.annotations.Immutable;
30 import java.math.BigInteger;
31 import java.security.GeneralSecurityException;
32 import java.security.KeyFactory;
33 import java.security.MessageDigest;
34 import java.security.NoSuchProviderException;
35 import java.security.interfaces.RSAPrivateCrtKey;
36 import java.security.interfaces.RSAPublicKey;
37 import java.security.spec.RSAPrivateCrtKeySpec;
38 import java.security.spec.RSAPublicKeySpec;
39 import javax.crypto.Cipher;
40 
41 /**
42  * RsaSsaPss (i.e. RSA Signature Schemes with Appendix (SSA) with PSS encoding) signing with JCE.
43  */
44 @Immutable
45 public final class RsaSsaPssSignJce implements PublicKeySign {
46   public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS =
47       TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
48 
49   private static final byte[] EMPTY = new byte[0];
50   private static final byte[] LEGACY_MESSAGE_SUFFIX = new byte[] {0};
51 
52   /**
53    * InternalImpl is an implementation of the RSA SSA PSS signature signing that only uses the JCE
54    * for raw RSA operations. The rest of the algorithm is implemented in Java. This allows it to be
55    * used on most Java platforms.
56    */
57   private static final class InternalImpl implements PublicKeySign {
58 
59     @SuppressWarnings("Immutable")
60     private final RSAPrivateCrtKey privateKey;
61 
62     @SuppressWarnings("Immutable")
63     private final RSAPublicKey publicKey;
64 
65     private final HashType sigHash;
66     private final HashType mgf1Hash;
67     private final int saltLength;
68 
69     @SuppressWarnings("Immutable")
70     private final byte[] outputPrefix;
71 
72     @SuppressWarnings("Immutable")
73     private final byte[] messageSuffix;
74 
75     private static final String RAW_RSA_ALGORITHM = "RSA/ECB/NOPADDING";
76 
InternalImpl( final RSAPrivateCrtKey priv, HashType sigHash, HashType mgf1Hash, int saltLength, byte[] outputPrefix, byte[] messageSuffix)77     private InternalImpl(
78         final RSAPrivateCrtKey priv,
79         HashType sigHash,
80         HashType mgf1Hash,
81         int saltLength,
82         byte[] outputPrefix,
83         byte[] messageSuffix)
84         throws GeneralSecurityException {
85       if (TinkFipsUtil.useOnlyFips()) {
86         throw new GeneralSecurityException(
87             "Can not use RSA PSS in FIPS-mode, as BoringCrypto module is not available.");
88       }
89 
90       Validators.validateSignatureHash(sigHash);
91       if (!sigHash.equals(mgf1Hash)) {
92         throw new GeneralSecurityException("sigHash and mgf1Hash must be the same");
93       }
94       Validators.validateRsaModulusSize(priv.getModulus().bitLength());
95       Validators.validateRsaPublicExponent(priv.getPublicExponent());
96       this.privateKey = priv;
97       KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("RSA");
98       this.publicKey =
99           (RSAPublicKey)
100               kf.generatePublic(new RSAPublicKeySpec(priv.getModulus(), priv.getPublicExponent()));
101       this.sigHash = sigHash;
102       this.mgf1Hash = mgf1Hash;
103       this.saltLength = saltLength;
104       this.outputPrefix = outputPrefix;
105       this.messageSuffix = messageSuffix;
106     }
107 
noPrefixSign(final byte[] data)108     private byte[] noPrefixSign(final byte[] data)
109         throws GeneralSecurityException { // https://tools.ietf.org/html/rfc8017#section-8.1.1.
110       int modBits = publicKey.getModulus().bitLength();
111 
112       byte[] em = emsaPssEncode(data, modBits - 1);
113       return rsasp1(em);
114     }
115 
116     @Override
sign(final byte[] data)117     public byte[] sign(final byte[] data) throws GeneralSecurityException {
118       byte[] signature = noPrefixSign(data);
119       if (outputPrefix.length == 0) {
120         return signature;
121       } else {
122         return Bytes.concat(outputPrefix, signature);
123       }
124     }
125 
rsasp1(byte[] m)126     private byte[] rsasp1(byte[] m) throws GeneralSecurityException {
127       Cipher decryptCipher = EngineFactory.CIPHER.getInstance(RAW_RSA_ALGORITHM);
128       decryptCipher.init(Cipher.DECRYPT_MODE, this.privateKey);
129       byte[] c = decryptCipher.doFinal(m);
130       // To make sure the private key operation is correct, we check the result with public key
131       // operation.
132       Cipher encryptCipher = EngineFactory.CIPHER.getInstance(RAW_RSA_ALGORITHM);
133       encryptCipher.init(Cipher.ENCRYPT_MODE, this.publicKey);
134       byte[] m0 = encryptCipher.doFinal(c);
135       if (!new BigInteger(1, m).equals(new BigInteger(1, m0))) {
136         throw new IllegalStateException("Security bug: RSA signature computation error");
137       }
138       return c;
139     }
140 
141     // https://tools.ietf.org/html/rfc8017#section-9.1.1.
emsaPssEncode(byte[] message, int emBits)142     private byte[] emsaPssEncode(byte[] message, int emBits) throws GeneralSecurityException {
143       // Step 1. Length checking.
144       // This step is unnecessary because Java's byte[] only supports up to 2^31 -1 bytes while the
145       // input limitation for the hash function is far larger (2^61 - 1 for SHA-1).
146 
147       // Step 2. Compute hash.
148       Validators.validateSignatureHash(sigHash);
149       MessageDigest digest =
150           EngineFactory.MESSAGE_DIGEST.getInstance(SubtleUtil.toDigestAlgo(this.sigHash));
151       // M = concat(message, messageSuffix)
152       digest.update(message);
153       if (messageSuffix.length != 0) {
154         digest.update(messageSuffix);
155       }
156       byte[] mHash = digest.digest();
157 
158       // Step 3. Check emLen.
159       int hLen = digest.getDigestLength();
160       int emLen = (emBits - 1) / 8 + 1;
161       if (emLen < hLen + this.saltLength + 2) {
162         throw new GeneralSecurityException("encoding error");
163       }
164 
165       // Step 4. Generate random salt.
166       byte[] salt = Random.randBytes(this.saltLength);
167 
168       // Step 5. Compute M'.
169       byte[] mPrime = new byte[8 + hLen + this.saltLength];
170       System.arraycopy(mHash, 0, mPrime, 8, hLen);
171       System.arraycopy(salt, 0, mPrime, 8 + hLen, salt.length);
172 
173       // Step 6. Compute H.
174       byte[] h = digest.digest(mPrime);
175 
176       // Step 7, 8. Generate DB.
177       byte[] db = new byte[emLen - hLen - 1];
178       db[emLen - this.saltLength - hLen - 2] = (byte) 0x01;
179       System.arraycopy(salt, 0, db, emLen - this.saltLength - hLen - 1, salt.length);
180 
181       // Step 9. Compute dbMask.
182       byte[] dbMask = SubtleUtil.mgf1(h, emLen - hLen - 1, this.mgf1Hash);
183 
184       // Step 10. Compute maskedDb.
185       byte[] maskedDb = new byte[emLen - hLen - 1];
186       for (int i = 0; i < maskedDb.length; i++) {
187         maskedDb[i] = (byte) (db[i] ^ dbMask[i]);
188       }
189 
190       // Step 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in maskedDB to
191       // zero.
192       for (int i = 0; i < (long) emLen * 8 - emBits; i++) {
193         int bytePos = i / 8;
194         int bitPos = 7 - i % 8;
195         maskedDb[bytePos] = (byte) (maskedDb[bytePos] & ~(1 << bitPos));
196       }
197 
198       // Step 12. Generate EM.
199       byte[] em = new byte[maskedDb.length + hLen + 1];
200       System.arraycopy(maskedDb, 0, em, 0, maskedDb.length);
201       System.arraycopy(h, 0, em, maskedDb.length, h.length);
202       em[maskedDb.length + hLen] = (byte) 0xbc;
203       return em;
204     }
205   }
206 
207   @SuppressWarnings("Immutable")
208   private final PublicKeySign sign;
209 
210   @AccessesPartialKey
create(RsaSsaPssPrivateKey key)211   public static PublicKeySign create(RsaSsaPssPrivateKey key) throws GeneralSecurityException {
212     try {
213       return RsaSsaPssSignConscrypt.create(key);
214     } catch (NoSuchProviderException e) {
215       // Ignore, and fall back to the Java implementation.
216     }
217     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("RSA");
218     RSAPrivateCrtKey privateKey =
219         (RSAPrivateCrtKey)
220             kf.generatePrivate(
221                 new RSAPrivateCrtKeySpec(
222                     key.getPublicKey().getModulus(),
223                     key.getParameters().getPublicExponent(),
224                     key.getPrivateExponent().getBigInteger(InsecureSecretKeyAccess.get()),
225                     key.getPrimeP().getBigInteger(InsecureSecretKeyAccess.get()),
226                     key.getPrimeQ().getBigInteger(InsecureSecretKeyAccess.get()),
227                     key.getPrimeExponentP().getBigInteger(InsecureSecretKeyAccess.get()),
228                     key.getPrimeExponentQ().getBigInteger(InsecureSecretKeyAccess.get()),
229                     key.getCrtCoefficient().getBigInteger(InsecureSecretKeyAccess.get())));
230     RsaSsaPssParameters params = key.getParameters();
231     return new InternalImpl(
232         privateKey,
233         RsaSsaPssVerifyJce.HASH_TYPE_CONVERTER.toProtoEnum(params.getSigHashType()),
234         RsaSsaPssVerifyJce.HASH_TYPE_CONVERTER.toProtoEnum(params.getMgf1HashType()),
235         params.getSaltLengthBytes(),
236         key.getOutputPrefix().toByteArray(),
237         key.getParameters().getVariant().equals(RsaSsaPssParameters.Variant.LEGACY)
238             ? LEGACY_MESSAGE_SUFFIX
239             : EMPTY);
240   }
241 
getHashType(HashType hash)242   private static RsaSsaPssParameters.HashType getHashType(HashType hash)
243       throws GeneralSecurityException {
244     switch (hash) {
245       case SHA256:
246         return RsaSsaPssParameters.HashType.SHA256;
247       case SHA384:
248         return RsaSsaPssParameters.HashType.SHA384;
249       case SHA512:
250         return RsaSsaPssParameters.HashType.SHA512;
251       default:
252         throw new GeneralSecurityException("Unsupported hash: " + hash);
253     }
254   }
255 
256   @AccessesPartialKey
convertKey( final RSAPrivateCrtKey key, HashType sigHash, HashType mgf1Hash, int saltLength)257   private RsaSsaPssPrivateKey convertKey(
258       final RSAPrivateCrtKey key, HashType sigHash, HashType mgf1Hash, int saltLength)
259       throws GeneralSecurityException {
260     RsaSsaPssParameters parameters =
261         RsaSsaPssParameters.builder()
262             .setModulusSizeBits(key.getModulus().bitLength())
263             .setPublicExponent(key.getPublicExponent())
264             .setSigHashType(getHashType(sigHash))
265             .setMgf1HashType(getHashType(mgf1Hash))
266             .setSaltLengthBytes(saltLength)
267             .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX)
268             .build();
269     return RsaSsaPssPrivateKey.builder()
270         .setPublicKey(
271             RsaSsaPssPublicKey.builder()
272                 .setParameters(parameters)
273                 .setModulus(key.getModulus())
274                 .build())
275         .setPrimes(
276             SecretBigInteger.fromBigInteger(key.getPrimeP(), InsecureSecretKeyAccess.get()),
277             SecretBigInteger.fromBigInteger(key.getPrimeQ(), InsecureSecretKeyAccess.get()))
278         .setPrivateExponent(
279             SecretBigInteger.fromBigInteger(
280                 key.getPrivateExponent(), InsecureSecretKeyAccess.get()))
281         .setPrimeExponents(
282             SecretBigInteger.fromBigInteger(key.getPrimeExponentP(), InsecureSecretKeyAccess.get()),
283             SecretBigInteger.fromBigInteger(key.getPrimeExponentQ(), InsecureSecretKeyAccess.get()))
284         .setCrtCoefficient(
285             SecretBigInteger.fromBigInteger(key.getCrtCoefficient(), InsecureSecretKeyAccess.get()))
286         .build();
287   }
288 
RsaSsaPssSignJce( final RSAPrivateCrtKey priv, HashType sigHash, HashType mgf1Hash, int saltLength)289   public RsaSsaPssSignJce(
290       final RSAPrivateCrtKey priv, HashType sigHash, HashType mgf1Hash, int saltLength)
291       throws GeneralSecurityException {
292     this.sign = create(convertKey(priv, sigHash, mgf1Hash, saltLength));
293   }
294 
295   @Override
sign(final byte[] data)296   public byte[] sign(final byte[] data) throws GeneralSecurityException {
297     return sign.sign(data);
298   }
299 }
300