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