1+ #!/usr/bin/env python
2+
3+ import unittest
4+ import time
5+ import json
6+ import os .path
7+
8+ from jwcrypto import jwk , jws as cryptoJWS
9+ from jwcrypto .common import json_encode
10+ from hyperwallet .exceptions import HyperwalletException
11+ from hyperwallet .utils .encryption import Encryption
12+ from django .core .validators import URLValidator
13+ from django .core .exceptions import ValidationError
14+
15+
16+ class EncryptionTest (unittest .TestCase ):
17+
18+ def test_should_successfully_encrypt_and_decrypt_text_message (self ):
19+
20+ localDir = os .path .abspath (os .path .dirname (__file__ ))
21+ clientPath = os .path .join (localDir , 'resources' , 'private-jwkset1' )
22+ hyperwalletPath = os .path .join (localDir , 'resources' , 'public-jwkset1' )
23+ encryption = Encryption (clientPath , hyperwalletPath )
24+ testMessage = 'Message for test'
25+ encryptedMessage = encryption .encrypt (testMessage )
26+ decryptedMessage = encryption .decrypt (encryptedMessage )
27+ self .assertEqual (decryptedMessage , testMessage )
28+
29+ def test_should_fail_decryption_when_wrong_private_key_is_used (self ):
30+
31+ localDir = os .path .abspath (os .path .dirname (__file__ ))
32+ clientPath1 = os .path .join (localDir , 'resources' , 'private-jwkset1' )
33+ hyperwalletPath1 = os .path .join (localDir , 'resources' , 'public-jwkset1' )
34+ clientPath2 = os .path .join (localDir , 'resources' , 'private-jwkset2' )
35+ hyperwalletPath2 = os .path .join (localDir , 'resources' , 'public-jwkset2' )
36+ encryption1 = Encryption (clientPath1 , hyperwalletPath1 )
37+ encryption2 = Encryption (clientPath2 , hyperwalletPath2 )
38+ testMessage = 'Message for test'
39+ encryptedMessage = encryption1 .encrypt (testMessage )
40+
41+ with self .assertRaises (HyperwalletException ) as exc :
42+ encryption2 .decrypt (encryptedMessage )
43+
44+ self .assertEqual (exc .exception .message , 'No recipient matched the provided key["Failed: [ValueError(\' Decryption failed.\' ,)]"]' )
45+
46+ def test_should_fail_signature_verification_when_wrong_public_key_is_used (self ):
47+
48+ localDir = os .path .abspath (os .path .dirname (__file__ ))
49+ clientPath1 = os .path .join (localDir , 'resources' , 'private-jwkset1' )
50+ hyperwalletPath1 = os .path .join (localDir , 'resources' , 'public-jwkset1' )
51+ clientPath2 = os .path .join (localDir , 'resources' , 'private-jwkset2' )
52+ hyperwalletPath2 = os .path .join (localDir , 'resources' , 'public-jwkset2' )
53+ encryption1 = Encryption (clientPath1 , hyperwalletPath1 )
54+ encryption2 = Encryption (clientPath1 , hyperwalletPath2 )
55+ testMessage = 'Message for test'
56+ encryptedMessage = encryption1 .encrypt (testMessage )
57+
58+ with self .assertRaises (HyperwalletException ) as exc :
59+ encryption2 .decrypt (encryptedMessage )
60+
61+ self .assertEqual (exc .exception .message , 'Signature verification failed.' )
62+
63+ def test_should_throw_exception_when_wrong_jwk_key_set_location_is_given (self ):
64+
65+ localDir = os .path .abspath (os .path .dirname (__file__ ))
66+ clientPath = 'wrong_keyset_path'
67+ hyperwalletPath = os .path .join (localDir , 'resources' , 'public-jwkset1' )
68+ encryption = Encryption (clientPath , hyperwalletPath )
69+ testMessage = 'Message for test'
70+
71+ with self .assertRaises (HyperwalletException ) as exc :
72+ encryptedMessage = encryption .encrypt (testMessage )
73+
74+ self .assertEqual (exc .exception .message , 'Wrong JWK key set location path = wrong_keyset_path' )
75+
76+ def test_should_throw_exception_when_not_supported_encryption_algorithm_is_given (self ):
77+
78+ localDir = os .path .abspath (os .path .dirname (__file__ ))
79+ clientPath = os .path .join (localDir , 'resources' , 'private-jwkset1' )
80+ hyperwalletPath = os .path .join (localDir , 'resources' , 'public-jwkset1' )
81+ encryption = Encryption (clientPath , hyperwalletPath , 'unsupported_encryption_algorithm' )
82+ testMessage = 'Message for test'
83+
84+ with self .assertRaises (HyperwalletException ) as exc :
85+ encryptedMessage = encryption .encrypt (testMessage )
86+
87+ self .assertEqual (exc .exception .message , 'JWK set doesn\' t contain key with algorithm = unsupported_encryption_algorithm' )
88+
89+ def test_should_throw_exception_when_jws_signature_does_not_contain_exp_header_param (self ):
90+
91+ localDir = os .path .abspath (os .path .dirname (__file__ ))
92+ clientPath = os .path .join (localDir , 'resources' , 'private-jwkset1' )
93+ hyperwalletPath = '/public-jwkset1'
94+ encryption = Encryption (clientPath , hyperwalletPath )
95+
96+ jwsKeySet = self .__getJwkKeySet (location = clientPath )
97+ jwkSignKey = self .__findJwkKeyByAlgorithm (jwkKeySet = jwsKeySet , algorithm = 'RS256' )
98+ privateKeyToSign = jwk .JWK (** jwkSignKey )
99+ body = "Test message"
100+ jwsToken = cryptoJWS .JWS (body .encode ('utf-8' ))
101+ jwsToken .add_signature (privateKeyToSign , None , json_encode ({
102+ "alg" : "RS256" ,
103+ "kid" : jwkSignKey ['kid' ]
104+ }))
105+ signedBody = jwsToken .serialize (True )
106+
107+ with self .assertRaises (HyperwalletException ) as exc :
108+ encryption .checkJwsExpiration (signedBody )
109+
110+ self .assertEqual (exc .exception .message , 'While trying to verify JWS signature no [exp] header is found' )
111+
112+ def test_should_throw_exception_when_jws_signature_exp_header_param_is_not_integer (self ):
113+
114+ localDir = os .path .abspath (os .path .dirname (__file__ ))
115+ clientPath = os .path .join (localDir , 'resources' , 'private-jwkset1' )
116+ hyperwalletPath = '/public-jwkset1'
117+ encryption = Encryption (clientPath , hyperwalletPath )
118+
119+ jwsKeySet = self .__getJwkKeySet (location = clientPath )
120+ jwkSignKey = self .__findJwkKeyByAlgorithm (jwkKeySet = jwsKeySet , algorithm = 'RS256' )
121+ privateKeyToSign = jwk .JWK (** jwkSignKey )
122+ body = "Test message"
123+ jwsToken = cryptoJWS .JWS (body .encode ('utf-8' ))
124+ jwsToken .add_signature (privateKeyToSign , None , json_encode ({
125+ "alg" : "RS256" ,
126+ "exp" : "153356exp" ,
127+ "kid" : jwkSignKey ['kid' ]
128+ }))
129+ signedBody = jwsToken .serialize (True )
130+
131+ with self .assertRaises (HyperwalletException ) as exc :
132+ encryption .checkJwsExpiration (signedBody )
133+
134+ self .assertEqual (exc .exception .message , 'Wrong value in [exp] header of JWS signature, must be integer' )
135+
136+ def test_should_throw_exception_when_jws_signature_has_expired (self ):
137+
138+ localDir = os .path .abspath (os .path .dirname (__file__ ))
139+ clientPath = os .path .join (localDir , 'resources' , 'private-jwkset1' )
140+ hyperwalletPath = '/public-jwkset1'
141+ encryption = Encryption (clientPath , hyperwalletPath )
142+
143+ jwsKeySet = self .__getJwkKeySet (location = clientPath )
144+ jwkSignKey = self .__findJwkKeyByAlgorithm (jwkKeySet = jwsKeySet , algorithm = 'RS256' )
145+ privateKeyToSign = jwk .JWK (** jwkSignKey )
146+ body = "Test message"
147+ jwsToken = cryptoJWS .JWS (body .encode ('utf-8' ))
148+ jwsToken .add_signature (privateKeyToSign , None , json_encode ({
149+ "alg" : "RS256" ,
150+ "exp" : int (time .time ()) - 6000 ,
151+ "kid" : jwkSignKey ['kid' ]
152+ }))
153+ signedBody = jwsToken .serialize (True )
154+
155+ with self .assertRaises (HyperwalletException ) as exc :
156+ encryption .checkJwsExpiration (signedBody )
157+
158+ self .assertEqual (exc .exception .message , 'JWS signature has expired, checked by [exp] JWS header' )
159+
160+
161+ def __getJwkKeySet (self , location ):
162+
163+ try :
164+ URLValidator ()(location )
165+ except ValidationError :
166+ if os .path .isfile (location ):
167+ with open (location ) as f :
168+ return f .read ()
169+ else :
170+ raise HyperwalletException ('Wrong JWK key set location path = ' + location )
171+
172+ return requests .get (location ).text
173+
174+ def __findJwkKeyByAlgorithm (self , jwkKeySet , algorithm ):
175+
176+ try :
177+ keySet = json .loads (jwkKeySet )
178+ except ValueError :
179+ raise HyperwalletException ('Wrong JWK key set' + jwkKeySet )
180+
181+ for key in keySet ['keys' ]:
182+ if key ['alg' ] == algorithm :
183+ return key
184+
185+ raise HyperwalletException ('JWK set doesn\' t contain key with algorithm = ' + algorithm )
186+
187+ if __name__ == '__main__' :
188+ unittest .main ()
0 commit comments