Home | History | Annotate | Download | only in conscrypt
      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.security.InvalidKeyException;
     20 import java.security.InvalidParameterException;
     21 import java.security.NoSuchAlgorithmException;
     22 import java.security.PrivateKey;
     23 import java.security.PublicKey;
     24 import java.security.SignatureException;
     25 import java.security.SignatureSpi;
     26 
     27 /**
     28  * Implements the subset of the JDK Signature interface needed for
     29  * signature verification using OpenSSL.
     30  */
     31 public 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