NativeDigestMD.java revision 14434:9db62c197dcd
1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.oracle.security.ucrypto; 27 28import java.lang.ref.*; 29 30import java.io.ByteArrayOutputStream; 31import java.util.*; 32import java.util.concurrent.ConcurrentSkipListSet; 33import java.security.*; 34 35/** 36 * MessageDigest implementation class for libMD API. This class currently supports 37 * MD5, SHA1, SHA256, SHA384, and SHA512 38 * 39 * @since 9 40 */ 41public abstract class NativeDigestMD extends MessageDigestSpi 42 implements Cloneable { 43 44 private static final int MECH_MD5 = 1; 45 private static final int MECH_SHA1 = 2; 46 private static final int MECH_SHA256 = 3; 47 private static final int MECH_SHA224 = 4; 48 private static final int MECH_SHA384 = 5; 49 private static final int MECH_SHA512 = 6; 50 51 private final int digestLen; 52 private final int mech; 53 54 // field for ensuring native memory is freed 55 private DigestContextRef pCtxt = null; 56 57 private static class DigestContextRef extends PhantomReference<NativeDigestMD> 58 implements Comparable<DigestContextRef> { 59 60 private static ReferenceQueue<NativeDigestMD> refQueue = 61 new ReferenceQueue<NativeDigestMD>(); 62 63 // Needed to keep these references from being GC'ed until when their 64 // referents are GC'ed so we can do post-mortem processing 65 private static Set<DigestContextRef> refList = 66 new ConcurrentSkipListSet<DigestContextRef>(); 67 // Collections.synchronizedSortedSet(new TreeSet<DigestContextRef>()); 68 69 private final long id; 70 private final int mech; 71 72 private static void drainRefQueueBounded() { 73 while (true) { 74 DigestContextRef next = (DigestContextRef) refQueue.poll(); 75 if (next == null) break; 76 next.dispose(true); 77 } 78 } 79 80 DigestContextRef(NativeDigestMD nc, long id, int mech) { 81 super(nc, refQueue); 82 this.id = id; 83 this.mech = mech; 84 refList.add(this); 85 UcryptoProvider.debug("Resource: track Digest Ctxt " + this.id); 86 drainRefQueueBounded(); 87 } 88 89 public int compareTo(DigestContextRef other) { 90 if (this.id == other.id) { 91 return 0; 92 } else { 93 return (this.id < other.id) ? -1 : 1; 94 } 95 } 96 97 void dispose(boolean needFree) { 98 refList.remove(this); 99 try { 100 if (needFree) { 101 UcryptoProvider.debug("Resource: free Digest Ctxt " + this.id); 102 NativeDigestMD.nativeFree(mech, id); 103 } else UcryptoProvider.debug("Resource: stop tracking Digest Ctxt " + this.id); 104 } finally { 105 this.clear(); 106 } 107 } 108 } 109 110 NativeDigestMD(int mech, int digestLen) { 111 this.digestLen = digestLen; 112 this.mech = mech; 113 } 114 115 // see JCA spec 116 protected int engineGetDigestLength() { 117 return digestLen; 118 } 119 120 // see JCA spec 121 protected synchronized void engineReset() { 122 if (pCtxt != null) { 123 pCtxt.dispose(true); 124 pCtxt = null; 125 } 126 } 127 128 // see JCA spec 129 protected synchronized byte[] engineDigest() { 130 byte[] digest = new byte[digestLen]; 131 try { 132 int len = engineDigest(digest, 0, digestLen); 133 if (len != digestLen) { 134 throw new UcryptoException("Digest length mismatch." + 135 " Len: " + len + ". digestLen: " + digestLen); 136 } 137 return digest; 138 } catch (DigestException de) { 139 throw new UcryptoException("Internal error", de); 140 } 141 } 142 143 // see JCA spec 144 protected synchronized int engineDigest(byte[] out, int ofs, int len) 145 throws DigestException { 146 if (len < digestLen) { 147 throw new DigestException("Output buffer must be at least " + 148 digestLen + " bytes long. Got: " + len); 149 } 150 if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) { 151 throw new DigestException("Buffer too short to store digest. " + 152 "ofs: " + ofs + ". len: " + len + ". out.length: " + out.length); 153 } 154 155 if (pCtxt == null) { 156 pCtxt = new DigestContextRef(this, nativeInit(mech), mech); 157 } 158 try { 159 int status = nativeDigest(mech, pCtxt.id, out, ofs, digestLen); 160 if (status != 0) { 161 throw new DigestException("Internal error: " + status); 162 } 163 } finally { 164 pCtxt.dispose(false); 165 pCtxt = null; 166 } 167 return digestLen; 168 } 169 170 // see JCA spec 171 protected synchronized void engineUpdate(byte in) { 172 byte[] temp = { in }; 173 engineUpdate(temp, 0, 1); 174 } 175 176 // see JCA spec 177 protected synchronized void engineUpdate(byte[] in, int ofs, int len) { 178 if (len == 0) { 179 return; 180 } 181 if ((ofs < 0) || (len < 0) || (ofs > in.length - len)) { 182 throw new ArrayIndexOutOfBoundsException("ofs: " + ofs + ". len: " 183 + len + ". in.length: " + in.length); 184 } 185 if (pCtxt == null) { 186 pCtxt = new DigestContextRef(this, nativeInit(mech), mech); 187 } 188 nativeUpdate(mech, pCtxt.id, in, ofs, len); 189 } 190 191 /** 192 * Clone this digest. 193 */ 194 public synchronized Object clone() throws CloneNotSupportedException { 195 NativeDigestMD copy = (NativeDigestMD) super.clone(); 196 // re-work the fields that cannot be copied over 197 if (pCtxt != null) { 198 copy.pCtxt = new DigestContextRef(this, nativeClone(mech, pCtxt.id), mech); 199 } 200 return copy; 201 } 202 203 // return pointer to the context 204 protected static final native long nativeInit(int mech); 205 // return status code; always 0 206 protected static final native int nativeUpdate(int mech, long pCtxt, byte[] in, int ofs, int inLen); 207 // return status code; always 0 208 protected static final native int nativeDigest(int mech, long pCtxt, byte[] out, int ofs, int digestLen); 209 // return pointer to the duplicated context 210 protected static final native long nativeClone(int mech, long pCtxt); 211 // free the specified context 212 private static final native void nativeFree(int mech, long id); 213 214 215 public static final class MD5 extends NativeDigestMD { 216 public MD5() { 217 super(MECH_MD5, 16); 218 } 219 } 220 221 public static final class SHA1 extends NativeDigestMD { 222 public SHA1() { 223 super(MECH_SHA1, 20); 224 } 225 } 226 227 public static final class SHA256 extends NativeDigestMD { 228 public SHA256() { 229 super(MECH_SHA256, 32); 230 } 231 } 232 233 234 public static final class SHA384 extends NativeDigestMD { 235 public SHA384() { 236 super(MECH_SHA384, 48); 237 } 238 } 239 240 241 public static final class SHA512 extends NativeDigestMD { 242 public SHA512() { 243 super(MECH_SHA512, 64); 244 } 245 } 246} 247