1 /* 2 * Copyright (c) 2014, 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 26 package com.oracle.security.ucrypto; 27 28 import java.lang.ref.*; 29 30 import java.io.ByteArrayOutputStream; 31 import java.util.*; 32 import java.util.concurrent.ConcurrentSkipListSet; 33 import java.security.*; 34 35 /** 36 * MessageDigest implementation class using native Ucrypto API. 37 * This class currently supports: MD5, SHA-2 (224, 256, 384, 512) 38 * and SHA-3 (224, 256, 384, 512) digests 39 * 40 * @since 9 41 */ 42 abstract class NativeDigest extends MessageDigestSpi { 43 44 public static final class MD5 extends NativeDigest { 45 public MD5() { 46 super(UcryptoMech.CRYPTO_MD5, 16); 47 } 48 } 49 public static final class SHA1 extends NativeDigest { 50 public SHA1() { 51 super(UcryptoMech.CRYPTO_SHA1, 20); 52 } 53 } 54 public static final class SHA224 extends NativeDigest { 55 public SHA224() { 56 super(UcryptoMech.CRYPTO_SHA224, 28); 57 } 58 } 59 public static final class SHA256 extends NativeDigest { 60 public SHA256() { 61 super(UcryptoMech.CRYPTO_SHA256, 32); 62 } 63 } 64 public static final class SHA384 extends NativeDigest { 65 public SHA384() { 66 super(UcryptoMech.CRYPTO_SHA384, 48); 67 } 68 } 69 public static final class SHA512 extends NativeDigest { 70 public SHA512() { 71 super(UcryptoMech.CRYPTO_SHA512, 64); 72 } 73 } 74 public static final class SHA3_224 extends NativeDigest { 75 public SHA3_224() { 76 super(UcryptoMech.CRYPTO_SHA3_224, 28); 77 } 78 } 79 public static final class SHA3_256 extends NativeDigest { 80 public SHA3_256() { 81 super(UcryptoMech.CRYPTO_SHA3_256, 32); 82 } 83 } 84 public static final class SHA3_384 extends NativeDigest { 85 public SHA3_384() { 86 super(UcryptoMech.CRYPTO_SHA3_384, 48); 87 } 88 } 89 public static final class SHA3_512 extends NativeDigest { 90 public SHA3_512() { 91 super(UcryptoMech.CRYPTO_SHA3_512, 64); 92 } 93 } 94 95 private final int digestLen; 96 private final UcryptoMech mech; 97 98 // field for ensuring native memory is freed 99 private DigestContextRef pCtxt = null; 100 101 private static class DigestContextRef extends PhantomReference<NativeDigest> 102 implements Comparable<DigestContextRef> { 103 104 private static ReferenceQueue<NativeDigest> refQueue = 105 new ReferenceQueue<NativeDigest>(); 106 107 // Needed to keep these references from being GC'ed until when their 108 // referents are GC'ed so we can do post-mortem processing 109 private static Set<DigestContextRef> refList = 110 new ConcurrentSkipListSet<DigestContextRef>(); 111 112 private final long id; 113 private final UcryptoMech mech; 114 115 private static void drainRefQueueBounded() { 116 while (true) { 117 DigestContextRef next = (DigestContextRef) refQueue.poll(); 118 if (next == null) break; 119 next.dispose(true); 120 } 121 } 122 123 DigestContextRef(NativeDigest nc, long id, UcryptoMech mech) { 124 super(nc, refQueue); 125 this.id = id; 126 this.mech = mech; 127 refList.add(this); 128 UcryptoProvider.debug("Resource: track Digest Ctxt " + this.id); 129 drainRefQueueBounded(); 130 } 131 132 public int compareTo(DigestContextRef other) { 133 if (this.id == other.id) { 134 return 0; 135 } else { 136 return (this.id < other.id) ? -1 : 1; 137 } 138 } 139 140 void dispose(boolean needFree) { 141 refList.remove(this); 142 try { 143 if (needFree) { 144 UcryptoProvider.debug("Resource: free Digest Ctxt " + 145 this.id); 146 NativeDigest.nativeFree(mech.value(), id); 147 } else { 148 UcryptoProvider.debug("Resource: discard Digest Ctxt " + 149 this.id); 150 } 151 } finally { 152 this.clear(); 153 } 154 } 155 } 156 157 NativeDigest(UcryptoMech mech, int digestLen) { 158 this.mech = mech; 159 this.digestLen = digestLen; 160 } 161 162 // see JCA spec 163 protected int engineGetDigestLength() { 164 return digestLen; 165 } 166 167 // see JCA spec 168 protected synchronized void engineReset() { 169 if (pCtxt != null) { 170 pCtxt.dispose(true); 171 pCtxt = null; 172 } 173 } 174 175 // see JCA spec 176 protected synchronized byte[] engineDigest() { 177 byte[] digest = new byte[digestLen]; 178 try { 179 int len = engineDigest(digest, 0, digestLen); 180 if (len != digestLen) { 181 throw new UcryptoException("Digest length mismatch." + 182 " Len: " + len + ". digestLen: " + digestLen); 183 } 184 return digest; 185 } catch (DigestException de) { 186 throw new UcryptoException("Internal error", de); 187 } 188 } 189 190 // see JCA spec 191 protected synchronized int engineDigest(byte[] out, int ofs, int len) 192 throws DigestException { 193 if (len < digestLen) { 194 throw new DigestException("Output buffer must be at least " + 195 digestLen + " bytes long. Got: " + len); 196 } 197 if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) { 198 throw new DigestException("Buffer too short to store digest. " + 199 "ofs: " + ofs + ". len: " + len + ". out.length: " + out.length); 200 } 201 202 if (pCtxt == null) { 203 pCtxt = new DigestContextRef(this, nativeInit(mech.value()), mech); 204 } 205 try { 206 int status = nativeDigest(mech.value(), pCtxt.id, out, ofs, digestLen); 207 if (status != 0) { 208 throw new DigestException("Internal error: " + status); 209 } 210 } finally { 211 pCtxt.dispose(false); 212 pCtxt = null; 213 } 214 return digestLen; 215 } 216 217 // see JCA spec 218 protected synchronized void engineUpdate(byte in) { 219 byte[] temp = { in }; 220 engineUpdate(temp, 0, 1); 221 } 222 223 // see JCA spec 224 protected synchronized void engineUpdate(byte[] in, int ofs, int len) { 225 if (len == 0) { 226 return; 227 } 228 if ((ofs < 0) || (len < 0) || (ofs > in.length - len)) { 229 throw new ArrayIndexOutOfBoundsException("ofs: " + ofs + ". len: " 230 + len + ". in.length: " + in.length); 231 } 232 if (pCtxt == null) { 233 pCtxt = new DigestContextRef(this, nativeInit(mech.value()), mech); 234 } 235 nativeUpdate(mech.value(), pCtxt.id, in, ofs, len); 236 } 237 238 /** 239 * Clone this digest. 240 */ 241 public synchronized Object clone() throws CloneNotSupportedException { 242 throw new CloneNotSupportedException("Clone is not supported"); 243 } 244 245 // return pointer to the context 246 protected static final native long nativeInit(int mech); 247 // return status code; always 0 248 protected static final native int nativeUpdate(int mech, long pCtxt, byte[] in, int ofs, int inLen); 249 // return status code; always 0 250 protected static final native int nativeDigest(int mech, long pCtxt, byte[] out, int ofs, int digestLen); 251 // free the specified context 252 private static final native void nativeFree(int mech, long id); 253 }