• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package org.conscrypt;
18 
19 import java.nio.ByteBuffer;
20 import java.security.AlgorithmParameters;
21 import java.security.InvalidAlgorithmParameterException;
22 import java.security.InvalidKeyException;
23 import java.security.InvalidParameterException;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.PrivateKey;
26 import java.security.ProviderException;
27 import java.security.PublicKey;
28 import java.security.SignatureException;
29 import java.security.SignatureSpi;
30 import java.security.spec.AlgorithmParameterSpec;
31 import java.security.spec.InvalidParameterSpecException;
32 import java.security.spec.MGF1ParameterSpec;
33 import java.security.spec.PSSParameterSpec;
34 
35 /**
36  * Implements the subset of the JDK Signature interface needed for
37  * signature verification using OpenSSL.
38  *
39  * @hide
40  */
41 @Internal
42 public class OpenSSLSignature extends SignatureSpi {
43     private static enum EngineType {
44         RSA, EC,
45     }
46 
47     private NativeRef.EVP_MD_CTX ctx;
48 
49     /**
50      * The current OpenSSL key we're operating on.
51      */
52     private OpenSSLKey key;
53 
54     /**
55      * Holds the type of the Java algorithm.
56      */
57     private final EngineType engineType;
58 
59     /**
60      * Digest algorithm (reference to {@code EVP_MD}).
61      */
62     private final long evpMdRef;
63 
64     /**
65      * Holds a dummy buffer for writing single bytes to the digest.
66      */
67     private final byte[] singleByte = new byte[1];
68 
69     /**
70      * True when engine is initialized to signing.
71      */
72     private boolean signing;
73 
74     /**
75      * Public key algorithm context (reference to {@code EVP_PKEY_CTX}).
76      */
77     private long evpPkeyCtx;
78 
79     /**
80      * Creates a new OpenSSLSignature instance for the given algorithm name.
81      *
82      * @param evpMdRef digest algorithm ({@code EVP_MD} reference).
83      */
OpenSSLSignature(long evpMdRef, EngineType engineType)84     private OpenSSLSignature(long evpMdRef, EngineType engineType) {
85         this.engineType = engineType;
86         this.evpMdRef = evpMdRef;
87     }
88 
resetContext()89     private final void resetContext() throws InvalidAlgorithmParameterException {
90         NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
91         if (signing) {
92             evpPkeyCtx = NativeCrypto.EVP_DigestSignInit(ctxLocal, evpMdRef, key.getNativeRef());
93         } else {
94             evpPkeyCtx = NativeCrypto.EVP_DigestVerifyInit(ctxLocal, evpMdRef, key.getNativeRef());
95         }
96         configureEVP_PKEY_CTX(evpPkeyCtx);
97         this.ctx = ctxLocal;
98     }
99 
100     /**
101      * Configures the public key algorithm context ({@code EVP_PKEY_CTX}) associated with this
102      * operation.
103      *
104      * <p>The default implementation does nothing.
105      *
106      * @param ctx reference to the context ({@code EVP_PKEY_CTX}).
107      */
configureEVP_PKEY_CTX(long ctx)108     protected void configureEVP_PKEY_CTX(long ctx) throws InvalidAlgorithmParameterException {}
109 
110     @Override
engineUpdate(byte input)111     protected void engineUpdate(byte input) {
112         singleByte[0] = input;
113         engineUpdate(singleByte, 0, 1);
114     }
115 
116     @Override
engineUpdate(byte[] input, int offset, int len)117     protected void engineUpdate(byte[] input, int offset, int len) {
118         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
119         if (signing) {
120             NativeCrypto.EVP_DigestSignUpdate(ctxLocal, input, offset, len);
121         } else {
122             NativeCrypto.EVP_DigestVerifyUpdate(ctxLocal, input, offset, len);
123         }
124     }
125 
126     @Override
engineUpdate(ByteBuffer input)127     protected void engineUpdate(ByteBuffer input) {
128         // Optimization: Avoid copying/allocation for direct buffers because their contents are
129         // stored as a contiguous region in memory and thus can be efficiently accessed from native
130         // code.
131 
132         if (!input.hasRemaining()) {
133             return;
134         }
135 
136         if (!input.isDirect()) {
137             super.engineUpdate(input);
138             return;
139         }
140 
141         long baseAddress = NativeCrypto.getDirectBufferAddress(input);
142         if (baseAddress == 0) {
143             // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
144             // is good enough to handle this.
145             super.engineUpdate(input);
146             return;
147         }
148 
149         // Process the contents between Buffer's position and limit (remaining() number of bytes)
150         int position = input.position();
151         if (position < 0) {
152             throw new RuntimeException("Negative position");
153         }
154         long ptr = baseAddress + position;
155         int len = input.remaining();
156         if (len < 0) {
157             throw new RuntimeException("Negative remaining amount");
158         }
159 
160         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
161         if (signing) {
162             NativeCrypto.EVP_DigestSignUpdateDirect(ctxLocal, ptr, len);
163         } else {
164             NativeCrypto.EVP_DigestVerifyUpdateDirect(ctxLocal, ptr, len);
165         }
166         input.position(position + len);
167     }
168 
169     @Deprecated
170     @Override
engineGetParameter(String param)171     protected Object engineGetParameter(String param) throws InvalidParameterException {
172         return null;
173     }
174 
checkEngineType(OpenSSLKey pkey)175     private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException {
176         final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getNativeRef());
177 
178         switch (engineType) {
179             case RSA:
180                 if (pkeyType != NativeConstants.EVP_PKEY_RSA) {
181                     throw new InvalidKeyException("Signature initialized as " + engineType
182                             + " (not RSA)");
183                 }
184                 break;
185             case EC:
186                 if (pkeyType != NativeConstants.EVP_PKEY_EC) {
187                     throw new InvalidKeyException("Signature initialized as " + engineType
188                             + " (not EC)");
189                 }
190                 break;
191             default:
192                 throw new InvalidKeyException("Key must be of type " + engineType);
193         }
194     }
195 
initInternal(OpenSSLKey newKey, boolean signing)196     private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException {
197         checkEngineType(newKey);
198         key = newKey;
199 
200         this.signing = signing;
201         try {
202             resetContext();
203         } catch (InvalidAlgorithmParameterException e) {
204             throw new InvalidKeyException(e);
205         }
206     }
207 
208     @Override
engineInitSign(PrivateKey privateKey)209     protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
210         initInternal(OpenSSLKey.fromPrivateKey(privateKey), true);
211     }
212 
213     @Override
engineInitVerify(PublicKey publicKey)214     protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
215         initInternal(OpenSSLKey.fromPublicKey(publicKey), false);
216     }
217 
218     @Deprecated
219     @Override
engineSetParameter(String param, Object value)220     protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
221     }
222 
223     @Override
224     @SuppressWarnings("Finally")
engineSign()225     protected byte[] engineSign() throws SignatureException {
226         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
227         try {
228             return NativeCrypto.EVP_DigestSignFinal(ctxLocal);
229         } catch (Exception ex) {
230             throw new SignatureException(ex);
231         } finally {
232             /*
233              * Java expects the digest context to be reset completely after sign
234              * calls.
235              */
236             try {
237                 resetContext();
238             } catch (InvalidAlgorithmParameterException e) {
239                 throw new AssertionError("Reset of context failed after it was successful once");
240             }
241         }
242     }
243 
244     @Override
245     @SuppressWarnings("Finally")
engineVerify(byte[] sigBytes)246     protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
247         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
248         try {
249             return NativeCrypto.EVP_DigestVerifyFinal(ctxLocal, sigBytes, 0, sigBytes.length);
250         } catch (Exception ex) {
251             throw new SignatureException(ex);
252         } finally {
253             /*
254              * Java expects the digest context to be reset completely after
255              * verify calls.
256              */
257             try {
258                 resetContext();
259             } catch (InvalidAlgorithmParameterException e) {
260                 throw new AssertionError("Reset of context failed after it was successful once");
261             }
262         }
263     }
264 
265     /**
266      * Returns the public key algorithm context ({@code EVP_PKEY_CTX} reference) associated with
267      * this operation or {@code 0} if operation hasn't been initialized.
268      */
getEVP_PKEY_CTX()269     protected final long getEVP_PKEY_CTX() {
270         return evpPkeyCtx;
271     }
272 
273     /**
274      * Base class for {@code RSASSA-PKCS1-v1_5} signatures.
275      */
276     abstract static class RSAPKCS1Padding extends OpenSSLSignature {
RSAPKCS1Padding(long evpMdRef)277         RSAPKCS1Padding(long evpMdRef) {
278             super(evpMdRef, EngineType.RSA);
279         }
280 
281         @Override
configureEVP_PKEY_CTX(long ctx)282         protected final void configureEVP_PKEY_CTX(long ctx)
283                 throws InvalidAlgorithmParameterException {
284             NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PADDING);
285         }
286     }
287 
288     public static final class MD5RSA extends RSAPKCS1Padding {
MD5RSA()289         public MD5RSA() {
290             super(EvpMdRef.MD5.EVP_MD);
291         }
292     }
293     public static final class SHA1RSA extends RSAPKCS1Padding {
SHA1RSA()294         public SHA1RSA() {
295             super(EvpMdRef.SHA1.EVP_MD);
296         }
297     }
298     public static final class SHA224RSA extends RSAPKCS1Padding {
SHA224RSA()299         public SHA224RSA() {
300             super(EvpMdRef.SHA224.EVP_MD);
301         }
302     }
303     public static final class SHA256RSA extends RSAPKCS1Padding {
SHA256RSA()304         public SHA256RSA() {
305             super(EvpMdRef.SHA256.EVP_MD);
306         }
307     }
308     public static final class SHA384RSA extends RSAPKCS1Padding {
SHA384RSA()309         public SHA384RSA() {
310             super(EvpMdRef.SHA384.EVP_MD);
311         }
312     }
313     public static final class SHA512RSA extends RSAPKCS1Padding {
SHA512RSA()314         public SHA512RSA() {
315             super(EvpMdRef.SHA512.EVP_MD);
316         }
317     }
318 
319     public static final class SHA1ECDSA extends OpenSSLSignature {
SHA1ECDSA()320         public SHA1ECDSA() {
321             super(EvpMdRef.SHA1.EVP_MD, EngineType.EC);
322         }
323     }
324     public static final class SHA224ECDSA extends OpenSSLSignature {
SHA224ECDSA()325         public SHA224ECDSA() {
326             super(EvpMdRef.SHA224.EVP_MD, EngineType.EC);
327         }
328     }
329     public static final class SHA256ECDSA extends OpenSSLSignature {
SHA256ECDSA()330         public SHA256ECDSA() {
331             super(EvpMdRef.SHA256.EVP_MD, EngineType.EC);
332         }
333     }
334     public static final class SHA384ECDSA extends OpenSSLSignature {
SHA384ECDSA()335         public SHA384ECDSA() {
336             super(EvpMdRef.SHA384.EVP_MD, EngineType.EC);
337         }
338     }
339     public static final class SHA512ECDSA extends OpenSSLSignature {
SHA512ECDSA()340         public SHA512ECDSA() {
341             super(EvpMdRef.SHA512.EVP_MD, EngineType.EC);
342         }
343     }
344 
345     /**
346      * Base class for {@code RSASSA-PSS} signatures.
347      */
348     abstract static class RSAPSSPadding extends OpenSSLSignature {
349         private static final int TRAILER_FIELD_BC_ID = 1;
350 
351         private final String contentDigestAlgorithm;
352 
353         private String mgf1DigestAlgorithm;
354         private long mgf1EvpMdRef;
355         private int saltSizeBytes;
356 
RSAPSSPadding( long contentDigestEvpMdRef, String contentDigestAlgorithm, int saltSizeBytes)357         public RSAPSSPadding(
358                 long contentDigestEvpMdRef, String contentDigestAlgorithm, int saltSizeBytes) {
359             super(contentDigestEvpMdRef, EngineType.RSA);
360             this.contentDigestAlgorithm = contentDigestAlgorithm;
361             this.mgf1DigestAlgorithm = contentDigestAlgorithm;
362             this.mgf1EvpMdRef = contentDigestEvpMdRef;
363             this.saltSizeBytes = saltSizeBytes;
364         }
365 
366         @Override
configureEVP_PKEY_CTX(long ctx)367         protected final void configureEVP_PKEY_CTX(long ctx)
368                 throws InvalidAlgorithmParameterException {
369             NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PSS_PADDING);
370             NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1EvpMdRef);
371             NativeCrypto.EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, saltSizeBytes);
372         }
373 
374         @Override
engineSetParameter(AlgorithmParameterSpec params)375         protected final void engineSetParameter(AlgorithmParameterSpec params)
376                 throws InvalidAlgorithmParameterException {
377             if (!(params instanceof PSSParameterSpec)) {
378                 throw new InvalidAlgorithmParameterException(
379                         "Unsupported parameter: " + params + ". Only "
380                                 + PSSParameterSpec.class.getName() + " supported");
381             }
382             PSSParameterSpec spec = (PSSParameterSpec) params;
383             String specContentDigest = EvpMdRef
384                     .getJcaDigestAlgorithmStandardName(spec.getDigestAlgorithm());
385             if (specContentDigest == null) {
386                 throw new InvalidAlgorithmParameterException(
387                         "Unsupported content digest algorithm: " + spec.getDigestAlgorithm());
388             } else if (!contentDigestAlgorithm.equalsIgnoreCase(specContentDigest)) {
389                 throw new InvalidAlgorithmParameterException(
390                         "Changing content digest algorithm not supported");
391             }
392 
393             String specMgfAlgorithm = spec.getMGFAlgorithm();
394             if ((!EvpMdRef.MGF1_ALGORITHM_NAME.equalsIgnoreCase(specMgfAlgorithm))
395                     && (!EvpMdRef.MGF1_OID.equals(specMgfAlgorithm))) {
396                 throw new InvalidAlgorithmParameterException(
397                         "Unsupported MGF algorithm: " + specMgfAlgorithm + ". Only "
398                                 + EvpMdRef.MGF1_ALGORITHM_NAME + " supported");
399             }
400 
401             AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
402             if (!(mgfSpec instanceof MGF1ParameterSpec)) {
403                 throw new InvalidAlgorithmParameterException(
404                         "Unsupported MGF parameters: " + mgfSpec + ". Only "
405                                 + MGF1ParameterSpec.class.getName() + " supported");
406             }
407             MGF1ParameterSpec specMgf1Spec = (MGF1ParameterSpec) spec.getMGFParameters();
408 
409             String specMgf1Digest = EvpMdRef
410                     .getJcaDigestAlgorithmStandardName(specMgf1Spec.getDigestAlgorithm());
411             if (specMgf1Digest == null) {
412                 throw new InvalidAlgorithmParameterException(
413                         "Unsupported MGF1 digest algorithm: " + specMgf1Spec.getDigestAlgorithm());
414             }
415             long specMgf1EvpMdRef;
416             try {
417                 specMgf1EvpMdRef = EvpMdRef
418                         .getEVP_MDByJcaDigestAlgorithmStandardName(specMgf1Digest);
419             } catch (NoSuchAlgorithmException e) {
420                 throw new ProviderException("Failed to obtain EVP_MD for " + specMgf1Digest, e);
421             }
422 
423             int specSaltSizeBytes = spec.getSaltLength();
424             if (specSaltSizeBytes < 0) {
425                 throw new InvalidAlgorithmParameterException(
426                         "Salt length must be non-negative: " + specSaltSizeBytes);
427             }
428 
429             int specTrailer = spec.getTrailerField();
430             if (specTrailer != TRAILER_FIELD_BC_ID) {
431                 throw new InvalidAlgorithmParameterException(
432                         "Unsupported trailer field: " + specTrailer + ". Only "
433                                 + TRAILER_FIELD_BC_ID + " supported");
434             }
435 
436             this.mgf1DigestAlgorithm = specMgf1Digest;
437             this.mgf1EvpMdRef = specMgf1EvpMdRef;
438             this.saltSizeBytes = specSaltSizeBytes;
439 
440             long ctx = getEVP_PKEY_CTX();
441             if (ctx != 0) {
442                 configureEVP_PKEY_CTX(ctx);
443             }
444         }
445 
446         @Override
engineGetParameters()447         protected final AlgorithmParameters engineGetParameters() {
448             try {
449                 AlgorithmParameters result = AlgorithmParameters.getInstance("PSS");
450                 result.init(
451                         new PSSParameterSpec(
452                                 contentDigestAlgorithm,
453                                 EvpMdRef.MGF1_ALGORITHM_NAME,
454                                 new MGF1ParameterSpec(mgf1DigestAlgorithm),
455                                 saltSizeBytes,
456                                 TRAILER_FIELD_BC_ID));
457                 return result;
458             } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) {
459                 throw new ProviderException("Failed to create PSS AlgorithmParameters", e);
460             }
461         }
462     }
463 
464     public static final class SHA1RSAPSS extends RSAPSSPadding {
SHA1RSAPSS()465         public SHA1RSAPSS() {
466             super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.JCA_NAME, EvpMdRef.SHA1.SIZE_BYTES);
467         }
468     }
469 
470     public static final class SHA224RSAPSS extends RSAPSSPadding {
SHA224RSAPSS()471         public SHA224RSAPSS() {
472             super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.JCA_NAME, EvpMdRef.SHA224.SIZE_BYTES);
473         }
474     }
475 
476     public static final class SHA256RSAPSS extends RSAPSSPadding {
SHA256RSAPSS()477         public SHA256RSAPSS() {
478             super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.JCA_NAME, EvpMdRef.SHA256.SIZE_BYTES);
479         }
480     }
481 
482     public static final class SHA384RSAPSS extends RSAPSSPadding {
SHA384RSAPSS()483         public SHA384RSAPSS() {
484             super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.JCA_NAME, EvpMdRef.SHA384.SIZE_BYTES);
485         }
486     }
487 
488     public static final class SHA512RSAPSS extends RSAPSSPadding {
SHA512RSAPSS()489         public SHA512RSAPSS() {
490             super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.JCA_NAME, EvpMdRef.SHA512.SIZE_BYTES);
491         }
492     }
493 }
494