Hallo Gast! Bitte registriere dich um Beiträge schreiben zu können und Zugang zu allen Bereichen zu bekommen. Hier registrieren

Auch ohne Registrierung direkt eine Support-Anfrage stellen: Zum Support-Fomular


Nachrichtenempfang mit der Python-API?
#1
Hallo zusammen!

Ich habe mal den Threema Gateway ausprobiert und kann per Python-API Nachrichten verschicken. In der Readme der API steht folgendes (Hervorhebung von mir):


Zitat:threema-gateway is a Python 3 module for the Threema gateway service. This API can be used to send and receive text messages to and from any Threema user.

Ich finde in der API aber nur Funktionen zum Versand von Nachrichten, nicht zum Empfang. Ich kann in der Konfiguration eine Callback-URL angeben was aber ja bedeutet, dass ich die Empfangsfunktion im Webserver implementieren müsste. Ich dachte dass es vielleicht eine Möglichkeit gibt, mittels Polling auf dem Gateway-Server nach neuen Nachrichten zu schauen und diese dann herunterzuladen. Hat jemand damit Erfahrung?

Danke und beste Grüße,
  Marek
Zitieren
#2
Also das ist das Python SDK, nicht die API. Die API ist ja auf msgapi.threema.ch und die ist immer gleich. Das Python-SDK nutzt diese nur. So viel erst einmal zur Terminologie.

An dem Nachrichtenempfang wird soweit ich sehe noch gearbeitet. Aber ich glaube @"lgrahl" kann dir da mehr sagen.
Zitieren
#3
Empfang geht in der "develop"-Branch. Dort gibt es den Callback Server.
Zitieren
#4
Genau das habe ich gesucht, vielen Dank! Werde ich mir morgen mal genauer anschauen.

Natürlich meinte ich das SDK, nicht die API - und jetzt sehe ich auch den Grund für die Verwechslung: Der Satz "This API can be used to send and receive text messages..." bezieht sich auf den "gateway service" und nicht das "Python 3 module", wie ich zuerst dachte. Insofern habe ich ja Glück dass das SDK auch beides kann, denn genaugenommen steht das da gar nicht ;-)
Zitieren
#5
Wenn die anderen SDKs nicht mittlerweile nachgezogen haben, ist der Server bisher auch nur im Python SDK verfügbar.
Zitieren
#6
(13.02.2017., 10:58)lgrahl schrieb: Wenn die anderen SDKs nicht mittlerweile nachgezogen haben, ist der Server bisher auch nur im Python SDK verfügbar.

Ich glaube nicht. Ich finde es beim PHP-SDK aber relativ leicht zu implementieren. Da macht es auch Sinn, den nicht fertig bereitzustellen, da das Callback ja in die übergeordnete PHP-Anwendung eingebunden werden muss. Die Methoden zur Entschlüsselung & co sind alle da.
Zitieren
#7
Es ist vermutlich noch wichtig zu erwähnen, dass der Empfang von Nachrichten nicht über den vom SDK bereitgstellten Server durchgeführt werden muss. Der Callback wird per POST an deine Callback-URL geliefert und kann da mit beliebigem Code entschlüsselt werden.

Wenn du da einen generischen Request Handler hast, musst du zusammengefasst folgendes durchführen:

- MAC prüfen (HMAC-SHA256 aus der Python stdlib)
- Public key des peers fetchen (entweder mit dem SDK, benötigt dann asyncio, oder synchron via urllib oder requests)
- Nachricht entschlüsseln (geht mit threema.gateway.e2e.decrypt)

Hier mal etwas Pseudocode:

Code:
import binascii
import hmac
from threema.gateway.e2e import Message, decrypt
from threema.gateway.key import Key

class DecryptionError(RuntimeError):
   pass

class IntegrityError(RuntimeError):
   pass

def calculate_mac(data):
   """
   Calculate the HMAC-SHA256 value for this message.

   For each callback, the server includes a mac parameter than can be used to
   verify the authenticity of the call and the included information. This
   parameter is calculated as follows:

       mac = HMAC-SHA256(from || to || messageId || date || nonce || box, secret)

   It is recommended that receivers verify the mac parameter before attempting
   to parse the other parameters and decrypt the message.

   """
   mac = hmac.new(THREEMA_GW_SECRET.encode('ascii'), digestmod='sha256')
   mac.update(data['from'].encode('ascii'))
   mac.update(data['to'].encode('ascii'))
   mac.update(data['messageId'].encode('ascii'))
   mac.update(data['date'].encode('ascii'))
   mac.update(data['nonce'].encode('ascii'))
   mac.update(data['box'].encode('ascii'))
   return binascii.hexlify(mac.digest()).decode('ascii')

def decrypt_gateway_msg(data) -> Message:
   """
   Decrypt data coming from the Threema Gateway server.

   May raise a DecryptionError.

   """
   # Validate MAC
   calculated_mac = calculate_mac(data)
   sent_mac = data['mac']
   if calculated_mac != sent_mac:
       raise IntegrityError('Invalid MAC')

   # Prepare decryption info
   public_key = Key.decode(get_public_key(data['from']), Key.Type.public)
   private_key = Key.decode(THREEMA_GW_PRIVATE_KEY, Key.Type.private)
   try:
       nonce = binascii.unhexlify(data['nonce'])
   except binascii.Error as e:
       raise DecryptionError('Nonce: %s' % e)
   try:
       box = binascii.unhexlify(data['box'])
   except binascii.Error as e:
       raise DecryptionError('Box: %s' % e)

   # Decrypt
   return decrypt(private_key, public_key, nonce, box)
Zitieren
#8
Vielen Dank für die ausführlichen Antworten!

Sicher kann ich das Callback auch in nginx o.ä. realisieren, aber der Server aus der Python-SDK ist für meine Zwecke schon sehr gut geeignet. Ich finde es auch aus Sicherheitsgründen durchaus sinnvoll, das vom restlichen Webbetrieb getrennt zu halten.

Muss ich beim Zertifikat/TLS etwas besonderes beachten? Ich benutze momentan ein Zertifikat von LetsEncrypt mit TLS 1.2.
Zitieren
#9
(13.02.2017., 23:36)marek schrieb: Sicher kann ich das Callback auch in nginx o.ä. realisieren, aber der Server aus der Python-SDK ist für meine Zwecke schon sehr gut geeignet. Ich finde es auch aus Sicherheitsgründen durchaus sinnvoll, das vom restlichen Webbetrieb getrennt zu halten.

Also mit nginx hat das ja mal nichts zu tun. Du kannst allerdings nginx als reverse proxy verwenden um das Callback an das Python-SDK (welches dann ja als Serveranwendung läuft) weiterzuleiten. So kannst du eventuell eine bessere HTTPS-Verbindung realisieren oder den Traffic begrenzen oder so…
Aus Sicherheitsgründen macht es also sehr wohl Sinn, nginx zu verwenden. Eine "Trennung" in diesem Sinne ist das eher nicht.

(13.02.2017., 23:36)marek schrieb: Muss ich beim Zertifikat/TLS etwas besonderes beachten? Ich benutze momentan ein Zertifikat von LetsEncrypt mit TLS 1.2.

Wie auf der Gateway-Webseite steht: Es muss gültig sein, sonst alles wie normal. Let's Encrypt geht also.
Zitieren
#10
nginx funktioniert auch als ganz normaler Webserver. Aber das war auch nur als Beispiel für "irgendein Webserver der PHP-Skripte ausführen kann" gedacht. Feel free to s/nginx/apache.

Mit dem LetsEncrypt-Zertifikat und dem Callback-Server aus dem Python-SDK bekomme ich folgende Fehlermeldung beim Handshake:
Code:
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown

Im Browser kann ich die URL problemlos aufrufen, Firefox und Python einigen sich dann auf TLS 1.2.

Update: Nevermind, hab den Fehler gefunden. Das Zertifikat wurde nicht richtig geladen und der Browser hatte es wohl noch im Cache.
Zitieren


Möglicherweise verwandte Themen…
Thema Verfasser Antworten Ansichten Letzter Beitrag
  Simple Python-GUI? mibere 13 8.127 14.03.2016., 18:58
Letzter Beitrag: mibere
  How-to: Threema Gateway auf OS X (Python) mibere 11 9.032 03.03.2016., 13:15
Letzter Beitrag: Ingenuus