Commit 3b668522 by Diana Huang

Add unicode handling to the signing and verifying logic

parent 231c71c3
...@@ -54,7 +54,7 @@ def processor_hash(value): ...@@ -54,7 +54,7 @@ def processor_hash(value):
Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page
""" """
shared_secret = settings.CC_PROCESSOR['CyberSource'].get('SHARED_SECRET', '') shared_secret = settings.CC_PROCESSOR['CyberSource'].get('SHARED_SECRET', '')
hash_obj = hmac.new(shared_secret, value, sha1) hash_obj = hmac.new(shared_secret.encode('utf-8'), value.encode('utf-8'), sha1)
return binascii.b2a_base64(hash_obj.digest())[:-1] # last character is a '\n', which we don't want return binascii.b2a_base64(hash_obj.digest())[:-1] # last character is a '\n', which we don't want
...@@ -71,10 +71,10 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order ...@@ -71,10 +71,10 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order
params['orderPage_timestamp'] = int(time.time() * 1000) params['orderPage_timestamp'] = int(time.time() * 1000)
params['orderPage_version'] = order_page_version params['orderPage_version'] = order_page_version
params['orderPage_serialNumber'] = serial_number params['orderPage_serialNumber'] = serial_number
fields = ",".join(params.keys()) fields = u",".join(params.keys())
values = ",".join(["{0}={1}".format(i, params[i]) for i in params.keys()]) values = u",".join([u"{0}={1}".format(i, params[i]) for i in params.keys()])
fields_sig = processor_hash(fields) fields_sig = processor_hash(fields)
values += ",signedFieldsPublicSignature=" + fields_sig values += u",signedFieldsPublicSignature=" + fields_sig
params[full_sig_key] = processor_hash(values) params[full_sig_key] = processor_hash(values)
params[signed_fields_key] = fields params[signed_fields_key] = fields
...@@ -90,13 +90,14 @@ def verify_signatures(params, signed_fields_key='signedFields', full_sig_key='si ...@@ -90,13 +90,14 @@ def verify_signatures(params, signed_fields_key='signedFields', full_sig_key='si
raises CCProcessorSignatureException if not verified raises CCProcessorSignatureException if not verified
""" """
signed_fields = params.get(signed_fields_key, '').split(',') signed_fields = params.get(signed_fields_key, '').split(',')
data = ",".join(["{0}={1}".format(k, params.get(k, '')) for k in signed_fields]) data = u",".join([u"{0}={1}".format(k, params.get(k, '')) for k in signed_fields])
signed_fields_sig = processor_hash(params.get(signed_fields_key, '')) signed_fields_sig = processor_hash(params.get(signed_fields_key, ''))
data += ",signedFieldsPublicSignature=" + signed_fields_sig data += u",signedFieldsPublicSignature=" + signed_fields_sig
returned_sig = params.get(full_sig_key, '') returned_sig = params.get(full_sig_key, '')
if processor_hash(data) != returned_sig: if processor_hash(data) != returned_sig:
raise CCProcessorSignatureException() raise CCProcessorSignatureException()
def render_purchase_form_html(cart): def render_purchase_form_html(cart):
""" """
Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource
......
...@@ -58,6 +58,28 @@ class CyberSourceTests(TestCase): ...@@ -58,6 +58,28 @@ class CyberSourceTests(TestCase):
# testing for the absence of that exception. the trivial assert below does that # testing for the absence of that exception. the trivial assert below does that
self.assertEqual(1, 1) self.assertEqual(1, 1)
def test_sign_then_verify_unicode(self):
"""
Similar to the test above, which loops back to the original.
Testing to make sure we can handle unicode parameters
"""
params = {
'card_accountNumber': '1234',
'card_cardType': '001',
'billTo_firstName': u'\u2699',
'billTo_lastName': u"\u2603",
'orderNumber': '1',
'orderCurrency': 'usd',
'decision': 'ACCEPT',
'ccAuthReply_amount': '0.00'
}
verify_signatures(sign(params), signed_fields_key='orderPage_signedFields',
full_sig_key='orderPage_signaturePublic')
# if the above verify_signature fails it will throw an exception, so basically we're just
# testing for the absence of that exception. the trivial assert below does that
self.assertEqual(1, 1)
def test_verify_exception(self): def test_verify_exception(self):
""" """
Tests that failure to verify raises the proper CCProcessorSignatureException Tests that failure to verify raises the proper CCProcessorSignatureException
...@@ -162,6 +184,7 @@ class CyberSourceTests(TestCase): ...@@ -162,6 +184,7 @@ class CyberSourceTests(TestCase):
'card_accountNumber': '1234', 'card_accountNumber': '1234',
'card_cardType': '001', 'card_cardType': '001',
'billTo_firstName': student1.first_name, 'billTo_firstName': student1.first_name,
'billTo_lastName': u"\u2603",
'orderNumber': str(order1.id), 'orderNumber': str(order1.id),
'orderCurrency': 'usd', 'orderCurrency': 'usd',
'decision': 'ACCEPT', 'decision': 'ACCEPT',
...@@ -194,6 +217,7 @@ class CyberSourceTests(TestCase): ...@@ -194,6 +217,7 @@ class CyberSourceTests(TestCase):
# finally, tests an accepted order # finally, tests an accepted order
self.assertTrue(payment_accepted(params)['accepted']) self.assertTrue(payment_accepted(params)['accepted'])
@patch('shoppingcart.processors.CyberSource.render_to_string', autospec=True) @patch('shoppingcart.processors.CyberSource.render_to_string', autospec=True)
def test_render_purchase_form_html(self, render): def test_render_purchase_form_html(self, render):
""" """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment