package org.bouncycastle.tsp.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import junit.framework.TestCase;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampResponseGenerator;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenGenerator;
import org.bouncycastle.tsp.ers.ArchiveTimeStampValidationException;
import org.bouncycastle.tsp.ers.ERSArchiveTimeStamp;
import org.bouncycastle.tsp.ers.ERSArchiveTimeStampGenerator;
import org.bouncycastle.tsp.ers.ERSByteData;
import org.bouncycastle.tsp.ers.ERSData;
import org.bouncycastle.tsp.ers.ERSDataGroup;
import org.bouncycastle.tsp.ers.ERSDirectoryDataGroup;
import org.bouncycastle.tsp.ers.ERSEvidenceRecord;
import org.bouncycastle.tsp.ers.ERSEvidenceRecordGenerator;
import org.bouncycastle.tsp.ers.ERSEvidenceRecordSelector;
import org.bouncycastle.tsp.ers.ERSEvidenceRecordStore;
import org.bouncycastle.tsp.ers.ERSException;
import org.bouncycastle.tsp.ers.ERSFileData;
import org.bouncycastle.tsp.ers.ERSInputStreamData;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.Streams;

public class ERSTest
    extends TestCase
{
    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
    public static final byte[] H1_DATA = Strings.toByteArray("This is H1");
    public static final byte[] H2_DATA = Strings.toByteArray("This is H2");
    public static final byte[] H3A_DATA = Strings.toByteArray("This is H3A");
    public static final byte[] H3B_DATA = Strings.toByteArray("This is H3B");
    public static final byte[] H3C_DATA = Strings.toByteArray("This is H3C");
    public static final byte[] H4_DATA = Strings.toByteArray("This is H4");
    public static final byte[] H5A_DATA = Strings.toByteArray("This is H5A");
    public static final byte[] H5B_DATA = Strings.toByteArray("This is H5B");
    public static final byte[] H5C_DATA = Strings.toByteArray("This is H5C");

    public void setUp()
    {
        Security.addProvider(new BouncyCastleProvider());
    }

    public void testBasicBuild()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("98fbf91c1aebdfec514d4a76532ec95f27ebcf4c8b6f7e2947afcbbfe7084cd4"),
            tspReq.getMessageImprintDigest()));

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        List<ERSArchiveTimeStamp> atss = ersGen.generateArchiveTimeStamps(tsResp);

        ERSArchiveTimeStamp ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(2)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h3Docs, new Date());
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h1Doc);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(0)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h1Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h2Doc);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(1)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h2Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h1Doc);

        // check for individual sub-documents
        ats = (ERSArchiveTimeStamp)atss.get(2);
        List<byte[]> h3Hashes = h3Docs.getHashes(digestCalculator, null);
        for (int i = 0; i != h3Hashes.size(); i++)
        {
            ats.validatePresent(false, (byte[])h3Hashes.get(i), new Date());
        }

        X509CertificateHolder tspCert = ats.getSigningCertificate();

        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(1);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(2);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));
    }

    public void testBuildMultiGroups()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});
        ERSDataGroup h5Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H5A_DATA),
                new ERSByteData(H5B_DATA),
                new ERSByteData(H5C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);
        ersGen.addData(h5Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("06836dfdec4b556e05535d5696b0e4add5cee7d765bcba1f4c1613ddb9176813"),
            tspReq.getMessageImprintDigest()));

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        List<ERSArchiveTimeStamp> atss = ersGen.generateArchiveTimeStamps(tsResp);

        assertEquals(4, atss.size());

        ERSArchiveTimeStamp ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(2)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h3Docs, new Date());
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h1Doc);
        checkAbsent(ats, h5Docs);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(3)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(new ERSByteData(H5A_DATA), new Date());
        ats.validatePresent(new ERSByteData(H5B_DATA), new Date());
        ats.validatePresent(h5Docs, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h1Doc);
        checkAbsent(ats, h2Doc);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(0)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h1Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h5Docs);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(1)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h2Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h1Doc);
        checkAbsent(ats, h5Docs);

        // check for individual sub-documents
        ats = (ERSArchiveTimeStamp)atss.get(2);
        List<byte[]> h3Hashes = h3Docs.getHashes(digestCalculator, null);
        for (int i = 0; i != h3Hashes.size(); i++)
        {
            ats.validatePresent(false, (byte[])h3Hashes.get(i), new Date());
        }

        X509CertificateHolder tspCert = ats.getSigningCertificate();

        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(1);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(2);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));
    }

    public void testBuildMulti()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});
        ERSData h4Doc = new ERSByteData(H4_DATA);
        ERSDataGroup h5Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H5A_DATA),
                new ERSByteData(H5B_DATA),
                new ERSByteData(H5C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);
        ersGen.addData(h4Doc);
        ersGen.addData(h5Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("b7efd5e742df672584e69b36ba5592748f841cc400ef989180aa2a69e43499e8"),
            tspReq.getMessageImprintDigest()));

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        List<ERSArchiveTimeStamp> atss = ersGen.generateArchiveTimeStamps(tsResp);

        assertEquals(5, atss.size());

        ERSArchiveTimeStamp ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(2)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h3Docs, new Date());
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h1Doc);
        checkAbsent(ats, h5Docs);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(3)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h4Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h5Docs);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(4)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(new ERSByteData(H5A_DATA), new Date());
        ats.validatePresent(new ERSByteData(H5B_DATA), new Date());
        ats.validatePresent(h5Docs, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h1Doc);
        checkAbsent(ats, h2Doc);

        ats = new ERSArchiveTimeStamp(((ERSArchiveTimeStamp)atss.get(0)).getEncoded(), digestCalculatorProvider);

        ats.validatePresent(h1Doc, new Date());
        checkAbsent(ats, h3Docs);
        checkAbsent(ats, h2Doc);
        checkAbsent(ats, h5Docs);

        // check for individual sub-documents
        ats = (ERSArchiveTimeStamp)atss.get(2);
        List<byte[]> h3Hashes = h3Docs.getHashes(digestCalculator, null);
        for (int i = 0; i != h3Hashes.size(); i++)
        {
            ats.validatePresent(false, (byte[])h3Hashes.get(i), new Date());
        }

        X509CertificateHolder tspCert = ats.getSigningCertificate();

        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(1);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ats = (ERSArchiveTimeStamp)atss.get(2);
        ats.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));
    }

    private void checkAbsent(ERSArchiveTimeStamp ats, ERSData data)
        throws OperatorCreationException, ERSException
    {
        try
        {
            ats.validatePresent(data, new Date());
            fail();
        }
        catch (ArchiveTimeStampValidationException e)
        {
            assertEquals(e.getMessage(), "object hash not found");
        }
    }

    public void testMonteMulti()
        throws Exception
    {
        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        for (int i = 0; i != 101; i++)
        {
            ersGen.addData(new ERSByteData(new byte[]{(byte)i}));
        }

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        List<ERSArchiveTimeStamp> atss = ersGen.generateArchiveTimeStamps(tsResp);

        int count = 0;
        for (int j = 0; j != 101; j++)
        {
            for (int i = 0; i != atss.size(); i++)
            {
                try
                {
                    ((ERSArchiveTimeStamp)atss.get(i)).validatePresent(new ERSByteData(new byte[]{(byte)j}), new Date());
                    count++;
                    break;
                }
                catch (Exception e)
                {
                    // ignore
                }
            }
        }
        assertEquals(atss.size(), count);
    }

    private void checkAbsent(ERSEvidenceRecord ats, ERSData data)
        throws OperatorCreationException, ERSException
    {
        try
        {
            ats.validatePresent(data, new Date());
            fail();
        }
        catch (ArchiveTimeStampValidationException e)
        {
            assertEquals(e.getMessage(), "object hash not found");
        }
    }

    public void testSingleTimeStamp()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);

        DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);


        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        ERSArchiveTimeStamp ats = ersGen.generateArchiveTimeStamp(tsResp);

        ats.validatePresent(h1Doc, new Date());

        try
        {
            ats.validatePresent(h2Doc, new Date());
            fail();
        }
        catch (ArchiveTimeStampValidationException e)
        {
            assertEquals("object hash not found in wrapped timestamp", e.getMessage());
        }
    }

    public void testBasicBuildEvidenceRecord()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("98fbf91c1aebdfec514d4a76532ec95f27ebcf4c8b6f7e2947afcbbfe7084cd4"),
            tspReq.getMessageImprintDigest()));


        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);


        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        List<ERSArchiveTimeStamp> ats = ersGen.generateArchiveTimeStamps(tsResp);

        ERSEvidenceRecordGenerator evGen = new ERSEvidenceRecordGenerator(digestCalculatorProvider);

        List<ERSEvidenceRecord> evs = evGen.generate(ats);

        checkAbsent((ERSEvidenceRecord)evs.get(2), h1Doc);
        checkAbsent((ERSEvidenceRecord)evs.get(2), h2Doc);
        ((ERSEvidenceRecord)evs.get(2)).validatePresent(h3Docs, new Date());

        ((ERSEvidenceRecord)evs.get(0)).validatePresent(h1Doc, new Date());
        checkAbsent((ERSEvidenceRecord)evs.get(0), h2Doc);
        checkAbsent((ERSEvidenceRecord)evs.get(0), h3Docs);

        checkAbsent((ERSEvidenceRecord)evs.get(1), h1Doc);
        ((ERSEvidenceRecord)evs.get(1)).validatePresent(h2Doc, new Date());
        checkAbsent((ERSEvidenceRecord)evs.get(1), h3Docs);

        assertTrue(((ERSEvidenceRecord)evs.get(0)).isRelatedTo((ERSEvidenceRecord)evs.get(1)));
        assertTrue(((ERSEvidenceRecord)evs.get(0)).isRelatedTo((ERSEvidenceRecord)evs.get(2)));
        assertTrue(((ERSEvidenceRecord)evs.get(1)).isRelatedTo((ERSEvidenceRecord)evs.get(2)));

        assertTrue(Arrays.areEqual(((ERSEvidenceRecord)evs.get(0)).getPrimaryRootHash(), ((ERSEvidenceRecord)evs.get(1)).getPrimaryRootHash()));
        assertTrue(Arrays.areEqual(((ERSEvidenceRecord)evs.get(0)).getPrimaryRootHash(), ((ERSEvidenceRecord)evs.get(2)).getPrimaryRootHash()));
        assertTrue(Arrays.areEqual(((ERSEvidenceRecord)evs.get(2)).getPrimaryRootHash(), ((ERSEvidenceRecord)evs.get(1)).getPrimaryRootHash()));

        ERSEvidenceRecord ev = (ERSEvidenceRecord)evs.get(2);

        // check for individual sub-documents
        List<byte[]> h3Hashes = h3Docs.getHashes(digestCalculator, null);
        for (int i = 0; i != h3Hashes.size(); i++)
        {
            ev.validatePresent(false, (byte[])h3Hashes.get(i), new Date());
        }

        X509CertificateHolder tspCert = ev.getSigningCertificate();

        ev.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ERSEvidenceRecord ev2 = new ERSEvidenceRecord(ev.getEncoded(), digestCalculatorProvider);

        tspCert = ev2.getSigningCertificate();

        ev2.validate(new JcaSimpleSignerInfoVerifierBuilder().build(tspCert));

        ev2.validatePresent(h3Docs, new Date());

        ERSEvidenceRecordStore store = new ERSEvidenceRecordStore(evs);

        Collection<ERSEvidenceRecord> recs = store.getMatches(new ERSEvidenceRecordSelector(h3Docs));

        assertEquals(1, recs.size());
        ERSEvidenceRecord r1 = (ERSEvidenceRecord)recs.iterator().next();

        recs = store.getMatches(new ERSEvidenceRecordSelector(new ERSByteData(H3A_DATA)));

        assertEquals(1, recs.size());
        ERSEvidenceRecord r2 = (ERSEvidenceRecord)recs.iterator().next();

        assertTrue(r2 == r1);
    }

    private void checkPresent(ERSEvidenceRecord ev, ERSData data)
        throws ERSException
    {
        ev.validatePresent(data, new Date());
    }

    public void testTimeStampRenewal()
        throws Exception
    {
        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        TimeStampResponse tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        List<ERSArchiveTimeStamp> ats = ersGen.generateArchiveTimeStamps(tspResp);

        // simple renewal of timestamp, same hash, same data.

        ERSEvidenceRecordGenerator evGen = new ERSEvidenceRecordGenerator(digestCalculatorProvider);

        List<ERSEvidenceRecord> evs = evGen.generate(ats);

        checkPresent((ERSEvidenceRecord)evs.get(0), h1Doc);
        checkPresent((ERSEvidenceRecord)evs.get(1), h2Doc);
        checkPresent((ERSEvidenceRecord)evs.get(2), h3Docs);

        ERSEvidenceRecord ev = (ERSEvidenceRecord)evs.get(2);

        tspReq = ev.generateTimeStampRenewalRequest(tspReqGen);

        signKP = TSPTestUtil.makeKeyPair();
        signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        origKP = TSPTestUtil.makeKeyPair();
        origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        certs = new JcaCertStore(certList);

        tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        ev = ev.renewTimeStamp(tspResp);

//        ev.validatePresent(h1Doc, new Date());
//        ev.validatePresent(h2Doc, new Date());
        ev.validatePresent(h3Docs, new Date());

        ev.validate(new JcaSimpleSignerInfoVerifierBuilder().build(origCert));

        // as the time stamp is shared between records we should be able to reuse the response
        ERSEvidenceRecord ev1 = ((ERSEvidenceRecord)evs.get(0)).renewTimeStamp(tspResp);

        ev1.validatePresent(h1Doc, new Date());
        checkAbsent(ev1, h2Doc);
        checkAbsent(ev1, h3Docs);

        ev1.validate(new JcaSimpleSignerInfoVerifierBuilder().build(origCert));

        assertEquals(2, ev.toASN1Structure().getArchiveTimeStampSequence().getArchiveTimeStampChains()[0].getArchiveTimestamps().length);
        assertEquals(2, ev1.toASN1Structure().getArchiveTimeStampSequence().getArchiveTimeStampChains()[0].getArchiveTimestamps().length);

        tspReq = ev.generateTimeStampRenewalRequest(tspReqGen);

        signKP = TSPTestUtil.makeKeyPair();
        signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        origKP = TSPTestUtil.makeKeyPair();
        origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        certs = new JcaCertStore(certList);

        tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        ev = ev.renewTimeStamp(tspResp);

        checkAbsent(ev, h1Doc);
        checkAbsent(ev, h2Doc);
        ev.validatePresent(h3Docs, new Date());

        ev.validate(new JcaSimpleSignerInfoVerifierBuilder().build(origCert));

        assertEquals(3, ev.toASN1Structure().getArchiveTimeStampSequence().getArchiveTimeStampChains()[0].getArchiveTimestamps().length);

        // check validation on loading.
        ev = new ERSEvidenceRecord(ev.getEncoded(), digestCalculatorProvider);
    }

    public void testHashRenewal()
        throws Exception
    {
        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        TimeStampResponse tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        List<ERSArchiveTimeStamp> ats = ersGen.generateArchiveTimeStamps(tspResp);

        // simple renewal of timestamp, same hash, same data.

        ERSEvidenceRecordGenerator evGen = new ERSEvidenceRecordGenerator(digestCalculatorProvider);

        List<ERSEvidenceRecord> evs = evGen.generate(ats);

        ((ERSEvidenceRecord)evs.get(0)).validatePresent(h1Doc, new Date());
        ((ERSEvidenceRecord)evs.get(1)).validatePresent(h2Doc, new Date());
        ((ERSEvidenceRecord)evs.get(2)).validatePresent(h3Docs, new Date());

        DigestCalculator newDigCalc = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512));

        ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h3Docs);

        ERSEvidenceRecord ev = (ERSEvidenceRecord)evs.get(2);
        tspReq = ev.generateHashRenewalRequest(newDigCalc, h3Docs, tspReqGen);

        signKP = TSPTestUtil.makeKeyPair();
        signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        origKP = TSPTestUtil.makeKeyPair();
        origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        certs = new JcaCertStore(certList);

        tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        ev = ev.renewHash(newDigCalc, h3Docs, tspResp);

        ev.validatePresent(h3Docs, new Date());

        ev.validate(new JcaSimpleSignerInfoVerifierBuilder().build(origCert));

        assertEquals(2, ev.toASN1Structure().getArchiveTimeStampSequence().size());

        tspReq = ev.generateTimeStampRenewalRequest(tspReqGen);

        signKP = TSPTestUtil.makeKeyPair();
        signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        origKP = TSPTestUtil.makeKeyPair();
        origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        certs = new JcaCertStore(certList);

        tspResp = doTimeStamp(origKP.getPrivate(), origCert, certs, tspReq);

        ev = ev.renewTimeStamp(tspResp);

        ev.validatePresent(h3Docs, new Date());

        ev.validate(new JcaSimpleSignerInfoVerifierBuilder().build(origCert));

        assertEquals(2, ev.toASN1Structure().getArchiveTimeStampSequence().getArchiveTimeStampChains()[1].getArchiveTimestamps().length);

        assertTrue(Arrays.areEqual(((ERSEvidenceRecord)evs.get(0)).getPrimaryRootHash(), ev.getPrimaryRootHash()));

        try
        {
            tspReq = ev.generateHashRenewalRequest(newDigCalc, h1Doc, tspReqGen);
        }
        catch (ERSException e)
        {
            assertEquals("attempt to hash renew on invalid data", e.getMessage());
        }

        // check validation on loading.
        ev = new ERSEvidenceRecord(ev.getEncoded(), digestCalculatorProvider);
    }

    public void testTSPConversion()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);

        MessageDigest sha1 = MessageDigest.getInstance("SHA1");

        String signDN = "O=Bouncy Castle, C=AU";
        KeyPair signKP = TSPTestUtil.makeKeyPair();
        X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
            signDN, signKP, signDN);

        String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        KeyPair origKP = TSPTestUtil.makeKeyPair();
        X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
            origDN, signKP, signDN);

        List certList = new ArrayList();
        certList.add(origCert);
        certList.add(signCert);

        Store certs = new JcaCertStore(certList);

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
            new JcaSimpleSignerInfoGeneratorBuilder().build("SHA256withRSA", origKP.getPrivate(), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2"));

        tsTokenGen.addCertificates(certs);

        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
        TimeStampRequest request = reqGen.generate(TSPAlgorithms.SHA1, sha1.digest(H1_DATA), BigInteger.valueOf(100));

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date());

        tsResp = new TimeStampResponse(tsResp.getEncoded());

        TimeStampToken tsToken = tsResp.getTimeStampToken();

        tsToken.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(origCert));

        ERSArchiveTimeStamp ersAts = ERSArchiveTimeStamp.fromTimeStampToken(tsToken, new JcaDigestCalculatorProviderBuilder().build());

        ersAts.validatePresent(h1Doc, new Date());

        try
        {
            ersAts.validatePresent(h2Doc, new Date());
            fail("no exception");
        }
        catch (ArchiveTimeStampValidationException e)
        {
            assertEquals("object hash not found in wrapped timestamp", e.getMessage());
        }

    }

    private TimeStampResponse doTimeStamp(PrivateKey tspKey, X509Certificate tspCert, Store certs, TimeStampRequest tspReq)
        throws Exception
    {
        JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(tspKey), tspCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);

        TimeStampResponse tsResp;

        try
        {
            tsResp = tsRespGen.generateGrantedResponse(tspReq, new BigInteger("23"), new Date());
        }
        catch (TSPException e)
        {
            tsResp = tsRespGen.generateRejectedResponse(e);
        }

        return tsResp;
    }

    public void test4NodeBuild()
        throws Exception
    {
        ERSData h1Doc = new ERSByteData(H1_DATA);
        ERSData h2Doc = new ERSByteData(H2_DATA);
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA)});
        ERSData h4Doc = new ERSByteData(H4_DATA);

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
        DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

        ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        ersGen.addData(h1Doc);
        ersGen.addData(h2Doc);
        ersGen.addData(h3Docs);
        ersGen.addData(h4Doc);

        TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("d82fea0eaff4b12925a201dff2332965953ca38c1eef6c9e31b55bbce4ce2984"),
            tspReq.getMessageImprintDigest()));

        ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

        List<ERSData> dataList = new ArrayList<ERSData>();

        dataList.add(h1Doc);
        dataList.add(h2Doc);
        dataList.add(h3Docs);
        dataList.add(h4Doc);

        ersGen.addAllData(dataList);

        tspReqGen = new TimeStampRequestGenerator();

        tspReqGen.setCertReq(true);

        tspReq = ersGen.generateTimeStampRequest(tspReqGen);

        assertTrue(Arrays.areEqual(Hex.decode("d82fea0eaff4b12925a201dff2332965953ca38c1eef6c9e31b55bbce4ce2984"),
            tspReq.getMessageImprintDigest()));
    }

    public void testDirUtil()
        throws Exception
    {
        File rootDir = File.createTempFile("ers", ".dir");
        rootDir.delete();
        if (rootDir.mkdir())
        {
            DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
            DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));

            File h1 = new File(rootDir, "h1");
            OutputStream fOut = new FileOutputStream(h1);
            fOut.write(H1_DATA);
            fOut.close();

            File h2 = new File(rootDir, "h2");
            fOut = new FileOutputStream(h2);
            fOut.write(H2_DATA);
            fOut.close();

            File h3 = new File(rootDir, "h3");
            h3.mkdir();
            fOut = new FileOutputStream(new File(h3, "a"));
            fOut.write(H3A_DATA);
            fOut.close();
            fOut = new FileOutputStream(new File(h3, "b"));
            fOut.write(H3B_DATA);
            fOut.close();
            fOut = new FileOutputStream(new File(h3, "c"));
            fOut.write(H3C_DATA);
            fOut.close();

            ERSArchiveTimeStampGenerator ersGen = new ERSArchiveTimeStampGenerator(digestCalculator);

            ersGen.addData(new ERSFileData(h1));
            ersGen.addData(new ERSFileData(h2));
            ersGen.addData(new ERSDirectoryDataGroup(h3));

            TimeStampRequestGenerator tspReqGen = new TimeStampRequestGenerator();

            tspReqGen.setCertReq(true);

            TimeStampRequest tspReq = ersGen.generateTimeStampRequest(tspReqGen);

            assertTrue(Arrays.areEqual(Hex.decode("98fbf91c1aebdfec514d4a76532ec95f27ebcf4c8b6f7e2947afcbbfe7084cd4"),
                tspReq.getMessageImprintDigest()));

            deleteDirectory(rootDir);
        }
        else
        {
            throw new Exception("can't create temp dir");
        }
    }

    public void testBSIData()
        throws Exception
    {
        ERSEvidenceRecord ers = new ERSEvidenceRecord(Streams.readAll(getClass().getResourceAsStream("BIN_ER.ers")), new JcaDigestCalculatorProviderBuilder().build());

        ers.validate(new JcaSimpleSignerInfoVerifierBuilder().build(ers.getSigningCertificate()));

        ers.validatePresent(new ERSInputStreamData(getClass().getResourceAsStream("BIN.bin")), new Date());
    }

    public void testBSIDataXML()
        throws Exception
    {
        ERSEvidenceRecord ers = new ERSEvidenceRecord(Base64.decode("[encoded data, 7808 characters]"), new JcaDigestCalculatorProviderBuilder().build());

        ers.validate(new JcaSimpleSignerInfoVerifierBuilder().build(ers.getSigningCertificate()));

        ers.validatePresent(false, Hex.decode("9CE2D7D350F9418407439F0CA11DCC12AB9F7CDB15F9ECC5ECA935482602B872"), new Date());
        ers.validatePresent(false, Hex.decode("EECC4D3352C0E965FD88795EDFD1A60C5AC09B3C1100C052EBB6AE0BD3432B26"), new Date());

        // look for data group
        byte[] digest = MessageDigest.getInstance("SHA-256").digest(Arrays.concatenate(
            Hex.decode("9CE2D7D350F9418407439F0CA11DCC12AB9F7CDB15F9ECC5ECA935482602B872"),
            Hex.decode("EECC4D3352C0E965FD88795EDFD1A60C5AC09B3C1100C052EBB6AE0BD3432B26")));

        ers.validatePresent(true, digest, new Date());
    }

    private void deleteDirectory(File directory)
    {
        File[] files = directory.listFiles();
        if (files != null)
        {
            for (int i = 0; i != files.length; i++)
            {
                deleteDirectory(files[i]);
            }
        }
        directory.delete();
    }

    public void testSort()
        throws Exception
    {
        ERSDataGroup h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H1_DATA),
                new ERSByteData(H2_DATA)}
        );

        DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();

        trySort(h3Docs, NISTObjectIdentifiers.id_sha256, digestCalculatorProvider);

        h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H2_DATA),
                new ERSByteData(H1_DATA)}
        );

        trySort(h3Docs, NISTObjectIdentifiers.id_sha256, digestCalculatorProvider);

        h3Docs = new ERSDataGroup(
            new ERSData[]{new ERSByteData(H1_DATA),
                new ERSByteData(H2_DATA),
                new ERSByteData(H3A_DATA),
                new ERSByteData(H3B_DATA),
                new ERSByteData(H3C_DATA),
                new ERSByteData(H4_DATA)}
        );
        trySort(h3Docs, NISTObjectIdentifiers.id_sha256, digestCalculatorProvider);
        trySort(h3Docs, NISTObjectIdentifiers.id_sha224, digestCalculatorProvider);
        trySort(h3Docs, NISTObjectIdentifiers.id_sha384, digestCalculatorProvider);
    }

    private void trySort(ERSDataGroup h3Docs, ASN1ObjectIdentifier sha, DigestCalculatorProvider digestCalculatorProvider)
        throws OperatorCreationException
    {
        List<byte[]> hashes = h3Docs.getHashes(digestCalculatorProvider.get(
            new AlgorithmIdentifier(sha)), null);
        for (int i = 0; i != hashes.size() - 1; i++)
        {
            assertTrue(compare((byte[])hashes.get(i), (byte[])hashes.get(i + 1)) < 0);
        }
    }

    private int compare(byte[] a, byte[] b)
    {
        return new BigInteger(1, a).compareTo(new BigInteger(1, b));
    }

    public void testReducedHashTrees()
        throws Exception
    {
        /*checkReducedHashTree(
                // data objects
                new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09},
                // reduced hash tree
                new byte[][][]{
                        // 0x01
                        new byte[][]{
                                Hex.decode("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("03040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x02
                        new byte[][]{
                                Hex.decode("02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("03040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x03
                        new byte[][]{
                                Hex.decode("03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x04
                        new byte[][]{
                                Hex.decode("04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x05
                        new byte[][]{
                                Hex.decode("05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("07080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x06
                        new byte[][]{
                                Hex.decode("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("07080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x07
                        new byte[][]{
                                Hex.decode("07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x08
                        new byte[][]{
                                Hex.decode("08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("05060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                        // 0x09
                        new byte[][]{
                                Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                                Hex.decode("01020304050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                        },
                }
        );*/

        checkReducedHashTree(
            // data objects
            new byte[]{0x05, 0x07, 0x03, 0x04, 0x01, 0x06, 0x02, 0x09, 0x08},
            // reduced hash tree
            new byte[][][]{
                // 0x05
                new byte[][]{
                    Hex.decode("05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("07080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x07
                new byte[][]{
                    Hex.decode("07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x03
                new byte[][]{
                    Hex.decode("03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x04
                new byte[][]{
                    Hex.decode("04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x01
                new byte[][]{
                    Hex.decode("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("03040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x06
                new byte[][]{
                    Hex.decode("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("07080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x02
                new byte[][]{
                    Hex.decode("02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("03040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x09
                new byte[][]{
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020304050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
                // 0x08
                new byte[][]{
                    Hex.decode("08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("05060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("01020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                    Hex.decode("09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
                },
            }
        );

    }

    private void checkReducedHashTree(byte[] dataObjects, byte[][][] reducedHashTree)
        throws Exception
    {
        // warning: length of dataObjects has to be <= 64

        assertEquals(dataObjects.length, reducedHashTree.length);

        final DigestCalculator identityDigestCalculator = new DigestCalculator()
        {
            // fake SHA512 OID
            private final ASN1ObjectIdentifier algorithm = NISTObjectIdentifiers.id_sha512;
            private final ByteArrayOutputStream bOut = new ByteArrayOutputStream();

            public AlgorithmIdentifier getAlgorithmIdentifier()
            {
                return new AlgorithmIdentifier(algorithm);
            }

            public OutputStream getOutputStream()
            {
                return bOut;
            }
            
            public byte[] getDigest()
            {
                final byte[] bytes = bOut.toByteArray();
                bOut.reset();
                // fake of SHA512
                return Arrays.copyOf(trimBytes(bytes), 64);
            }

            private byte[] trimBytes(byte[] bytes)
            {
                ByteArrayOutputStream bOut = new ByteArrayOutputStream(bytes.length);
                for (int i = 0; i != bytes.length; i++)
                {
                    if (bytes[i] == '\0')
                    {
                        continue;
                    }
                    bOut.write(bytes[i]);
                }
                return bOut.toByteArray();
            }
        };

        final ERSArchiveTimeStampGenerator ersArchiveTimeStampGenerator = new ERSArchiveTimeStampGenerator(identityDigestCalculator);

        for (int i = 0; i < dataObjects.length; i++)
        {
            ersArchiveTimeStampGenerator.addData(new ERSByteData(BigInteger.valueOf(dataObjects[i]).toByteArray()));
        }

        final TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator();
        timeStampRequestGenerator.setCertReq(true);

        final TimeStampRequest timeStampRequest = ersArchiveTimeStampGenerator.generateTimeStampRequest(timeStampRequestGenerator);


        //assertTrue(Arrays.areEqual(Hex.decode("b7efd5e742df672584e69b36ba5592748f841cc400ef989180aa2a69e43499e8"),
        //       tspReq.getMessageImprintDigest()));

        final String signDN = "O=Bouncy Castle, C=AU";
        final KeyPair signKP = TSPTestUtil.makeKeyPair();
        final X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP, signDN, signKP, signDN);

        final String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
        final KeyPair origKP = TSPTestUtil.makeKeyPair();
        final X509Certificate origCert = TSPTestUtil.makeCertificate(origKP, origDN, signKP, signDN);

        final List<X509Certificate> certList = new ArrayList<X509Certificate>();
        certList.add(origCert);
        certList.add(signCert);

        final JcaCertStore certs = new JcaCertStore(certList);

        final JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());

        final TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(origKP.getPrivate()), origCert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));

        tsTokenGen.addCertificates(certs);

        final TimeStampResponseGenerator timeStampResponseGenerator = new TimeStampResponseGenerator(tsTokenGen, new HashSet<ASN1ObjectIdentifier>(Collections.singleton(identityDigestCalculator.getAlgorithmIdentifier().getAlgorithm()))  /*TSPAlgorithms.ALLOWED*/);

        TimeStampResponse tsResp;

        try
        {
            tsResp = timeStampResponseGenerator.generateGrantedResponse(timeStampRequest, new BigInteger("23"), new Date());
        }
        catch (final TSPException e)
        {
            tsResp = timeStampResponseGenerator.generateRejectedResponse(e);
        }

        final List<ERSArchiveTimeStamp> ersArchiveTimeStamps = ersArchiveTimeStampGenerator.generateArchiveTimeStamps(tsResp);
        assertEquals(dataObjects.length, ersArchiveTimeStamps.size());


        final DigestCalculatorProvider digestCalculatorProvider = new DigestCalculatorProvider()
        {
            public DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier)
                throws OperatorCreationException
            {
                return identityDigestCalculator;
            }
        };

        final ERSEvidenceRecordGenerator ersEvidenceRecordGenerator = new ERSEvidenceRecordGenerator(digestCalculatorProvider);

        final List<ERSEvidenceRecord> ersEvidenceRecords = ersEvidenceRecordGenerator.generate(ersArchiveTimeStamps);
        assertEquals(dataObjects.length, ersEvidenceRecords.size());

        final ERSEvidenceRecordStore ersEvidenceRecordStore = new ERSEvidenceRecordStore(ersEvidenceRecords);

        for (int i = 0; i < dataObjects.length; i++)
        {

            final Collection<ERSEvidenceRecord> ersEvidenceRecordMatches = ersEvidenceRecordStore.getMatches(
                new ERSEvidenceRecordSelector(new ERSByteData(new byte[]{dataObjects[i]}), new Date()));
            assertEquals(1, ersEvidenceRecordMatches.size());

            final ERSEvidenceRecord ersEvidenceRecord = (ERSEvidenceRecord)ersEvidenceRecordMatches.iterator().next();
            final org.bouncycastle.asn1.tsp.PartialHashtree[] ht = ersEvidenceRecord.toASN1Structure().getArchiveTimeStampSequence().getArchiveTimeStampChains()[0].getArchiveTimestamps()[0].getReducedHashTree();

//            System.out.println(
//                    "// 0x"+ Hex.toHexString(BigInteger.valueOf(dataObjects[i]).toByteArray())+"\n" +
//                    "new byte[][]{");
//            for (int j = 0; j < ht.length; j++) {
//                System.out.println("Hex.decode(\"" + Hex.toHexString(ht[j].getValues()[0]) + "\"),");
//            }
//            System.out.println("},");

            for (int j = 0; j < reducedHashTree[i].length; j++)
            {
                assertTrue(Arrays.areEqual(reducedHashTree[i][j], ht[j].getValues()[0]));
            }

        }

    }

    public void testCompareStreamAndByteData () throws TSPException, ERSException, OperatorCreationException, IOException,
        NoSuchAlgorithmException
    {
        // ER for the String "foo" using SHA-256 and a dummy cert/key.
        String evidenceRecordBase64 = "MIIDCgIBATANMAsGCWCGSAFlAwQCATCCAvQwggLwMIIC7DCCAugGCSqGSI"
            + "b3DQEHAqCCAtkwggLVAgEDMQ0wCwYJYIZIAWUDBAIDMG0GCyqGSIb3DQEJEAEEoF4EXDBaAgEBBgYEA"
            + "I9nAQEwLzALBglghkgBZQMEAgEEICwmtGto/8aP+ZtFPB0wQTQTQi1wZIO/oPmKXohiZueuAgEBGA8y"
            + "MDI0MTAwMjIzMDAzNloCCFO56J9TFmGGMYICUDCCAkwCAQEwBTAAAgEAMAsGCWCGSAFlAwQCA6CCAR4"
            + "wGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNDEwMDIyMzAwMzZaMC"
            + "sGCSqGSIb3DQEJNDEeMBwwCwYJYIZIAWUDBAIDoQ0GCSqGSIb3DQEBDQUAME8GCSqGSIb3DQEJBDFCB"
            + "EDPPkk9PN5EGrAvZyjv9wJR77tQCIwoA3xWwqESBGICShZxNjt6YU3LZor99ARJ4As+yiIjW/hTg5v3"
            + "vqbTrfSIMGQGCyqGSIb3DQEJEAIvMVUwUzBRME8wCwYJYIZIAWUDBAIDBEAPvi2mNblzqqI91nWRM9s"
            + "ocS7TJfpyQsx4ZcBVeGK1XjCW6BQ5KmrPFCc+IefB5FB/ZQsPwdyYv6umJzCYK0SzMA0GCSqGSIb3DQ"
            + "EBDQUABIIBALWgWcjxzY5QEOlK92GNf9kjBflbO65dYkAKxrrgcwQ6Dz+ablUwsG01ILDUUnSL9wTQC"
            + "OkYKb1oEFNrd9lbHWBOqlu5/lMjhZcWnYzbK3rzQRuoPwXYD/GWgiO0wLmF3FQ9xaum1Oui+Y075OS4"
            + "7fXfLlSe2wMPlnoDb/IFAgHGBK/3zJ7w7n9OCa1U6qwTYCpw9MTXsOI/PbNw2h3cHTVgbY+HCTB4oJC"
            + "GpY9bbEMuJboe4DkQx2Eqpq1pVaMKRxsjhrnbH8QlkUGtuGztqnZa5AoCth79x70Ch7WhdDcxG3wiFi"
            + "29pw69obUCh3c61Q2WKl+MKW/tqq7EGYu5+jE=";
        DigestCalculatorProvider digestProvider = new BcDigestCalculatorProvider();
        ERSEvidenceRecord ersEvidenceRecord = new ERSEvidenceRecord(
            Base64.decode(evidenceRecordBase64), digestProvider);

        // Sanity check, make sure root hash of ER is what we expect.
        byte[] sourceData = Strings.toUTF8ByteArray("foo");
        byte[] sourceSha256 = MessageDigest.getInstance("SHA-256").digest(sourceData);
        assertTrue(Arrays.areEqual(sourceSha256, ersEvidenceRecord.getPrimaryRootHash()));


        // Generate hash renewal request using ERSInputStreamData.
        ERSData ersStreamData = new ERSInputStreamData(new ByteArrayInputStream(sourceData));
        TimeStampRequest streamDataReq = ersEvidenceRecord.generateHashRenewalRequest(
            digestProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512)),
            ersStreamData,
            new TimeStampRequestGenerator(),
            BigInteger.ZERO);


        // Generate hash renewal request using ERSByteData to compare against.
        ERSData ersByteData = new ERSByteData(sourceData);
        TimeStampRequest byteDataReq = ersEvidenceRecord.generateHashRenewalRequest(
            digestProvider.get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512)),
            ersByteData,
            new TimeStampRequestGenerator(),
            BigInteger.ZERO);


        // check ERSByteData and ERSInputStreamData produce same output
        assertTrue(Arrays.areEqual(byteDataReq.getMessageImprintDigest(),
            streamDataReq.getMessageImprintDigest()));


        // Generate the digest we expect to see in the requests and compare.
        byte[] expectedDigest = generateExpectedRequestDigest(sourceData, ersEvidenceRecord,
            MessageDigest.getInstance("SHA-512"));
        assertTrue(Arrays.areEqual(byteDataReq.getMessageImprintDigest(), expectedDigest));
        assertTrue(Arrays.areEqual(streamDataReq.getMessageImprintDigest(), expectedDigest));
    }

    /** Based on RFC 4998 section 5.2. */
    private static byte[] generateExpectedRequestDigest (byte[] sourceData,
                                                         ERSEvidenceRecord evidenceRecord, MessageDigest digest) throws IOException
    {
        byte[] atsci = evidenceRecord.toASN1Structure().getArchiveTimeStampSequence().getEncoded(ASN1Encoding.DER);
        byte[] hi = digest.digest(sourceData);
        byte[] hai = digest.digest(atsci);
        byte[] hihai;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            outputStream.write(hi);
            outputStream.write(hai);
            hihai = outputStream.toByteArray();

        byte[] hiprime = digest.digest(hihai);
        return hiprime;
    }
}
