diff --git a/app/services/bill_signature_service.rb b/app/services/bill_signature_service.rb index 8e2d0fe69..797e5bf4b 100644 --- a/app/services/bill_signature_service.rb +++ b/app/services/bill_signature_service.rb @@ -12,5 +12,38 @@ class BillSignatureService signature = Certigna::API.timestamp(bill.digest) bill.set_signature(signature, day) bill.save! + + ensure_valid_signature(bill.reload) + rescue => error + operations.each { |o| o.update(bill_signature: nil) } + bill&.destroy + raise error + end + + def self.ensure_valid_signature(bill) + Dir.mktmpdir do |dir| + operations_path = File.join(dir, 'operations') + File.write(operations_path, bill.serialized.download, mode: 'wb') + + signature_path = File.join(dir, 'signature') + File.write(signature_path, bill.signature.download, mode: 'wb') + + authorities_path = Rails.application.config.root.join('app', 'lib', 'certigna', 'authorities.crt').to_s + + verify_cmd = "openssl ts -verify -CAfile #{authorities_path.shellescape} -data #{operations_path.shellescape} -in #{signature_path.shellescape} -token_in" + + openssl_errors = nil + openssl_output = nil + + process_status = Open3.popen3(verify_cmd) do |_stdin, stdout, stderr, wait_thr| + openssl_errors = stderr.read + openssl_output = stdout.read + wait_thr.value + end + + if !process_status.success? || openssl_output&.strip != 'Verification: OK' + raise StandardError, "openssl verification failed: #{openssl_errors}" + end + end end end diff --git a/spec/services/bill_signature_service_spec.rb b/spec/services/bill_signature_service_spec.rb index 6f4b377f0..20d8e2a01 100644 --- a/spec/services/bill_signature_service_spec.rb +++ b/spec/services/bill_signature_service_spec.rb @@ -32,4 +32,51 @@ describe BillSignatureService do it { is_expected.to eq 0 } end end + + describe ".sign_operations" do + let(:date) { Date.today } + + let(:operations_hash) { [['1', 'hash1'], ['2', 'hash2']] } + let(:operations) do + operations_hash + .map { |id, digest| DossierOperationLog.new(id:, digest:, operation: 'accepter') } + end + + let(:timestamp) { File.read('spec/fixtures/files/bill_signature/signature.der') } + + subject { BillSignatureService.sign_operations(operations, date) } + + before do + DossierOperationLog.where(id: [1, 2]).destroy_all + + expect(Certigna::API).to receive(:timestamp).and_return(timestamp) + end + + context "when everything is fine" do + it do + expect { subject }.not_to raise_error + expect(BillSignature.count).to eq(1) + end + end + + context "when the digest does not match with the pre recorded timestamp token" do + let(:operations_hash) { [['1', 'hash1'], ['2', 'hash3']] } + + it do + expect { subject }.to raise_error(/La validation a échoué : signature ne correspond pas à l’empreinte/) + expect(BillSignature.count).to eq(0) + end + end + + context "when the timestamp token cannot be verified by openssl" do + let(:timestamp) do + File.read('spec/fixtures/files/bill_signature/signature.der').tap { |s| s[-1] = 'd' } + end + + it do + expect { subject }.to raise_error(/openssl verification failed/) + expect(BillSignature.count).to eq(0) + end + end + end end