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