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