package org.apache.harmony.xnet.provider.jsse;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
 */
public class OpenSSLMessageDigestJDK extends MessageDigest implements Cloneable {

    /**
     * Holds a pointer to the native message digest context.
     */
    private int ctx;

    /**
     * Holds a dummy buffer for writing single bytes to the digest.
     */
    private byte[] singleByte = new byte[1];

    /**
     * Creates a new OpenSSLMessageDigest instance for the given algorithm
     * name.
     *
     * @param algorithm The name of the algorithm, e.g. "SHA-1".
     */
    private OpenSSLMessageDigestJDK(String algorithm) throws NoSuchAlgorithmException {
        super(algorithm);

        // We don't support MD2.
        if ("MD2".equals(algorithm)) {
            throw new NoSuchAlgorithmException(algorithm);
        }

        ctx = NativeCrypto.EVP_MD_CTX_create();
        try {
            NativeCrypto.EVP_DigestInit(ctx, getAlgorithm().replace("-", "").toLowerCase());
        } catch (Exception ex) {
            throw new NoSuchAlgorithmException(ex.getMessage() + " (" + algorithm + ")");
        }
    }

    @Override
    protected byte[] engineDigest() {
        byte[] result = new byte[NativeCrypto.EVP_MD_CTX_size(ctx)];
        int copy = 0;
        try {
            copy = NativeCrypto.EVP_MD_CTX_copy(ctx);
            NativeCrypto.EVP_DigestFinal(copy, result, 0);
            return result;
        } finally {
            if (copy != 0) {
                NativeCrypto.EVP_MD_CTX_destroy(copy);
            }
        }
    }

    @Override
    protected void engineReset() {
        NativeCrypto.EVP_DigestInit(ctx, getAlgorithm().replace("-", "").toLowerCase());
    }

    @Override
    protected int engineGetDigestLength() {
        return NativeCrypto.EVP_MD_CTX_size(ctx);
    }

    @Override
    protected void engineUpdate(byte input) {
        singleByte[0] = input;
        engineUpdate(singleByte, 0, 1);
    }

    @Override
    protected void engineUpdate(byte[] input, int offset, int len) {
        NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
    }

    public Object clone() throws CloneNotSupportedException {
        OpenSSLMessageDigestJDK d = (OpenSSLMessageDigestJDK) super.clone();
        d.ctx = NativeCrypto.EVP_MD_CTX_copy(ctx);
        return d;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        NativeCrypto.EVP_MD_CTX_destroy(ctx);
    }

    public static class MD5 extends OpenSSLMessageDigestJDK {
        public MD5() throws NoSuchAlgorithmException {
            super("MD5");
        }
    }

    public static class SHA1 extends OpenSSLMessageDigestJDK {
        public SHA1() throws NoSuchAlgorithmException {
            super("SHA-1");
        }
    }

    public static class SHA256 extends OpenSSLMessageDigestJDK {
        public SHA256() throws NoSuchAlgorithmException {
            super("SHA-256");
        }
    }

    public static class SHA384 extends OpenSSLMessageDigestJDK {
        public SHA384() throws NoSuchAlgorithmException {
            super("SHA-384");
        }
    }

    public static class SHA512 extends OpenSSLMessageDigestJDK {
        public SHA512() throws NoSuchAlgorithmException {
            super("SHA-512");
        }
    }
}

@@ -386,16 +386,140 @@
 t ctx;%0A%0A
+    /**%0A     * The OpenSSL version of the algorithm name for later use by reset.%0A     */%0A    private final String openssl;%0A%0A
     /**%0A
@@ -771,16 +771,25 @@
 thm The 
+standard 
 name of 
@@ -817,16 +817,88 @@
 SHA-1%22.%0A
+     * @param algorithm The name of the openssl algorithm, e.g. %22sha1%22.%0A
      */%0A
@@ -949,17 +949,45 @@
 lgorithm
-)
+, String openssl)%0A           
  throws 
@@ -1039,25 +1039,24 @@
 rithm);%0A
-%0A
         
 // We do
@@ -1051,140 +1051,31 @@
     
-// We don't support MD2.%0A        if (%22MD2%22.equals(algorithm)) %7B%0A            throw new NoSuchAlgorithmException(algorithm);%0A        %7D
+this.openssl = openssl;
 %0A%0A  
@@ -1183,53 +1183,15 @@
 tx, 
-getAlgorithm().replace(%22-%22, %22%22).toLowerCase()
+openssl
 );%0A 
@@ -1855,53 +1855,15 @@
 tx, 
-getAlgorithm().replace(%22-%22, %22%22).toLowerCase()
+openssl
 );%0A 
@@ -2621,24 +2621,41 @@
 stroy(ctx);%0A
+        ctx = 0;%0A
     %7D%0A%0A    p
@@ -2789,16 +2789,23 @@
 er(%22MD5%22
+, %22md5%22
 );%0A     
@@ -2960,16 +2960,24 @@
 (%22SHA-1%22
+, %22sha1%22
 );%0A     
@@ -3138,16 +3138,26 @@
 SHA-256%22
+, %22sha256%22
 );%0A     
@@ -3318,16 +3318,26 @@
 SHA-384%22
+, %22sha384%22
 );%0A     
@@ -3498,16 +3498,26 @@
 SHA-512%22
+, %22sha512%22
 );%0A     

OpenSSLMessageDigestJDK.reset should not change from SHA-1 to SHA-0