Skip to content

Commit 42d0fdd

Browse files
committed
Refactor XML signature verification and add comments
This commit refactors the XML signature verification process in the `FiskalEntity` struct. It introduces a placeholder function `verifyXML` that currently returns true without performing any actual verification. The commit also adds comments to explain the limitations of the current implementation and the challenges in implementing proper XML canonicalization. A reliable pure Go library don't exist this time so it's either try to help fix the existing ones if possible, write a new one or use external The `signXML` function is not affected by this change and continues to work as expected.
1 parent 01ece9b commit 42d0fdd

File tree

2 files changed

+25
-31
lines changed

2 files changed

+25
-31
lines changed

ciscomm.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,21 @@ func (fe *FiskalEntity) GetResponse(xmlPayload []byte, sign bool) ([]byte, int,
105105
return nil, resp.StatusCode, fmt.Errorf("failed to read response: %w", err)
106106
}
107107

108+
if sign {
109+
// Verify the signature
110+
_, err := fe.verifyXML(body)
111+
if err != nil {
112+
return body, resp.StatusCode, fmt.Errorf("failed to verify CIS signature: %w", err)
113+
}
114+
}
115+
108116
// Parse the SOAP response
109117
var soapResp iSOAPEnvelopeNoNamespace
110118
err = xml.Unmarshal(body, &soapResp)
111119
if err != nil {
112120
return body, resp.StatusCode, fmt.Errorf("failed to unmarshal SOAP response: %w", err)
113121
}
114122

115-
if sign {
116-
// Verify the signature
117-
_, err := fe.verifyXML(soapResp.Body.Content)
118-
if err != nil {
119-
return soapResp.Body.Content, resp.StatusCode, fmt.Errorf("failed to verify CIS signature: %w", err)
120-
}
121-
}
122-
123123
// Return the inner content of the SOAP Body (the actual response)
124124
if resp.StatusCode == http.StatusOK {
125125
return soapResp.Body.Content, resp.StatusCode, nil

dsignandverify.go

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,11 @@ func generateUniqueID() string {
2121
return fmt.Sprintf("%x", time.Now().UnixNano())
2222
}
2323

24-
// docC14N10 applies Canonical XML 1.0 (http://www.w3.org/TR/2001/REC-xml-c14n-20010315) to the input XML data
25-
func docC14N10(xmlData string) ([]byte, error) {
26-
// Parse the input XML string into an etree.Document
27-
doc := etree.NewDocument()
28-
if err := doc.ReadFromString(xmlData); err != nil {
29-
return nil, fmt.Errorf("failed to parse XML: %v", err)
30-
}
31-
32-
// Use the Canonical XML 1.0 algorithm
33-
canonicalizer := MakeC14N10RecCanonicalizer() // Without comments
34-
canonicalizedXML, err := canonicalizer.Canonicalize(doc.Root())
35-
if err != nil {
36-
return nil, fmt.Errorf("failed to canonicalize the XML: %v", err)
37-
}
38-
39-
return canonicalizedXML, nil
40-
}
41-
4224
// doc14n applies Exclusive Canonical XML (http://www.w3.org/2001/10/xml-exc-c14n#) to the input XML data
43-
func doc14n(xmlData string) ([]byte, error) {
25+
func doc14n(xmlData []byte) ([]byte, error) {
4426
// Parse the input XML string into an etree.Document
4527
doc := etree.NewDocument()
46-
if err := doc.ReadFromString(xmlData); err != nil {
28+
if err := doc.ReadFromBytes(xmlData); err != nil {
4729
return nil, fmt.Errorf("failed to parse XML: %v", err)
4830
}
4931

@@ -136,7 +118,7 @@ func (fe *FiskalEntity) signXML(xmlRequest []byte) ([]byte, error) {
136118
}
137119

138120
// Canonicalize the XML document
139-
xmlCanonical, err := doc14n(string(xmlRequest))
121+
xmlCanonical, err := doc14n(xmlRequest)
140122
if err != nil {
141123
return nil, fmt.Errorf("failed to canonicalize XML document: %v", err)
142124
}
@@ -154,7 +136,7 @@ func (fe *FiskalEntity) signXML(xmlRequest []byte) ([]byte, error) {
154136
// Convert the SignedInfo element to a string
155137
signedInfoDocument := etree.NewDocument()
156138
signedInfoDocument.SetRoot(signedInfoElement)
157-
signedInfoString, err := signedInfoDocument.WriteToString()
139+
signedInfoString, err := signedInfoDocument.WriteToBytes()
158140
if err != nil {
159141
return nil, fmt.Errorf("failed to serialize SignedInfo: %v", err)
160142
}
@@ -193,7 +175,19 @@ func (fe *FiskalEntity) signXML(xmlRequest []byte) ([]byte, error) {
193175
return output, nil
194176
}
195177

196-
// verifyXML verifies the signed XML document
178+
// verifyXML is currently a placeholder function for verifying signed XML documents.
179+
// It always returns true without performing any actual verification and should not be used in production environments
180+
// until proper XML signature verification is fully implemented.
181+
//
182+
// The primary challenge is the absence of a reliable pure Go implementation for the Canonicalization Method
183+
// (http://www.w3.org/TR/2001/REC-xml-c14n-20010315). Several libraries were evaluated, but all encountered subtle
184+
// issues during implementation. Without a robust xml canonicalization solution, xml signature verification is not possible.
185+
//
186+
// While the library supports Exclusive Canonicalization (http://www.w3.org/2001/10/xml-exc-c14n#), which suffices
187+
// for signing requests, the Croatian CIS system's responses use non-exclusive canonicalization, preventing verification at this time.
188+
//
189+
// This limitation will remain unresolved until a suitable library is found or a custom implementation is built,
190+
// or until fixes are contributed and merged into existing libraries.
197191
func (fe *FiskalEntity) verifyXML(xmlData []byte) (bool, error) {
198192
return true, nil
199193
}

0 commit comments

Comments
 (0)