Changeset 3504


Ignore:
Timestamp:
22/02/08 16:23:33 (12 years ago)
Author:
pjkersha
Message:

Updated for latest version of ElementTree.

  • Exclusive C14N now works with some exceptions: quoted Attribute Certificate in SOAP message gets mistaken for XML content of the SOAP message.
  • Added more tests based on examples from W3C site:  http://www.w3.org/TR/xml-c14n
File:
1 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/elementTreeC14n/testC14n.py

    r3438 r3504  
    1919import traceback 
    2020 
     21from difflib import unified_diff 
     22 
    2123from StringIO import StringIO 
    2224 
     
    2628# Minidom based Canonicalization from ZSI for comparison 
    2729from ZSI.wstools.c14n import Canonicalize 
     30 
     31from xml.xpath.Context import Context 
     32from xml import xpath 
     33from xml.dom.ext.reader import PyExpat 
    2834 
    2935xpdVars = os.path.expandvars 
     
    3844            pdb.set_trace()           
    3945 
    40  
    41     def test1UTF8DocEncoding(self): 
    42         xml = '<?xml version="1.0" encoding="UTF-8"?>\n<a/>\n' 
    43         elem = ET.fromstring(xml) 
    44         f = StringIO() 
    45         ET.ElementTree(elem).write_c14n(f) 
    46         c14n = f.getvalue() 
    47  
    48      
    49     def test2NormalizedLineBreaks(self): 
     46    def assertEqual(self, a, b): 
     47        '''Override default to get something easy to read than super class 
     48        behaviour''' 
     49        diffGen = unified_diff(a.split('\n'), b.split('\n')) 
     50        raise AssertionError('\n'+'\n'.join(diffGen)) 
     51         
     52    def test01UTF8DocEncoding(self): 
     53         
     54        # http://www.w3.org/TR/xml-c14n#Example-UTF8 
     55        xml = '<?xml version="1.0" encoding="ISO-8859-1"?><doc>&#169;</doc>' 
     56        elem = ET.fromstring(xml) 
     57        f = StringIO() 
     58        ETC14N.write(elem, f) 
     59        c14n = f.getvalue() 
     60        self.assertEqual(c14n, '<doc>#xC2#xA9</doc>') 
     61 
     62 
     63    def test01aPIsCommentsAndOutsideOfDocElem(self): 
     64        # http://www.w3.org/TR/xml-c14n#Example-OutsideDoc - PIs, Comments, and 
     65        # Outside of Document Element  
     66        xml = \ 
     67'''<?xml version="1.0"?> 
     68 
     69<?xml-stylesheet   href="doc.xsl" 
     70   type="text/xsl"   ?> 
     71 
     72<!DOCTYPE doc SYSTEM "doc.dtd"> 
     73 
     74<doc>Hello, world!<!-- Comment 1 --></doc> 
     75 
     76<?pi-without-data     ?> 
     77 
     78<!-- Comment 2 --> 
     79 
     80<!-- Comment 3 -->''' 
     81 
     82        exptdC14n = \ 
     83'''<?xml-stylesheet href="doc.xsl" 
     84   type="text/xsl"   ?> 
     85<doc>Hello, world!</doc> 
     86<?pi-without-data?>''' 
     87 
     88        elem = ET.fromstring(xml) 
     89        f = StringIO() 
     90        ETC14N.write(elem, f) 
     91        c14n = f.getvalue() 
     92        self.assertEqual(c14n, exptdC14n) 
     93 
     94    
     95    def test02NormalizeLineBreaks(self): 
    5096        xml = '<?xml version="1.0" encoding="UTF-8"?>\r\n<a/>\r\n' 
    5197        elem = ET.fromstring(xml) 
     
    56102 
    57103     
    58     def test3NormalizedAttrVals(self): 
     104    def test03NormalizedAttrVals(self): 
    59105        pass 
    60106 
    61107    
    62     def test4CharAndParsedEntityRefsReplaced(self): 
    63         pass 
    64  
    65      
    66     def test5CDATASectionsReplaced(self): 
     108    def test04CharAndParsedEntityRefsReplaced(self): 
     109        xml = '''<!DOCTYPE doc [ 
     110<!ATTLIST doc attrExtEnt ENTITY #IMPLIED> 
     111<!ENTITY ent1 "Hello"> 
     112<!ENTITY ent2 SYSTEM "world.txt"> 
     113<!ENTITY entExt SYSTEM "earth.gif" NDATA gif> 
     114<!NOTATION gif SYSTEM "viewgif.exe"> 
     115]> 
     116<doc attrExtEnt="entExt"> 
     117   &ent1;, &ent2;! 
     118</doc> 
     119 
     120<!-- Let world.txt contain "world" (excluding the quotes) -->''' 
     121 
     122        exptdC14n = '''<doc attrExtEnt="entExt"> 
     123   Hello, world! 
     124</doc>''' 
     125        elem = ET.fromstring(xml) 
     126        f = StringIO() 
     127        ET.ElementTree(elem).write_c14n(f) 
     128        c14n = f.getvalue() 
     129        self.assertEqual(c14n, exptdC14n) 
     130         
     131     
     132    def test05CDATASectionsReplaced(self): 
    67133        xml = \ 
    68134"""<?xml version="1.0" encoding="UTF-8"?> 
     
    101167         
    102168     
    103     def test6XMLDeclAndDTDRemoved(self): 
     169    def test06XMLDeclAndDTDRemoved(self): 
    104170        xmlDecl = '<?xml version="1.0" encoding="UTF-8"?>' 
    105171        dtd = \ 
     
    125191 
    126192    
    127     def test7EmptyElemsConvertedStartEndPairs(self): 
     193    def test07EmptyElemsConvertedStartEndPairs(self): 
    128194        elem = ET.fromstring('<?xml version="1.0" encoding="UTF-8"?><a/>') 
    129195        f = StringIO() 
     
    133199 
    134200           
    135     def test8WhitespaceNormalized(self): 
    136         '''...outside the document element and within start and end tags''' 
     201    def test08WhitespaceNormalized(self): 
     202        # ...outside the document element and within start and end tags 
    137203        dat = \ 
    138204'''        1 2  
     
    148214 
    149215''' % dat 
    150         elem = ET.fromstring(xml) 
    151         f = StringIO() 
    152         ET.ElementTree(elem).write_c14n(f) 
    153         c14n = f.getvalue() 
     216 
     217        elem = ET.fromstring(xml) 
     218        f = StringIO() 
     219        ET.ElementTree(elem).write_c14n(f) 
     220        c14n = f.getvalue() 
     221         
    154222        self.failUnless('a1="1" a2="2"' in c14n,  
    155223                        "Expecting single space between attributes") 
     
    157225                        "Expecting element content to be preserved") 
    158226         
    159         sub = xml[xml.find('<a'):xml.find('</a>')] 
     227        sub = c14n[c14n.find('<a'):c14n.find('>')] 
    160228        self.failIf('\n' in sub,  
    161229                    "Expecting removal of line breaks for 'a' element") 
    162230      
    163231      
    164     def test9WhitespaceInCharContentRetained(self): 
    165         '''Nb. excludes chars removed during line break normalization''' 
     232    def test09WhitespaceInCharContentRetained(self): 
     233        # http://www.w3.org/TR/xml-c14n#Example-WhitespaceInContent 
     234        # Nb. excludes chars removed during line break normalization 
     235        xml = \ 
     236'''<doc> 
     237   <clean>   </clean> 
     238   <dirty>   A   B   </dirty> 
     239   <mixed> 
     240      A 
     241      <clean>   </clean> 
     242      B 
     243      <dirty>   A   B   </dirty> 
     244      C 
     245   </mixed> 
     246</doc>''' 
     247 
     248        exptdC14n = \ 
     249'''<doc> 
     250   <clean>   </clean> 
     251   <dirty>   A   B   </dirty> 
     252   <mixed> 
     253      A 
     254      <clean>   </clean> 
     255      B 
     256      <dirty>   A   B   </dirty> 
     257      C 
     258   </mixed> 
     259</doc>''' 
     260 
     261        elem = ET.fromstring(xml) 
     262        f = StringIO() 
     263        ETC14N.write(elem, f) 
     264        c14n = f.getvalue() 
     265        self.assertEqual(c14n, exptdC14n) 
    166266 
    167267         
     
    182282     
    183283    def test11SpecialCharsReplaced(self): 
    184         '''i.e. within attribute values and character content''' 
     284        # i.e. within attribute values and character content 
     285        pass 
    185286         
    186287         
     
    198299        ET.ElementTree(elem).write_c14n(f) 
    199300        c14n = f.getvalue() 
    200         print c14n 
    201301         
    202302        # Namespace should now only occur once... 
     
    207307         
    208308    def test13DefAttrsAdded2EachElem(self): 
    209         '''Implies reference to doc schema??''' 
    210  
    211     
    212     def test14LexicographicOrderApplied(self): 
    213         '''... to namespace declarations and attributes of each element''' 
    214         xml = \ 
    215 '''<?xml version="1.0" encoding="UTF-8"?> 
    216 <doc xmlns:x="http://example.com/x" xmlns="http://example.com/default"> 
    217   <b y:a1='1' xmlns="http://example.com/default" a3='"3"' 
    218      xmlns:y='http://example.com/y' y:a2='2'/> 
    219 </doc> 
     309        # Ref. http://www.w3.org/TR/xml-c14n#Example-SETags 
     310        xml = '''<!DOCTYPE doc [<!ATTLIST e9 attr CDATA "default">]> 
     311<doc> 
     312   <e1   /> 
     313   <e2   ></e2> 
     314   <e3   name = "elem3"   id="elem3"   /> 
     315   <e4   name="elem4"   id="elem4"   ></e4> 
     316   <e5 a:attr="out" b:attr="sorted" attr2="all" attr="I'm" 
     317      xmlns:b="http://www.ietf.org" 
     318      xmlns:a="http://www.w3.org" 
     319      xmlns="http://example.org"/> 
     320   <e6 xmlns="" xmlns:a="http://www.w3.org"> 
     321      <e7 xmlns="http://www.ietf.org"> 
     322         <e8 xmlns="" xmlns:a="http://www.w3.org"> 
     323            <e9 xmlns="" xmlns:a="http://www.ietf.org"/> 
     324         </e8> 
     325      </e7> 
     326   </e6> 
     327</doc>''' 
     328 
     329        elem = ET.fromstring(xml) 
     330        f = StringIO() 
     331#        ET.ElementTree(elem).write_c14n(f) 
     332        ETC14N.write(elem, f) 
     333        c14n = f.getvalue() 
     334 
     335        exptdC14n = '''<doc> 
     336   <e1></e1> 
     337   <e2></e2> 
     338   <e3 id="elem3" name="elem3"></e3> 
     339   <e4 id="elem4" name="elem4"></e4> 
     340   <e5 xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" a:attr="out"></e5> 
     341   <e6 xmlns:a="http://www.w3.org"> 
     342      <e7 xmlns="http://www.ietf.org"> 
     343         <e8 xmlns=""> 
     344            <e9 xmlns:a="http://www.ietf.org" attr="default"></e9> 
     345         </e8> 
     346      </e7> 
     347   </e6> 
     348</doc>''' 
     349 
     350        self.assertEqual(c14n, exptdC14n) 
     351         
     352    def test14DocumentSubsets(self): 
     353        # Ref. http://www.w3.org/TR/xml-c14n#Example-DocSubsets 
     354        xml = \ 
     355"""<!DOCTYPE doc [ 
     356<!ATTLIST e2 xml:space (default|preserve) 'preserve'> 
     357<!ATTLIST e3 id ID #IMPLIED> 
     358]> 
     359<doc xmlns="http://www.ietf.org" xmlns:w3c="http://www.w3.org"> 
     360   <e1> 
     361      <e2 xmlns=""> 
     362         <e3 id="E3"/> 
     363      </e2> 
     364   </e1> 
     365</doc>""" 
     366 
     367#'''<!-- Evaluate with declaration xmlns:ietf="http://www.ietf.org" --> 
     368        xpathExpr = \ 
    220369''' 
     370(//. | //@* | //namespace::*) 
     371[ 
     372   self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2)) 
     373   or 
     374   count(id("E3")|ancestor-or-self::node()) = count(ancestor-or-self::node()) 
     375]''' 
     376 
     377        exptdC14n = \ 
     378'<e1 xmlns="http://www.ietf.org" xmlns:w3c="http://www.w3.org"><e3 xmlns="" id="E3" xml:space="preserve"></e3></e1>' 
     379 
     380        elem = ET.fromstring(xml) 
     381        f = StringIO() 
     382        subElem = elem.find(xpathExpr) 
     383        ETC14N.write(elem, f, subset_element=subElem) 
     384        c14n = f.getvalue() 
    221385 
    222386    def test15CmpZSIc14n(self): 
     
    259423         
    260424    def test17InclusiveC14nWithXPath(self): 
    261         '''Inclusive Canonicalization of portions of a SOAP message extracted  
    262         using XPath''' 
    263          
    264         # XPath 
    265         from xml.xpath.Context import Context 
    266         from xml import xpath 
    267          
    268         # PyExpat Parse 
    269         from xml.dom.ext.reader import PyExpat 
     425        # Inclusive Canonicalization of portions of a SOAP message extracted  
     426        # using XPath 
     427         
     428        inputFile = './soapGetAttCertResponse.xml' 
     429         
    270430        reader = PyExpat.Reader() 
    271         dom = reader.fromStream(open('./soapGetAttCertResponse.xml')) 
     431        dom = reader.fromStream(open(inputFile)) 
    272432        processorNss = \ 
    273433        { 
    274434            'wsu': \ 
    275 "http://docs.oasis-open.orgwss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 
     435"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 
    276436        } 
    277437     
    278438        ctxt = Context(dom, processorNss=processorNss) 
    279         refNodes = xpath.Evaluate('//*[@wsu:Id]',  
     439        zsiRefNodes = xpath.Evaluate('//*[@wsu:Id]',  
    280440                                  contextNode=dom,  
    281441                                  context=ctxt) 
    282442         
    283         # ElementTree - can't get find() to work - get elements 'manually' for  
    284         # now 
    285 #        elem = ET.parse().getroot() 
    286 #        binarySecurityToken = soapBody[0][0][0] 
    287 #        soapBody = elem[1] 
    288         # TODO: but how to feed these into C14N?? 
    289          
    290         for refNode in refNodes: 
     443        # ElementTree 
     444        elem = ETC14N.parse(inputFile) 
     445         
     446        # Extract nodes for signing 
     447        #etRefNodes = elem.findall('.//*[@wsu:Id]', namespaces=processorNss) 
     448 
     449        # Attribute based search doesn't work - search based on node name 
     450        # instead as a temporary measure 
     451        soapEnvNS = {'SOAP-ENV':"http://schemas.xmlsoap.org/soap/envelope/"} 
     452        soapBodyElem = elem.findall('.//SOAP-ENV:Body', namespaces=soapEnvNS) 
     453         
     454        wsseNS = {'wsse':\ 
     455"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"} 
     456        binSecTokElem = elem.findall('.//wsse:BinarySecurityToken', 
     457                                     namespaces=wsseNS) 
     458 
     459        etRefNodes = binSecTokElem + soapBodyElem 
     460         
     461        for zsiRefNode, etRefNode in zip(zsiRefNodes, etRefNodes): 
    291462            # Get ref node and all it's children 
    292             refC14n = Canonicalize(refNode) 
    293  
    294             print "_"*80 
    295             print "Inclusive C14N %s:\n" % refNode.nodeName 
    296             print refC14n 
    297             open('soapGetAttCertResponse-%s-c14n.xml' % refNode.localName,  
    298                  'w').write(refC14n) 
    299          
     463            zsiRefC14n = Canonicalize(zsiRefNode) 
     464 
     465#            print "_"*80 
     466#            print "ZSI Inclusive C14N %s:\n" % zsiRefNode.nodeName 
     467#            print zsiRefC14n 
     468            open('soapGetAttCertResponse-%s-zsi-c14n.xml'%zsiRefNode.localName,  
     469                 'w').write(zsiRefC14n) 
     470                  
     471            f = StringIO() 
     472#            ETC14N.write(elem, f, etRefNode) 
     473            ETC14N.write(etRefNode, f) 
     474            etRefC14n = f.getvalue() 
     475             
     476#            print "_"*80 
     477#            print "ElementTree Inclusive C14N %s:\n" % etRefNode.tag 
     478#            print etRefC14n 
     479            open('soapGetAttCertResponse-%s-et-c14n.xml' % \ 
     480                 etRefNode.tag.split('}')[-1],  
     481                 'w').write(etRefC14n) 
     482         
     483            self.assertEqual(zsiRefC14n, etRefC14n) 
    300484         
    301485    def test18ExclC14nWithXPath(self): 
    302         '''Exclusive C14N applied to portions of a SOAP message by extracting 
    303         using XPath''' 
     486        # Exclusive C14N applied to portions of a SOAP message by extracting 
     487        # using XPath 
     488         
     489        inputFile = './soapGetAttCertResponse.xml' 
     490 
     491        reader = PyExpat.Reader() 
     492        dom = reader.fromStream(open(inputFile)) 
     493        processorNss = \ 
     494        { 
     495            'wsu': \ 
     496"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 
     497        } 
     498     
     499        ctxt = Context(dom, processorNss=processorNss) 
     500        zsiRefNodes = xpath.Evaluate('//*[@wsu:Id]',  
     501                                  contextNode=dom,  
     502                                  context=ctxt) 
     503        # ElementTree 
     504        elem = ETC14N.parse(inputFile) 
     505         
     506        # Extract nodes for signing 
     507        #etRefNodes = elem.findall('.//*[@wsu:Id]', namespaces=processorNss) 
     508 
     509        # Attribute based search doesn't work - search based on node name 
     510        # instead as a temporary measure 
     511        soapEnvNS = {'SOAP-ENV':"http://schemas.xmlsoap.org/soap/envelope/"} 
     512        soapBodyElem = elem.findall('.//SOAP-ENV:Body', namespaces=soapEnvNS) 
     513         
     514        wsseNS = {'wsse':\ 
     515"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"} 
     516        binSecTokElem = elem.findall('.//wsse:BinarySecurityToken', 
     517                                     namespaces=wsseNS) 
     518 
     519        etRefNodes = binSecTokElem + soapBodyElem 
     520         
     521        for zsiRefNode, etRefNode in zip(zsiRefNodes, etRefNodes): 
     522            # Get ref node and all it's children 
     523            refSubsetList = getChildNodes(zsiRefNode) 
     524            zsiRefC14n = Canonicalize(dom, None, subset=refSubsetList, 
     525                                   unsuppressedPrefixes=[]) 
     526 
     527#            print "_"*80 
     528#            print "ZSI Exclusive C14N %s:\n" % zsiRefNode.nodeName 
     529#            print zsiRefC14n 
     530            open('soapGetAttCertResponse-%s-exclC14n.xml'%zsiRefNode.localName,  
     531                 'w').write(zsiRefC14n) 
     532         
     533            # ElementTree equivalent      
     534            f = StringIO() 
     535            ETC14N.write(elem, f, etRefNode) 
     536            etRefC14n = f.getvalue() 
     537             
     538#            print "_"*80 
     539#            print "ElementTree Exclusive C14N %s:\n" % etRefNode.tag 
     540#            print etRefC14n 
     541            open('soapGetAttCertResponse-%s-et-exclC14n.xml' % \ 
     542                 etRefNode.tag.split('}')[-1],  
     543                 'w').write(etRefC14n) 
     544         
     545            self.assertEqual(zsiRefC14n, etRefC14n) 
     546         
     547    def test19ExclC14nWithXPathAndInclusiveNSPfx(self): 
     548        # Exclusive C14N applied to portions of a SOAP message by extracting 
     549        # using XPath 
    304550        from xml.xpath.Context import Context 
    305551        from xml import xpath 
     
    314560     
    315561        ctxt = Context(dom, processorNss=processorNss) 
    316         refNodes = xpath.Evaluate('//*[@wsu:Id]',  
     562        zsiRefNodes = xpath.Evaluate('//*[@wsu:Id]',  
    317563                                  contextNode=dom,  
    318564                                  context=ctxt) 
    319         for refNode in refNodes: 
     565        for zsiRefNode in zsiRefNodes: 
    320566            # Get ref node and all it's children 
    321             refSubsetList = getChildNodes(refNode) 
    322             refC14n = Canonicalize(dom, None, subset=refSubsetList, 
    323                                    unsuppressedPrefixes=[]) 
    324  
    325             print "_"*80 
    326             print "Exclusive C14N %s:\n" % refNode.nodeName 
    327             print refC14n 
    328             open('soapGetAttCertResponse-%s-exclC14n.xml' % refNode.localName,  
    329                  'w').write(refC14n) 
    330          
    331          
    332     def test19ExclC14nWithXPathAndInclusiveNamespacePrefixes(self): 
    333         '''Exclusive C14N applied to portions of a SOAP message by extracting 
    334         using XPath''' 
    335         from xml.xpath.Context import Context 
    336         from xml import xpath 
    337         from xml.dom.ext.reader import PyExpat 
    338         reader = PyExpat.Reader() 
    339         dom = reader.fromStream(open('./soapGetAttCertResponse.xml')) 
    340         processorNss = \ 
    341         { 
    342             'wsu': \ 
    343 "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", 
    344         } 
    345      
    346         ctxt = Context(dom, processorNss=processorNss) 
    347         refNodes = xpath.Evaluate('//*[@wsu:Id]',  
    348                                   contextNode=dom,  
    349                                   context=ctxt) 
    350         for refNode in refNodes: 
    351             # Get ref node and all it's children 
    352             refSubsetList = getChildNodes(refNode) 
    353             refC14n = Canonicalize(dom, None, subset=refSubsetList, 
     567            refSubsetList = getChildNodes(zsiRefNode) 
     568            zsiRefC14n = Canonicalize(dom, None, subset=refSubsetList, 
    354569                                   unsuppressedPrefixes=['SOAP-ENV', 'ds']) 
    355570 
    356             print "_"*80 
    357             print "Exclusive C14N with Prefixes %s:\n" % refNode.nodeName 
    358             print refC14n 
     571#            print "_"*80 
     572#            print "Exclusive C14N with Prefixes %s:\n" % zsiRefNode.nodeName 
     573#            print zsiRefC14n 
    359574            open('soapGetAttCertResponse-%s-exclC14nWithInclPrefixes.xml' % \ 
    360                  refNode.localName,  
    361                  'w').write(refC14n) 
     575                 zsiRefNode.localName,  
     576                 'w').write(zsiRefC14n) 
    362577         
    363578 
Note: See TracChangeset for help on using the changeset viewer.