• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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