#! 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://di-mgt.com.au/contact/> <https://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()