Skip to content

Commit 958f0d0

Browse files
author
David Arnold
committed
WIP: Implement skeleton execution flow
- sign (params) vs _sign (option struct) interface - high level element sceleton - legacy mode ETSI TS 101 903 [i.2] - level concept
1 parent 7453fe3 commit 958f0d0

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed

signxml/xades/__init__.py

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
from __future__ import absolute_import, division, print_function, unicode_literals
2+
3+
from base64 import b64encode, b64decode
4+
from enum import Enum
5+
from uuid import uuid4
6+
from datetime import datetime
7+
import pytz
8+
9+
from eight import str, bytes
10+
from lxml import etree
11+
from lxml.etree import Element, SubElement
12+
from lxml.builder import ElementMaker
13+
from defusedxml.lxml import fromstring
14+
15+
from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec
16+
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
17+
from cryptography.hazmat.primitives.hashes import Hash, SHA1, SHA224, SHA256, SHA384, SHA512
18+
from cryptography.hazmat.backends import default_backend
19+
20+
from .. import XMLSignatureProcessor, XMLSigner, XMLVerifier, VerifyResult, namespaces
21+
22+
from ..exceptions import InvalidSignature, InvalidDigest, InvalidInput, InvalidCertificate # noqa
23+
from ..util import (bytes_to_long, long_to_bytes, strip_pem_header, add_pem_header, ensure_bytes, ensure_str, Namespace,
24+
XMLProcessor, iterate_pem, verify_x509_cert_chain)
25+
from collections import namedtuple
26+
27+
levels = Enum("Levels", "B T LT LTA")
28+
29+
30+
# Retrieved on 17. Oct. 2019: https://portal.etsi.org/pnns/uri-list
31+
namespaces.update(Namespace(
32+
# xades111="https://uri.etsi.org/01903/v1.1.1#", # superseeded
33+
# xades122="https://uri.etsi.org/01903/v1.2.2#", # superseeded
34+
xades132="https://uri.etsi.org/01903/v1.3.2#",
35+
xades141="https://uri.etsi.org/01903/v1.4.1#",
36+
))
37+
38+
XADES132 = ElementMaker(namespace=namespaces.xades132)
39+
XADES141 = ElementMaker(namespace=namespaces.xades141)
40+
DS = ElementMaker(namespace=namespaces.ds)
41+
42+
def _gen_id(suffix):
43+
return "{}-{suffix}".format(uuid4(), suffix=suffix)
44+
45+
46+
47+
class XAdESProcessor(XMLSignatureProcessor):
48+
schema_file = "v1.4.1/XAdES01903v141-201601.xsd"
49+
50+
def _resolve_target(self, doc_root, qualifying_properties):
51+
uri = qualifying_properties.get("Target")
52+
if not uri:
53+
return doc_root
54+
elif uri.startswith("#xpointer("):
55+
raise InvalidInput("XPointer references are not supported")
56+
# doc_root.xpath(uri.lstrip("#"))[0]
57+
elif uri.startswith("#"):
58+
for id_attribute in self.id_attributes:
59+
xpath_query = "//*[@*[local-name() = '{}']=$uri]".format(id_attribute)
60+
results = doc_root.xpath(xpath_query, uri=uri.lstrip("#"))
61+
if len(results) > 1:
62+
raise InvalidInput("Ambiguous reference URI {} resolved to {} nodes".format(uri, len(results)))
63+
elif len(results) == 1:
64+
return results[0]
65+
raise InvalidInput("Unable to resolve reference URI: {}".format(uri))
66+
67+
68+
class XAdESSignerOptions(namedtuple("XAdESSignerOptions", "cert_chain signed_xml signature_xml")):
69+
"""
70+
A containter to hold the XAdES Signer runtime options. It can be provided by
71+
directly calling ``signxml.XAdESSigner._sign()``.
72+
73+
:param cert_chain:
74+
OpenSSL.crypto.X509 objects containing the certificate and a chain of
75+
intermediate certificates.
76+
:type cert_chain: array of OpenSSL.crypto.X509 objects
77+
"""
78+
79+
80+
class XAdESSigner(XAdESProcessor, XMLSigner):
81+
"""
82+
...
83+
"""
84+
85+
def __init__(self, level=levels.LTA, legacy=False, tz=pytz.utc):
86+
self.xades_legacy = legacy
87+
self.xades_level = level
88+
self.xades_tz = tz
89+
90+
def sign(self, *args, **kwargs):
91+
options_struct = XAdESSignerOptions('foo', 'bar', 'baz')
92+
self._sign(options_struct)
93+
return super().sign(*args, **kwargs)
94+
95+
def _sign(self, options_struct):
96+
...
97+
98+
def _add_xades_reference(self, id):
99+
"""
100+
This ds:Reference element shall include the Type attribute with its
101+
value set to:
102+
"""
103+
DS.Reference(
104+
Type="http://uri.etsi.org/01903#SignedProperties"
105+
)
106+
pass
107+
108+
def _generate_xades_ssp_elements(self):
109+
"""
110+
Deprecation as listed in ETSI EN 319 132-1 V1.1.1 (2016-04), Annex D
111+
"""
112+
elements = []
113+
114+
elements.append(XADES132.SigningTime(datetime.now(self.xades_tz).isoformat()))
115+
116+
if self.xades_legacy:
117+
elements.append(XADES132.SigningCertificate()) # deprecated (legacy)
118+
else:
119+
elements.append(XADES132.SigningCertificateV2())
120+
121+
if self.xades_legacy:
122+
elements.append(XADES132.SignatureProductionPlace()) # deprecated (legacy)
123+
else:
124+
elements.append(XADES132.SignatureProductionPlaceV2())
125+
126+
elements.append(XADES132.SignaturePolicyIdentifier())
127+
128+
if self.xades_legacy:
129+
elements.append(XADES132.SignerRole()) # deprecated (legacy)
130+
else:
131+
elements.append(XADES132.SignerRoleV2())
132+
133+
# any ##other
134+
135+
return elements
136+
137+
def _generate_xades_sdop_elements(self):
138+
elements = []
139+
140+
elements.append(XADES132.DataObjectFormat())
141+
142+
elements.append(XADES132.CommitmentTypeIndication())
143+
144+
elements.append(XADES132.AllDataObjectsTimeStamp())
145+
146+
elements.append(XADES132.IndividualDataObjectsTimeStamp())
147+
148+
# any ##other
149+
150+
return elements
151+
152+
def _generate_xades_usp_elements(self):
153+
elements = []
154+
155+
elements.append(XADES132.CounterSignature())
156+
157+
elements.append(XADES132.SignatureTimeStamp())
158+
159+
if self.xades_legacy:
160+
elements.append(XADES132.CompleteCertificateRefs()) # deprecated (legacy)
161+
else:
162+
elements.append(XADES141.CompleteCertificateRefsV2())
163+
164+
elements.append(XADES132.CompleteRevocationRefs())
165+
166+
if self.xades_legacy:
167+
elements.append(XADES132.AttributeCertificateRefs()) # deprecated (legacy)
168+
else:
169+
elements.append(XADES141.AttributeCertificateRefsV2())
170+
171+
elements.append(XADES132.AttributeRevocationRefs())
172+
173+
if self.xades_legacy:
174+
elements.append(XADES132.SigAndRefsTimeStamp()) # deprecated (legacy)
175+
else:
176+
elements.append(XADES141.SigAndRefsTimeStampV2())
177+
178+
if self.xades_legacy:
179+
elements.append(XADES132.RefsOnlyTimeStamp()) # deprecated (legacy)
180+
else:
181+
elements.append(XADES141.RefsOnlyTimeStampV2())
182+
183+
elements.append(XADES132.CertificateValues())
184+
185+
elements.append(XADES132.RevocationValues())
186+
187+
elements.append(XADES132.AttrAuthoritiesCertValues())
188+
189+
elements.append(XADES132.AttributeRevocationValues())
190+
191+
elements.append(XADES132.ArchiveTimeStamp())
192+
193+
# any ##other
194+
195+
return elements
196+
197+
def _generate_xades_udop_elements(self):
198+
elements = []
199+
200+
elements.append(XADES132.UnsignedDataObjectProperty())
201+
202+
return elements
203+
204+
def _generate_xades(self):
205+
206+
"""
207+
Acronyms used in this method
208+
----------------------------
209+
Defined by ETSI EN 319 132-1 V1.1.1 (2016-04)
210+
qp := QualifyingProperties, c. 4.3.1
211+
sp := SignedProperties, c. 4.3.2
212+
ssp := SignedSignatureProperties, c. 4.3.4
213+
sdop := SignedDataObjectProperties, c. 4.3.5
214+
up := UnsignedProperties, c. 4.3.3
215+
usp := UnignedSignatureProperties, c. 4.3.6
216+
udop := UnignedDataObjectProperties, c. 4.3.7
217+
"""
218+
219+
ssp_elements = self._generate_xades_ssp_elements()
220+
sdop_elements = self._generate_xades_sdop_elements()
221+
222+
usp_elements = self._generate_xades_usp_elements()
223+
udop_elements = self._generate_xades_udop_elements()
224+
225+
# Step -3: Construction of SignedProperties
226+
sp_elements = []
227+
"""
228+
A XAdES signature shall not incorporate empty SignedSignatureProperties element.
229+
"""
230+
if ssp_elements:
231+
"""
232+
The Id attribute shall be used to reference the SignedSignatureProperties element.
233+
"""
234+
sp_elements.append(*ssp_elements,
235+
Id=_gen_id("signedsigprops") # optional
236+
)
237+
"""
238+
A XAdES signature shall not incorporate empty SignedDataObjectProperties element.
239+
"""
240+
if sdop_elements:
241+
"""
242+
The Id attribute shall be used to reference the SignedDataObjectProperties element.
243+
"""
244+
sp_elements.append(*sdop_elements,
245+
Id=_gen_id("signeddataobjprops") # optional
246+
)
247+
248+
# Step -2: Construction of UnsignedProperties
249+
up_elements = []
250+
"""
251+
A XAdES signature shall not incorporate empty UnignedSignatureProperties element.
252+
"""
253+
if usp_elements:
254+
"""
255+
The Id attribute shall be used to reference the UnignedSignatureProperties element.
256+
"""
257+
sp_elements.append(*usp_elements,
258+
Id=_gen_id("unsignedsigprops") # optional
259+
)
260+
"""
261+
A XAdES signature shall not incorporate empty UnignedDataObjectProperties element.
262+
"""
263+
if udop_elements:
264+
"""
265+
The Id attribute shall be used to reference the UnignedDataObjectProperties element.
266+
"""
267+
sp_elements.append(*udop_elements,
268+
Id=_gen_id("unsigneddataobjprops") # optional
269+
)
270+
271+
# Step -1: Construction of QualifyingProperties
272+
qp_elements = []
273+
274+
"""
275+
A XAdES signature shall not incorporate empty SignedProperties element.
276+
"""
277+
if sp_elements:
278+
"""
279+
The Id attribute shall be used to reference the SignedProperties element.
280+
"""
281+
qp_elements.append(
282+
XADES132.SignedProperties(*sp_elements,
283+
Id=_gen_id("signedprops") # optional
284+
)
285+
)
286+
"""
287+
In order to protect the qualifying properties with the signature,
288+
a ds:Reference element shall be added to the XML signature.
289+
"""
290+
self._add_xades_reference(sp_attributes.get('Id'))
291+
292+
"""
293+
A XAdES signature shall not incorporate empty UnsignedProperties elements.
294+
"""
295+
if up_elements:
296+
"""
297+
The Id attribute shall be used to reference the UnsignedProperties element.
298+
"""
299+
qp_elements.append(
300+
XADES132.UnsignedProperties(*up_elements,
301+
Id=_gen_id("unsignedprops") # optional
302+
)
303+
)
304+
305+
"""
306+
The Target attribute shall refer to the Id attribute of the
307+
corresponding ds:Signature. The Id attribute shall be used to reference
308+
the QualifyingProperties container.
309+
"""
310+
qp_attributes = {
311+
"Target": '', # required
312+
"Id": _gen_id("qualifyingprops"), # optional
313+
}
314+
315+
"""
316+
A XAdES signature shall not incorporate empty QualifyingProperties elements.
317+
"""
318+
if not qp_elements:
319+
return None
320+
return XADES132.QualifyingProperties(*qp_elements, **qp_attributes)

0 commit comments

Comments
 (0)