]> Softwares of Agnibho - medscript.git/commitdiff
Implemented SMIME Signature
authorAgnibho Mondal <mondal@agnibho.com>
Mon, 11 Sep 2023 20:41:17 +0000 (02:11 +0530)
committerAgnibho Mondal <mondal@agnibho.com>
Mon, 11 Sep 2023 20:41:17 +0000 (02:11 +0530)
config.py
config/config.json
filehandler.py
prescription.py
signature.py [new file with mode: 0644]
window.py

index c2f70c11f133f010ed47bd769d1f641584de7974..0f7feb828805edc33b6d39ef1dd2c5ee3828a14d 100644 (file)
--- a/config.py
+++ b/config.py
@@ -32,7 +32,9 @@ default = {
         "template": "default_prescription",
         "preset_directory": "preset",
         "preset_newline": "True",
-        "preset_delimiter": ","
+        "preset_delimiter": ",",
+        "private_key": "",
+        "certificate": ""
         }
 
 with open(config_file) as conf:
index 73edfd22f30d3439b2dbfaa8bddeb38012c952cf..de0481a74e0653a51cdecc24c7fe36949f9fbbfb 100644 (file)
@@ -8,5 +8,7 @@
     "template": "default",
     "preset_directory": "preset",
     "preset_newline": "True",
-    "preset_delimiter": ","
+    "preset_delimiter": ",",
+    "certificate": "",
+    "private_key": ""
 }
index b80da90300ffa1c9cf042473b4e78cceded8d6c6..edec0bfafb947538dc7a55a0223cce57e79db291 100644 (file)
@@ -8,6 +8,7 @@
 import os, shutil, glob, tempfile, json
 from zipfile import ZipFile
 from config import config
+from signature import Signature
 
 class FileHandler():
 
@@ -56,3 +57,41 @@ class FileHandler():
             self.file=file
         with ZipFile(self.file, "r", strict_timestamps=False) as source:
             source.extractall(self.directory.name)
+
+    #def hash(self):
+    #    allfiles=[]
+    #    allhash=""
+    #    #with open(os.path.join(self.directory.name, "prescription.json"), "rb") as f:
+    #    #    print(sha256(f.read()).hexdigest())
+    #    for root, dirs, files in os.walk(self.directory.name):
+    #        for file in files:
+    #            allfiles.append(os.path.join(root, file))
+    #    try:
+    #        allfiles.remove(os.path.join(self.directory.name, "certificate.pem"))
+    #        allfiles.remove(os.path.join(self.directory.name, "signature.p7m"))
+    #    except ValueError as e:
+    #        pass
+    #    for file in allfiles:
+    #        with open(file, "rb") as f:
+    #            allhash=allhash+sha256(f.read()).hexdigest()
+    #    return(allhash)
+
+    def sign(self):
+        with open(os.path.join(self.directory.name, "prescription.json"), "r") as file:
+            data=file.read()
+        signature=Signature.sign(data, certificate=config["certificate"], privkey=config["private_key"])
+        with open(os.path.join(self.directory.name, "signature.p7m"), "w") as file:
+            file.write(signature)
+        shutil.copyfile(config["certificate"], os.path.join(self.directory.name, "certificate.pem"))
+
+    def verify(self):
+        with open(os.path.join(self.directory.name, "prescription.json"), "r") as file:
+            data=file.read()
+        try:
+            with open(os.path.join(self.directory.name, "certificate.pem")) as file:
+                certificate=file.read()
+            with open(os.path.join(self.directory.name, "signature.p7m")) as file:
+                signature=file.read()
+            return Signature.verify(data, certificate=os.path.join(self.directory.name, "certificate.pem"), signature=os.path.join(self.directory.name, "signature.p7m"))
+        except FileNotFoundError as e:
+            print(e)
index 678a822efecf61a9f4679468f41c311e4d0f3450..ab895699688f55f49ca50bd1489b9c6824f9a3b8 100644 (file)
@@ -90,6 +90,7 @@ class Prescription:
 
     def write_to(self, file):
         with open(file, "w") as f:
+            del self.file
             f.write(self.get_json())
         self.file=file
 
diff --git a/signature.py b/signature.py
new file mode 100644 (file)
index 0000000..7587411
--- /dev/null
@@ -0,0 +1,49 @@
+# MedScript
+# Copyright (C) 2023 Dr. Agnibho Mondal
+# This file is part of MedScript.
+# MedScript is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+# MedScript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License along with MedScript. If not, see <https://www.gnu.org/licenses/>.
+
+from M2Crypto import BIO, Rand, SMIME, X509
+from config import config
+from hashlib import sha256
+
+class Signature():
+
+    def sign(data, certificate, privkey):
+        try:
+            hash=sha256(data.encode()).hexdigest()
+            smime=SMIME.SMIME()
+            smime.load_key(privkey, certificate)
+            p7=smime.sign(BIO.MemoryBuffer(hash.encode()), SMIME.PKCS7_DETACHED)
+            out=BIO.MemoryBuffer()
+            smime.write(out, p7)
+            return(out.read().decode())
+        except Exception as e:
+            print(e)
+            return None
+
+    def verify(data, certificate, signature):
+        hash=sha256(data.encode()).hexdigest()
+        smime=SMIME.SMIME()
+
+        x509=X509.load_cert(certificate)
+        sk=X509.X509_Stack()
+        sk.push(x509)
+        smime.set_x509_stack(sk)
+
+        st=X509.X509_Store()
+        st.load_info(certificate)
+        smime.set_x509_store(st)
+
+        with open(signature) as file:
+            buf=BIO.MemoryBuffer(file.read().encode())
+        p7=SMIME.smime_load_pkcs7_bio(buf)[0]
+
+        try:
+            v=smime.verify(p7, BIO.MemoryBuffer(hash.encode()))
+            return(x509.get_subject().as_text())
+        except SMIME.PKCS7_Error as e:
+            print(e)
+            return False
index 584548156d7ee17056c05465cd1f6d7b35089ed5..68a4242a4f36c934681367925c3016e03d71b397 100644 (file)
--- a/window.py
+++ b/window.py
@@ -93,7 +93,42 @@ class MainWindow(QMainWindow):
             self.signal_view.emit(target)
             self.renderbox.showMaximized()
         else:
-           QMessageBox.information(self,"Save first", "Please save the file before rendering.")
+           QMessageBox.information(self, "Save first", "Please save the file before rendering.")
+
+    def cmd_sign(self):
+        self.update_instance()
+        if(self.save_state==md5(self.prescription.get_json().encode()).hexdigest()):
+            try:
+                self.current_file.sign()
+                self.cmd_save()
+            except FileNotFoundError as e:
+                print(e)
+                QMessageBox.information(self, "Save first", "Please save the file before signing.")
+            except TypeError as e:
+                print(e)
+                QMessageBox.information(self, "Configure", "Please add valid key and certificate to the config file.")
+            except Exception as e:
+                print(e)
+                QMessageBox.information(self, "Failed", "Failed to sign.")
+        else:
+           QMessageBox.information(self, "Save first", "Please save the file before signing.")
+
+    def cmd_verify(self):
+        try:
+            result=self.current_file.verify()
+            if result is False:
+               QMessageBox.critical(self, "Verification failed", "Signature is invalid.")
+            elif result is None:
+                QMessageBox.warning(self, "No Siganture", "No signature was found.")
+            else:
+                print(result)
+                QMessageBox.information(self, "Valid signature", "Valid signature found with the following information:\n"+result)
+        except FileNotFoundError as e:
+            print(e)
+            QMessageBox.warning(self, "No Siganture", "No signature was found.")
+        except Exception as e:
+            print(e)
+            QMessageBox.warning(self, "Failed", "Failed to verify.")
 
     def cmd_prescriber(self):
         self.edit_prescriber.show()
@@ -352,6 +387,10 @@ class MainWindow(QMainWindow):
         action_render2=QAction(icon_render, "Render", self)
         action_render.triggered.connect(self.cmd_render)
         action_render2.triggered.connect(self.cmd_render)
+        action_sign=QAction("Sign", self)
+        action_sign.triggered.connect(self.cmd_sign)
+        action_verify=QAction("Verify", self)
+        action_verify.triggered.connect(self.cmd_verify)
         action_prescriber=QAction("Prescriber", self)
         action_prescriber.triggered.connect(self.cmd_prescriber)
         action_switch=QAction("Switch", self)
@@ -370,6 +409,8 @@ class MainWindow(QMainWindow):
         menu_file.addAction(action_quit)
         menu_prepare=menubar.addMenu("Prepare")
         menu_prepare.addAction(action_render)
+        menu_prepare.addAction(action_sign)
+        menu_prepare.addAction(action_verify)
         menu_prepare.addAction(action_refresh)
         menu_prepare.addAction(action_prescriber)
         menu_prepare.addAction(action_switch)