Opportunistic encryption of DMs sent via notification client

merge-requests/30/head
Bob Mottram 2021-03-11 20:33:45 +00:00
parent e330a75696
commit 43e98aba6d
3 changed files with 61 additions and 6 deletions

View File

@ -11,6 +11,7 @@ import html
import time
import sys
import select
from random import randint
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import getFullDomain
@ -26,6 +27,8 @@ from follow import sendUnfollowRequestViaServer
from posts import sendPostViaServer
from announce import sendAnnounceViaServer
from pgp import pgpDecrypt
from pgp import hasLocalPGPkey
from pgp import pgpEncryptToActor
def _waitForKeypress(timeout: int, debug: bool) -> str:
@ -320,6 +323,32 @@ def _notificationNewDM(session, toHandle: str,
subject = None
commentsEnabled = True
subject = None
# if there is a local PGP key then attempt to encrypt the DM
# using the PGP public key of the recipient
if hasLocalPGPkey():
sayStr = \
'Local PGP key detected...' + \
'Fetching PGP public key for ' + toHandle
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
paddedMessage = newMessage
if len(paddedMessage) < 32:
# add some padding before and after
# This is to guard against cribs based on small messages, like "Hi"
for before in range(randint(1, 16)):
paddedMessage = ' ' + paddedMessage
for after in range(randint(1, 16)):
paddedMessage += ' '
cipherText = \
pgpEncryptToActor(paddedMessage, toHandle)
if not cipherText:
sayStr = toHandle + ' has no PGP public key'
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
else:
newMessage = cipherText
sayStr = 'Message encrypted'
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
sayStr = 'Sending'
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
if sendPostViaServer(__version__,

35
pgp.py
View File

@ -6,7 +6,9 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
import os
import subprocess
from pathlib import Path
from person import getActorJson
@ -300,7 +302,7 @@ def _pgpImportPubKey(recipientPubKey: str) -> str:
return keyId
def pgpEncrypt(content: str, recipientPubKey: str) -> str:
def _pgpEncrypt(content: str, recipientPubKey: str) -> str:
""" Encrypt using your default pgp key to the given recipient
"""
keyId = _pgpImportPubKey(recipientPubKey)
@ -321,18 +323,22 @@ def pgpEncrypt(content: str, recipientPubKey: str) -> str:
return encryptResult
def _getPGPPublicKeyFromActor(handle: str) -> str:
def _getPGPPublicKeyFromActor(handle: str, actorJson=None) -> str:
"""Searches tags on the actor to see if there is any PGP
public key specified
"""
actorJson = getActorJson(handle, False, False, True)
if not actorJson:
actorJson = getActorJson(handle, False, False, True)
if not actorJson:
return None
if not actorJson.get('attachment'):
return None
if not isinstance(actorJson['attachment'], list):
return None
# search through the tags on the actor
for tag in actorJson['attachment']:
if not isinstance(tag, dict):
continue
if not tag.get('value'):
continue
if '--BEGIN PGP PUBLIC KEY BLOCK--' in tag['value']:
@ -340,6 +346,27 @@ def _getPGPPublicKeyFromActor(handle: str) -> str:
return None
def hasLocalPGPkey() -> bool:
"""Returns true if there is a local .gnupg directory
"""
homeDir = str(Path.home())
gpgDir = homeDir + '/.gnupg'
if os.path.isfile(gpgDir):
return True
return False
def pgpEncryptToActor(content: str, toHandle: str) -> str:
"""PGP encrypt a message to the given actor or handle
"""
# get the actor and extract the pgp public key from it
recipientPubKey = _getPGPPublicKeyFromActor(toHandle)
if not recipientPubKey:
return None
# encrypt using the recipient public key
return _pgpEncrypt(content, recipientPubKey)
def pgpDecrypt(content: str, fromHandle: str) -> str:
""" Encrypt using your default pgp key to the given recipient
fromHandle can be a handle or actor url
@ -363,5 +390,5 @@ def pgpDecrypt(content: str, fromHandle: str) -> str:
(decryptResult, err) = proc.communicate()
if not decryptResult:
return content
decryptResult = decryptResult.decode('utf-8')
decryptResult = decryptResult.decode('utf-8').strip()
return decryptResult

View File

@ -3000,8 +3000,7 @@ def testFunctions():
'E2EEremoveDevice',
'setOrganizationScheme',
'fill_headers',
'_nothing',
"pgpEncrypt"
'_nothing'
]
excludeImports = [
'link',