using System;
using System.Text;
using System.Diagnostics;
using System.IO;
using Sc14n;

/*  
 * $Id: TestSc14n.cs $
 * Last updated:
 *   $Date: 2017-07-15 16:23 $
 *   $Version: 0.9.0a $
 */
/* Some tests using the SC14N .NET interface.
 * 
 * Requires `Sc14n` to be installed on your system: available from <http://di-mgt.com.au/sc14n/>
 * Add a reference to `diSc14nNet.dll`
 * 
 * Test files, e.g. `olamundo.xml`, are in `sc14n-testfiles.zip`. These must be in the CWD.
 * 
 * This is a Console Application written for target .NET Framework 2.0 and above 
 * Please report any bugs to <http://www.di-mgt.com.au/contact>
 */
/******************************* LICENSE ***********************************
 * Copyright (C) 2017 David Ireland, DI Management Services Pty Limited.
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 * The code in this module is licensed under the terms of the MIT license.  
 * For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/

namespace TestSc14nNet
{
    class TestSc14nNet
    {
        static void Main(string[] args)
        {
            int n, r;
            string s, fname, oname, digval, digval1;
            byte[] b;

            // SHOW GENERAL INFO ABOUT THE CORE LIBRARY DLL
            n = Gen.Version();
            Console.WriteLine("Version={0}", n);
            Console.WriteLine("Platform={0}", Gen.Platform());
            Console.WriteLine("ModuleName={0}", Gen.ModuleName());
            Console.WriteLine("CompileTime={0}", Gen.CompileTime());
            Console.WriteLine("LicenceType={0}", Gen.LicenceType());


            // DO TESTS ON INPUT.XML (these are from the manual)

            Console.WriteLine("FILE: {0}", "input.xml");

            // Example 1. Excludes the first element with the tag name <Signature>
            r = C14n.ToFile("c14nfile1.txt", "input.xml", "Signature", Tran.ExcludeByTag);
            Debug.Assert(0 == r, "C14n.ToFile failed");

            // Example 2. Finds and transforms the first element with the tag name <SignedInfo>
            r = C14n.ToFile("c14nfile2.txt", "input.xml", "SignedInfo", Tran.SubsetByTag);
            Debug.Assert(0 == r, "C14n.ToFile failed");

            // Example 3. Finds and transforms the third element with the tag name <Data>
            r = C14n.ToFile("c14nfile3.txt", "input.xml", "Data[3]", Tran.SubsetByTag);
            Debug.Assert(0 == r, "C14n.ToFile failed");

            // Example 4. Finds and transforms the element with attribute Id="foo"
            r = C14n.ToFile("c14nfile4.txt", "input.xml", "foo", Tran.SubsetById);
            Debug.Assert(0 == r, "C14n.ToFile failed");

            // Example 5. Finds and transforms the element with attribute ID="bar"
            r = C14n.ToFile("c14nfile5.txt", "input.xml", "ID=bar", Tran.SubsetById);
            Debug.Assert(0 == r, "C14n.ToFile failed");

            // Example 6. Excludes element with attribute Id="thesig"
            r = C14n.ToFile("c14nfile6.txt", "input.xml", "thesig", Tran.ExcludeById);
            Debug.Assert(0 == r, "C14n.ToFile failed");


            // REDO LAST TESTS BUT COMPUTE DIGEST INSTEAD OF CREATING NEW FILE

            // Example 1. Excludes the first element with the tag name <Signature>
            digval = C14n.ToDigest("input.xml", "Signature", Tran.ExcludeByTag, DigAlg.Sha1);
            Console.WriteLine("DIG1={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            // Example 2. Finds and transforms the first element with the tag name <SignedInfo>
            digval = C14n.ToDigest("input.xml", "SignedInfo", Tran.SubsetByTag, DigAlg.Sha1);
            Console.WriteLine("DIG2={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            // Example 3. Finds and transforms the third element with the tag name <Data>
            digval = C14n.ToDigest("input.xml", "Data[3]", Tran.SubsetByTag, DigAlg.Sha1);
            Console.WriteLine("DIG3={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            // Example 4. Finds and transforms the element with attribute Id="foo"
            digval = C14n.ToDigest("input.xml", "foo", Tran.SubsetById, DigAlg.Sha1);
            Console.WriteLine("DIG4={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            // Example 5. Finds and transforms the element with attribute ID="bar"
            digval = C14n.ToDigest("input.xml", "ID=bar", Tran.SubsetById, DigAlg.Sha1);
            Console.WriteLine("DIG5={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            // Example 6. Excludes element with attribute Id="thesig"
            digval = C14n.ToDigest("input.xml", "thesig", Tran.ExcludeById, DigAlg.Sha1);
            Console.WriteLine("DIG6={0}", digval);
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed");

            Console.WriteLine("NOTE: Should have DIG1==DIG6 and DIG3==DIG4 above.");

            // Transform entire file
            fname = "olamundo.xml";
            oname = "olamundo-out.xml";
            Console.WriteLine("FILE: {0}", fname);
            n = C14n.ToFile(oname, fname);
            Console.WriteLine("C14n.ToFile()->{0} returns {1} (expected 0 => OK)", oname, n);
            Debug.Assert(0 == n, "C14n.ToFile failed");

            // Compute digest of entire file
            digval = C14n.ToDigest(fname, 0);
            Console.WriteLine("C14n.ToDigest({0})={1}", fname, digval);
            // and do the same to the transformed file we made above
            digval1 = C14n.ToDigest(oname, 0);
            Console.WriteLine("C14n.ToDigest({0})={1}", oname, digval1);
            Debug.Assert(digval == digval1);

            // Same data, different encoding plus UTF-8 Byte Order Mark
            fname = "olamundo-utf8bom.xml";
            oname = "olamundo-utf8bom-out.xml";
            Console.WriteLine("FILE: {0}", fname);
            digval = C14n.ToDigest(fname, 0);
            Console.WriteLine("C14n.ToDigest({0})={1}", fname, digval);

            // -- we can transform (X)HTML files, too. 
            // This example is used in a detached signature: see `detached.xml`
            fname = "abc.html";
            oname = "abc-c14n-html.txt";
            Console.WriteLine("FILE: {0}", fname);
            n = C14n.ToFile(oname, fname);
            Console.WriteLine("C14n.ToFile()->{0} returns {1} (expected 0 => OK)", oname, n);
            if (n != 0) Console.WriteLine(displayError(n));
            Debug.Assert(0 == n, "C14n.ToFile failed");
            // Display entire file after transforming...
            s = System.Text.Encoding.UTF8.GetString(C14n.ToBytes(fname));
            Console.WriteLine("C14N('{0}'):", fname);
            Console.WriteLine(s);

            // Compute SHA-1 digest of entire file
            digval = C14n.ToDigest(fname, 0);
            Console.WriteLine("C14n.ToDigest({0})={1}", fname, digval);
            // and do the same to the transformed file we made above
            digval1 = C14n.ToDigest(oname, 0);
            Console.WriteLine("C14n.ToDigest({0})={1}", oname, digval1);
            Debug.Assert(digval == digval1);

            // Same again but using SHA-256
            digval = C14n.ToDigest(fname, DigAlg.Sha256);
            Console.WriteLine("SHA256('{0}'): {1}", fname, digval);

            // Extract and transform the body element
            // Output to a byte array
            fname = "olamundo-base.xml";
            Console.WriteLine("FILE: {0}", fname);
            b = C14n.ToBytes(fname, "Body", Tran.SubsetByTag);
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed");
            Console.WriteLine("C14n.ToBytes(SubsetByTag) returns {0} bytes.", b.Length);
            // Note this a byte array containing UTF-8-encoded text, so be careful printing
            Console.WriteLine("C14N(Body):");
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b));

            // Compute SHA-1 digest value of the XML document excluding the <Signature> element
            // ... this is the value to be inserted into <SignedInfo>
            digval = C14n.ToDigest(fname, "Signature", Tran.ExcludeByTag, DigAlg.Sha1);
            Console.WriteLine("DIGVAL:");
            Console.WriteLine(digval);

            // Extract the SignedInfo element into memory
            // Note %digval% parameter to be completed
            b = C14n.ToBytes(fname, "SignedInfo", Tran.SubsetByTag);
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed");
            Console.WriteLine("SIGNEDINFO (BASE):");
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b));

            // Insert the required DigestValue we prepared earlier
            // Note the SignedInfo element is *always* US-ASCII encoded,
            // so we can use the more convenient String.Replace function
            s = System.Text.Encoding.UTF8.GetString(b).Replace("%digval%", digval);
            Console.WriteLine("SIGNEDINFO (COMPLETED):");
            Console.WriteLine(s);
            // Now compute the digest value of this string
            // We could use this to compute the required signature value.
            // See the module `TestScn14PKI` for a full example using a cryptographic library.
            digval1 = C14n.ToDigest(System.Text.Encoding.UTF8.GetBytes(s), DigAlg.Sha1);
            Console.WriteLine("SHA1(signedinfo)= {0}", digval1);


            Console.WriteLine("\nALL DONE.");

        }

        /// <summary>
        /// Display last error status
        /// </summary>
        /// <param name="errCode">Error code (+ve or -ve)</param>
        /// <returns>String containing error status</returns>
        private static string displayError(int errCode)
        {
            string se = Err.LastError();
            string s = string.Format("ERROR {0}: ", (errCode < 0 ? -errCode : errCode)); 
            s += Err.ErrorLookup(errCode);
            if (se.Length > 0) {
                s += ": " + se;
            }
            return s;
        }
    }
}