From 99ca4da931f5c720e242543cb2e55ab59312ddae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Tue, 7 May 2019 22:18:24 -0400 Subject: [PATCH] dte.data_models: alter field `DteDataL2.signature_x509_cert_pem` Rename to `signature_x509_cert_der` and hold the X.509 certificate's DER-encoded data instead of PEM-encoded data. **Breaks backwards compatibility**. --- cl_sii/dte/data_models.py | 20 ++++++++++---------- cl_sii/dte/parse.py | 4 ++-- tests/test_dte_data_models.py | 20 ++++++++++++++++---- tests/test_dte_parse.py | 15 +++++++++++++-- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/cl_sii/dte/data_models.py b/cl_sii/dte/data_models.py index 138ea16e..a9044fd6 100644 --- a/cl_sii/dte/data_models.py +++ b/cl_sii/dte/data_models.py @@ -22,7 +22,6 @@ import cl_sii.contribuyente.constants import cl_sii.rut.constants -from cl_sii.libs import encoding_utils from cl_sii.libs import tz_utils from cl_sii.rut import Rut @@ -361,11 +360,13 @@ class DteDataL2(DteDataL1): DTE's digital signature's value (raw bytes, without base64 encoding). """ - signature_x509_cert_pem: Optional[bytes] = dc_field(default=None) + signature_x509_cert_der: Optional[bytes] = dc_field(default=None) """ - DTE's digital signature's PEM-encoded X.509 cert. + DTE's digital signature's DER-encoded X.509 cert. - PEM-encoded implies base64-encoded. + .. seealso:: + Functions :func:`cl_sii.libs.crypto_utils.load_der_x509_cert` + and :func:`cl_sii.libs.crypto_utils.x509_cert_der_to_pem`. """ emisor_giro: Optional[str] = dc_field(default=None) @@ -415,12 +416,11 @@ def __post_init__(self) -> None: validate_clean_bytes(self.signature_value) validate_non_empty_bytes(self.signature_value) - if self.signature_x509_cert_pem is not None: - if not isinstance(self.signature_x509_cert_pem, bytes): - raise TypeError("Inappropriate type of 'signature_x509_cert_pem'.") - validate_clean_bytes(self.signature_x509_cert_pem) - validate_non_empty_bytes(self.signature_x509_cert_pem) - encoding_utils.validate_base64(self.signature_x509_cert_pem) + if self.signature_x509_cert_der is not None: + if not isinstance(self.signature_x509_cert_der, bytes): + raise TypeError("Inappropriate type of 'signature_x509_cert_der'.") + validate_clean_bytes(self.signature_x509_cert_der) + validate_non_empty_bytes(self.signature_x509_cert_der) if self.emisor_giro is not None: if not isinstance(self.emisor_giro, str): diff --git a/cl_sii/dte/parse.py b/cl_sii/dte/parse.py index 77b5cc79..d7a5f0ed 100644 --- a/cl_sii/dte/parse.py +++ b/cl_sii/dte/parse.py @@ -454,7 +454,7 @@ def parse_dte_xml(xml_doc: XmlElement) -> data_models.DteDataL2: signature_signature_value = encoding_utils.decode_base64_strict( signature_signature_value_em.text.strip()) - signature_key_info_x509_cert_pem = encoding_utils.clean_base64( + signature_key_info_x509_cert_der = encoding_utils.decode_base64_strict( signature_key_info_x509_cert_em.text.strip()) return data_models.DteDataL2( @@ -469,7 +469,7 @@ def parse_dte_xml(xml_doc: XmlElement) -> data_models.DteDataL2: fecha_vencimiento_date=fecha_vencimiento_value, firma_documento_dt=tmst_firma_value, signature_value=signature_signature_value, - signature_x509_cert_pem=signature_key_info_x509_cert_pem, + signature_x509_cert_der=signature_key_info_x509_cert_der, emisor_giro=emisor_giro_value, emisor_email=emisor_email_value, receptor_email=receptor_email_value, diff --git a/tests/test_dte_data_models.py b/tests/test_dte_data_models.py index 03fdcdc0..d4e1d567 100644 --- a/tests/test_dte_data_models.py +++ b/tests/test_dte_data_models.py @@ -2,6 +2,7 @@ import unittest from datetime import date, datetime +from cl_sii.libs import encoding_utils from cl_sii.libs import tz_utils from cl_sii.rut import Rut # noqa: F401 @@ -11,6 +12,8 @@ validate_contribuyente_razon_social, validate_dte_folio, validate_dte_monto_total, ) +from .utils import read_test_file_bytes + class DteNaturalKeyTest(unittest.TestCase): @@ -138,6 +141,15 @@ def test_vendedor_rut_deudor_rut(self) -> None: class DteDataL2Test(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + + cls.dte_1_xml_signature_value = encoding_utils.decode_base64_strict(read_test_file_bytes( + 'test_data/sii-crypto/DTE--76354771-K--33--170-signature-value-base64.txt')) + cls.dte_1_xml_cert_der = read_test_file_bytes( + 'test_data/sii-crypto/DTE--76354771-K--33--170-cert.der') + def setUp(self) -> None: super().setUp() @@ -154,8 +166,8 @@ def setUp(self) -> None: firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware( dt=datetime(2019, 4, 1, 1, 36, 40), tz=DteDataL2.DATETIME_FIELDS_TZ), - signature_value=None, - signature_x509_cert_pem=None, + signature_value=self.dte_1_xml_signature_value, + signature_x509_cert_der=self.dte_1_xml_cert_der, emisor_giro='Ingenieria y Construccion', emisor_email='hello@example.com', receptor_email=None, @@ -181,8 +193,8 @@ def test_as_dict(self) -> None: firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware( dt=datetime(2019, 4, 1, 1, 36, 40), tz=DteDataL2.DATETIME_FIELDS_TZ), - signature_value=None, - signature_x509_cert_pem=None, + signature_value=self.dte_1_xml_signature_value, + signature_x509_cert_der=self.dte_1_xml_cert_der, emisor_giro='Ingenieria y Construccion', emisor_email='hello@example.com', receptor_email=None, diff --git a/tests/test_dte_parse.py b/tests/test_dte_parse.py index f18b4396..d8f4198e 100644 --- a/tests/test_dte_parse.py +++ b/tests/test_dte_parse.py @@ -295,9 +295,13 @@ def setUpClass(cls) -> None: cls.dte_clean_xml_1_cert_pem_bytes = encoding_utils.clean_base64( crypto_utils.remove_pem_cert_header_footer( read_test_file_bytes('test_data/sii-crypto/DTE--76354771-K--33--170-cert.pem'))) + cls.dte_clean_xml_1_cert_der = read_test_file_bytes( + 'test_data/sii-crypto/DTE--76354771-K--33--170-cert.der') cls.dte_clean_xml_2_cert_pem_bytes = encoding_utils.clean_base64( crypto_utils.remove_pem_cert_header_footer( read_test_file_bytes('test_data/sii-crypto/DTE--76399752-9--33--25568-cert.pem'))) + cls.dte_clean_xml_2_cert_der = read_test_file_bytes( + 'test_data/sii-crypto/DTE--76399752-9--33--25568-cert.der') cls._TEST_DTE_1_SIGNATURE_VALUE = encoding_utils.decode_base64_strict( read_test_file_bytes( @@ -327,6 +331,13 @@ def test_data(self): b"\xe5]E\xed\x9c\xcb\xc2\x84\x15i\xd0tT]\x8b\x8a\x1f'\xe9\x0b:\x88\x05|\xa0b\xb2" b"\x19{\x1cW\x80\xe4\xa7*\xef\xf2\x1a") + self.assertEqual( + crypto_utils.x509_cert_pem_to_der(self.dte_clean_xml_1_cert_pem_bytes), + self.dte_clean_xml_1_cert_der) + self.assertEqual( + crypto_utils.x509_cert_pem_to_der(self.dte_clean_xml_2_cert_pem_bytes), + self.dte_clean_xml_2_cert_der) + def test_parse_dte_xml_ok_1(self) -> None: xml_doc = xml_utils.parse_untrusted_xml(self.dte_clean_xml_1_xml_bytes) @@ -347,7 +358,7 @@ def test_parse_dte_xml_ok_1(self) -> None: dt=datetime(2019, 4, 1, 1, 36, 40), tz=DteDataL2.DATETIME_FIELDS_TZ), signature_value=self._TEST_DTE_1_SIGNATURE_VALUE, - signature_x509_cert_pem=self.dte_clean_xml_1_cert_pem_bytes, + signature_x509_cert_der=self.dte_clean_xml_1_cert_der, emisor_giro='Ingenieria y Construccion', emisor_email='ENACONLTDA@GMAIL.COM', receptor_email=None, @@ -373,7 +384,7 @@ def test_parse_dte_xml_ok_2(self) -> None: dt=datetime(2019, 3, 28, 13, 59, 52), tz=DteDataL2.DATETIME_FIELDS_TZ), signature_value=self._TEST_DTE_2_SIGNATURE_VALUE, - signature_x509_cert_pem=self.dte_clean_xml_2_cert_pem_bytes, + signature_x509_cert_der=self.dte_clean_xml_2_cert_der, emisor_giro='COMERCIALIZACION DE PRODUCTOS PARA EL HOGAR', emisor_email='ANGEL.PEZO@APCASESORIAS.CL', receptor_email=None,