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 static com.google.crypto.tink.internal.Util.isPrefix; 20 21 import com.google.crypto.tink.AccessesPartialKey; 22 import com.google.crypto.tink.PublicKeyVerify; 23 import com.google.crypto.tink.config.internal.TinkFipsUtil; 24 import com.google.crypto.tink.internal.EnumTypeProtoConverter; 25 import com.google.crypto.tink.signature.RsaSsaPssParameters; 26 import com.google.crypto.tink.signature.RsaSsaPssPublicKey; 27 import com.google.crypto.tink.signature.internal.RsaSsaPssVerifyConscrypt; 28 import com.google.crypto.tink.subtle.Enums.HashType; 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.RSAPublicKey; 36 import java.security.spec.RSAPublicKeySpec; 37 import java.util.Arrays; 38 39 /** 40 * RsaSsaPss (i.e. RSA Signature Schemes with Appendix (SSA) using PSS encoding) verifying with JCE. 41 */ 42 @Immutable 43 public final class RsaSsaPssVerifyJce implements PublicKeyVerify { 44 public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS = 45 TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO; 46 47 // This converter is not used with a proto but rather with an ordinary enum type. 48 static final EnumTypeProtoConverter<HashType, RsaSsaPssParameters.HashType> HASH_TYPE_CONVERTER = 49 EnumTypeProtoConverter.<HashType, RsaSsaPssParameters.HashType>builder() 50 .add(HashType.SHA256, RsaSsaPssParameters.HashType.SHA256) 51 .add(HashType.SHA384, RsaSsaPssParameters.HashType.SHA384) 52 .add(HashType.SHA512, RsaSsaPssParameters.HashType.SHA512) 53 .build(); 54 55 private static final byte[] EMPTY = new byte[0]; 56 private static final byte[] LEGACY_MESSAGE_SUFFIX = new byte[] {0}; 57 58 /** 59 * InternalImpl is an implementation of the RSA SSA PSS signature signing that only uses the JCE 60 * for raw RSA operations. The rest of the algorithm is implemented in Java. This allows it to be 61 * used on most Java platforms. 62 */ 63 private static final class InternalImpl implements PublicKeyVerify { 64 65 @SuppressWarnings("Immutable") 66 private final RSAPublicKey publicKey; 67 68 private final HashType sigHash; 69 private final HashType mgf1Hash; 70 private final int saltLength; 71 72 @SuppressWarnings("Immutable") 73 private final byte[] outputPrefix; 74 75 @SuppressWarnings("Immutable") 76 private final byte[] messageSuffix; 77 InternalImpl( final RSAPublicKey pubKey, HashType sigHash, HashType mgf1Hash, int saltLength, byte[] outputPrefix, byte[] messageSuffix)78 private InternalImpl( 79 final RSAPublicKey pubKey, 80 HashType sigHash, 81 HashType mgf1Hash, 82 int saltLength, 83 byte[] outputPrefix, 84 byte[] messageSuffix) 85 throws GeneralSecurityException { 86 if (TinkFipsUtil.useOnlyFips()) { 87 throw new GeneralSecurityException( 88 "Can not use RSA PSS in FIPS-mode, as BoringCrypto module is not available."); 89 } 90 91 Validators.validateSignatureHash(sigHash); 92 if (!sigHash.equals(mgf1Hash)) { 93 throw new GeneralSecurityException("sigHash and mgf1Hash must be the same"); 94 } 95 Validators.validateRsaModulusSize(pubKey.getModulus().bitLength()); 96 Validators.validateRsaPublicExponent(pubKey.getPublicExponent()); 97 this.publicKey = pubKey; 98 this.sigHash = sigHash; 99 this.mgf1Hash = mgf1Hash; 100 this.saltLength = saltLength; 101 this.outputPrefix = outputPrefix; 102 this.messageSuffix = messageSuffix; 103 } 104 noPrefixVerify(final byte[] signature, final byte[] data)105 private void noPrefixVerify(final byte[] signature, final byte[] data) 106 throws GeneralSecurityException { 107 // The algorithm is described at (https://tools.ietf.org/html/rfc8017#section-8.1.2). As 108 // signature verification is a public operation, throwing different exception messages 109 // doesn't 110 // give attacker any useful information. 111 BigInteger e = publicKey.getPublicExponent(); 112 BigInteger n = publicKey.getModulus(); 113 int nLengthInBytes = (n.bitLength() + 7) / 8; 114 int mLen = (n.bitLength() - 1 + 7) / 8; 115 116 // Step 1. Length checking. 117 if (nLengthInBytes != signature.length) { 118 throw new GeneralSecurityException("invalid signature's length"); 119 } 120 121 // Step 2. RSA verification. 122 BigInteger s = SubtleUtil.bytes2Integer(signature); 123 if (s.compareTo(n) >= 0) { 124 throw new GeneralSecurityException("signature out of range"); 125 } 126 BigInteger m = s.modPow(e, n); 127 byte[] em = SubtleUtil.integer2Bytes(m, mLen); 128 129 // Step 3. PSS encoding verification. 130 emsaPssVerify(data, em, n.bitLength() - 1); 131 } 132 133 // https://tools.ietf.org/html/rfc8017#section-9.1.2. emsaPssVerify(byte[] message, byte[] em, int emBits)134 private void emsaPssVerify(byte[] message, byte[] em, int emBits) 135 throws GeneralSecurityException { 136 // Step 1. Length checking. 137 // This step is unnecessary because Java's byte[] only supports up to 2^31 -1 bytes while the 138 // input limitation for the hash function is far larger (2^61 - 1 for SHA-1). 139 140 // Step 2. Compute hash. 141 Validators.validateSignatureHash(sigHash); 142 MessageDigest digest = 143 EngineFactory.MESSAGE_DIGEST.getInstance(SubtleUtil.toDigestAlgo(this.sigHash)); 144 // M = concat(message, messageSuffix) 145 digest.update(message); 146 if (messageSuffix.length != 0) { 147 digest.update(messageSuffix); 148 } 149 byte[] mHash = digest.digest(); 150 int hLen = digest.getDigestLength(); 151 152 int emLen = em.length; 153 154 // Step 3. Check emLen. 155 if (emLen < hLen + this.saltLength + 2) { 156 throw new GeneralSecurityException("inconsistent"); 157 } 158 159 // Step 4. Check right most byte of EM. 160 if (em[em.length - 1] != (byte) 0xbc) { 161 throw new GeneralSecurityException("inconsistent"); 162 } 163 164 // Step 5. Extract maskedDb and H from EM. 165 byte[] maskedDb = Arrays.copyOf(em, emLen - hLen - 1); 166 byte[] h = Arrays.copyOfRange(em, maskedDb.length, maskedDb.length + hLen); 167 168 // Step 6. Check whether the leftmost 8 * emLen - emBits bits of the leftmost octet in 169 // maskedDB 170 // are all zeros. 171 for (int i = 0; i < (long) emLen * 8 - emBits; i++) { 172 int bytePos = i / 8; 173 int bitPos = 7 - i % 8; 174 if (((maskedDb[bytePos] >> bitPos) & 1) != 0) { 175 throw new GeneralSecurityException("inconsistent"); 176 } 177 } 178 179 // Step 7. Compute dbMask. 180 byte[] dbMask = SubtleUtil.mgf1(h, emLen - hLen - 1, mgf1Hash); 181 182 // Step 8. Compute db. 183 byte[] db = new byte[dbMask.length]; 184 for (int i = 0; i < db.length; i++) { 185 db[i] = (byte) (dbMask[i] ^ maskedDb[i]); 186 } 187 188 // Step 9. Set the leftmost 8*emLen - emBits bits of the leftmost octet in DB to zero. 189 for (int i = 0; i <= (long) emLen * 8 - emBits; i++) { 190 int bytePos = i / 8; 191 int bitPos = 7 - i % 8; 192 db[bytePos] = (byte) (db[bytePos] & ~(1 << bitPos)); 193 } 194 195 // Step 10. Check db. 196 for (int i = 0; i < emLen - hLen - this.saltLength - 2; i++) { 197 if (db[i] != 0) { 198 throw new GeneralSecurityException("inconsistent"); 199 } 200 } 201 if (db[emLen - hLen - this.saltLength - 2] != (byte) 0x01) { 202 throw new GeneralSecurityException("inconsistent"); 203 } 204 205 // Step 11. Extract salt from db. 206 byte[] salt = Arrays.copyOfRange(db, db.length - this.saltLength, db.length); 207 208 // Step 12. Generate M'. 209 byte[] mPrime = new byte[8 + hLen + this.saltLength]; 210 System.arraycopy(mHash, 0, mPrime, 8, mHash.length); 211 System.arraycopy(salt, 0, mPrime, 8 + hLen, salt.length); 212 213 // Step 13. Compute H' 214 byte[] hPrime = digest.digest(mPrime); 215 if (!Bytes.equal(hPrime, h)) { 216 throw new GeneralSecurityException("inconsistent"); 217 } 218 } 219 220 @Override verify(final byte[] signature, final byte[] data)221 public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException { 222 if (outputPrefix.length == 0) { 223 noPrefixVerify(signature, data); 224 return; 225 } 226 if (!isPrefix(outputPrefix, signature)) { 227 throw new GeneralSecurityException("Invalid signature (output prefix mismatch)"); 228 } 229 byte[] signatureNoPrefix = 230 Arrays.copyOfRange(signature, outputPrefix.length, signature.length); 231 noPrefixVerify(signatureNoPrefix, data); 232 } 233 } 234 235 @SuppressWarnings("Immutable") 236 private final PublicKeyVerify verify; 237 238 @AccessesPartialKey create(RsaSsaPssPublicKey key)239 public static PublicKeyVerify create(RsaSsaPssPublicKey key) throws GeneralSecurityException { 240 try { 241 return RsaSsaPssVerifyConscrypt.create(key); 242 } catch (NoSuchProviderException e) { 243 // Ignore, and fall back to the Java implementation. 244 } 245 KeyFactory keyFactory = EngineFactory.KEY_FACTORY.getInstance("RSA"); 246 RSAPublicKey publicKey = 247 (RSAPublicKey) 248 keyFactory.generatePublic( 249 new RSAPublicKeySpec(key.getModulus(), key.getParameters().getPublicExponent())); 250 RsaSsaPssParameters params = key.getParameters(); 251 return new InternalImpl( 252 publicKey, 253 HASH_TYPE_CONVERTER.toProtoEnum(params.getSigHashType()), 254 HASH_TYPE_CONVERTER.toProtoEnum(params.getMgf1HashType()), 255 params.getSaltLengthBytes(), 256 key.getOutputPrefix().toByteArray(), 257 key.getParameters().getVariant().equals(RsaSsaPssParameters.Variant.LEGACY) 258 ? LEGACY_MESSAGE_SUFFIX 259 : EMPTY); 260 } 261 getHashType(HashType hash)262 private static RsaSsaPssParameters.HashType getHashType(HashType hash) 263 throws GeneralSecurityException { 264 switch (hash) { 265 case SHA256: 266 return RsaSsaPssParameters.HashType.SHA256; 267 case SHA384: 268 return RsaSsaPssParameters.HashType.SHA384; 269 case SHA512: 270 return RsaSsaPssParameters.HashType.SHA512; 271 default: 272 throw new GeneralSecurityException("Unsupported hash: " + hash); 273 } 274 } 275 276 @AccessesPartialKey convertKey( final RSAPublicKey pubKey, HashType sigHash, HashType mgf1Hash, int saltLength)277 private RsaSsaPssPublicKey convertKey( 278 final RSAPublicKey pubKey, HashType sigHash, HashType mgf1Hash, int saltLength) 279 throws GeneralSecurityException { 280 RsaSsaPssParameters parameters = 281 RsaSsaPssParameters.builder() 282 .setModulusSizeBits(pubKey.getModulus().bitLength()) 283 .setPublicExponent(pubKey.getPublicExponent()) 284 .setSigHashType(getHashType(sigHash)) 285 .setMgf1HashType(getHashType(mgf1Hash)) 286 .setSaltLengthBytes(saltLength) 287 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 288 .build(); 289 return RsaSsaPssPublicKey.builder() 290 .setParameters(parameters) 291 .setModulus(pubKey.getModulus()) 292 .build(); 293 } 294 RsaSsaPssVerifyJce( final RSAPublicKey pubKey, HashType sigHash, HashType mgf1Hash, int saltLength)295 public RsaSsaPssVerifyJce( 296 final RSAPublicKey pubKey, HashType sigHash, HashType mgf1Hash, int saltLength) 297 throws GeneralSecurityException { 298 this.verify = create(convertKey(pubKey, sigHash, mgf1Hash, saltLength)); 299 } 300 301 @Override verify(final byte[] signature, final byte[] data)302 public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException { 303 verify.verify(signature, data); 304 } 305 } 306