1 /*
   2  * Copyright (c) 2014, 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. This class currently supports
  37  * MD5, SHA1, SHA256, SHA384, and SHA512
  38  *
  39  * @since 1.9
  40  */
  41 public abstract class NativeDigest 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<NativeDigest>
  58         implements Comparable<DigestContextRef> {
  59 
  60         private static ReferenceQueue<NativeDigest> refQueue =
  61             new ReferenceQueue<NativeDigest>();
  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(NativeDigest 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                     NativeDigest.nativeFree(mech, id);
 103                 } else UcryptoProvider.debug("Resource: stop tracking Digest Ctxt " + this.id);
 104             } finally {
 105                 this.clear();
 106             }
 107         }
 108     }
 109 
 110     NativeDigest(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         NativeDigest copy = (NativeDigest) 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 native long nativeInit(int mech);
 205     // return status code; always 0
 206     protected static native int nativeUpdate(int mech, long pCtxt, byte[] in, int ofs, int inLen);
 207     // return status code; always 0
 208     protected static native int nativeDigest(int mech, long pCtxt, byte[] out, int ofs, int digestLen);
 209     // return pointer to the duplicated context
 210     protected static native long nativeClone(int mech, long pCtxt);
 211     // free the specified context
 212     private native static void nativeFree(int mech, long id);
 213 
 214 
 215     public static final class MD5 extends NativeDigest {
 216         public MD5() {
 217             super(MECH_MD5, 16);
 218         }
 219     }
 220 
 221     public static final class SHA1 extends NativeDigest {
 222         public SHA1() {
 223             super(MECH_SHA1, 20);
 224         }
 225     }
 226 
 227     public static final class SHA256 extends NativeDigest {
 228         public SHA256() {
 229             super(MECH_SHA256, 32);
 230         }
 231     }
 232 
 233 
 234     public static final class SHA384 extends NativeDigest {
 235         public SHA384() {
 236             super(MECH_SHA384, 48);
 237         }
 238     }
 239 
 240 
 241     public static final class SHA512 extends NativeDigest {
 242         public SHA512() {
 243             super(MECH_SHA512, 64);
 244         }
 245     }
 246 }