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