From 6cdfd52137746b4cd07d218aa40371d000acda25 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Thu, 10 Oct 2019 18:20:45 +0700 Subject: [PATCH 1/3] implements https://github.com/blynkkk/lib-python/issues/19 --- README.md | 5 ++++- _blynk-cloudcom.crt | 23 +++++++++++++++++++++++ blynklib.py | 20 ++++++++++++++------ 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 _blynk-cloudcom.crt diff --git a/README.md b/README.md index f296dd4..3e99117 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,10 @@ blynk = blynklib.Blynk(BLYNK_AUTH) # advanced options of lib init # from __future__ import print_function -# blynk = blynklib.Blynk(BLYNK_AUTH, server='blynk-cloud.com', port=80, heartbeat=10, rcv_buffer=1024, log=print) +# blynk = blynklib.Blynk(BLYNK_AUTH, server='blynk-cloud.com', port=80, ssl=False, heartbeat=10, rcv_buffer=1024, log=print) + +# Lib init with hardware SSL connection example +# blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl=True) # register handler for Virtual Pin V22 reading by Blynk App. # when a widget in Blynk App asks Virtual Pin data from server within given configurable interval (1,2,5,10 sec etc) diff --git a/_blynk-cloudcom.crt b/_blynk-cloudcom.crt new file mode 100644 index 0000000..a83f9d8 --- /dev/null +++ b/_blynk-cloudcom.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID5TCCAs2gAwIBAgIJAIHSnb+cv4ECMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD +VQQGEwJVQTENMAsGA1UECAwES3lpdjENMAsGA1UEBwwES3lpdjELMAkGA1UECgwC +SVQxEzARBgNVBAsMCkJseW5rIEluYy4xGDAWBgNVBAMMD2JseW5rLWNsb3VkLmNv +bTEfMB0GCSqGSIb3DQEJARYQZG1pdHJpeUBibHluay5jYzAeFw0xNjAzMTcxMTU4 +MDdaFw0yMTAzMTYxMTU4MDdaMIGIMQswCQYDVQQGEwJVQTENMAsGA1UECAwES3lp +djENMAsGA1UEBwwES3lpdjELMAkGA1UECgwCSVQxEzARBgNVBAsMCkJseW5rIElu +Yy4xGDAWBgNVBAMMD2JseW5rLWNsb3VkLmNvbTEfMB0GCSqGSIb3DQEJARYQZG1p +dHJpeUBibHluay5jYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALso +bhbXQuNlzYBFa9h9pd69n43yrGTL4Ba6k5Q1zDwY9HQbMdfC5ZfnCkqT7Zf+R5MO +RW0Q9nLsFNLJkwKnluRCYGyUES8NAmDLQBbZoVc8mv9K3mIgAQvGyY2LmKak5GSI +V0PC3x+iN03xU2774+Zi7DaQd7vTl/9RGk8McyHe/s5Ikbe14bzWcY9ZV4PKgCck +p1chbmLhSfGbT3v64sL8ZbIppQk57/JgsZMrVpjExvxQPZuJfWbtoypPfpYO+O8l +1szaMlTEPIZVMoYi9uE+DnOlhzJFn6Ac4FMrDzJXzMmCweSX3IxguvXALeKhUHQJ ++VP3G6Q3pkZRVKz+5XsCAwEAAaNQME4wHQYDVR0OBBYEFJtqtI62Io66cZgiTR5L +A5Tl5m+xMB8GA1UdIwQYMBaAFJtqtI62Io66cZgiTR5LA5Tl5m+xMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKphjtEOGs7oC3S87+AUgIw4gFNOuv+L +C98/l47OD6WtsqJKvCZ1lmKxY5aIro9FBPk8ktCOsbwEjE+nyr5wul+6CLFr+rnv +7OHYGwLpjoz+rZgYJiQ61E1m0AZ4y9Fyd+D90HW6247vrBXyEiUXOhN/oDDVfDQA +eqmNBx1OqWel81D3tA7zPMA7vUItyWcFIXNjOCP+POy7TMxZuhuPMh5bVu+/cthl +/Q9u/Z2lKl4CWV0Ivt2BtlN6iefva0e2AP/As+gfwjxrb0t11zSILLNJ+nxRIwg+ +k4MGb1zihKbIXUzsjslONK4FY5rlQUSwKJgEAVF0ClxB4g6dECm0ckc= +-----END CERTIFICATE----- diff --git a/blynklib.py b/blynklib.py index 0341f2e..6fcffec 100644 --- a/blynklib.py +++ b/blynklib.py @@ -67,7 +67,7 @@ class Protocol(object): STATUS_OK = const(200) VPIN_MAX_NUM = const(32) - _msg_id = 1 + _msg_id = 0 def _get_msg_id(self, **kwargs): if 'msg_id' in kwargs: @@ -153,13 +153,14 @@ class Connection(Protocol): _last_ping_time = 0 _last_send_time = 0 - def __init__(self, token, server='blynk-cloud.com', port=80, heartbeat=10, rcv_buffer=1024, log=stub_log): + def __init__(self, token, server='blynk-cloud.com', port=80, ssl=False, heartbeat=10, rcv_buffer=1024, log=stub_log): self.token = token self.server = server self.port = port self.heartbeat = heartbeat self.rcv_buffer = rcv_buffer self.log = log + self.ssl = ssl def _set_socket_timeout(self, timeout): if getattr(self._socket, 'settimeout', None): @@ -188,7 +189,7 @@ def receive(self, length, timeout): d_buff = d_buff[:length] return d_buff except (IOError, OSError) as err: - if str(err) == 'timed out': + if str(err) == 'timed out' or str(err) == 'The read operation timed out': return b'' if str(self.EAGAIN) in str(err) or str(self.ETIMEDOUT) in str(err): return b'' @@ -211,9 +212,16 @@ def is_server_alive(self): def _get_socket(self): try: self._state = self.CONNECTING - self._socket = socket.socket() - self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][4]) - self._set_socket_timeout(self.SOCK_TIMEOUT) + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.connect((self.server, self.port)) + if (self.ssl): + self.log('Using SSL socket...') + import ssl, os + sslContext = ssl.create_default_context() + sslContext.verify_mode = ssl.CERT_REQUIRED + caFile = os.path.dirname(__file__) + "/_blynk-cloudcom.crt" + sslContext.load_verify_locations(cafile=caFile) + self._socket = sslContext.wrap_socket(sock=self._socket, server_hostname=self.server) self.log('Connected to blynk server') except Exception as g_exc: raise BlynkError('Connection with the Blynk server failed: {}'.format(g_exc)) From d8e1baf1af5f9f6bd87e805a64a6fdc00721127f Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Fri, 18 Oct 2019 17:37:05 +0700 Subject: [PATCH 2/3] commit : * move ssl implementation to `blynklib_cp.py` * move cert to certificate folder --- blynklib.py | 20 ++++++------------- blynklib_cp.py | 19 ++++++++++++++++-- .../_blynk-cloudcom.crt | 0 3 files changed, 23 insertions(+), 16 deletions(-) rename _blynk-cloudcom.crt => certificate/_blynk-cloudcom.crt (100%) diff --git a/blynklib.py b/blynklib.py index 6fcffec..e5ab5fc 100644 --- a/blynklib.py +++ b/blynklib.py @@ -153,14 +153,13 @@ class Connection(Protocol): _last_ping_time = 0 _last_send_time = 0 - def __init__(self, token, server='blynk-cloud.com', port=80, ssl=False, heartbeat=10, rcv_buffer=1024, log=stub_log): + def __init__(self, token, server='blynk-cloud.com', port=80, heartbeat=10, rcv_buffer=1024, log=stub_log): self.token = token self.server = server self.port = port self.heartbeat = heartbeat self.rcv_buffer = rcv_buffer self.log = log - self.ssl = ssl def _set_socket_timeout(self, timeout): if getattr(self._socket, 'settimeout', None): @@ -189,7 +188,7 @@ def receive(self, length, timeout): d_buff = d_buff[:length] return d_buff except (IOError, OSError) as err: - if str(err) == 'timed out' or str(err) == 'The read operation timed out': + if str(err) == 'timed out': return b'' if str(self.EAGAIN) in str(err) or str(self.ETIMEDOUT) in str(err): return b'' @@ -212,16 +211,9 @@ def is_server_alive(self): def _get_socket(self): try: self._state = self.CONNECTING - self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._socket.connect((self.server, self.port)) - if (self.ssl): - self.log('Using SSL socket...') - import ssl, os - sslContext = ssl.create_default_context() - sslContext.verify_mode = ssl.CERT_REQUIRED - caFile = os.path.dirname(__file__) + "/_blynk-cloudcom.crt" - sslContext.load_verify_locations(cafile=caFile) - self._socket = sslContext.wrap_socket(sock=self._socket, server_hostname=self.server) + self._socket = socket.socket() + self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][4]) + self._set_socket_timeout(self.SOCK_TIMEOUT) self.log('Connected to blynk server') except Exception as g_exc: raise BlynkError('Connection with the Blynk server failed: {}'.format(g_exc)) @@ -389,4 +381,4 @@ def run(self): self.log(b_err) self.disconnect() except Exception as g_exc: - self.log(g_exc) + self.log(g_exc) \ No newline at end of file diff --git a/blynklib_cp.py b/blynklib_cp.py index f8730aa..85c3628 100644 --- a/blynklib_cp.py +++ b/blynklib_cp.py @@ -127,6 +127,7 @@ def internal_msg(self, *args): class Connection(Protocol): SOCK_MAX_TIMEOUT = 5 SOCK_TIMEOUT = 0.05 + SOCK_SSL_TIMEOUT = 1 EAGAIN = 11 ETIMEDOUT = 60 RETRIES_TX_DELAY = 2 @@ -144,13 +145,14 @@ class Connection(Protocol): _last_ping_time = 0 _last_send_time = 0 - def __init__(self, token, server='blynk-cloud.com', port=80, heartbeat=10, rcv_buffer=1024, log=stub_log): + def __init__(self, token, server='blynk-cloud.com', port=80, ssl_cert=None, heartbeat=10, rcv_buffer=1024, log=stub_log): self.token = token self.server = server self.port = port self.heartbeat = heartbeat self.rcv_buffer = rcv_buffer self.log = log + self.ssl_cert = ssl_cert def send(self, data): retries = self.RETRIES_TX_MAX_NUM @@ -196,7 +198,20 @@ def _get_socket(self): self._state = self.CONNECTING self._socket = socket.socket() self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][4]) - self._socket.settimeout(self.SOCK_TIMEOUT) + if (self.ssl_cert is not None): + self.log('Using SSL socket...') + import ssl, os + sslContext = ssl.create_default_context() + sslContext.verify_mode = ssl.CERT_REQUIRED + if (self.ssl_cert == "default"): + caFile = os.path.dirname(__file__) + "/certificate/_blynk-cloudcom.crt" + else: + caFile = self.ssl_cert + sslContext.load_verify_locations(cafile=caFile) + self._socket.settimeout(self.SOCK_SSL_TIMEOUT) + self._socket = sslContext.wrap_socket(sock=self._socket, server_hostname=self.server) + else: + self._socket.settimeout(self.SOCK_TIMEOUT) self.log('Connected to blynk server') except Exception as g_exc: raise BlynkError('Connection with the Blynk server failed: {}'.format(g_exc)) diff --git a/_blynk-cloudcom.crt b/certificate/_blynk-cloudcom.crt similarity index 100% rename from _blynk-cloudcom.crt rename to certificate/_blynk-cloudcom.crt From 7cf8f6ebb5b12fea97b9ef0503af9093f554ed58 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Mon, 21 Oct 2019 16:19:01 +0700 Subject: [PATCH 3/3] commit : * update README.md * move imports to the top * fix some flow when wrapping ssl socket --- README.md | 7 ++++++- blynklib_cp.py | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3e99117..5593253 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,12 @@ blynk = blynklib.Blynk(BLYNK_AUTH) # blynk = blynklib.Blynk(BLYNK_AUTH, server='blynk-cloud.com', port=80, ssl=False, heartbeat=10, rcv_buffer=1024, log=print) # Lib init with hardware SSL connection example -# blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl=True) +# blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl_cert='default') +# the ssl_cert='default' is for default blynk server (blynk-cloud.com) or self hosted server with Let's Encrypt certificate. + +# if you have self hosted server with your own self signed certificate, then you can change ssl_cert with your +# own CA file. For example if you have CA file in /home/blynk/ca.crt, then the param will be +# blynk = blynklib.Blynk(BLYNK_AUTH, port=443, ssl_mode='/home/blynk/ca.crt') # register handler for Virtual Pin V22 reading by Blynk App. # when a widget in Blynk App asks Virtual Pin data from server within given configurable interval (1,2,5,10 sec etc) diff --git a/blynklib_cp.py b/blynklib_cp.py index 85c3628..c7e25d9 100644 --- a/blynklib_cp.py +++ b/blynklib_cp.py @@ -4,9 +4,11 @@ __version__ = '0.2.5' +import os import socket -import time +import ssl import struct +import time LOGO = """ ___ __ __ @@ -198,20 +200,18 @@ def _get_socket(self): self._state = self.CONNECTING self._socket = socket.socket() self._socket.connect(socket.getaddrinfo(self.server, self.port)[0][4]) - if (self.ssl_cert is not None): + self._socket.settimeout(self.SOCK_TIMEOUT) + if self.ssl_cert: self.log('Using SSL socket...') - import ssl, os - sslContext = ssl.create_default_context() - sslContext.verify_mode = ssl.CERT_REQUIRED if (self.ssl_cert == "default"): + sslContext = ssl.create_default_context() caFile = os.path.dirname(__file__) + "/certificate/_blynk-cloudcom.crt" + sslContext.load_verify_locations(cafile=caFile) else: - caFile = self.ssl_cert - sslContext.load_verify_locations(cafile=caFile) + sslContext = ssl.create_default_context(cafile=self.ssl_cert) + sslContext.verify_mode = ssl.CERT_REQUIRED self._socket.settimeout(self.SOCK_SSL_TIMEOUT) self._socket = sslContext.wrap_socket(sock=self._socket, server_hostname=self.server) - else: - self._socket.settimeout(self.SOCK_TIMEOUT) self.log('Connected to blynk server') except Exception as g_exc: raise BlynkError('Connection with the Blynk server failed: {}'.format(g_exc))