1db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom/*
2db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * Copyright (C) 2008 The Android Open Source Project
3db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom *
4db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
5db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * you may not use this file except in compliance with the License.
6db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * You may obtain a copy of the License at
7db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom *
8db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
9db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom *
10db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
11db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
12db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * See the License for the specific language governing permissions and
14db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom * limitations under the License.
15db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom */
16db9ae4fb599a534bd0703bec27fccd4493b04be7Brian Carlstrom
17860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootpackage org.conscrypt;
1808ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
1991fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubinimport java.nio.ByteBuffer;
20652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Rootimport java.security.MessageDigestSpi;
2108ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Projectimport java.security.NoSuchAlgorithmException;
2208ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
2308ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project/**
2408ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
255070bdfc6277af136b7eb5fe5d0d72ad2ff6a2ebKenny Root *
265070bdfc6277af136b7eb5fe5d0d72ad2ff6a2ebKenny Root * @hide
2708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project */
28dbe082cb70a1ffbe1a693bd583a06ecad585f46dNathan Mittler@Internal
2988b56b0666fe3a702da4edc7b5b58266033c5871Kenny Rootpublic class OpenSSLMessageDigestJDK extends MessageDigestSpi implements Cloneable {
3075d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin    private final NativeRef.EVP_MD_CTX ctx;
3108ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
3208ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    /**
33079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom     * Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1");
34e47676a3143deb907b770a461f42d865f767932cBrian Carlstrom     */
3538c70d393f14cf0963a289caefb72e6ac14e23d3Joel Dice    private final long evp_md;
36079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom
37079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom    /**
38079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom     * Holds the output size of the message digest.
39079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom     */
40079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom    private final int size;
41e47676a3143deb907b770a461f42d865f767932cBrian Carlstrom
42e47676a3143deb907b770a461f42d865f767932cBrian Carlstrom    /**
4308ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project     * Holds a dummy buffer for writing single bytes to the digest.
4408ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project     */
455763f15af2a820056075dd9007d03d3e855a27adBrian Carlstrom    private final byte[] singleByte = new byte[1];
4608ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
4708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    /**
4875d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin     * Whether the digest struct has been initialized inside EVP_MD_CTX.
4975d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin     */
5075d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin    private boolean digestInitializedInContext;
5175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin
5275d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin    /**
53652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root     * Creates a new OpenSSLMessageDigest instance for the given algorithm name.
5408ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project     */
55652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root    private OpenSSLMessageDigestJDK(long evp_md, int size) throws NoSuchAlgorithmException {
56079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom        this.evp_md = evp_md;
57079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom        this.size = size;
5875d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
5975d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        this.ctx = ctxLocal;
60652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root    }
61652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root
6275d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin    private OpenSSLMessageDigestJDK(long evp_md, int size, NativeRef.EVP_MD_CTX ctx,
6375d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin            boolean digestInitializedInContext) {
6488b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root        this.evp_md = evp_md;
6588b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root        this.size = size;
6688b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root        this.ctx = ctx;
6775d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        this.digestInitializedInContext = digestInitializedInContext;
6888b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root    }
6988b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root
7075d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin    private void ensureDigestInitializedInContext() {
7175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        if (!digestInitializedInContext) {
7275d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin            final NativeRef.EVP_MD_CTX ctxLocal = ctx;
7367cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin            NativeCrypto.EVP_DigestInit_ex(ctxLocal, evp_md);
7475d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin            digestInitializedInContext = true;
7575d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        }
7608ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
7708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
7808ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    @Override
7908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    protected void engineReset() {
8075d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        // Reset to the same state as at the end of the <init>(long evp_md, int size). We can avoid
8175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        // allocating a new EVP_MD_CTX by invoking EVP_MD_CTX_cleanup on the existing one.
8275d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        // EVP_MD_CTX_cleanup cleans up and reinitializes the EVP_MD_CTX.
8375d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
8475d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        NativeCrypto.EVP_MD_CTX_cleanup(ctxLocal);
8575d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        digestInitializedInContext = false;
8608ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
8708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
8808ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    @Override
8908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    protected int engineGetDigestLength() {
90079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom        return size;
9108ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
9208ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
9308ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    @Override
9408ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    protected void engineUpdate(byte input) {
9508ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        singleByte[0] = input;
9608ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        engineUpdate(singleByte, 0, 1);
9708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
9808ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
9908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    @Override
10008ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    protected void engineUpdate(byte[] input, int offset, int len) {
10175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        ensureDigestInitializedInContext();
102652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root        NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
103079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom    }
104079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom
105079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom    @Override
10691fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin    protected void engineUpdate(ByteBuffer input) {
10791fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        // Optimization: Avoid copying/allocation for direct buffers because their contents are
10891fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        // stored as a contiguous region in memory and thus can be efficiently accessed from native
10991fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        // code.
11091fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
11191fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        if (!input.hasRemaining()) {
11291fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            return;
11391fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        }
11491fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
11591fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        if (!input.isDirect()) {
11691fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            super.engineUpdate(input);
11791fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            return;
11891fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        }
11991fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
12091fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        long baseAddress = NativeCrypto.getDirectBufferAddress(input);
12191fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        if (baseAddress == 0) {
12291fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
12391fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            // is good enough to handle this.
12491fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            super.engineUpdate(input);
12591fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin            return;
12691fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        }
12791fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
12891fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        // Digest the contents between Buffer's position and limit (remaining() number of bytes)
12991fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        int position = input.position();
13049ebea12f4954f3dbcd16a33a8fa98532d29f8c9Alex Klyubin        if (position < 0) {
13149ebea12f4954f3dbcd16a33a8fa98532d29f8c9Alex Klyubin            throw new RuntimeException("Negative position");
13291fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        }
13349ebea12f4954f3dbcd16a33a8fa98532d29f8c9Alex Klyubin        long ptr = baseAddress + position;
13491fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        int len = input.remaining();
13549ebea12f4954f3dbcd16a33a8fa98532d29f8c9Alex Klyubin        if (len < 0) {
13649ebea12f4954f3dbcd16a33a8fa98532d29f8c9Alex Klyubin            throw new RuntimeException("Negative remaining amount");
13791fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        }
13891fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
13975d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        ensureDigestInitializedInContext();
14091fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        NativeCrypto.EVP_DigestUpdateDirect(ctx, ptr, len);
14191fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin        input.position(position + len);
14291fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin    }
14391fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin
14491fd701b4587210b794fa1a537c38f306a3241d6Alex Klyubin    @Override
145079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom    protected byte[] engineDigest() {
14675d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        ensureDigestInitializedInContext();
147652ff53bd48ed61389337a42d8e50cdb7ace0fecKenny Root        final byte[] result = new byte[size];
14867cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin        NativeCrypto.EVP_DigestFinal_ex(ctx, result, 0);
14975d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin
15075d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        // Optimized reset path:
15175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        // 1. No need to wipe EVP_MD_CTX because EVP_DigestFinal_ex has already cleansed any
15275d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        //    sensitive state from it.
15367cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin        // 2. Require EVP_DigestInit_ex to be invoked before this MessageDigestSpi starts computing
15467cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin        //    a new digest.
15575d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        digestInitializedInContext = false;
15675d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin
157079fab65ece9d6c13d3f67206550985f5d8439fdBrian Carlstrom        return result;
15808ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
15908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project
1602ecdfa100af0c18814445608ac7b2bc1e11ef4b2Brian Carlstrom    public static class MD5 extends OpenSSLMessageDigestJDK {
16108ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        public MD5() throws NoSuchAlgorithmException {
162b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
16308ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        }
16408ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
16573f1fad27323ed00b318de046cfe43236625af09Elliott Hughes
1662ecdfa100af0c18814445608ac7b2bc1e11ef4b2Brian Carlstrom    public static class SHA1 extends OpenSSLMessageDigestJDK {
16708ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        public SHA1() throws NoSuchAlgorithmException {
168b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
16908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        }
17008ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
17173f1fad27323ed00b318de046cfe43236625af09Elliott Hughes
172d2db2c558ef6afc14d59f4a6b547598ff3973597Kenny Root    public static class SHA224 extends OpenSSLMessageDigestJDK {
173d2db2c558ef6afc14d59f4a6b547598ff3973597Kenny Root        public SHA224() throws NoSuchAlgorithmException {
174b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
175d2db2c558ef6afc14d59f4a6b547598ff3973597Kenny Root        }
176d2db2c558ef6afc14d59f4a6b547598ff3973597Kenny Root    }
177d2db2c558ef6afc14d59f4a6b547598ff3973597Kenny Root
1782ecdfa100af0c18814445608ac7b2bc1e11ef4b2Brian Carlstrom    public static class SHA256 extends OpenSSLMessageDigestJDK {
17908ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        public SHA256() throws NoSuchAlgorithmException {
180b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
18108ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project        }
18208ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project    }
183af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom
1842ecdfa100af0c18814445608ac7b2bc1e11ef4b2Brian Carlstrom    public static class SHA384 extends OpenSSLMessageDigestJDK {
185af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom        public SHA384() throws NoSuchAlgorithmException {
186b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
187af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom        }
188af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom    }
189af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom
1902ecdfa100af0c18814445608ac7b2bc1e11ef4b2Brian Carlstrom    public static class SHA512 extends OpenSSLMessageDigestJDK {
191af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom        public SHA512() throws NoSuchAlgorithmException {
192b234f49221b02ff61705a8cc69dd4fcb8cb1c121Kenny Root            super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
193af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom        }
194af957f4171aee023d696a514744942d6a003bdbfBrian Carlstrom    }
19588b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root
19688b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root    @Override
19788b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root    public Object clone() {
1984bff0a15ae03c8a3e1ae95590cc8c4240837bff6Kenny Root        NativeRef.EVP_MD_CTX ctxCopy = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
19967cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin        // EVP_MD_CTX_copy_ex requires that the digest struct of source EVP_MD_CTX is initialized.
20067cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin        // There's no need to invoke EVP_MD_CTX_copy_ex when the digest struct isn't initialized.
20175d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        if (digestInitializedInContext) {
20267cf2fcd35de497bb4dd7a2cc9bf2a02f0b06349Alex Klyubin            NativeCrypto.EVP_MD_CTX_copy_ex(ctxCopy, ctx);
20375d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        }
20475d8933cc4b5053080612f44f9d975f659d933d2Alex Klyubin        return new OpenSSLMessageDigestJDK(evp_md, size, ctxCopy, digestInitializedInContext);
20588b56b0666fe3a702da4edc7b5b58266033c5871Kenny Root    }
20608ecc8c0f00f1a7f2258c569187e36606ed73045The Android Open Source Project}
207