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 {
MD5()45         public MD5() {
46             super(UcryptoMech.CRYPTO_MD5, 16);
47         }
48     }
49     public static final class SHA1 extends NativeDigest {
SHA1()50         public SHA1() {
51             super(UcryptoMech.CRYPTO_SHA1, 20);
52         }
53     }
54     public static final class SHA224 extends NativeDigest {
SHA224()55         public SHA224() {
56             super(UcryptoMech.CRYPTO_SHA224, 28);
57         }
58     }
59     public static final class SHA256 extends NativeDigest {
SHA256()60         public SHA256() {
61             super(UcryptoMech.CRYPTO_SHA256, 32);
62         }
63     }
64     public static final class SHA384 extends NativeDigest {
SHA384()65         public SHA384() {
66             super(UcryptoMech.CRYPTO_SHA384, 48);
67         }
68     }
69     public static final class SHA512 extends NativeDigest {
SHA512()70         public SHA512() {
71             super(UcryptoMech.CRYPTO_SHA512, 64);
72         }
73     }
74     public static final class SHA3_224 extends NativeDigest {
SHA3_224()75         public SHA3_224() {
76             super(UcryptoMech.CRYPTO_SHA3_224, 28);
77         }
78     }
79     public static final class SHA3_256 extends NativeDigest {
SHA3_256()80         public SHA3_256() {
81             super(UcryptoMech.CRYPTO_SHA3_256, 32);
82         }
83     }
84     public static final class SHA3_384 extends NativeDigest {
SHA3_384()85         public SHA3_384() {
86             super(UcryptoMech.CRYPTO_SHA3_384, 48);
87         }
88     }
89     public static final class SHA3_512 extends NativeDigest {
SHA3_512()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 
drainRefQueueBounded()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 
DigestContextRef(NativeDigest nc, long id, UcryptoMech mech)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 
compareTo(DigestContextRef other)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 
dispose(boolean needFree)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 
NativeDigest(UcryptoMech mech, int digestLen)157     NativeDigest(UcryptoMech mech, int digestLen) {
158         this.mech = mech;
159         this.digestLen = digestLen;
160     }
161 
162     // see JCA spec
engineGetDigestLength()163     protected int engineGetDigestLength() {
164         return digestLen;
165     }
166 
167     // see JCA spec
engineReset()168     protected synchronized void engineReset() {
169         if (pCtxt != null) {
170             pCtxt.dispose(true);
171             pCtxt = null;
172         }
173     }
174 
175     // see JCA spec
engineDigest()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
engineDigest(byte[] out, int ofs, int len)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
engineUpdate(byte in)218     protected synchronized void engineUpdate(byte in) {
219         byte[] temp = { in };
220         engineUpdate(temp, 0, 1);
221     }
222 
223     // see JCA spec
engineUpdate(byte[] in, int ofs, int len)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      */
clone()241     public synchronized Object clone() throws CloneNotSupportedException {
242         throw new CloneNotSupportedException("Clone is not supported");
243     }
244 
245     // return pointer to the context
nativeInit(int mech)246     protected static final native long nativeInit(int mech);
247     // return status code; always 0
nativeUpdate(int mech, long pCtxt, byte[] in, int ofs, int inLen)248     protected static final native int nativeUpdate(int mech, long pCtxt, byte[] in, int ofs, int inLen);
249     // return status code; always 0
nativeDigest(int mech, long pCtxt, byte[] out, int ofs, int digestLen)250     protected static final native int nativeDigest(int mech, long pCtxt, byte[] out, int ofs, int digestLen);
251     // free the specified context
nativeFree(int mech, long id)252     private static final native void nativeFree(int mech, long id);
253 }
254