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.nio.ByteBuffer;
     20 import java.security.MessageDigestSpi;
     21 import java.security.NoSuchAlgorithmException;
     22 
     23 /**
     24  * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
     25  *
     26  * @hide
     27  */
     28 @Internal
     29 public class OpenSSLMessageDigestJDK extends MessageDigestSpi implements Cloneable {
     30     private final NativeRef.EVP_MD_CTX ctx;
     31 
     32     /**
     33      * Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1");
     34      */
     35     private final long evp_md;
     36 
     37     /**
     38      * Holds the output size of the message digest.
     39      */
     40     private final int size;
     41 
     42     /**
     43      * Holds a dummy buffer for writing single bytes to the digest.
     44      */
     45     private final byte[] singleByte = new byte[1];
     46 
     47     /**
     48      * Whether the digest struct has been initialized inside EVP_MD_CTX.
     49      */
     50     private boolean digestInitializedInContext;
     51 
     52     /**
     53      * Creates a new OpenSSLMessageDigest instance for the given algorithm name.
     54      */
     55     private OpenSSLMessageDigestJDK(long evp_md, int size) throws NoSuchAlgorithmException {
     56         this.evp_md = evp_md;
     57         this.size = size;
     58         NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
     59         this.ctx = ctxLocal;
     60     }
     61 
     62     private OpenSSLMessageDigestJDK(long evp_md, int size, NativeRef.EVP_MD_CTX ctx,
     63             boolean digestInitializedInContext) {
     64         this.evp_md = evp_md;
     65         this.size = size;
     66         this.ctx = ctx;
     67         this.digestInitializedInContext = digestInitializedInContext;
     68     }
     69 
     70     private void ensureDigestInitializedInContext() {
     71         if (!digestInitializedInContext) {
     72             final NativeRef.EVP_MD_CTX ctxLocal = ctx;
     73             NativeCrypto.EVP_DigestInit_ex(ctxLocal, evp_md);
     74             digestInitializedInContext = true;
     75         }
     76     }
     77 
     78     @Override
     79     protected void engineReset() {
     80         // Reset to the same state as at the end of the <init>(long evp_md, int size). We can avoid
     81         // allocating a new EVP_MD_CTX by invoking EVP_MD_CTX_cleanup on the existing one.
     82         // EVP_MD_CTX_cleanup cleans up and reinitializes the EVP_MD_CTX.
     83         final NativeRef.EVP_MD_CTX ctxLocal = ctx;
     84         NativeCrypto.EVP_MD_CTX_cleanup(ctxLocal);
     85         digestInitializedInContext = false;
     86     }
     87 
     88     @Override
     89     protected int engineGetDigestLength() {
     90         return size;
     91     }
     92 
     93     @Override
     94     protected void engineUpdate(byte input) {
     95         singleByte[0] = input;
     96         engineUpdate(singleByte, 0, 1);
     97     }
     98 
     99     @Override
    100     protected void engineUpdate(byte[] input, int offset, int len) {
    101         ensureDigestInitializedInContext();
    102         NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
    103     }
    104 
    105     @Override
    106     protected void engineUpdate(ByteBuffer input) {
    107         // Optimization: Avoid copying/allocation for direct buffers because their contents are
    108         // stored as a contiguous region in memory and thus can be efficiently accessed from native
    109         // code.
    110 
    111         if (!input.hasRemaining()) {
    112             return;
    113         }
    114 
    115         if (!input.isDirect()) {
    116             super.engineUpdate(input);
    117             return;
    118         }
    119 
    120         long baseAddress = NativeCrypto.getDirectBufferAddress(input);
    121         if (baseAddress == 0) {
    122             // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
    123             // is good enough to handle this.
    124             super.engineUpdate(input);
    125             return;
    126         }
    127 
    128         // Digest the contents between Buffer's position and limit (remaining() number of bytes)
    129         int position = input.position();
    130         if (position < 0) {
    131             throw new RuntimeException("Negative position");
    132         }
    133         long ptr = baseAddress + position;
    134         int len = input.remaining();
    135         if (len < 0) {
    136             throw new RuntimeException("Negative remaining amount");
    137         }
    138 
    139         ensureDigestInitializedInContext();
    140         NativeCrypto.EVP_DigestUpdateDirect(ctx, ptr, len);
    141         input.position(position + len);
    142     }
    143 
    144     @Override
    145     protected byte[] engineDigest() {
    146         ensureDigestInitializedInContext();
    147         final byte[] result = new byte[size];
    148         NativeCrypto.EVP_DigestFinal_ex(ctx, result, 0);
    149 
    150         // Optimized reset path:
    151         // 1. No need to wipe EVP_MD_CTX because EVP_DigestFinal_ex has already cleansed any
    152         //    sensitive state from it.
    153         // 2. Require EVP_DigestInit_ex to be invoked before this MessageDigestSpi starts computing
    154         //    a new digest.
    155         digestInitializedInContext = false;
    156 
    157         return result;
    158     }
    159 
    160     public static final class MD5 extends OpenSSLMessageDigestJDK {
    161         public MD5() throws NoSuchAlgorithmException {
    162             super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
    163         }
    164     }
    165 
    166     public static final class SHA1 extends OpenSSLMessageDigestJDK {
    167         public SHA1() throws NoSuchAlgorithmException {
    168             super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
    169         }
    170     }
    171 
    172     public static final class SHA224 extends OpenSSLMessageDigestJDK {
    173         public SHA224() throws NoSuchAlgorithmException {
    174             super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
    175         }
    176     }
    177 
    178     public static final class SHA256 extends OpenSSLMessageDigestJDK {
    179         public SHA256() throws NoSuchAlgorithmException {
    180             super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
    181         }
    182     }
    183 
    184     public static final class SHA384 extends OpenSSLMessageDigestJDK {
    185         public SHA384() throws NoSuchAlgorithmException {
    186             super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
    187         }
    188     }
    189 
    190     public static final class SHA512 extends OpenSSLMessageDigestJDK {
    191         public SHA512() throws NoSuchAlgorithmException {
    192             super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
    193         }
    194     }
    195 
    196     @Override
    197     public Object clone() {
    198         NativeRef.EVP_MD_CTX ctxCopy = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
    199         // EVP_MD_CTX_copy_ex requires that the digest struct of source EVP_MD_CTX is initialized.
    200         // There's no need to invoke EVP_MD_CTX_copy_ex when the digest struct isn't initialized.
    201         if (digestInitializedInContext) {
    202             NativeCrypto.EVP_MD_CTX_copy_ex(ctxCopy, ctx);
    203         }
    204         return new OpenSSLMessageDigestJDK(evp_md, size, ctxCopy, digestInitializedInContext);
    205     }
    206 }
    207