epics 2.11.0 → 3.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.semaphore/semaphore.yml +17 -8
  4. data/CHANGELOG.md +103 -0
  5. data/EBICS_REFERENCES.md +169 -0
  6. data/README.md +223 -50
  7. data/lib/epics/azv.rb +3 -10
  8. data/lib/epics/b2b.rb +3 -10
  9. data/lib/epics/bka.rb +3 -14
  10. data/lib/epics/builders/body_builder/base.rb +26 -0
  11. data/lib/epics/builders/body_builder/v2.rb +9 -0
  12. data/lib/epics/builders/body_builder/v3.rb +9 -0
  13. data/lib/epics/builders/body_builder.rb +6 -0
  14. data/lib/epics/builders/data_encryption_info_builder.rb +29 -0
  15. data/lib/epics/builders/data_transfer_builder/base.rb +52 -0
  16. data/lib/epics/builders/data_transfer_builder/v2.rb +9 -0
  17. data/lib/epics/builders/data_transfer_builder/v3.rb +12 -0
  18. data/lib/epics/builders/data_transfer_builder.rb +6 -0
  19. data/lib/epics/builders/header_builder/base.rb +26 -0
  20. data/lib/epics/builders/header_builder/v2.rb +9 -0
  21. data/lib/epics/builders/header_builder/v3.rb +9 -0
  22. data/lib/epics/builders/header_builder.rb +6 -0
  23. data/lib/epics/builders/mutable_builder.rb +28 -0
  24. data/lib/epics/builders/order_details_builder/base.rb +100 -0
  25. data/lib/epics/builders/order_details_builder/v2.rb +21 -0
  26. data/lib/epics/builders/order_details_builder/v3.rb +51 -0
  27. data/lib/epics/builders/order_details_builder.rb +10 -0
  28. data/lib/epics/builders/request_builder.rb +25 -0
  29. data/lib/epics/builders/static_builder/base.rb +75 -0
  30. data/lib/epics/builders/static_builder/v2.rb +9 -0
  31. data/lib/epics/builders/static_builder/v3.rb +9 -0
  32. data/lib/epics/builders/static_builder.rb +9 -0
  33. data/lib/epics/builders/transfer_receipt_builder.rb +19 -0
  34. data/lib/epics/builders/xml_builder/base.rb +92 -0
  35. data/lib/epics/builders/xml_builder/v24.rb +31 -0
  36. data/lib/epics/builders/xml_builder/v25.rb +31 -0
  37. data/lib/epics/builders/xml_builder/v3.rb +31 -0
  38. data/lib/epics/builders/xml_builder.rb +7 -0
  39. data/lib/epics/builders.rb +13 -0
  40. data/lib/epics/c2s.rb +3 -10
  41. data/lib/epics/c52.rb +2 -14
  42. data/lib/epics/c53.rb +2 -14
  43. data/lib/epics/c54.rb +2 -14
  44. data/lib/epics/c5n.rb +3 -14
  45. data/lib/epics/ccs.rb +3 -10
  46. data/lib/epics/cct.rb +3 -10
  47. data/lib/epics/cd1.rb +3 -10
  48. data/lib/epics/cdb.rb +3 -10
  49. data/lib/epics/cdd.rb +3 -10
  50. data/lib/epics/cds.rb +3 -10
  51. data/lib/epics/cdz.rb +3 -14
  52. data/lib/epics/cip.rb +3 -10
  53. data/lib/epics/client.rb +511 -125
  54. data/lib/epics/crypt/aes.rb +56 -0
  55. data/lib/epics/crypt/x_509.rb +52 -0
  56. data/lib/epics/crypt.rb +5 -0
  57. data/lib/epics/crz.rb +3 -14
  58. data/lib/epics/factories/crypt/aes_factory.rb +5 -0
  59. data/lib/epics/factories/crypt.rb +4 -0
  60. data/lib/epics/factories/request_factory/base.rb +236 -0
  61. data/lib/epics/factories/request_factory/v2.rb +194 -0
  62. data/lib/epics/factories/request_factory/v24.rb +32 -0
  63. data/lib/epics/factories/request_factory/v25.rb +31 -0
  64. data/lib/epics/factories/request_factory/v3.rb +305 -0
  65. data/lib/epics/factories/request_factory.rb +8 -0
  66. data/lib/epics/factories.rb +5 -0
  67. data/lib/epics/fdl.rb +3 -18
  68. data/lib/epics/ful.rb +3 -15
  69. data/lib/epics/generic_request.rb +36 -92
  70. data/lib/epics/generic_upload_request.rb +14 -67
  71. data/lib/epics/haa.rb +3 -9
  72. data/lib/epics/hac.rb +3 -14
  73. data/lib/epics/handlers/auth_signature_handler.rb +102 -0
  74. data/lib/epics/handlers/order_data_handler/base.rb +98 -0
  75. data/lib/epics/handlers/order_data_handler/v2.rb +39 -0
  76. data/lib/epics/handlers/order_data_handler/v24.rb +11 -0
  77. data/lib/epics/handlers/order_data_handler/v25.rb +11 -0
  78. data/lib/epics/handlers/order_data_handler/v3.rb +41 -0
  79. data/lib/epics/handlers/order_data_handler.rb +8 -0
  80. data/lib/epics/handlers/user_signature_handler/base.rb +10 -0
  81. data/lib/epics/handlers/user_signature_handler/v2.rb +17 -0
  82. data/lib/epics/handlers/user_signature_handler/v3.rb +17 -0
  83. data/lib/epics/handlers/user_signature_handler.rb +6 -0
  84. data/lib/epics/handlers.rb +6 -0
  85. data/lib/epics/hev.rb +2 -15
  86. data/lib/epics/hia.rb +4 -55
  87. data/lib/epics/hkd.rb +3 -9
  88. data/lib/epics/hpb.rb +3 -13
  89. data/lib/epics/hpd.rb +3 -9
  90. data/lib/epics/htd.rb +3 -9
  91. data/lib/epics/ini.rb +4 -46
  92. data/lib/epics/keyring.rb +17 -0
  93. data/lib/epics/letter_renderer.rb +17 -22
  94. data/lib/epics/ptk.rb +3 -14
  95. data/lib/epics/response.rb +51 -31
  96. data/lib/epics/services/crypt_service.rb +62 -0
  97. data/lib/epics/services/digest_resolver/base.rb +30 -0
  98. data/lib/epics/services/digest_resolver/v2.rb +13 -0
  99. data/lib/epics/services/digest_resolver/v3.rb +19 -0
  100. data/lib/epics/services/digest_resolver.rb +6 -0
  101. data/lib/epics/services.rb +5 -0
  102. data/lib/epics/signature.rb +50 -0
  103. data/lib/epics/{key.rb → signature_algorithm/base.rb} +14 -21
  104. data/lib/epics/signature_algorithm/rsa.rb +50 -0
  105. data/lib/epics/signature_algorithm/rsapkcs1.rb +16 -0
  106. data/lib/epics/signature_algorithm/rsapss.rb +20 -0
  107. data/lib/epics/signature_algorithm.rb +2 -0
  108. data/lib/epics/sta.rb +3 -14
  109. data/lib/epics/version.rb +3 -1
  110. data/lib/epics/vmk.rb +3 -14
  111. data/lib/epics/wss.rb +3 -9
  112. data/lib/epics/xct.rb +3 -10
  113. data/lib/epics/xds.rb +3 -10
  114. data/lib/epics/xe2.rb +3 -10
  115. data/lib/epics/xe3.rb +3 -10
  116. data/lib/epics/xek.rb +5 -0
  117. data/lib/epics/yct.rb +6 -0
  118. data/lib/epics/z01.rb +2 -14
  119. data/lib/epics/z52.rb +2 -14
  120. data/lib/epics/z53.rb +2 -14
  121. data/lib/epics/z54.rb +2 -14
  122. data/lib/epics/zsr.rb +5 -0
  123. data/lib/epics.rb +94 -5
  124. data/lib/letter/ini.erb +27 -6
  125. data/spec/aes_spec.rb +46 -0
  126. data/spec/auth_signature_handler_spec.rb +58 -0
  127. data/spec/builders_spec.rb +616 -0
  128. data/spec/certificate_handling_spec.rb +189 -0
  129. data/spec/client_setup_block_spec.rb +41 -0
  130. data/spec/client_spec.rb +143 -91
  131. data/spec/crypt_service_spec.rb +132 -0
  132. data/spec/date_range_spec.rb +46 -0
  133. data/spec/digest_resolver_spec.rb +164 -0
  134. data/spec/fixtures/bank_x509.pem +3 -0
  135. data/spec/fixtures/xml/haa.xml +40 -1
  136. data/spec/fixtures/xml/haa_receipt.xml +33 -30
  137. data/spec/fixtures/xml/haa_response_h004.xml +4 -0
  138. data/spec/fixtures/xml/haa_response_h005.xml +38 -0
  139. data/spec/fixtures/xml/hia_request_order_data.xml +24 -1
  140. data/spec/fixtures/xml/hpb_request.xml +1 -1
  141. data/spec/fixtures/xml/hpb_response_x509.xml +16 -0
  142. data/spec/fixtures/xml/htd_order_data_h005.xml +55 -0
  143. data/spec/fixtures/xml/jruby/hia.xml +20 -22
  144. data/spec/fixtures/xml/jruby/ini.xml +1 -3
  145. data/spec/fixtures/xml/ruby/hia.xml +21 -1
  146. data/spec/fixtures/xml/ruby/ini.xml +21 -1
  147. data/spec/handlers_spec.rb +267 -0
  148. data/spec/hpb_malformed_certificate_spec.rb +36 -0
  149. data/spec/key_file_decryption_spec.rb +27 -0
  150. data/spec/key_file_preservation_spec.rb +78 -0
  151. data/spec/keyring_spec.rb +349 -0
  152. data/spec/middleware/parse_ebics_spec.rb +2 -1
  153. data/spec/order_id_spec.rb +65 -0
  154. data/spec/orders/azv_spec.rb +63 -7
  155. data/spec/orders/b2b_spec.rb +68 -3
  156. data/spec/orders/bka_spec.rb +34 -4
  157. data/spec/orders/c2s_spec.rb +62 -9
  158. data/spec/orders/c52_spec.rb +34 -4
  159. data/spec/orders/c53_spec.rb +34 -4
  160. data/spec/orders/c54_spec.rb +36 -4
  161. data/spec/orders/c5n_spec.rb +36 -4
  162. data/spec/orders/camt_parity_spec.rb +80 -0
  163. data/spec/orders/ccs_spec.rb +62 -9
  164. data/spec/orders/cct_spec.rb +83 -9
  165. data/spec/orders/cd1_spec.rb +50 -7
  166. data/spec/orders/cdb_spec.rb +64 -7
  167. data/spec/orders/cdd_spec.rb +64 -7
  168. data/spec/orders/cds_spec.rb +74 -0
  169. data/spec/orders/cdz_spec.rb +67 -4
  170. data/spec/orders/cip_spec.rb +61 -9
  171. data/spec/orders/crz_spec.rb +67 -4
  172. data/spec/orders/fdl_spec.rb +143 -13
  173. data/spec/orders/ful_spec.rb +51 -10
  174. data/spec/orders/haa_spec.rb +115 -9
  175. data/spec/orders/hac_spec.rb +89 -10
  176. data/spec/orders/hev_spec.rb +1 -6
  177. data/spec/orders/hia_spec.rb +25 -22
  178. data/spec/orders/hkd_spec.rb +23 -4
  179. data/spec/orders/hpb_spec.rb +7 -4
  180. data/spec/orders/hpd_spec.rb +23 -4
  181. data/spec/orders/htd_spec.rb +23 -4
  182. data/spec/orders/ini_spec.rb +23 -18
  183. data/spec/orders/ptk_spec.rb +27 -4
  184. data/spec/orders/sta_spec.rb +129 -11
  185. data/spec/orders/vmk_spec.rb +36 -4
  186. data/spec/orders/wss_spec.rb +29 -3
  187. data/spec/orders/x_509_certificate_spec.rb +9 -1
  188. data/spec/orders/xct_spec.rb +63 -0
  189. data/spec/orders/xds_spec.rb +13 -6
  190. data/spec/orders/xe2_spec.rb +64 -7
  191. data/spec/orders/xe3_spec.rb +64 -7
  192. data/spec/orders/xek_spec.rb +31 -0
  193. data/spec/orders/yct_spec.rb +43 -0
  194. data/spec/orders/z01_spec.rb +34 -4
  195. data/spec/orders/z52_spec.rb +34 -4
  196. data/spec/orders/z53_spec.rb +34 -4
  197. data/spec/orders/z54_spec.rb +36 -4
  198. data/spec/orders/zsr_spec.rb +31 -0
  199. data/spec/request_factory_spec.rb +444 -0
  200. data/spec/response_business_error_spec.rb +45 -0
  201. data/spec/response_spec.rb +31 -0
  202. data/spec/response_validation_spec.rb +23 -0
  203. data/spec/segmented_download_spec.rb +248 -0
  204. data/spec/{key_spec.rb → signature_algorithm/rsa_pss_spec.rb} +4 -13
  205. data/spec/support/ebics_matcher.rb +17 -9
  206. data/spec/support/ebics_shared_examples.rb +642 -0
  207. data/spec/support/h005_client_helper.rb +23 -0
  208. data/spec/upload_digest_spec.rb +69 -0
  209. data/spec/user_signature_handler_spec.rb +25 -0
  210. data/spec/version_support_error_spec.rb +76 -0
  211. data/spec/xsd/{ebics_hev.xsd → H000/ebics_hev.xsd} +0 -0
  212. data/spec/xsd/H003/ebics.xsd +11 -0
  213. data/spec/xsd/H003/ebics_keymgmt_request.xsd +555 -0
  214. data/spec/xsd/H003/ebics_keymgmt_response.xsd +131 -0
  215. data/spec/xsd/H003/ebics_orders.xsd +1811 -0
  216. data/spec/xsd/H003/ebics_request.xsd +354 -0
  217. data/spec/xsd/H003/ebics_response.xsd +161 -0
  218. data/spec/xsd/{ebics_signature.xsd → H003/ebics_signature.xsd} +0 -0
  219. data/spec/xsd/H003/ebics_types.xsd +2361 -0
  220. data/spec/xsd/H003/xmldsig-core-schema.xsd +318 -0
  221. data/spec/xsd/H004/ebics_signature.xsd +217 -0
  222. data/spec/xsd/H005/ebics_H005.xsd +11 -0
  223. data/spec/xsd/H005/ebics_keymgmt_request_H005.xsd +523 -0
  224. data/spec/xsd/H005/ebics_keymgmt_response_H005.xsd +137 -0
  225. data/spec/xsd/H005/ebics_orders_H005.xsd +2094 -0
  226. data/spec/xsd/H005/ebics_request_H005.xsd +349 -0
  227. data/spec/xsd/H005/ebics_response_H005.xsd +167 -0
  228. data/spec/xsd/H005/ebics_signature_S002.xsd +177 -0
  229. data/spec/xsd/H005/ebics_types_H005.xsd +1885 -0
  230. data/spec/xsd/H005/xmldsig-core-schema.xsd +318 -0
  231. metadata +199 -36
  232. data/lib/epics/header_request.rb +0 -58
  233. data/lib/epics/middleware/xmlsig.rb +0 -16
  234. data/lib/epics/signer.rb +0 -40
  235. data/lib/epics/x_509_certificate.rb +0 -15
  236. data/lib/letter/ini_with_certs.erb +0 -335
  237. data/spec/generic_upload_spec.rb +0 -58
  238. data/spec/hpb_spec.rb +0 -15
  239. data/spec/signer_spec.rb +0 -34
  240. /data/spec/xsd/{ebics_H004.xsd → H004/ebics_H004.xsd} +0 -0
  241. /data/spec/xsd/{ebics_keymgmt_request_H004.xsd → H004/ebics_keymgmt_request_H004.xsd} +0 -0
  242. /data/spec/xsd/{ebics_keymgmt_response_H004.xsd → H004/ebics_keymgmt_response_H004.xsd} +0 -0
  243. /data/spec/xsd/{ebics_orders_H004.xsd → H004/ebics_orders_H004.xsd} +0 -0
  244. /data/spec/xsd/{ebics_request_H004.xsd → H004/ebics_request_H004.xsd} +0 -0
  245. /data/spec/xsd/{ebics_response_H004.xsd → H004/ebics_response_H004.xsd} +0 -0
  246. /data/spec/xsd/{ebics_types_H004.xsd → H004/ebics_types_H004.xsd} +0 -0
  247. /data/spec/xsd/{xmldsig-core-schema.xsd → H004/xmldsig-core-schema.xsd} +0 -0
@@ -0,0 +1,248 @@
1
+ RSpec.describe 'segmented downloads' do
2
+ versions = [Epics::Keyring::VERSION_24, Epics::Keyring::VERSION_25, Epics::Keyring::VERSION_30]
3
+
4
+ let(:url) { 'https://194.180.18.30/ebicsweb/ebicsweb' }
5
+ let(:version) { Epics::Keyring::VERSION_25 }
6
+ let(:client) do
7
+ Epics::Client.new(File.open(File.join(File.dirname(__FILE__), 'fixtures', 'SIZBN001.key')),
8
+ 'secret', url, 'SIZBN001', 'EBIX', 'EBICS', version:)
9
+ end
10
+ let(:ns) { { 'e' => client.urn_schema } }
11
+ let(:transaction_id) { 'ECD6F062AAEDFA77250526A68CBEC549' }
12
+
13
+ describe 'Epics::GenericRequest#to_transfer_download_xml' do
14
+ let(:order) { Epics::C53.new(client, from: Date.new(2026, 9, 24), to: Date.new(2026, 9, 24)) }
15
+ let(:xml) { Nokogiri::XML(order.to_transfer_download_xml(2, false)) }
16
+
17
+ before { order.transaction_id = transaction_id }
18
+
19
+ versions.each do |version|
20
+ context version do
21
+ let(:version) { version }
22
+
23
+ it 'is a valid EBICS document' do
24
+ expect(xml.to_xml).to be_a_valid_ebics_doc(version)
25
+ end
26
+
27
+ it 'requests the segment of the transaction without sending order data' do
28
+ expect(xml.at_xpath('//e:header/e:static/e:TransactionID', ns).text).to eq(transaction_id)
29
+ expect(xml.at_xpath('//e:header/e:mutable/e:TransactionPhase', ns).text).to eq('Transfer')
30
+ expect(xml.at_xpath('//e:header/e:mutable/e:SegmentNumber', ns).text).to eq('2')
31
+ expect(xml.at_xpath('//e:header/e:mutable/e:SegmentNumber', ns)['lastSegment']).to eq('false')
32
+ expect(xml.at_xpath('//e:body', ns).elements).to be_empty
33
+ end
34
+ end
35
+ end
36
+
37
+ it 'marks the last segment' do
38
+ segment_number = Nokogiri::XML(order.to_transfer_download_xml(3, true))
39
+ .at_xpath('//e:header/e:mutable/e:SegmentNumber', ns)
40
+
41
+ expect(segment_number.text).to eq('3')
42
+ expect(segment_number['lastSegment']).to eq('true')
43
+ end
44
+ end
45
+
46
+ describe 'Epics::GenericRequest#to_receipt_xml' do
47
+ let(:order) { Epics::C53.new(client, from: Date.new(2026, 9, 24), to: Date.new(2026, 9, 24)) }
48
+
49
+ before { order.transaction_id = transaction_id }
50
+
51
+ def receipt_code(xml)
52
+ Nokogiri::XML(xml).at_xpath('//e:body/e:TransferReceipt/e:ReceiptCode', ns).text
53
+ end
54
+
55
+ it 'acknowledges the download by default' do
56
+ expect(receipt_code(order.to_receipt_xml)).to eq('0')
57
+ end
58
+
59
+ versions.each do |version|
60
+ context version do
61
+ let(:version) { version }
62
+
63
+ it 'rejects the download when not acknowledged' do
64
+ xml = order.to_receipt_xml(acknowledged: false)
65
+
66
+ expect(xml).to be_a_valid_ebics_doc(version)
67
+ expect(receipt_code(xml)).to eq('1')
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ describe 'Epics::Client#download' do
74
+ def response(phase, order_data: nil, segment_number: nil, num_segments: nil, return_code: '000000')
75
+ <<~XML
76
+ <?xml version="1.0" encoding="UTF-8"?>
77
+ <ebicsResponse xmlns="#{client.urn_schema}" Version="#{version}" Revision="1">
78
+ <header authenticate="true">
79
+ <static>
80
+ <TransactionID>#{transaction_id}</TransactionID>
81
+ #{"<NumSegments>#{num_segments}</NumSegments>" if num_segments}
82
+ </static>
83
+ <mutable>
84
+ <TransactionPhase>#{phase}</TransactionPhase>
85
+ #{segment_number_xml(segment_number) if segment_number}
86
+ <ReturnCode>#{return_code}</ReturnCode>
87
+ </mutable>
88
+ </header>
89
+ <body>
90
+ #{data_transfer_xml(phase, order_data) if order_data}
91
+ <ReturnCode authenticate="true">000000</ReturnCode>
92
+ </body>
93
+ </ebicsResponse>
94
+ XML
95
+ end
96
+
97
+ def segment_number_xml(segment_number)
98
+ %(<SegmentNumber lastSegment="#{segment_number == segments.size}">#{segment_number}</SegmentNumber>)
99
+ end
100
+
101
+ def data_transfer_xml(phase, order_data)
102
+ <<~XML
103
+ <DataTransfer>
104
+ #{data_encryption_info_xml if phase == 'Initialisation'}
105
+ <OrderData>#{Base64.strict_encode64(order_data)}</OrderData>
106
+ </DataTransfer>
107
+ XML
108
+ end
109
+
110
+ def data_encryption_info_xml
111
+ <<~XML
112
+ <DataEncryptionInfo authenticate="true">
113
+ <TransactionKey>#{Base64.strict_encode64(client.encryption_key.key.public_encrypt(transaction_key))}</TransactionKey>
114
+ </DataEncryptionInfo>
115
+ XML
116
+ end
117
+
118
+ def summarize(request)
119
+ segment_number = request.at_xpath('//e:header/e:mutable/e:SegmentNumber', ns)
120
+ receipt_code = request.at_xpath('//e:body/e:TransferReceipt/e:ReceiptCode', ns)
121
+
122
+ [request.at_xpath('//e:header/e:mutable/e:TransactionPhase', ns).text,
123
+ segment_number&.text,
124
+ segment_number&.[]('lastSegment'),
125
+ receipt_code&.text].compact
126
+ end
127
+
128
+ let(:zipped_order_data) { File.binread(File.join(File.dirname(__FILE__), 'fixtures', 'test.zip')) }
129
+ let(:transaction_key) { SecureRandom.random_bytes(16) }
130
+ let(:order_data_encrypted) do
131
+ cipher = OpenSSL::Cipher.new('aes-128-cbc').encrypt
132
+ cipher.key = transaction_key
133
+
134
+ cipher.update(Zlib::Deflate.deflate(zipped_order_data)) + cipher.final
135
+ end
136
+ let(:missing_bytes) { 0 }
137
+ let(:segment_count) { 3 }
138
+ let(:segments) do
139
+ received = order_data_encrypted.byteslice(0, order_data_encrypted.bytesize - missing_bytes)
140
+
141
+ received.bytes.each_slice((received.bytesize / segment_count.to_f).ceil).map { |bytes| bytes.pack('C*') }
142
+ end
143
+ let(:initialisation_response) do
144
+ response('Initialisation', order_data: segments.first, segment_number: 1, num_segments: segments.size)
145
+ end
146
+ let(:aborted_segment) { nil }
147
+ let(:requests) { [] }
148
+
149
+ subject { client.C53(Date.new(2026, 9, 24), Date.new(2026, 9, 24)) }
150
+
151
+ before do
152
+ stub_request(:post, url).to_return do |http_request|
153
+ requests << summarize(Nokogiri::XML(http_request.body))
154
+ phase, *details = requests.last
155
+ body = case phase
156
+ when 'Initialisation' then initialisation_response
157
+ when 'Transfer'
158
+ segment_number = Integer(details.first)
159
+ if segment_number == aborted_segment
160
+ response('Transfer', return_code: '091102')
161
+ else
162
+ response('Transfer', order_data: segments[segment_number - 1], segment_number:)
163
+ end
164
+ when 'Receipt' then response('Receipt', return_code: details == ['0'] ? '011000' : '011001')
165
+ end
166
+
167
+ { status: 200, body: }
168
+ end
169
+ end
170
+
171
+ context 'when the order data arrives short by whole cipher blocks' do
172
+ let(:missing_bytes) { 32 }
173
+
174
+ it 'raises after sending a negative receipt' do
175
+ expect { subject }.to raise_error(Epics::InvalidOrderDataError, /C53/) do |error|
176
+ expect(error.cause).to be_a(Zlib::BufError)
177
+ end
178
+ expect(requests).to eq([%w[Initialisation], %w[Transfer 2 false], %w[Transfer 3 true], %w[Receipt 1]])
179
+ end
180
+ end
181
+
182
+ context 'when the order data arrives short by part of a cipher block' do
183
+ let(:missing_bytes) { 5 }
184
+
185
+ it 'raises after sending a negative receipt' do
186
+ expect { subject }.to raise_error(Epics::InvalidOrderDataError, /C53/) do |error|
187
+ expect(error.cause).to be_a(OpenSSL::Cipher::CipherError)
188
+ end
189
+ expect(requests).to eq([%w[Initialisation], %w[Transfer 2 false], %w[Transfer 3 true], %w[Receipt 1]])
190
+ end
191
+ end
192
+
193
+ context 'when the bank aborts the transaction during the transfer' do
194
+ let(:aborted_segment) { 2 }
195
+
196
+ it "raises the bank's error without sending a receipt" do
197
+ expect { subject }.to raise_error(Epics::Error::TechnicalError) do |error|
198
+ expect(error.symbol).to eq('EBICS_TX_ABORT')
199
+ end
200
+ expect(requests).to eq([%w[Initialisation], %w[Transfer 2 false]])
201
+ end
202
+ end
203
+
204
+ context 'when the negative receipt cannot be sent' do
205
+ let(:missing_bytes) { 32 }
206
+
207
+ before do
208
+ stub_request(:post, url).with { |request| summarize(Nokogiri::XML(request.body)) == %w[Receipt 1] }
209
+ .to_raise(Faraday::ConnectionFailed)
210
+ end
211
+
212
+ it 'raises the reason the order data could not be read' do
213
+ expect { subject }.to raise_error(Epics::InvalidOrderDataError, /C53/) do |error|
214
+ expect(error.cause).to be_a(Zlib::BufError)
215
+ end
216
+ end
217
+ end
218
+
219
+ context 'when the response is not segmented' do
220
+ let(:initialisation_response) { response('Initialisation', order_data: order_data_encrypted) }
221
+
222
+ it 'returns the order data without sending a receipt' do
223
+ expect(subject).to eq(["ebics is great\n"])
224
+ expect(requests).to eq([%w[Initialisation]])
225
+ end
226
+ end
227
+
228
+ context 'when the order data fits into a single segment' do
229
+ let(:segment_count) { 1 }
230
+
231
+ it 'returns the order data and sends the receipt' do
232
+ expect(subject).to eq(["ebics is great\n"])
233
+ expect(requests).to eq([%w[Initialisation], %w[Receipt 0]])
234
+ end
235
+ end
236
+
237
+ versions.each do |version|
238
+ context "when the order data spans several segments (#{version})" do
239
+ let(:version) { version }
240
+
241
+ it 'returns the order data of all segments and sends the receipt after the last one' do
242
+ expect(subject).to eq(["ebics is great\n"])
243
+ expect(requests).to eq([%w[Initialisation], %w[Transfer 2 false], %w[Transfer 3 true], %w[Receipt 0]])
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -1,6 +1,6 @@
1
- RSpec.describe Epics::Key do
1
+ RSpec.describe Epics::SignatureAlgorithm::RsaPss do
2
2
 
3
- subject { described_class.new( File.read(File.join( File.dirname(__FILE__), 'fixtures', 'e002.pem'))) }
3
+ subject { described_class.new( File.read(File.join( File.dirname(__FILE__), '..', 'fixtures', 'e002.pem'))) }
4
4
 
5
5
  describe '#public_digest' do
6
6
 
@@ -14,17 +14,8 @@ RSpec.describe Epics::Key do
14
14
  let(:dsi) { OpenSSL::Digest::SHA256.new.digest("ruby is great") }
15
15
 
16
16
  it 'will generated a digest that can be verified with openssl key.verify_pss' do
17
- signed_digest = subject.sign(dsi)
18
-
19
- key = subject.key
20
-
21
- verification_result = key.verify_pss(
22
- 'SHA256',
23
- Base64.decode64(signed_digest),
24
- dsi,
25
- salt_length: :digest,
26
- mgf1_hash: 'SHA256',
27
- )
17
+ signed_digest = Base64.encode64(subject.sign(dsi)).strip
18
+ verification_result = subject.verify(signed_digest, dsi)
28
19
 
29
20
  expect(verification_result).to eq(true)
30
21
  end
@@ -1,22 +1,30 @@
1
- RSpec::Matchers.define :be_a_valid_ebics_doc do
2
-
1
+ RSpec::Matchers.define :be_a_valid_ebics_doc do |version|
3
2
  ##
4
3
  # use #open instead of #read to have the includes working
5
4
  # http://stackoverflow.com/questions/11996326/nokogirixmlschema-syntaxerror-on-schema-load/22971456#22971456
6
- def xsd
7
- @xsd ||= Nokogiri::XML::Schema(File.open( File.join( File.dirname(__FILE__), '..', 'xsd', 'ebics_H004.xsd') ))
5
+ def xsd(version)
6
+ @xsd ||= {}
7
+ @xsd[version] ||= Nokogiri::XML::Schema(case version
8
+ when Epics::Keyring::VERSION_30
9
+ File.open( File.join( File.dirname(__FILE__), '..', 'xsd', 'H005', 'ebics_H005.xsd') )
10
+ when Epics::Keyring::VERSION_25
11
+ File.open( File.join( File.dirname(__FILE__), '..', 'xsd', 'H004', 'ebics_H004.xsd') )
12
+ when Epics::Keyring::VERSION_24
13
+ File.open( File.join( File.dirname(__FILE__), '..', 'xsd', 'H003', 'ebics.xsd') )
14
+ when 'H000'
15
+ File.open( File.join( File.dirname(__FILE__), '..', 'xsd', 'H000', 'ebics_hev.xsd') )
16
+ end)
8
17
  end
9
18
 
10
19
  match do |actual|
11
- xsd.valid?(Nokogiri::XML(actual))
20
+ xsd(version).valid?(Nokogiri::XML(actual))
12
21
  end
13
22
 
14
23
  failure_message do |actual|
15
- "expected that #{actual} would be a valid EBICS doc:\n\n #{xsd.validate(Nokogiri::XML(actual))}"
24
+ "expected that #{actual} would be a valid EBICS doc:\n\n #{xsd(version).validate(Nokogiri::XML(actual))}"
16
25
  end
17
26
 
18
27
  description do
19
- "be a valid EBICS document"
28
+ 'be a valid EBICS document'
20
29
  end
21
-
22
- end
30
+ end