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 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.PublicKeyVerify; 23 import com.google.crypto.tink.config.internal.TinkFipsUtil; 24 import com.google.crypto.tink.internal.ConscryptUtil; 25 import com.google.crypto.tink.signature.EcdsaParameters; 26 import com.google.crypto.tink.signature.EcdsaPrivateKey; 27 import com.google.crypto.tink.subtle.EllipticCurves.CurveType; 28 import com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding; 29 import com.google.crypto.tink.subtle.Enums.HashType; 30 import com.google.errorprone.annotations.Immutable; 31 import java.security.GeneralSecurityException; 32 import java.security.Provider; 33 import java.security.Signature; 34 import java.security.interfaces.ECPrivateKey; 35 import java.security.spec.EllipticCurve; 36 37 /** 38 * ECDSA signing with JCE. 39 * 40 * @since 1.0.0 41 */ 42 @Immutable 43 public final class EcdsaSignJce implements PublicKeySign { 44 public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS = 45 TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO; 46 47 private static final byte[] EMPTY = new byte[0]; 48 private static final byte[] LEGACY_MESSAGE_SUFFIX = new byte[] {0}; 49 private static final byte[] TEST_DATA = new byte[] {1, 2, 3}; 50 51 @SuppressWarnings("Immutable") 52 private final ECPrivateKey privateKey; 53 54 private final String signatureAlgorithm; 55 private final EcdsaEncoding encoding; 56 57 @SuppressWarnings("Immutable") 58 private final byte[] outputPrefix; 59 60 @SuppressWarnings("Immutable") 61 private final byte[] messageSuffix; 62 63 @SuppressWarnings("Immutable") 64 private final Provider provider; 65 EcdsaSignJce( final ECPrivateKey priv, HashType hash, EcdsaEncoding encoding, byte[] outputPrefix, byte[] messageSuffix)66 private EcdsaSignJce( 67 final ECPrivateKey priv, 68 HashType hash, 69 EcdsaEncoding encoding, 70 byte[] outputPrefix, 71 byte[] messageSuffix) 72 throws GeneralSecurityException { 73 if (!FIPS.isCompatible()) { 74 throw new GeneralSecurityException( 75 "Can not use ECDSA in FIPS-mode, as BoringCrypto is not available."); 76 } 77 78 this.privateKey = priv; 79 this.signatureAlgorithm = SubtleUtil.toEcdsaAlgo(hash); 80 this.encoding = encoding; 81 this.outputPrefix = outputPrefix; 82 this.messageSuffix = messageSuffix; 83 this.provider = ConscryptUtil.providerOrNull(); 84 } 85 EcdsaSignJce(final ECPrivateKey priv, HashType hash, EcdsaEncoding encoding)86 public EcdsaSignJce(final ECPrivateKey priv, HashType hash, EcdsaEncoding encoding) 87 throws GeneralSecurityException { 88 this(priv, hash, encoding, EMPTY, EMPTY); 89 } 90 91 @AccessesPartialKey create(EcdsaPrivateKey key)92 public static PublicKeySign create(EcdsaPrivateKey key) throws GeneralSecurityException { 93 HashType hashType = 94 EcdsaVerifyJce.HASH_TYPE_CONVERTER.toProtoEnum(key.getParameters().getHashType()); 95 EcdsaEncoding ecdsaEncoding = 96 EcdsaVerifyJce.ENCODING_CONVERTER.toProtoEnum(key.getParameters().getSignatureEncoding()); 97 CurveType curveType = 98 EcdsaVerifyJce.CURVE_TYPE_CONVERTER.toProtoEnum(key.getParameters().getCurveType()); 99 100 ECPrivateKey privateKey = 101 EllipticCurves.getEcPrivateKey( 102 curveType, 103 key.getPrivateValue().getBigInteger(InsecureSecretKeyAccess.get()).toByteArray()); 104 105 PublicKeySign signer = 106 new EcdsaSignJce( 107 privateKey, 108 hashType, 109 ecdsaEncoding, 110 key.getOutputPrefix().toByteArray(), 111 key.getParameters().getVariant().equals(EcdsaParameters.Variant.LEGACY) 112 ? LEGACY_MESSAGE_SUFFIX 113 : EMPTY); 114 PublicKeyVerify verify = EcdsaVerifyJce.create(key.getPublicKey()); 115 try { 116 verify.verify(signer.sign(TEST_DATA), TEST_DATA); 117 } catch (GeneralSecurityException e) { 118 throw new GeneralSecurityException( 119 "ECDSA signing with private key followed by verifying with public key failed." 120 + " The key may be corrupted.", 121 e); 122 } 123 return signer; 124 } 125 getInstance(String signatureAlgorithm)126 private Signature getInstance(String signatureAlgorithm) throws GeneralSecurityException { 127 if (provider != null) { 128 return Signature.getInstance(signatureAlgorithm, provider); 129 } 130 return EngineFactory.SIGNATURE.getInstance(signatureAlgorithm); 131 } 132 133 @Override sign(final byte[] data)134 public byte[] sign(final byte[] data) throws GeneralSecurityException { 135 Signature signer = getInstance(signatureAlgorithm); 136 signer.initSign(privateKey); 137 signer.update(data); 138 if (messageSuffix.length > 0) { 139 signer.update(messageSuffix); 140 } 141 byte[] signature = signer.sign(); 142 if (encoding == EcdsaEncoding.IEEE_P1363) { 143 EllipticCurve curve = privateKey.getParams().getCurve(); 144 signature = 145 EllipticCurves.ecdsaDer2Ieee(signature, 2 * EllipticCurves.fieldSizeInBytes(curve)); 146 } 147 if (outputPrefix.length == 0) { 148 return signature; 149 } else { 150 return Bytes.concat(outputPrefix, signature); 151 } 152 } 153 } 154