1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.security.MessageDigestSpi;
20import java.security.NoSuchAlgorithmException;
21
22/**
23 * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
24 */
25public class OpenSSLMessageDigestJDK extends MessageDigestSpi implements Cloneable {
26    private OpenSSLDigestContext ctx;
27
28    /**
29     * Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1");
30     */
31    private final long evp_md;
32
33    /**
34     * Holds the output size of the message digest.
35     */
36    private final int size;
37
38    /**
39     * Holds a dummy buffer for writing single bytes to the digest.
40     */
41    private final byte[] singleByte = new byte[1];
42
43    /**
44     * Creates a new OpenSSLMessageDigest instance for the given algorithm name.
45     */
46    private OpenSSLMessageDigestJDK(long evp_md, int size) throws NoSuchAlgorithmException {
47        this.evp_md = evp_md;
48        this.size = size;
49
50        resetContext();
51    }
52
53    private OpenSSLMessageDigestJDK(long evp_md, int size, OpenSSLDigestContext ctx) {
54        this.evp_md = evp_md;
55        this.size = size;
56        this.ctx = ctx;
57    }
58
59    private final void resetContext() {
60        OpenSSLDigestContext ctxLocal = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
61        NativeCrypto.EVP_MD_CTX_init(ctxLocal);
62        NativeCrypto.EVP_DigestInit(ctxLocal, evp_md);
63        ctx = ctxLocal;
64    }
65
66    @Override
67    protected void engineReset() {
68        resetContext();
69    }
70
71    @Override
72    protected int engineGetDigestLength() {
73        return size;
74    }
75
76    @Override
77    protected void engineUpdate(byte input) {
78        singleByte[0] = input;
79        engineUpdate(singleByte, 0, 1);
80    }
81
82    @Override
83    protected void engineUpdate(byte[] input, int offset, int len) {
84        NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
85    }
86
87    @Override
88    protected byte[] engineDigest() {
89        final byte[] result = new byte[size];
90        NativeCrypto.EVP_DigestFinal(ctx, result, 0);
91        resetContext();
92        return result;
93    }
94
95    public static class MD5 extends OpenSSLMessageDigestJDK {
96        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("md5");
97        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
98        public MD5() throws NoSuchAlgorithmException {
99            super(EVP_MD, SIZE);
100        }
101    }
102
103    public static class SHA1 extends OpenSSLMessageDigestJDK {
104        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1");
105        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
106        public SHA1() throws NoSuchAlgorithmException {
107            super(EVP_MD, SIZE);
108        }
109    }
110
111    public static class SHA224 extends OpenSSLMessageDigestJDK {
112        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha224");
113        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
114        public SHA224() throws NoSuchAlgorithmException {
115            super(EVP_MD, SIZE);
116        }
117    }
118
119    public static class SHA256 extends OpenSSLMessageDigestJDK {
120        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256");
121        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
122        public SHA256() throws NoSuchAlgorithmException {
123            super(EVP_MD, SIZE);
124        }
125    }
126
127    public static class SHA384 extends OpenSSLMessageDigestJDK {
128        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384");
129        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
130        public SHA384() throws NoSuchAlgorithmException {
131            super(EVP_MD, SIZE);
132        }
133    }
134
135    public static class SHA512 extends OpenSSLMessageDigestJDK {
136        private static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512");
137        private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD);
138        public SHA512() throws NoSuchAlgorithmException {
139            super(EVP_MD, SIZE);
140        }
141    }
142
143    @Override
144    public Object clone() {
145        OpenSSLDigestContext ctxCopy = new OpenSSLDigestContext(NativeCrypto.EVP_MD_CTX_create());
146        NativeCrypto.EVP_MD_CTX_init(ctxCopy);
147        NativeCrypto.EVP_MD_CTX_copy(ctxCopy, ctx);
148        return new OpenSSLMessageDigestJDK(evp_md, size, ctxCopy);
149    }
150}
151