commit 6c27c2206c000db151acde595657ecde809cb652
parent 52a296abfb5ae51894631b07f7f9f75639175f27
Author: lash <dev@holbrook.no>
Date: Wed, 9 Nov 2022 13:11:31 +0000
Add pgp signature, with tests direct and via basket
Diffstat:
4 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/piknik/crypto.py b/piknik/crypto.py
@@ -1,13 +1,31 @@
+# standard imports
+from email.message import Message
+
# external imports
import gnupg
-class Signer:
+class PGPSigner:
- def __init__(self, home_dir=None, default_key=None):
+ def __init__(self, home_dir=None, default_key=None, passphrase=None):
self.gpg = gnupg.GPG(gnupghome=home_dir)
self.default_key = default_key
+ self.passphrase = passphrase
+
+
+ def sign(self, msg, passphrase=None): # msg = IssueMessage object
+ m = Message()
+ v = msg.as_string()
+ m.set_type('multipart/relative')
+ ms = Message()
+ ms.set_type('application/pgp-signature')
+ fn = '{}.asc'.format(msg.get('X-Piknik-Msg-Id'))
+ ms.add_header('Content-Disposition', 'attachment', filename=fn)
+ sig = self.gpg.sign(v, keyid=self.default_key, detach=True, passphrase=self.passphrase)
+ ms.set_payload(str(sig))
+
+ m.attach(msg)
+ m.attach(ms)
- def sign(self, issue_msg):
- pass
+ return m
diff --git a/piknik/msg.py b/piknik/msg.py
@@ -24,7 +24,7 @@ class IssueMessage:
self.__m.add_header('X-Piknik-Id', issue.id)
self.__m.add_header('Date', formatdate(time.time()))
self.__m.set_payload(None)
- self.__m.set_type('multipart/mixed')
+ self.__m.set_type('multipart/relative')
self.__m.set_boundary(str(uuid.uuid4()))
diff --git a/requirements.txt b/requirements.txt
@@ -1,3 +1,3 @@
-shep~=0.2.11
+shep~=0.3.0
leveldir~=0.3.1
python-gnupg~=0.5.0
diff --git a/tests/test_crypto.py b/tests/test_crypto.py
@@ -0,0 +1,61 @@
+# standard imports
+import os
+import unittest
+import logging
+import json
+import tempfile
+import shutil
+from email.message import Message
+
+# external imports
+import gnupg
+
+# local imports
+from piknik import Basket
+from piknik import Issue
+from piknik.crypto import PGPSigner
+
+# test imports
+from tests.common import TestStates
+from tests.common import TestMsgStore
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+test_dir = os.path.realpath(os.path.dirname(__file__))
+
+
+class TestMsg(unittest.TestCase):
+
+ def setUp(self):
+ self.store = TestStates()
+ self.gpg_dir = tempfile.mkdtemp()
+ gpg = gnupg.GPG(gnupghome=self.gpg_dir)
+ gpg_input = gpg.gen_key_input(key_type='RSA', key_length=1024, passphrase='foo')
+ gpg_key = gpg.gen_key(gpg_input)
+ self.crypto = PGPSigner(self.gpg_dir, default_key=gpg_key.fingerprint, passphrase='foo')
+ self.b = Basket(self.store, message_wrapper=self.crypto.sign)
+
+
+ def tearDown(self):
+ shutil.rmtree(self.gpg_dir)
+
+ def test_wrap_sig(self):
+ m = Message()
+ m.set_charset('utf-8')
+ m.set_payload('foo')
+ r = self.crypto.sign(m, passphrase='foo')
+ print(str(r))
+
+
+ def test_wrap_basket_sig(self):
+ o = Issue('foo')
+ v = self.b.add(o)
+ r = self.b.msg(v, 's:foo', 's:bar')
+ print(r)
+
+
+
+
+if __name__ == '__main__':
+ unittest.main()