#! python3 # -*- coding: utf8 -*- """Some tests for `sc14n.py` the Python interface to SC14N.""" # test_sc14n.py: version 2.1.1 # $Date: 2019-12-28 20:53:00 $ # ************************** LICENSE ***************************************** # Copyright (C) 2017-19 David Ireland, DI Management Services Pty Limited. # <http://www.di-mgt.com.au/contact/> <www.cryptosys.net> # The code in this module is licensed under the terms of the MIT license. # For a copy, see <https://opensource.org/licenses/MIT> # **************************************************************************** # import context # Setup path to module in parent from sc14n import * # @UnusedWildImport import os import sys _MIN_DLL_VERSION = 20100 # Show some info about the core DLL print("DLL version =", Gen.version()) print("cwd =", os.getcwd()) if Gen.version() < _MIN_DLL_VERSION: raise Exception('Require DLL version ' + str(_MIN_DLL_VERSION) + ' or greater') # FILE-RELATED UTILITIES def read_binary_file(fname): with open(fname, "rb") as f: return bytearray(f.read()) def write_binary_file(fname, data): with open(fname, "wb") as f: f.write(data) def read_text_file(fname, enc='utf8'): with open(fname, encoding=enc) as f: return f.read() def write_text_file(fname, s, enc='utf8'): with open(fname, "w", encoding=enc) as f: f.write(s) def _print_file(fname): """Print contents of text file""" s = read_text_file(fname) print(s) # ERROR def disp_error(n): """Display details of last error.""" s = Err.last_error() print("ERROR %d: %s: %s" % (n, Err.error_lookup(n), "\n" + s if s else "")) ################### # THE TESTS PROPER ################### def test_version(): print("DLL VERSION:", Gen.version()) print("module_name =", Gen.module_name()) print("compile_time =", Gen.compile_time()) print("platform =", Gen.core_platform()) print("licence_type =", Gen.licence_type()) def test_olamundo(): # Compute digest value of entire file (after transforming) fname = "olamundo.xml" print("FILE:", fname) digval = C14n.file2digest(fname) print("DIG:", digval) fname = "olamundo-utf8.xml" print("FILE:", fname) digval = C14n.file2digest(fname) print("DIG:", digval) fname = "olamundo-utf8bom.xml" print(("FILE:", fname)) digval = C14n.file2digest(fname) print("DIG:", digval) fname = "olamundo-base.xml" print("FILE:", fname) s = C14n.file2string(fname, "Signature", Tran.OMITBYTAG) print("EXCLUDE <Signature>:\n", s) def test_input_examples(): print("Testing input examples...") # Example 1. Excludes the first element with the tag name <Signature> r = C14n.file2file("c14nfile1.txt", "input.xml", "Signature", Tran.OMITBYTAG) assert(r) # Example 2. Finds and transforms the first element with the tag name <SignedInfo> r = C14n.file2file("c14nfile2.txt", "input.xml", "SignedInfo", Tran.SUBSETBYTAG) assert(r) # Example 3. Finds and transforms the third element with the tag name <Data> r = C14n.file2file("c14nfile3.txt", "input.xml", "Data[3]", Tran.SUBSETBYTAG) assert(r) # Example 4. Finds and transforms the element with attribute Id="foo" r = C14n.file2file("c14nfile4.txt", "input.xml", "foo", Tran.SUBSETBYID) assert(r) # Example 5. Finds and transforms the element with attribute ID="bar" r = C14n.file2file("c14nfile5.txt", "input.xml", "ID=bar", Tran.SUBSETBYID) assert(r) # Example 6. Excludes element with attribute Id="thesig" r = C14n.file2file("c14nfile6.txt", "input.xml", "thesig", Tran.OMITBYID) assert(r) print("...done input examples.") def test_input_to_digest(): print("Testing input examples to digest...") # Same as test_input_examples() except output diget value directly... # Example 1. Excludes the first element with the tag name <Signature> digval = C14n.file2digest("input.xml", "Signature", Tran.OMITBYTAG) print("DIG1 =", digval) assert(len(digval) > 0) # Example 2. Finds and transforms the first element with the tag name <SignedInfo> digval = C14n.file2digest("input.xml", "SignedInfo", Tran.SUBSETBYTAG) print("DIG2 =", digval) assert(len(digval) > 0) # Example 3. Finds and transforms the third element with the tag name <Data> digval = C14n.file2digest("input.xml", "Data[3]", Tran.SUBSETBYTAG) print("DIG3 =", digval) assert(len(digval) > 0) # Example 4. Finds and transforms the element with attribute Id="foo" digval = C14n.file2digest("input.xml", "foo", Tran.SUBSETBYID) print("DIG4 =", digval) assert(len(digval) > 0) # Example 5. Finds and transforms the element with attribute ID="bar" digval = C14n.file2digest("input.xml", "ID=bar", Tran.SUBSETBYID) print("DIG5 =", digval) assert(len(digval) > 0) # Example 6. Excludes element with attribute Id="thesig" digval = C14n.file2digest("input.xml", "thesig", Tran.OMITBYID) print("DIG6 =", digval) assert(len(digval) > 0) print("Expecting DIG3==DIG4 and DIG1==DIG6 in above results.") def test_exclusive(): print("Examples from Section 2.2 of Exclusive XML Canonicalization Version 1.0 [RFC 3741]...") fname = "example1.xml" oname = "example1-incl-out.xml" print("FILE: ", fname) print("Using inclusive c14n:") r = C14n.file2file(oname, fname, "n1:elem2", Tran.SUBSETBYTAG) assert(r) s = C14n.file2string(fname, "n1:elem2", Tran.SUBSETBYTAG) assert(len(s) > 0) print(s) digval = C14n.file2digest(fname, "n1:elem2", Tran.SUBSETBYTAG) digok = "RSTxYngjk7kroYxpMtbJP2g7Q3s=" print("SHA1(subset) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) fname = "example2.xml" oname = "example2-incl-out.xml" print("FILE: ", fname) print("Using inclusive c14n:") r = C14n.file2file(oname, fname, "n1:elem2", Tran.SUBSETBYTAG) assert(r) s = C14n.file2string(fname, "n1:elem2", Tran.SUBSETBYTAG) assert(len(s) > 0) print(s) digval = C14n.file2digest(fname, "n1:elem2", Tran.SUBSETBYTAG) digok = "x9seNaaK3lTVs9n2WIIrIgDDU1E=" print("SHA1(subset) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) # Use exclusive method - outputs should be identical fname = "example1.xml" oname = "example1-excl-out.xml" print("FILE: ", fname) print("Using exclusive c14n:") r = C14n.file2file(oname, fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) assert(r) s = C14n.file2string(fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) assert(len(s) > 0) print(s) digval = C14n.file2digest(fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) digok = "qYwgpdgV1/b3PQ3aSpMx9wKGtqY=" print("SHA1(subset) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) fname = "example2.xml" oname = "example2-excl-out.xml" print("FILE: ", fname) print("Using exclusive c14n:") r = C14n.file2file(oname, fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) assert(r) s = C14n.file2string(fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) assert(len(s) > 0) print(s) digval = C14n.file2digest(fname, "n1:elem2", Tran.SUBSETBYTAG, TranMethod.EXCLUSIVE) digok = "qYwgpdgV1/b3PQ3aSpMx9wKGtqY=" print("SHA1(subset) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) def test_prefixlist(): # Show use of PrefixList with excl-c14n fname = "soap-ts3-signed-by-alice.xml" print("FILE: ", fname) print("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" s = C14n.file2string(fname, "wsu:Id=TS-3", Tran.SUBSETBYID, TranMethod.EXCLUSIVE, "wsse SOAP-ENV") assert(len(s) > 0) print(s) # Compute SHA-256 digest (to be inserted into <DigestValue> element) digok = "a4cojI7ZDOI1lKvGD7OHNus7qy1DQgpqNdGZ/YEDJQo=" digval = C14n.file2digest(fname, "wsu:Id=TS-3", Tran.SUBSETBYID, DigAlg.SHA256, TranMethod.EXCLUSIVE, "wsse SOAP-ENV") assert(len(digval) > 0) print("SHA256(#TS-3) =", digval) print("Correct SHA256 =", digok) assert(digval == digok) # Transform SignedInfo using excl-c14n with PrefixList="SOAP-ENV" # We can use this digest value to compute the required signature value. ''' <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> ''' digok = "VenfIoZjs3/LxtvQdQuHIizR3vKi7TViE1ZF7Ddnn8I=" digval = C14n.file2digest(fname, "ds:SignedInfo", Tran.SUBSETBYTAG, DigAlg.SHA256, TranMethod.EXCLUSIVE, "SOAP-ENV") assert(len(digval) > 0) print("SHA256(ds:SignedInfo) =", digval) print("Correct SHA256 =", digok) assert(digval == digok) def test_flatten(): # Show use of Flatten option to remove whitespace between tags fname = "ignorable_ws.xml" print("FILE: ", fname) print("Default without flatten...") s = C14n.file2string(fname, "", Tran.ENTIRE) assert(len(s) > 0) print(s) digok = "JNluoz+Z+MbLrTX8W//wEEgeFpo=" digval = C14n.file2digest(fname, "", Tran.ENTIRE) assert(len(digval) > 0) print("SHA1(NO-FLATTEN) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) print("With flatten option...") s = C14n.file2string(fname, "", Tran.ENTIRE, advopts=AdvOptions.FLATTEN) assert(len(s) > 0) print(s) digok = "4ZKWJnP7dUperStlOKrq7athzxw=" digval = C14n.file2digest(fname, "", Tran.ENTIRE, advopts=AdvOptions.FLATTEN) assert(len(digval) > 0) print("SHA1(NFLATTEN) =", digval) print("Correct SHA1 =", digok) assert(digval == digok) def main(): test_version() test_olamundo() test_input_examples() test_input_to_digest() test_exclusive() test_prefixlist() test_flatten() print("ALL DONE.") if __name__ == "__main__": main()