Imports System
Imports System.Text
Imports System.Diagnostics
Imports System.IO
Imports Sc14n

'  
' * $Id: TestSc14n.vb $
' * Last updated:
' *   $Date: 2019-12-11 09:01 $
' *   $Version: 2.1.0 $
' 

' Some tests using the SC14N .NET interface.
' * 
' * Requires `Sc14n` to be installed on your system: available from <http://cryptosys.net/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 4.0 and above 
' * Please report any bugs to <https://cryptosys.net/contact>
' 

'****************************** LICENSE ***********************************
' * Copyright (C) 2017-19 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>
'****************************************************************************
'


' Ported from C# to VB.NET using icsharpcode.net's SharpDevelop.

Namespace TestSc14nNet
    Class TestSc14nNet
        Public Shared Sub Main(args As String())
            Dim n As Integer, r As Integer
            Dim s As String, fname As String, oname As String, digval As String, digval1 As String, digok As String
            Dim dig1 As String, dig2 As String, dig3 As String, dig4 As String, dig5 As String, dig6 As String
            Dim b As Byte()

            ' 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.OmitByTag)
            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.OmitById)
            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.OmitByTag, DigAlg.Sha1)
            Console.WriteLine("DIG1={0}", digval)
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed")
            dig1 = digval

            ' 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")
            dig2 = digval

            ' 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")
            dig3 = digval

            ' 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")
            dig4 = digval

            ' 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")
            dig5 = digval

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

            Console.WriteLine("NOTE: Should have DIG1==DIG6 and DIG3==DIG4 above.")
            Debug.Assert(dig1 = dig6 AndAlso dig3 = dig4, "Digests do not match expected values")

            ' 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 Then
                Console.WriteLine(displayError(n))
            End If
            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.OmitByTag, 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)

            ' USE EXCLUSIVE C14N

            ' Examples from Section 2.2 of Exclusive XML Canonicalization Version 1.0 [RFC 3741] 

            ' Use inclusive method
            fname = "example1.xml"
            oname = "example1-incl-out.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Using inclusive c14n:")
            r = C14n.ToFile(oname, fname, "n1:elem2", Tran.SubsetByTag)
            Debug.Assert(0 = r, "C14n.ToFile failed")
            b = C14n.ToBytes(fname, "n1:elem2", Tran.SubsetByTag)
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digval = C14n.ToDigest(fname, "n1:elem2", Tran.SubsetByTag, DigAlg.Sha1)
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed")
            digok = "RSTxYngjk7kroYxpMtbJP2g7Q3s="
            Console.WriteLine("SHA1(subset)= {0}", digval)
            Console.WriteLine("Correct SHA1= {0}", digok)
            Debug.Assert(digval = digok, "Digests do not match")

            fname = "example2.xml"
            oname = "example2-incl-out.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Using inclusive c14n:")
            r = C14n.ToFile(oname, fname, "n1:elem2", Tran.SubsetByTag)
            Debug.Assert(0 = r, "C14n.ToFile failed")
            b = C14n.ToBytes(fname, "n1:elem2", Tran.SubsetByTag)
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digval = C14n.ToDigest(fname, "n1:elem2", Tran.SubsetByTag, DigAlg.Sha1)
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed")
            digok = "x9seNaaK3lTVs9n2WIIrIgDDU1E="
            Console.WriteLine("SHA1(subset)= {0}", digval)
            Console.WriteLine("Correct SHA1= {0}", digok)
            Debug.Assert(digval = digok, "Digests do not match")

            ' Use exclusive method - outputs should be identical
            fname = "example1.xml"
            oname = "example1-excl-out.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Using exclusive c14n:")
            r = C14n.ToFile(oname, fname, "n1:elem2", Tran.SubsetByTag, TranMethod.Exclusive)
            Debug.Assert(0 = r, "C14n.ToFile failed")
            b = C14n.ToBytes(fname, "n1:elem2", Tran.SubsetByTag, TranMethod.Exclusive)
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digval = C14n.ToDigest(fname, "n1:elem2", Tran.SubsetByTag, DigAlg.Sha1, TranMethod.Exclusive)
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed")
            digok = "qYwgpdgV1/b3PQ3aSpMx9wKGtqY="
            Console.WriteLine("SHA1(subset)= {0}", digval)
            Console.WriteLine("Correct SHA1= {0}", digok)
            ' Correct SHA1=qYwgpdgV1/b3PQ3aSpMx9wKGtqY=
            Debug.Assert(digval = digok, "Digests do not match")

            fname = "example2.xml"
            oname = "example2-excl-out.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Using exclusive c14n:")
            r = C14n.ToFile(oname, fname, "n1:elem2", Tran.SubsetByTag, TranMethod.Exclusive)
            Debug.Assert(0 = r, "C14n.ToFile failed")
            b = C14n.ToBytes(fname, "n1:elem2", Tran.SubsetByTag, TranMethod.Exclusive)
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digval = C14n.ToDigest(fname, "n1:elem2", Tran.SubsetByTag, DigAlg.Sha1, TranMethod.Exclusive)
            Debug.Assert(digval.Length > 0, "C14n.ToDigest failed")
            digok = "qYwgpdgV1/b3PQ3aSpMx9wKGtqY="
            Console.WriteLine("SHA1(subset)= {0}", digval)
            Console.WriteLine("Correct SHA1= {0}", digok)
            ' Correct SHA1=qYwgpdgV1/b3PQ3aSpMx9wKGtqY=
            Debug.Assert(digval = digok, "Digests do not match")

            ' Show use of PrefixList with excl-c14n
            fname = "soap-ts3-signed-by-alice.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Using exclusive c14n with PrefixList...")
            '
            '            <ds:Reference URI="#TS-3">
            '            <ds:Transforms>
            '            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
            '            <ec:InclusiveNamespaces PrefixList="wsse SOAP-ENV" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
            '            </ds:Transform>
            '            </ds:Transforms>
            '            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
            '            

            ' Transform element with wsu:Id="TS-3" using excl-c14n with PrefixList="wsse SOAP-ENV"
            b = C14n.ToBytes(fname, "wsu:Id=TS-3", Tran.SubsetById, TranMethod.Exclusive, "wsse SOAP-ENV")
            Debug.Assert(b.Length > 0, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))

            ' Compute SHA-256 digest (to be inserted into <DigestValue> element)
            digok = "a4cojI7ZDOI1lKvGD7OHNus7qy1DQgpqNdGZ/YEDJQo="
            digval = C14n.ToDigest(fname, "wsu:Id=TS-3", Tran.SubsetById, DigAlg.Sha256, TranMethod.Exclusive, "wsse SOAP-ENV")
            Console.WriteLine("SHA256(#TS-3)  = {0}", digval)
            Console.WriteLine("Correct SHA256 = {0}", digok)
            Debug.Assert(digval = digok, "Digests do not match")

            ' Transform SignedInfo using excl-c14n with PrefixList="SOAP-ENV"
            '
            '            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
            '            <ec:InclusiveNamespaces PrefixList="SOAP-ENV" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" />
            '            </ds:CanonicalizationMethod>
            '            

            ' Compute SHA-256 digest (to be used to compute the SignatureValue using crypto toolkit)
            digok = "VenfIoZjs3/LxtvQdQuHIizR3vKi7TViE1ZF7Ddnn8I="
            digval = C14n.ToDigest(fname, "ds:SignedInfo", Tran.SubsetByTag, DigAlg.Sha256, TranMethod.Exclusive, "SOAP-ENV")
            Console.WriteLine("SHA256(ds:SignedInfo)= {0}", digval)
            Console.WriteLine("Correct SHA256       = {0}", digok)
            Debug.Assert(digval = digok, "Digests do not match")


            ' TEST "FLATTEN" OPTION ADDED IN V2.1

            fname = "ignorable_ws.xml"
            Console.WriteLine()
            Console.WriteLine("FILE: {0}", fname)
            Console.WriteLine("Default without flatten:")
            b = C14n.ToBytes(fname, "", Tran.Entire, advOpts:=AdvOptions.[Default])
            Debug.Assert(0 = r, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digok = "JNluoz+Z+MbLrTX8W//wEEgeFpo="
            digval = C14n.ToDigest(fname, "", Tran.Entire, DigAlg.Sha1, advOpts:=AdvOptions.[Default])
            Console.WriteLine("SHA1(NO-FLATTEN)= {0}", digval)
            Console.WriteLine("Correct SHA1    = {0}", digok)

            Console.WriteLine("With flatten option:")
            b = C14n.ToBytes(fname, "", Tran.Entire, advOpts:=AdvOptions.Flatten)
            Debug.Assert(0 = r, "C14n.ToBytes failed")
            Console.WriteLine(System.Text.Encoding.UTF8.GetString(b))
            digok = "4ZKWJnP7dUperStlOKrq7athzxw="
            digval = C14n.ToDigest(fname, "", Tran.Entire, DigAlg.Sha1, advOpts:=AdvOptions.Flatten)
            Console.WriteLine("SHA1(FLATTEN)= {0}", digval)
            Console.WriteLine("Correct SHA1 = {0}", digok)

            Console.WriteLine(vbLf & "ALL DONE.")

        End Sub

        ''' <summary>
        ''' Display last error status
        ''' </summary>
        ''' <param name="errCode">Error code (+ve or -ve)</param>
        ''' <returns>String containing error status</returns>
        Private Shared Function displayError(errCode As Integer) As String
            Dim se As String = Err.LastError()
            Dim s As String = String.Format("ERROR {0}: ", (If(errCode < 0, -errCode, errCode)))
            s += Err.ErrorLookup(errCode)
            If se.Length > 0 Then
                s += ": " & se
            End If
            Return s
        End Function
    End Class
End Namespace