diff --git a/config.json-dist b/config.json-dist index 6c2fc58bb546ef2082b369491ffd10a44e7e91e1..c31b8f0a3175b0c15812dbbdc3824e3d1be1e6ae 100644 --- a/config.json-dist +++ b/config.json-dist @@ -1,5 +1,6 @@ { "host": "localhost", "port": 8081, - "auth": false + "auth": false, + "user": "" } diff --git a/ucoin.py b/ucoin.py index 26f4e11de62d509f89ec58a0016787e7908c6e98..2ccb91190550916c196845341b27267d6754ff53 100755 --- a/ucoin.py +++ b/ucoin.py @@ -25,7 +25,6 @@ logger = logging.getLogger("cli") def action_peering(): pprint(ucoin.ucg.Peering().get()) - pprint(ucoin.ucg.Peering().post()) def action_amendments(): for am in ucoin.hdc.amendments.List().get(): diff --git a/ucoin/__init__.py b/ucoin/__init__.py index ac0d23ea4f1a375677d97284306626342dba28ee..30c50e0a8c6f62ce44e5cd632e9cb8ad5559e26b 100644 --- a/ucoin/__init__.py +++ b/ucoin/__init__.py @@ -22,8 +22,7 @@ __author__ = 'Caner Candan' __version__ = '0.0.1' __nonsense__ = 'uCoin' -import requests -import logging +import requests, logging, gnupg, json settings = { 'host': 'localhost', @@ -33,6 +32,70 @@ settings = { logger = logging.getLogger("ucoin") +class Response: + """Wrapper of requests.Response class in order to verify signed message.""" + + def __init__(self, response): + """ + Arguments: + - `self`: + - `response`: + """ + + self.response = response + + if settings.get('user'): + logger.debug('selected keyid: %s' % settings.get('user')) + self.gpg = gnupg.GPG(options=['-u %s' % settings['user']]) + else: + self.gpg = gnupg.GPG() + + self.status_code = response.status_code + self.headers = response.headers + + if settings.get('auth'): + self.verified, clear, self.signature = self.split_n_verify(response) + + if not self.verified: + raise ValueError('bad signature verification') + + self.text = self.clear_text = clear + self.content = self.clear_content = self.text.encode('ascii') + else: + self.text = response.text + self.content = response.content + + def json(self): + if not settings.get('auth'): + return self.response.json() + + return json.loads(self.text) + + def split_n_verify(self, response): + """ + Split the signed message thanks to the boundary value got in content-type header. + + returns a tuple with the status, the clear message and the signature. + + `response`: the response returns by requests.get() needed to access to headers and response content. + """ + + begin = '-----BEGIN PGP SIGNATURE-----' + end = '-----END PGP SIGNATURE-----' + boundary_pattern = 'boundary=' + + content_type = response.headers['content-type'] + boundary = content_type[content_type.index(boundary_pattern)+len(boundary_pattern):] + boundary = boundary[:boundary.index(';')].strip() + + data = [x.strip() for x in response.text.split('--%s' % boundary)] + + clear = data[1] + signed = data[2][data[2].index(begin):] + clearsigned = '-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n\n%s\n%s' % (clear, signed) + + return (bool(self.gpg.verify(clearsigned)), clear, signed) + class API: """APIRequest is a class used as an interface. The intermediate derivated classes are the modules and the leaf classes are the API requests.""" @@ -50,6 +113,12 @@ class API: if settings['auth']: self.headers['Accept'] = 'multipart/signed' + if settings.get('user'): + logger.debug('selected keyid: %s' % settings.get('user')) + self.gpg = gnupg.GPG(options=['-u %s' % settings['user']]) + else: + self.gpg = gnupg.GPG() + def reverse_url(self, path): """ Reverses the url using self.url and path given in parameter. @@ -94,7 +163,10 @@ class API: - `path`: the request path """ - return requests.get(self.reverse_url(path), headers=self.headers) + if not settings.get('auth'): + return requests.get(self.reverse_url(path), headers=self.headers) + + return Response(requests.get(self.reverse_url(path), headers=self.headers)) def requests_post(self, path): """ diff --git a/verify.py b/verify.py new file mode 100755 index 0000000000000000000000000000000000000000..f0f3349282292e65b3229b44bd750ecb681fcbd9 --- /dev/null +++ b/verify.py @@ -0,0 +1,37 @@ +#!/bin/env python3 + +import requests, gnupg +from pprint import pprint + +def split_n_verify(response): + """ + Split the signed message thanks to the boundary value got in content-type header. + + returns a tuple with the status, the clear message and the signature. + + `response`: the response returns by requests.get() needed to access to headers and response content. + """ + + begin = '-----BEGIN PGP SIGNATURE-----' + end = '-----END PGP SIGNATURE-----' + boundary_pattern = 'boundary=' + + content_type = response.headers['content-type'] + boundary = content_type[content_type.index(boundary_pattern)+len(boundary_pattern):] + boundary = boundary[:boundary.index(';')].strip() + + data = [x.strip() for x in response.text.split('--%s' % boundary)] + + clear = data[1] + signed = data[2][data[2].index(begin):] + clearsigned = '-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n\n%s\n%s' % (clear, signed) + + gpg = gnupg.GPG() + + return (bool(gpg.verify(clearsigned)), clear, signed) + +r = requests.get('http://mycurrency.candan.fr:8081/ucg/peering', headers={'Accept': 'multipart/signed'}) + +verified, clear, signed = split_n_verify(r) + +assert verified == True