From cae250d132c3c9b35916d7eceebef538d56e98ec Mon Sep 17 00:00:00 2001
From: Serghei Mihai <smihai@entrouvert.com>
Date: Tue, 15 Sep 2020 14:54:38 +0200
Subject: [PATCH] systempayv2: implement HMAC SHA256 signature (#46658)

---
 eopayment/systempayv2.py  | 25 +++++++++++++++++++++----
 tests/test_systempayv2.py | 16 ++++++++++++++++
 2 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/eopayment/systempayv2.py b/eopayment/systempayv2.py
index 6ff2b1a..99f1cb4 100644
--- a/eopayment/systempayv2.py
+++ b/eopayment/systempayv2.py
@@ -15,9 +15,11 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import base64
 import pytz
 import datetime as dt
 import hashlib
+import hmac
 import string
 from six.moves.urllib import parse as urlparse
 import warnings
@@ -270,6 +272,13 @@ class Payment(PaymentCommon):
             {'name': 'secret_production',
                 'caption': _(u'Secret pour la configuration de PRODUCTION'),
                 'validation': lambda value: str.isalnum(value), },
+            {'name': 'signature_algo',
+                'caption': _(u'Algorithme de signature'),
+                'default': 'hmac_sha256',
+                'choices': (
+                    ('sha1', 'SHA-1'),
+                    ('hmac_sha256', 'HMAC-SHA-256'),
+                )},
             {
                 'name': 'manual_validation',
                 'caption': 'Validation manuelle',
@@ -462,16 +471,24 @@ class Payment(PaymentCommon):
             test=test)
         return response
 
+    def sha1_sign(self, secret, signed_data):
+        return hashlib.sha1(signed_data).hexdigest()
+
+    def hmac_sha256_sign(self, secret, signed_data):
+        digest = hmac.HMAC(secret, digestmod=hashlib.sha256, msg=signed_data).digest()
+        return base64.b64encode(digest)
+
     def signature(self, fields):
         self.logger.debug('got fields %s to sign' % fields)
         ordered_keys = sorted(
             [key for key in fields.keys() if key.startswith('vads_')])
         self.logger.debug('ordered keys %s' % ordered_keys)
         ordered_fields = [force_byte(fields[key]) for key in ordered_keys]
-        secret = getattr(self, 'secret_%s' % fields['vads_ctx_mode'].lower())
+        secret = force_byte(getattr(self, 'secret_%s' % fields['vads_ctx_mode'].lower()))
         signed_data = b'+'.join(ordered_fields)
-        signed_data = b'%s+%s' % (signed_data, force_byte(secret))
+        signed_data = b'%s+%s' % (signed_data, secret)
         self.logger.debug(u'generating signature on «%s»', signed_data)
-        sign = hashlib.sha1(signed_data).hexdigest()
+        sign_method = getattr(self, '%s_sign' % self.signature_algo)
+        sign = sign_method(secret, signed_data)
         self.logger.debug(u'signature «%s»', sign)
-        return sign
+        return force_text(sign)
diff --git a/tests/test_systempayv2.py b/tests/test_systempayv2.py
index 240f936..e796360 100644
--- a/tests/test_systempayv2.py
+++ b/tests/test_systempayv2.py
@@ -31,6 +31,7 @@ PARAMS = {
     'vads_site_id': u'12345678',
     'vads_ctx_mode': u'TEST',
     'vads_trans_date': u'20090501193530',
+    'signature_algo': 'sha1'
 }
 
 
@@ -80,6 +81,21 @@ def test_systempayv2():
     assert response.transaction_date
     assert response.transaction_date.isoformat() == '2020-03-30T16:25:30+00:00'
 
+    PARAMS['signature_algo'] = 'hmac_sha256'
+    p = Payment(PARAMS)
+    assert p.signature(qs) == 'aHrJ7IzSGFa4pcYA8kh99+M/xBzoQ4Odnu3f4BUrpIA='
+    response_qs = 'vads_amount=1042&vads_auth_mode=FULL&vads_auth_number=3feadf' \
+                  '&vads_auth_result=00&vads_capture_delay=0&vads_card_brand=CB' \
+                  '&vads_card_number=497010XXXXXX0000' \
+                  '&vads_payment_certificate=582ba2b725057618706d7a06e9e59acdbf69ff53' \
+                  '&vads_ctx_mode=TEST&vads_currency=978&vads_effective_amount=1042' \
+                  '&vads_site_id=70168983&vads_trans_date=20161013101355' \
+                  '&vads_trans_id=226787&vads_trans_uuid=4b5053b3b1fe4b02a07753e7a' \
+                  '&vads_effective_creation_date=20200330162530' \
+                  '&signature=PeU30M6ilqwhligBAIMQIR3yqxWFGZHJ8Hwtb%2B3IrOM%3D'
+    response = p.response(response_qs)
+    assert response.signed
+
     # bad response
     with pytest.raises(ResponseError, match='missing signature, vads_ctx_mode or vads_auth_result'):
         p.response('foo=bar')
-- 
2.28.0

