1 // Copyright 2017 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.ConscryptUtil; 25 import com.google.crypto.tink.internal.EnumTypeProtoConverter; 26 import com.google.crypto.tink.signature.EcdsaParameters; 27 import com.google.crypto.tink.signature.EcdsaPublicKey; 28 import com.google.crypto.tink.subtle.EllipticCurves.CurveType; 29 import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; 30 import com.google.crypto.tink.subtle.Enums.HashType; 31 import com.google.errorprone.annotations.Immutable; 32 import java.security.GeneralSecurityException; 33 import java.security.Provider; 34 import java.security.Signature; 35 import java.security.interfaces.ECPublicKey; 36 import java.security.spec.EllipticCurve; 37 import java.util.Arrays; 38 39 /** 40 * ECDSA verifying with JCE. 41 * 42 * @since 1.0.0 43 */ 44 @Immutable 45 public final class EcdsaVerifyJce implements PublicKeyVerify { 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 @SuppressWarnings("Immutable") 53 private final ECPublicKey publicKey; 54 55 private final String signatureAlgorithm; 56 private final EcdsaEncoding encoding; 57 58 @SuppressWarnings("Immutable") 59 private final byte[] outputPrefix; 60 61 @SuppressWarnings("Immutable") 62 private final byte[] messageSuffix; 63 64 @SuppressWarnings("Immutable") 65 private final Provider provider; 66 67 // This converter is not used with a proto but rather with an ordinary enum type. 68 static final EnumTypeProtoConverter<HashType, EcdsaParameters.HashType> HASH_TYPE_CONVERTER = 69 EnumTypeProtoConverter.<HashType, EcdsaParameters.HashType>builder() 70 .add(HashType.SHA256, EcdsaParameters.HashType.SHA256) 71 .add(HashType.SHA384, EcdsaParameters.HashType.SHA384) 72 .add(HashType.SHA512, EcdsaParameters.HashType.SHA512) 73 .build(); 74 static final EnumTypeProtoConverter<EcdsaEncoding, EcdsaParameters.SignatureEncoding> 75 ENCODING_CONVERTER = 76 EnumTypeProtoConverter.<EcdsaEncoding, EcdsaParameters.SignatureEncoding>builder() 77 .add(EcdsaEncoding.IEEE_P1363, EcdsaParameters.SignatureEncoding.IEEE_P1363) 78 .add(EcdsaEncoding.DER, EcdsaParameters.SignatureEncoding.DER) 79 .build(); 80 static final EnumTypeProtoConverter<CurveType, EcdsaParameters.CurveType> CURVE_TYPE_CONVERTER = 81 EnumTypeProtoConverter.<CurveType, EcdsaParameters.CurveType>builder() 82 .add(CurveType.NIST_P256, EcdsaParameters.CurveType.NIST_P256) 83 .add(CurveType.NIST_P384, EcdsaParameters.CurveType.NIST_P384) 84 .add(CurveType.NIST_P521, EcdsaParameters.CurveType.NIST_P521) 85 .build(); 86 87 @AccessesPartialKey create(EcdsaPublicKey key)88 public static PublicKeyVerify create(EcdsaPublicKey key) throws GeneralSecurityException { 89 ECPublicKey publicKey = 90 EllipticCurves.getEcPublicKey( 91 CURVE_TYPE_CONVERTER.toProtoEnum(key.getParameters().getCurveType()), 92 key.getPublicPoint().getAffineX().toByteArray(), 93 key.getPublicPoint().getAffineY().toByteArray()); 94 95 return new EcdsaVerifyJce( 96 publicKey, 97 HASH_TYPE_CONVERTER.toProtoEnum(key.getParameters().getHashType()), 98 ENCODING_CONVERTER.toProtoEnum(key.getParameters().getSignatureEncoding()), 99 key.getOutputPrefix().toByteArray(), 100 key.getParameters().getVariant().equals(EcdsaParameters.Variant.LEGACY) 101 ? LEGACY_MESSAGE_SUFFIX 102 : EMPTY); 103 } 104 EcdsaVerifyJce( final ECPublicKey pubKey, HashType hash, EcdsaEncoding encoding, byte[] outputPrefix, byte[] messageSuffix)105 private EcdsaVerifyJce( 106 final ECPublicKey pubKey, 107 HashType hash, 108 EcdsaEncoding encoding, 109 byte[] outputPrefix, 110 byte[] messageSuffix) 111 throws GeneralSecurityException { 112 if (!FIPS.isCompatible()) { 113 throw new GeneralSecurityException( 114 "Can not use ECDSA in FIPS-mode, as BoringCrypto is not available."); 115 } 116 117 EllipticCurves.checkPublicKey(pubKey); 118 this.signatureAlgorithm = SubtleUtil.toEcdsaAlgo(hash); 119 this.publicKey = pubKey; 120 this.encoding = encoding; 121 this.outputPrefix = outputPrefix; 122 this.messageSuffix = messageSuffix; 123 this.provider = ConscryptUtil.providerOrNull(); 124 } 125 EcdsaVerifyJce(final ECPublicKey pubKey, HashType hash, EcdsaEncoding encoding)126 public EcdsaVerifyJce(final ECPublicKey pubKey, HashType hash, EcdsaEncoding encoding) 127 throws GeneralSecurityException { 128 this(pubKey, hash, encoding, EMPTY, EMPTY); 129 } 130 getInstance(String signatureAlgorithm)131 private Signature getInstance(String signatureAlgorithm) throws GeneralSecurityException { 132 if (provider != null) { 133 return Signature.getInstance(signatureAlgorithm, provider); 134 } 135 return EngineFactory.SIGNATURE.getInstance(signatureAlgorithm); 136 } 137 noPrefixVerify(final byte[] signature, final byte[] data)138 private void noPrefixVerify(final byte[] signature, final byte[] data) 139 throws GeneralSecurityException { 140 byte[] derSignature = signature; 141 if (encoding == EcdsaEncoding.IEEE_P1363) { 142 EllipticCurve curve = publicKey.getParams().getCurve(); 143 if (signature.length != 2 * EllipticCurves.fieldSizeInBytes(curve)) { 144 throw new GeneralSecurityException("Invalid signature"); 145 } 146 derSignature = EllipticCurves.ecdsaIeee2Der(signature); 147 } 148 if (!EllipticCurves.isValidDerEncoding(derSignature)) { 149 throw new GeneralSecurityException("Invalid signature"); 150 } 151 Signature verifier = getInstance(signatureAlgorithm); 152 verifier.initVerify(publicKey); 153 verifier.update(data); 154 if (messageSuffix.length > 0) { 155 verifier.update(messageSuffix); 156 } 157 boolean verified = false; 158 try { 159 verified = verifier.verify(derSignature); 160 } catch (RuntimeException ex) { 161 verified = false; 162 } 163 if (!verified) { 164 throw new GeneralSecurityException("Invalid signature"); 165 } 166 } 167 168 @Override verify(final byte[] signature, final byte[] data)169 public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException { 170 if (outputPrefix.length == 0) { 171 noPrefixVerify(signature, data); 172 return; 173 } 174 if (!isPrefix(outputPrefix, signature)) { 175 throw new GeneralSecurityException("Invalid signature (output prefix mismatch)"); 176 } 177 byte[] signatureNoPrefix = Arrays.copyOfRange(signature, outputPrefix.length, signature.length); 178 noPrefixVerify(signatureNoPrefix, data); 179 } 180 } 181