1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.security.InvalidKeyException;
20import java.security.InvalidParameterException;
21import java.security.NoSuchAlgorithmException;
22import java.security.PrivateKey;
23import java.security.PublicKey;
24import java.security.SignatureException;
25import java.security.SignatureSpi;
26
27/**
28 * Implements the subset of the JDK Signature interface needed for
29 * signature verification using OpenSSL.
30 */
31public class OpenSSLSignature extends SignatureSpi {
32    private static enum EngineType {
33        RSA, DSA, EC,
34    };
35
36    private OpenSSLDigestContext ctx;
37
38    /**
39     * The current OpenSSL key we're operating on.
40     */
41    private OpenSSLKey key;
42
43    /**
44     * Holds the type of the Java algorithm.
45     */
46    private final EngineType engineType;
47
48    /**
49     * Holds the OpenSSL name of the algorithm (lower case, no dashes).
50     */
51    private final long evpAlgorithm;
52
53    /**
54     * Holds a dummy buffer for writing single bytes to the digest.
55     */
56    private final byte[] singleByte = new byte[1];
57
58    /**
59     * True when engine is initialized to signing.
60     */
61    private boolean signing;
62
63    /**
64     * Creates a new OpenSSLSignature instance for the given algorithm name.
65     *
66     * @param algorithm OpenSSL name of the algorithm, e.g. "RSA-SHA1".
67     */
68    private OpenSSLSignature(long algorithm, EngineType engineType)
69            throws NoSuchAlgorithmException {
70        this.engineType = engineType;
71        this.evpAlgorithm = algorithm;
72    }
73
74    private final void resetContext() {
75        OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
76        NativeCrypto.EVP_MD_CTX_init(ctxLocal);
77        if (signing) {
78            enableDSASignatureNonceHardeningIfApplicable();
79            NativeCrypto.EVP_SignInit(ctxLocal, evpAlgorithm);
80        } else {
81            NativeCrypto.EVP_VerifyInit(ctxLocal, evpAlgorithm);
82        }
83        this.ctx = ctxLocal;
84    }
85
86    @Override
87    protected void engineUpdate(byte input) {
88        singleByte[0] = input;
89        engineUpdate(singleByte, 0, 1);
90    }
91
92    @Override
93    protected void engineUpdate(byte[] input, int offset, int len) {
94        final OpenSSLDigestContext ctxLocal = ctx;
95        if (signing) {
96            NativeCrypto.EVP_SignUpdate(ctxLocal, input, offset, len);
97        } else {
98            NativeCrypto.EVP_VerifyUpdate(ctxLocal, input, offset, len);
99        }
100    }
101
102    @Override
103    protected Object engineGetParameter(String param) throws InvalidParameterException {
104        return null;
105    }
106
107    private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException {
108        final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getPkeyContext());
109
110        switch (engineType) {
111            case RSA:
112                if (pkeyType != NativeCrypto.EVP_PKEY_RSA) {
113                    throw new InvalidKeyException("Signature initialized as " + engineType
114                            + " (not RSA)");
115                }
116                break;
117            case DSA:
118                if (pkeyType != NativeCrypto.EVP_PKEY_DSA) {
119                    throw new InvalidKeyException("Signature initialized as " + engineType
120                            + " (not DSA)");
121                }
122                break;
123            case EC:
124                if (pkeyType != NativeCrypto.EVP_PKEY_EC) {
125                    throw new InvalidKeyException("Signature initialized as " + engineType
126                            + " (not EC)");
127                }
128                break;
129            default:
130                throw new InvalidKeyException("Key must be of type " + engineType);
131        }
132    }
133
134    private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException {
135        checkEngineType(newKey);
136        key = newKey;
137
138        this.signing = signing;
139        resetContext();
140    }
141
142    @Override
143    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
144        initInternal(OpenSSLKey.fromPrivateKey(privateKey), true);
145    }
146
147    /**
148     * Enables a mitigation against private key leakage through DSA and ECDSA signatures when weak
149     * nonces (per-message k values) are used. To mitigate the issue, private key and message being
150     * signed is mixed into the randomly generated nonce (k).
151     *
152     * <p>Does nothing for signatures that are neither DSA nor ECDSA.
153     */
154    private void enableDSASignatureNonceHardeningIfApplicable() {
155        final OpenSSLKey key = this.key;
156        switch (engineType) {
157            case DSA:
158                NativeCrypto.set_DSA_flag_nonce_from_hash(key.getPkeyContext());
159                break;
160            case EC:
161                NativeCrypto.EC_KEY_set_nonce_from_hash(key.getPkeyContext(), true);
162                break;
163            default:
164              // Hardening not applicable
165        }
166    }
167
168    @Override
169    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
170        initInternal(OpenSSLKey.fromPublicKey(publicKey), false);
171    }
172
173    @Override
174    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
175    }
176
177    @Override
178    protected byte[] engineSign() throws SignatureException {
179        if (key == null) {
180            // This can't actually happen, but you never know...
181            throw new SignatureException("Need DSA or RSA or EC private key");
182        }
183
184        final OpenSSLDigestContext ctxLocal = ctx;
185        try {
186            byte[] buffer = new byte[NativeCrypto.EVP_PKEY_size(key.getPkeyContext())];
187            int bytesWritten = NativeCrypto.EVP_SignFinal(ctxLocal, buffer, 0,
188                    key.getPkeyContext());
189
190            byte[] signature = new byte[bytesWritten];
191            System.arraycopy(buffer, 0, signature, 0, bytesWritten);
192
193            return signature;
194        } catch (Exception ex) {
195            throw new SignatureException(ex);
196        } finally {
197            /*
198             * Java expects the digest context to be reset completely after sign
199             * calls.
200             */
201            resetContext();
202        }
203    }
204
205    @Override
206    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
207        if (key == null) {
208            // This can't actually happen, but you never know...
209            throw new SignatureException("Need DSA or RSA public key");
210        }
211
212        try {
213            int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length,
214                    key.getPkeyContext());
215            return result == 1;
216        } catch (Exception ex) {
217            return false;
218        } finally {
219            /*
220             * Java expects the digest context to be reset completely after
221             * verify calls.
222             */
223            resetContext();
224        }
225    }
226
227    public static final class MD5RSA extends OpenSSLSignature {
228        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-MD5");
229        public MD5RSA() throws NoSuchAlgorithmException {
230            super(EVP_MD, EngineType.RSA);
231        }
232    }
233    public static final class SHA1RSA extends OpenSSLSignature {
234        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA1");
235        public SHA1RSA() throws NoSuchAlgorithmException {
236            super(EVP_MD, EngineType.RSA);
237        }
238    }
239    public static final class SHA224RSA extends OpenSSLSignature {
240        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA224");
241        public SHA224RSA() throws NoSuchAlgorithmException {
242            super(EVP_MD, EngineType.RSA);
243        }
244    }
245    public static final class SHA256RSA extends OpenSSLSignature {
246        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA256");
247        public SHA256RSA() throws NoSuchAlgorithmException {
248            super(EVP_MD, EngineType.RSA);
249        }
250    }
251    public static final class SHA384RSA extends OpenSSLSignature {
252        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA384");
253        public SHA384RSA() throws NoSuchAlgorithmException {
254            super(EVP_MD, EngineType.RSA);
255        }
256    }
257    public static final class SHA512RSA extends OpenSSLSignature {
258        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("RSA-SHA512");
259        public SHA512RSA() throws NoSuchAlgorithmException {
260            super(EVP_MD, EngineType.RSA);
261        }
262    }
263    public static final class SHA1DSA extends OpenSSLSignature {
264        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("DSA-SHA1");
265        public SHA1DSA() throws NoSuchAlgorithmException {
266            super(EVP_MD, EngineType.DSA);
267        }
268    }
269    public static final class SHA1ECDSA extends OpenSSLSignature {
270        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA1");
271        public SHA1ECDSA() throws NoSuchAlgorithmException {
272            super(EVP_MD, EngineType.EC);
273        }
274    }
275    public static final class SHA224ECDSA extends OpenSSLSignature {
276        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA224");
277        public SHA224ECDSA() throws NoSuchAlgorithmException {
278            super(EVP_MD, EngineType.EC);
279        }
280    }
281    public static final class SHA256ECDSA extends OpenSSLSignature {
282        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA256");
283        public SHA256ECDSA() throws NoSuchAlgorithmException {
284            super(EVP_MD, EngineType.EC);
285        }
286    }
287    public static final class SHA384ECDSA extends OpenSSLSignature {
288        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA384");
289        public SHA384ECDSA() throws NoSuchAlgorithmException {
290            super(EVP_MD, EngineType.EC);
291        }
292    }
293    public static final class SHA512ECDSA extends OpenSSLSignature {
294        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("SHA512");
295        public SHA512ECDSA() throws NoSuchAlgorithmException {
296            super(EVP_MD, EngineType.EC);
297        }
298    }
299}
300
301