funga

Signer and keystore daemon and library for cryptocurrency software development
Log | Files | Refs | README | LICENSE

commit 421901e3b0ec114336c377d58495e23ef67f7678
parent c9634a6d5735d64d59b39e534476c8e01002d6e7
Author: lash <dev@holbrook.no>
Date:   Fri, 23 Jun 2023 00:20:12 +0100

Add xml schema checker

Diffstat:
MMANIFEST.in | 2+-
Afunga/data/__init__.py | 4++++
Afunga/data/xmldsig1-schema.xsd | 29+++++++++++++++++++++++++++++
Mfunga/xml/xml.py | 76+++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msetup.py | 14+++++++++++++-
Mtests/test_xml.py | 7+++++++
Mtests/testdata/sign.xml | 2+-
Axml_requirements.txt | 1+
8 files changed, 105 insertions(+), 30 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in @@ -1 +1 @@ -include *requirements* CHANGELOG WAIVER WAIVER.asc LICENSE +include *requirements* CHANGELOG WAIVER WAIVER.asc LICENSE funga/data/*.xsd diff --git a/funga/data/__init__.py b/funga/data/__init__.py @@ -0,0 +1,4 @@ +# standard imports +import os + +data_dir = os.path.dirname(os.path.realpath(__file__)) diff --git a/funga/data/xmldsig1-schema.xsd b/funga/data/xmldsig1-schema.xsd @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- +# +# Copyright ©[2011] World Wide Web Consortium +# (Massachusetts Institute of Technology, +# European Research Consortium for Informatics and Mathematics, +# Keio University). All Rights Reserved. +# This work is distributed under the W3C® Software License [1] in the +# hope that it will be useful, but WITHOUT ANY WARRANTY; without even +# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. +# [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 +# +--> + +<schema xmlns="http://www.w3.org/2001/XMLSchema" + xmlns:dsig="http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/" + targetNamespace="http://www.w3.org/2000/09/xmldsig#" + version="0.1" elementFormDefault="qualified"> + + <include + schemaLocation="http://www.w3.org/TR/2008/REC-xmldsig-core-20080610/xmldsig-core-schema.xsd"/> + + + <import namespace="http://www.w3.org/2009/xmldsig11#" + schemaLocation="http://www.w3.org/TR/xmldsig-core/xmldsig11-schema.xsd"/> +</schema> + diff --git a/funga/xml/xml.py b/funga/xml/xml.py @@ -1,15 +1,20 @@ # standard imports +import os from enum import Enum import logging import xml.etree.ElementTree as ET from base64 import b64decode +# local imports +from funga.data import data_dir + logg = logging.getLogger(__name__) SignatureAccept = Enum('SignatureAccept', 'CANONICALIZATION SIGNING TRANSFORM DIGEST') SignatureVerify = Enum('SignatureVerify', 'SIGNATURE DIGEST PUBLICKEY') + def cryptobinary_to_int(v): b = b64decode(v) return int.from_bytes(b, byteorder='big') @@ -22,7 +27,7 @@ class SignatureParser: 'dsig11': "http://www.w3.org/2009/xmldsig11", } - def __init__(self): + def __init__(self, validate_schema=True): self.__tree = None self.__signature_verifier = None self.__settings = [ @@ -36,22 +41,34 @@ class SignatureParser: None, None, ] - self.__r = None + self.__schema = None + if validate_schema: + self.__load_schema_validator() self.clear() + + def __load_schema_validator(self): + import importlib + m = None + try: + m = importlib.import_module('xmlschema') + except ModuleNotFoundError: + return + sp = os.path.join(data_dir, 'xmldsig1-schema.xsd') + self.__schema = m.XMLSchema(sp) + def clear(self): - self.__r = { - 'sig': None, - 'pubkey': None, - 'prime': None, - 'curve_a': None, - 'curve_b': None, - 'base': None, - 'order': None, - 'cofactor': None, - 'keyname': None, - } + self.signature = None + self.digest = None + self.public_key = None + self.prime = None + self.curve_a = None + self.curve_b = None + self.base = None + self.order = None + self.cofactor = None + self.keyname = None def set(self, k, v): @@ -69,8 +86,15 @@ class SignatureParser: return self.__verify[k.value - 1] raise ValueError('invalid key: {}'.format(k)) + + def __verify_schema(self, fp): + if self.__schema == None: + return + schema.validate(fp) + def process_file(self, fp): + self.__verify_schema(fp) self.__tree = ET.parse(fp) self.__root = self.__tree.getroot() self.__verify_canonicalization(self.__root[0][0]) @@ -82,7 +106,6 @@ class SignatureParser: r = self.__root.find('./KeyInfo', namespaces=self.namespaces) if r != None: self.__opt_verify_keyinfo(r) - logg.debug('result {}'.format(self.__r)) def __verify_canonicalization(self, el): @@ -98,7 +121,7 @@ class SignatureParser: m = self.get(SignatureVerify.SIGNATURE) if m != None: assert m(b) - self.__r['sig'] = b + self.signature = b def __opt_verify_signedinfo(self, el): r = el.find('./DigestMethod', namespaces=self.namespaces) @@ -110,17 +133,17 @@ class SignatureParser: m = self.get(SignatureVerify.DIGEST) if m != None: assert m(b) - self.__r['digest'] = b + self.digest = b def __opt_verify_keyinfo(self, el): r = el.find('./dsig11:ECKeyValue', namespaces=self.namespaces) if r != None: - assert self.__opt_verify_keyinfo_eckey(r) + self.__opt_verify_keyinfo_eckey(r) r = el.find('./KeyName', namespaces=self.namespaces) if r != None: - self.__r['keyname'] = r.text + self.keyname = r.text def __opt_verify_keyinfo_eckey(self, el): @@ -130,18 +153,17 @@ class SignatureParser: m = self.get(SignatureVerify.PUBLICKEY) if m != None: assert m(b) - self.__r['pubkey'] = b + self.public_key = b r = el.find('./dsig11:ECParameters/dsig11:FieldID/dsig11:Prime/dsig11:P', namespaces=self.namespaces) - self.__r['prime'] = cryptobinary_to_int(r.text) + self.prime = cryptobinary_to_int(r.text) r = el.find('./dsig11:ECParameters/dsig11:Curve/dsig11:A', namespaces=self.namespaces) - self.__r['curve_a'] = cryptobinary_to_int(r.text) + self.curve_a = cryptobinary_to_int(r.text) r = el.find('./dsig11:ECParameters/dsig11:Curve/dsig11:B', namespaces=self.namespaces) - self.__r['curve_b'] = cryptobinary_to_int(r.text) + self.curve_b = cryptobinary_to_int(r.text) r = el.find('./dsig11:ECParameters/dsig11:Base', namespaces=self.namespaces) - self.__r['base'] = cryptobinary_to_int(r.text) + self.base = cryptobinary_to_int(r.text) r = el.find('./dsig11:ECParameters/dsig11:Order', namespaces=self.namespaces) - self.__r['order'] = cryptobinary_to_int(r.text) + self.order = cryptobinary_to_int(r.text) r = el.find('./dsig11:ECParameters/dsig11:CoFactor', namespaces=self.namespaces) - self.__r['cofactor'] = int(r.text) - - return True + if r != None: + self.cofactor = int(r.text) diff --git a/setup.py b/setup.py @@ -22,9 +22,18 @@ while True: test_requirements.append(l.rstrip()) f.close() +xml_requirements = [] +f = open('xml_requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + xml_requirements.append(l.rstrip()) +f.close() + setup( name="funga", - version="0.5.6", + version="0.5.7", description="A signer and keystore daemon and library for cryptocurrency software development", author="Louis Holbrook", author_email="dev@holbrook.no", @@ -35,5 +44,8 @@ setup( tests_require=test_requirements, long_description=long_description, long_description_content_type='text/markdown', + extras_require={ + 'xml': xml_requirements, + }, url='https://git.defalsify.org/funga', ) diff --git a/tests/test_xml.py b/tests/test_xml.py @@ -39,13 +39,16 @@ class TestXmlSig(unittest.TestCase): with self.assertRaises(AssertionError): self.parser.process_file(self.xml_file) self.parser.set(SignatureAccept.CANONICALIZATION, 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315') + with self.assertRaises(AssertionError): self.parser.process_file(self.xml_file) self.parser.set(SignatureAccept.SIGNING, 'http://tools.ietf.org/html/rfc6931') + with self.assertRaises(AssertionError): self.parser.process_file(self.xml_file) self.parser.set(SignatureAccept.DIGEST, 'https://csrc.nist.gov/glossary/term/sha_256') self.parser.process_file(self.xml_file) + self.parser.set(SignatureVerify.SIGNATURE, verify_fail) with self.assertRaises(AssertionError): self.parser.process_file(self.xml_file) @@ -70,6 +73,10 @@ class TestXmlSig(unittest.TestCase): self.parser.set(SignatureVerify.PUBLICKEY, verify_pub_ok) self.parser.process_file(self.xml_file) + self.assertEqual(self.parser.signature.hex(), 'af77767edbccdf46380fed6f06af43c807de4bedce2eda129923340e9577c97e76bc55d587103a367057167f351ae1cfd6b8dea6e0282257de3594fbe3d8780700') + self.assertEqual(self.parser.public_key.hex(), '049f6bb6a7e3f5b7ee71756a891233d1415658f8712bac740282e083dc9240f5368bdb3b256a5bf40a8f7f9753414cb447ee3f796c5f30f7eb40a7f5018fc7f02e') + self.assertEqual(self.parser.digest.hex(), '76b2e96714d3b5e6eb1d1c509265430b907b44f72b2a22b06fcd4d96372b8565') + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/sign.xml b/tests/testdata/sign.xml @@ -15,7 +15,7 @@ <SignatureValue>r3d2ftvM30Y4D+1vBq9DyAfeS+3OLtoSmSM0DpV3yX52vFXVhxA6NnBXFn81GuHP1rjepuAoIlfeNZT749h4BwA=</SignatureValue> <KeyInfo> - <KeyName>eb3907ecad74a0013c259d5874ae7f22dcbcc95c</KeyName> + <KeyName>6zkH7K10oAE8JZ1YdK5/Ity8yVw=</KeyName> <!-- public key: 049f6bb6a7e3f5b7ee71756a891233d1415658f8712bac740282e083dc9240f5368bdb3b256a5bf40a8f7f9753414cb447ee3f796c5f30f7eb40a7f5018fc7f02e --> <ECKeyValue xmlns="http://www.w3.org/2009/xmldsig11"> <PublicKey>BJ9rtqfj9bfucXVqiRIz0UFWWPhxK6x0AoLgg9ySQPU2i9s7JWpb9AqPf5dTQUy0R+4/eWxfMPfrQKf1AY/H8C4=</PublicKey> diff --git a/xml_requirements.txt b/xml_requirements.txt @@ -0,0 +1 @@ +xmlschema~=2.3.1