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 }