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
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 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 */
42abstract 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}
254