labendsem
This commit is contained in:
		
							parent
							
								
									040219580e
								
							
						
					
					
						commit
						9d70a80337
					
				
					 9 changed files with 1349 additions and 0 deletions
				
			
		
							
								
								
									
										434
									
								
								IS/Lab/Eval-Endsem/ans.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								IS/Lab/Eval-Endsem/ans.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,434 @@ | |||
| 
 | ||||
| import os | ||||
| import json | ||||
| import base64 | ||||
| import uuid | ||||
| from datetime import datetime, timezone | ||||
| 
 | ||||
| from Crypto.PublicKey import RSA, ElGamal | ||||
| from Crypto.Cipher import PKCS1_OAEP | ||||
| from Crypto.Hash import MD5 | ||||
| from Crypto.Random import random, get_random_bytes | ||||
| from Crypto.Util.number import GCD, inverse | ||||
| 
 | ||||
| try: | ||||
|     from phe import paillier | ||||
| except ImportError: | ||||
|     print("Install dependency: pip install phe") | ||||
|     raise | ||||
| 
 | ||||
| STATE_FILE = "server_state.json" | ||||
| INPUT_DIR = "inputdata" | ||||
| 
 | ||||
| def ensure_dirs(): | ||||
|     if not os.path.exists(INPUT_DIR): | ||||
|         os.makedirs(INPUT_DIR) | ||||
| 
 | ||||
| def load_state(): | ||||
|     if not os.path.exists(STATE_FILE): | ||||
|         return { | ||||
|             "server": {}, | ||||
|             "doctors": {}, | ||||
|             "reports": [], | ||||
|             "expenses": {} | ||||
|         } | ||||
|     with open(STATE_FILE, "r") as f: | ||||
|         return json.load(f) | ||||
| 
 | ||||
| def save_state(state): | ||||
|     with open(STATE_FILE, "w") as f: | ||||
|         json.dump(state, f, indent=2) | ||||
| 
 | ||||
| def gen_server_keys(state): | ||||
|     if "rsa_oaep" in state["server"]: | ||||
|         print("Server keys already exist.") | ||||
|         return | ||||
|     rsa_oaep_key = RSA.generate(512) | ||||
|     pub_pem = rsa_oaep_key.publickey().export_key().decode() | ||||
|     priv_pem = rsa_oaep_key.export_key().decode() | ||||
| 
 | ||||
|     homo_rsa = RSA.generate(512) | ||||
|     n = int(homo_rsa.n) | ||||
|     e = int(homo_rsa.e) | ||||
|     d = int(homo_rsa.d) | ||||
|     # base for exponent-trick homomorphic addition | ||||
|     while True: | ||||
|         base = random.randint(2, n - 2) | ||||
|         if GCD(base, n) == 1: | ||||
|             break | ||||
|     max_exp = 10000 | ||||
| 
 | ||||
|     pub, priv = paillier.generate_paillier_keypair(n_length=1024) | ||||
| 
 | ||||
|     state["server"]["rsa_oaep"] = {"pub_pem": pub_pem, "priv_pem": priv_pem} | ||||
|     state["server"]["homo_rsa"] = {"n": n, "e": e, "d": d, "base": base, "max_exp": max_exp} | ||||
|     state["server"]["paillier"] = {"n": pub.n, "p": priv.p, "q": priv.q} | ||||
|     save_state(state) | ||||
|     print("Server RSA-OAEP, Homo-RSA, and Paillier keys generated.") | ||||
| 
 | ||||
| def get_paillier_keys(state): | ||||
|     n = state["server"]["paillier"]["n"] | ||||
|     p = state["server"]["paillier"]["p"] | ||||
|     q = state["server"]["paillier"]["q"] | ||||
|     pub = paillier.PaillierPublicKey(n) | ||||
|     priv = paillier.PaillierPrivateKey(pub, p, q) | ||||
|     return pub, priv | ||||
| 
 | ||||
| def register_doctor(state): | ||||
|     name = input("Doctor name: ").strip() | ||||
|     dept = input("Department: ").strip() | ||||
|     doc_id = "doc_" + uuid.uuid4().hex[:8] | ||||
| 
 | ||||
|     eg_key = ElGamal.generate(512, get_random_bytes) | ||||
|     # ElGamal object has p,g,y,x attributes | ||||
|     p = int(eg_key.p) | ||||
|     g = int(eg_key.g) | ||||
|     y = int(eg_key.y) | ||||
|     x = int(eg_key.x) | ||||
| 
 | ||||
|     # Paillier encrypt department hash | ||||
|     pub, _ = get_paillier_keys(state) | ||||
|     dept_md5_int = int.from_bytes(MD5.new(dept.encode()).digest(), "big") | ||||
|     dept_enc = pub.encrypt(dept_md5_int) | ||||
| 
 | ||||
|     state["doctors"][doc_id] = { | ||||
|         "name": name, | ||||
|         "department": dept, | ||||
|         "department_md5": dept_md5_int, | ||||
|         "department_paillier": { | ||||
|             "ciphertext": int(dept_enc.ciphertext()), | ||||
|             "exponent": dept_enc.exponent | ||||
|         }, | ||||
|         "elgamal": {"p": p, "g": g, "y": y, "x": x} | ||||
|     } | ||||
|     save_state(state) | ||||
|     print(f"Registered doctor {name} with id {doc_id} in dept {dept}.") | ||||
|     print(f"ElGamal pub (p,g,y) set. Department stored both plaintext and Paillier-encrypted.") | ||||
| 
 | ||||
| def list_markdown_files(): | ||||
|     ensure_dirs() | ||||
|     files = [f for f in os.listdir(INPUT_DIR) if f.lower().endswith(".md")] | ||||
|     for i, f in enumerate(files, 1): | ||||
|         print(f"{i}. {f}") | ||||
|     return files | ||||
| 
 | ||||
| def rsa_oaep_encrypt_large(data_bytes, pub_pem): | ||||
|     pub = RSA.import_key(pub_pem) | ||||
|     cipher = PKCS1_OAEP.new(pub)  # SHA1 by default | ||||
|     k = pub.size_in_bytes() | ||||
|     hlen = 20 | ||||
|     max_pt = k - 2*hlen - 2 | ||||
|     out = b"" | ||||
|     for i in range(0, len(data_bytes), max_pt): | ||||
|         block = data_bytes[i:i+max_pt] | ||||
|         out += cipher.encrypt(block) | ||||
|     return base64.b64encode(out).decode() | ||||
| 
 | ||||
| def rsa_oaep_decrypt_large(b64, priv_pem): | ||||
|     data = base64.b64decode(b64.encode()) | ||||
|     priv = RSA.import_key(priv_pem) | ||||
|     cipher = PKCS1_OAEP.new(priv) | ||||
|     k = priv.size_in_bytes() | ||||
|     out = b"" | ||||
|     for i in range(0, len(data), k): | ||||
|         block = data[i:i+k] | ||||
|         out += cipher.decrypt(block) | ||||
|     return out | ||||
| 
 | ||||
| def elgamal_sign(doc_eg, msg_bytes): | ||||
|     p = int(doc_eg["p"]) | ||||
|     g = int(doc_eg["g"]) | ||||
|     x = int(doc_eg["x"]) | ||||
|     H = int(MD5.new(msg_bytes).hexdigest(), 16) % (p - 1) | ||||
|     while True: | ||||
|         k = random.randint(2, p - 2) | ||||
|         if GCD(k, p - 1) == 1: | ||||
|             break | ||||
|     r = pow(g, k, p) | ||||
|     kinv = inverse(k, p - 1) | ||||
|     s = (kinv * (H - x * r)) % (p - 1) | ||||
|     return int(r), int(s) | ||||
| 
 | ||||
| def elgamal_verify(pub_eg, msg_bytes, sig): | ||||
|     p = int(pub_eg["p"]) | ||||
|     g = int(pub_eg["g"]) | ||||
|     y = int(pub_eg["y"]) | ||||
|     r, s = sig | ||||
|     if not (1 < r < p): | ||||
|         return False | ||||
|     H = int(MD5.new(msg_bytes).hexdigest(), 16) % (p - 1) | ||||
|     v1 = (pow(y, r, p) * pow(r, s, p)) % p | ||||
|     v2 = pow(g, H, p) | ||||
|     return v1 == v2 | ||||
| 
 | ||||
| def doctor_submit_report(state): | ||||
|     if not state["doctors"]: | ||||
|         print("No doctors. Register first.") | ||||
|         return | ||||
|     doc_id = input("Enter your doctor id: ").strip() | ||||
|     if doc_id not in state["doctors"]: | ||||
|         print("Unknown doctor.") | ||||
|         return | ||||
|     files = list_markdown_files() | ||||
|     if not files: | ||||
|         print("Place a markdown file in inputdata/") | ||||
|         return | ||||
|     idx = int(input("Select file #: ").strip()) | ||||
|     filename = files[idx - 1] | ||||
|     path = os.path.join(INPUT_DIR, filename) | ||||
|     with open(path, "rb") as f: | ||||
|         report_bytes = f.read() | ||||
|     md5_hex = MD5.new(report_bytes).hexdigest() | ||||
|     ts = datetime.now(timezone.utc).isoformat() | ||||
| 
 | ||||
|     msg_to_sign = report_bytes + ts.encode() | ||||
|     r, s = elgamal_sign(state["doctors"][doc_id]["elgamal"], msg_to_sign) | ||||
| 
 | ||||
|     pub_pem = state["server"]["rsa_oaep"]["pub_pem"] | ||||
|     ct_b64 = rsa_oaep_encrypt_large(report_bytes, pub_pem) | ||||
| 
 | ||||
|     rep_id = "rep_" + uuid.uuid4().hex[:8] | ||||
|     rec = { | ||||
|         "report_id": rep_id, | ||||
|         "doctor_id": doc_id, | ||||
|         "doctor_name": state["doctors"][doc_id]["name"], | ||||
|         "filename": filename, | ||||
|         "timestamp_utc": ts, | ||||
|         "md5_hex": md5_hex, | ||||
|         "elgamal_sig": {"r": r, "s": s}, | ||||
|         "rsa_oaep_b64": ct_b64 | ||||
|     } | ||||
|     state["reports"].append(rec) | ||||
|     save_state(state) | ||||
|     print("Report submitted.") | ||||
|     print(f"id: {rep_id}") | ||||
|     print(f"md5: {md5_hex}") | ||||
|     print(f"sig: r={r} s={s}") | ||||
|     print(f"enc blocks (base64 len): {len(ct_b64)}") | ||||
| 
 | ||||
| def homo_rsa_encrypt_amount(state, amount): | ||||
|     n = int(state["server"]["homo_rsa"]["n"]) | ||||
|     e = int(state["server"]["homo_rsa"]["e"]) | ||||
|     base = int(state["server"]["homo_rsa"]["base"]) | ||||
|     max_exp = int(state["server"]["homo_rsa"]["max_exp"]) | ||||
|     if amount < 0 or amount > max_exp: | ||||
|         raise ValueError("amount out of allowed range") | ||||
|     m = pow(base, amount, n) | ||||
|     c = pow(m, e, n) | ||||
|     return int(c) | ||||
| 
 | ||||
| def homo_rsa_discrete_log(m, base, mod, max_k): | ||||
|     val = 1 % mod | ||||
|     if m == 1 % mod: | ||||
|         return 0 | ||||
|     for k in range(1, max_k + 1): | ||||
|         val = (val * base) % mod | ||||
|         if val == m: | ||||
|             return k | ||||
|     return None | ||||
| 
 | ||||
| def doctor_submit_expense(state): | ||||
|     if not state["doctors"]: | ||||
|         print("No doctors. Register first.") | ||||
|         return | ||||
|     doc_id = input("Enter your doctor id: ").strip() | ||||
|     if doc_id not in state["doctors"]: | ||||
|         print("Unknown doctor.") | ||||
|         return | ||||
|     amt = int(input("Expense integer (<=10000): ").strip()) | ||||
|     c = homo_rsa_encrypt_amount(state, amt) | ||||
|     state["expenses"].setdefault(doc_id, []).append(c) | ||||
|     save_state(state) | ||||
|     print(f"Encrypted expense stored for {doc_id}.") | ||||
|     print(f"ciphertext: {c}") | ||||
| 
 | ||||
| def auditor_list_reports(state): | ||||
|     if not state["reports"]: | ||||
|         print("No reports.") | ||||
|         return | ||||
|     for r in state["reports"]: | ||||
|         print(f"{r['report_id']} by {r['doctor_id']} at {r['timestamp_utc']} file={r['filename']} md5={r['md5_hex']}") | ||||
| 
 | ||||
| def auditor_verify_report(state): | ||||
|     if not state["reports"]: | ||||
|         print("No reports.") | ||||
|         return | ||||
|     rep_id = input("Report id: ").strip() | ||||
|     rec = next((r for r in state["reports"] if r["report_id"] == rep_id), None) | ||||
|     if not rec: | ||||
|         print("Not found.") | ||||
|         return | ||||
|     priv_pem = state["server"]["rsa_oaep"]["priv_pem"] | ||||
|     pt = rsa_oaep_decrypt_large(rec["rsa_oaep_b64"], priv_pem) | ||||
|     md5_calc = MD5.new(pt).hexdigest() | ||||
|     ok_md5 = (md5_calc == rec["md5_hex"]) | ||||
| 
 | ||||
|     doc = state["doctors"][rec["doctor_id"]] | ||||
|     pub_eg = {"p": doc["elgamal"]["p"], "g": doc["elgamal"]["g"], "y": doc["elgamal"]["y"]} | ||||
|     msg = pt + rec["timestamp_utc"].encode() | ||||
|     ok_sig = elgamal_verify(pub_eg, msg, (rec["elgamal_sig"]["r"], rec["elgamal_sig"]["s"])) | ||||
| 
 | ||||
|     ts = datetime.fromisoformat(rec["timestamp_utc"]) | ||||
|     now = datetime.now(timezone.utc) | ||||
|     skew_sec = (now - ts).total_seconds() | ||||
| 
 | ||||
|     print("Verification results:") | ||||
|     print(f"md5 match: {ok_md5}") | ||||
|     print(f"signature valid: {ok_sig}") | ||||
|     print(f"timestamp: {rec['timestamp_utc']}") | ||||
|     print(f"server now: {now.isoformat()}") | ||||
|     print(f"age seconds: {int(skew_sec)} (future? {skew_sec < 0})") | ||||
| 
 | ||||
| def auditor_keyword_search(state): | ||||
|     if not state["doctors"]: | ||||
|         print("No doctors.") | ||||
|         return | ||||
|     dept_q = input("Search department: ").strip() | ||||
|     pub, priv = get_paillier_keys(state) | ||||
|     q_int = int.from_bytes(MD5.new(dept_q.encode()).digest(), "big") | ||||
|     q_enc = pub.encrypt(q_int) | ||||
| 
 | ||||
|     print("Records:") | ||||
|     found = [] | ||||
|     for doc_id, doc in state["doctors"].items(): | ||||
|         enc_info = doc["department_paillier"] | ||||
|         enc_doc = paillier.EncryptedNumber(pub, int(enc_info["ciphertext"]), int(enc_info["exponent"])) | ||||
|         diff = enc_doc - q_enc | ||||
|         val = priv.decrypt(diff) | ||||
|         is_match = (val == 0) | ||||
|         if is_match: | ||||
|             found.append(doc_id) | ||||
|         print(f"{doc_id}: dept='{doc['department']}' enc_ct={enc_info['ciphertext']} match={is_match}") | ||||
|     print(f"Matches: {found}") | ||||
| 
 | ||||
| def auditor_sum_expenses(state): | ||||
|     homo = state["server"]["homo_rsa"] | ||||
|     n = int(homo["n"]); d = int(homo["d"]); base = int(homo["base"]); max_exp = int(homo["max_exp"]) | ||||
|     if not state["expenses"]: | ||||
|         print("No expenses.") | ||||
|         return | ||||
|     choice = input("Sum for 'all' or specific doc_id: ").strip() | ||||
|     c_list = [] | ||||
|     if choice.lower() == "all": | ||||
|         for doc_id, lst in state["expenses"].items(): | ||||
|             c_list += lst | ||||
|     else: | ||||
|         if choice not in state["expenses"]: | ||||
|             print("No expenses for given id.") | ||||
|             return | ||||
|         c_list = state["expenses"][choice] | ||||
|     if not c_list: | ||||
|         print("No expenses to sum.") | ||||
|         return | ||||
|     prod = 1 | ||||
|     for c in c_list: | ||||
|         prod = (prod * int(c)) % n | ||||
|     m = pow(prod, d, n) | ||||
|     s = homo_rsa_discrete_log(m, base, n, max_exp) | ||||
|     print("Homomorphic sum result:") | ||||
|     print(f"combined ciphertext (mod n): {prod}") | ||||
|     if s is None: | ||||
|         print("decrypted sum: could not recover (out of range)") | ||||
|     else: | ||||
|         print(f"decrypted sum: {s}") | ||||
| 
 | ||||
| def list_doctors(state): | ||||
|     if not state["doctors"]: | ||||
|         print("No doctors.") | ||||
|         return | ||||
|     for doc_id, d in state["doctors"].items(): | ||||
|         print(f"{doc_id}: {d['name']} dept='{d['department']}'") | ||||
| 
 | ||||
| def doctor_list_my_data(state): | ||||
|     doc_id = input("Enter your doctor id: ").strip() | ||||
|     if doc_id not in state["doctors"]: | ||||
|         print("Unknown doctor.") | ||||
|         return | ||||
|     print("Reports:") | ||||
|     for r in state["reports"]: | ||||
|         if r["doctor_id"] == doc_id: | ||||
|             print(f"{r['report_id']} {r['filename']} {r['timestamp_utc']}") | ||||
|     print("Expenses (ciphertexts):") | ||||
|     for c in state["expenses"].get(doc_id, []): | ||||
|         print(c) | ||||
| 
 | ||||
| def main(): | ||||
|     ensure_dirs() | ||||
|     state = load_state() | ||||
|     while True: | ||||
|         print("\nMain Menu") | ||||
|         print("1. Setup server keys") | ||||
|         print("2. Register doctor") | ||||
|         print("3. Doctor menu") | ||||
|         print("4. Auditor menu") | ||||
|         print("5. List doctors") | ||||
|         print("0. Exit") | ||||
|         ch = input("Choice: ").strip() | ||||
|         if ch == "1": | ||||
|             gen_server_keys(state) | ||||
|         elif ch == "2": | ||||
|             if "rsa_oaep" not in state["server"]: | ||||
|                 print("Setup server keys first.") | ||||
|             else: | ||||
|                 register_doctor(state) | ||||
|         elif ch == "3": | ||||
|             while True: | ||||
|                 print("\nDoctor Menu") | ||||
|                 print("1. Submit report (md file in inputdata/)") | ||||
|                 print("2. Submit expense (homomorphic RSA)") | ||||
|                 print("3. List my reports/expenses") | ||||
|                 print("0. Back") | ||||
|                 dch = input("Choice: ").strip() | ||||
|                 if dch == "1": | ||||
|                     if "rsa_oaep" not in state["server"]: | ||||
|                         print("Setup server keys first.") | ||||
|                     else: | ||||
|                         doctor_submit_report(state) | ||||
|                 elif dch == "2": | ||||
|                     if "homo_rsa" not in state["server"]: | ||||
|                         print("Setup server keys first.") | ||||
|                     else: | ||||
|                         doctor_submit_expense(state) | ||||
|                 elif dch == "3": | ||||
|                     doctor_list_my_data(state) | ||||
|                 elif dch == "0": | ||||
|                     break | ||||
|                 else: | ||||
|                     print("Invalid.") | ||||
|         elif ch == "4": | ||||
|             while True: | ||||
|                 print("\nAuditor Menu") | ||||
|                 print("1. List reports") | ||||
|                 print("2. Verify a report (sig + timestamp)") | ||||
|                 print("3. Dept keyword search (Paillier)") | ||||
|                 print("4. Sum expenses (homomorphic RSA)") | ||||
|                 print("0. Back") | ||||
|                 ach = input("Choice: ").strip() | ||||
|                 if ach == "1": | ||||
|                     auditor_list_reports(state) | ||||
|                 elif ach == "2": | ||||
|                     auditor_verify_report(state) | ||||
|                 elif ach == "3": | ||||
|                     if "paillier" not in state["server"]: | ||||
|                         print("Setup server keys first.") | ||||
|                     else: | ||||
|                         auditor_keyword_search(state) | ||||
|                 elif ach == "4": | ||||
|                     if "homo_rsa" not in state["server"]: | ||||
|                         print("Setup server keys first.") | ||||
|                     else: | ||||
|                         auditor_sum_expenses(state) | ||||
|                 elif ach == "0": | ||||
|                     break | ||||
|                 else: | ||||
|                     print("Invalid.") | ||||
|         elif ch == "5": | ||||
|             list_doctors(state) | ||||
|         elif ch == "0": | ||||
|             print("Bye.") | ||||
|             break | ||||
|         else: | ||||
|             print("Invalid.") | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										47
									
								
								IS/Lab/Eval-Endsem/client.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								IS/Lab/Eval-Endsem/client.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| import os | ||||
| import json | ||||
| import socket | ||||
| import base64 | ||||
| import hashlib | ||||
| from datetime import datetime | ||||
| from Crypto.PublicKey import RSA, ElGamal | ||||
| from Crypto.Random import get_random_bytes | ||||
| from Crypto.Util.number import inverse, GCD | ||||
| from Crypto.Cipher import AES, PKCS1_OAEP | ||||
| from phe import paillier | ||||
| 
 | ||||
| SERVER_HOST = "127.0.0.1" | ||||
| SERVER_PORT = 5000 | ||||
| INPUT_DIR = "inputdata" | ||||
| KEYS_DIR = "client_keys" | ||||
| 
 | ||||
| server_info = {}  # filled by get_public_info | ||||
| cached_paillier_pub = None | ||||
| 
 | ||||
| def ensure_dirs(): | ||||
|     os.makedirs(INPUT_DIR, exist_ok=True) | ||||
|     os.makedirs(KEYS_DIR, exist_ok=True) | ||||
| 
 | ||||
| def b64e(b: bytes) -> str: | ||||
|     return base64.b64encode(b).decode() | ||||
| 
 | ||||
| def b64d(s: str) -> bytes: | ||||
|     return base64.b64decode(s.encode()) | ||||
| 
 | ||||
| def send_request(action, role, body): | ||||
|     req = {"action": action, "role": role, "body": body} | ||||
|     with socket.create_connection((SERVER_HOST, SERVER_PORT), timeout=5) as s: | ||||
|         s.sendall((json.dumps(req) + "\n").encode()) | ||||
|         data = s.recv(1024*1024) | ||||
|     resp = json.loads(data.decode()) | ||||
|     if resp.get("status") != "ok": | ||||
|         print("server error:", resp.get("error")) | ||||
|         return None | ||||
|     return resp.get("data") if "data" in resp else {} | ||||
| 
 | ||||
| def get_public_info(): | ||||
|     global server_info, cached_paillier_pub | ||||
|     data = send_request("get result:") | ||||
|     if data is None: | ||||
| 
 | ||||
| def rs | ||||
							
								
								
									
										5
									
								
								IS/Lab/Eval-Endsem/inputdata/diag2.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								IS/Lab/Eval-Endsem/inputdata/diag2.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| hello | ||||
| world | ||||
| test  | ||||
| medical | ||||
| doc | ||||
							
								
								
									
										19
									
								
								IS/Lab/Eval-Endsem/inputdata/diagnosis.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								IS/Lab/Eval-Endsem/inputdata/diagnosis.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| Patient - Aadit | ||||
| 
 | ||||
| Patient No - 230953344 | ||||
| 
 | ||||
| Blood Grp - A Positive | ||||
| 
 | ||||
| Org - MIT Manipal | ||||
| 
 | ||||
| Ailments | ||||
| -------- | ||||
| Allergic Rhinitis | ||||
| Pancreatic Rest | ||||
| Migraine | ||||
| 
 | ||||
| Medicine | ||||
| -------- | ||||
| Pantoprazole | ||||
| SOS inhaler | ||||
| Omeprazole | ||||
							
								
								
									
										0
									
								
								IS/Lab/Eval-Endsem/q.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								IS/Lab/Eval-Endsem/q.md
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										407
									
								
								IS/Lab/Eval-Endsem/rough.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								IS/Lab/Eval-Endsem/rough.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,407 @@ | |||
| import os | ||||
| import json | ||||
| import threading | ||||
| import socketserver | ||||
| import base64 | ||||
| import time | ||||
| from datetime import datetime, timezone | ||||
| from Crypto.PublicKey import RSA | ||||
| from Crypto.Cipher import PKCS1_OAEP, AES | ||||
| from Crypto.Random import get_random_bytes | ||||
| from Crypto.Util.number import GCD | ||||
| from phe import paillier | ||||
| 
 | ||||
| DATA_DIR = server_data | ||||
| DOCTORS_FILE = os.path.join(DATA_DIR, doctors.json) | ||||
| EXPENSES_FILE = os.path.join(DATA_DIR, expenses.json) | ||||
| REPORTS_FILE = os.path.join(DATA_DIR, reports.json) | ||||
| CONF_FILE = os.path.join(DATA_DIR, config.json) | ||||
| RSA_PRIV_FILE = os.path.join(DATA_DIR, server_rsa_priv.pem) | ||||
| RSA_PUB_FILE = os.path.join(DATA_DIR, server_rsa_pub.pem) | ||||
| PORT = 5000 | ||||
| 
 | ||||
| lock = threading.Lock() | ||||
| 
 | ||||
| def ensure_dirs(): | ||||
|     os.makedirs(DATA_DIR, exist_ok=True) | ||||
| 
 | ||||
| def read_json(path, default): | ||||
|     if not os.path.exists(path): | ||||
|         return default | ||||
|     with open(path, r) as f: | ||||
|         return json.load(f) | ||||
| 
 | ||||
| def write_json(path, obj): | ||||
|     tmp = path + .tmp | ||||
|     with open(tmp, w) as f: | ||||
|         json.dump(obj, f, indent=2) | ||||
|     os.replace(tmp, path) | ||||
| 
 | ||||
| def load_or_create_rsa(): | ||||
|     if not os.path.exists(RSA_PRIV_FILE): | ||||
|         key = RSA.generate(2048) | ||||
|         with open(RSA_PRIV_FILE, wb) as f: | ||||
|             f.write(key.export_key()) | ||||
|         with open(RSA_PUB_FILE, wb) as f: | ||||
|             f.write(key.public_key().export_key()) | ||||
|     with open(RSA_PRIV_FILE, rb) as f: | ||||
|         priv = RSA.import_key(f.read()) | ||||
|     with open(RSA_PUB_FILE, rb) as f: | ||||
|         pub = RSA.import_key(f.read()) | ||||
|     return priv, pub | ||||
| 
 | ||||
| def load_or_create_paillier(): | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     if paillier not in conf: | ||||
|         pubkey, privkey = paillier.generate_paillier_keypair() | ||||
|         conf[paillier] = { | ||||
|             n: str(pubkey.n), | ||||
|             p: str(privkey.p), | ||||
|             q: str(privkey.q), | ||||
|         } | ||||
|         write_json(CONF_FILE, conf) | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     n = int(conf[paillier][n]) | ||||
|     p = int(conf[paillier][p]) | ||||
|     q = int(conf[paillier][q]) | ||||
|     pubkey = paillier.PaillierPublicKey(n) | ||||
|     privkey = paillier.PaillierPrivateKey(pubkey, p, q) | ||||
|     return pubkey, privkey | ||||
| 
 | ||||
| def load_or_create_config_rsa_homomorphic_base(rsa_pub): | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     n = rsa_pub.n | ||||
|     if rsa_homomorphic not in conf: | ||||
|         # pick base g coprime to n | ||||
|         import random | ||||
|         while True: | ||||
|             g = random.randrange(2, n - 1) | ||||
|             if GCD(g, n) == 1: | ||||
|                 break | ||||
|         conf[rsa_homomorphic] = { | ||||
|             g: str(g) | ||||
|         } | ||||
|         write_json(CONF_FILE, conf) | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     g = int(conf[rsa_homomorphic][g]) | ||||
|     return g | ||||
| 
 | ||||
| def b64e(b: bytes) -> str: | ||||
|     return base64.b64encode(b).decode() | ||||
| 
 | ||||
| def b64d(s: str) -> bytes: | ||||
|     return base64.b64decode(s.encode()) | ||||
| 
 | ||||
| def init_storage(): | ||||
|     ensure_dirs() | ||||
|     priv, pub = load_or_create_rsa() | ||||
|     _ = load_or_create_paillier() | ||||
|     if not os.path.exists(DOCTORS_FILE): | ||||
|         write_json(DOCTORS_FILE, {}) | ||||
|     if not os.path.exists(EXPENSES_FILE): | ||||
|         write_json(EXPENSES_FILE, []) | ||||
|     if not os.path.exists(REPORTS_FILE): | ||||
|         write_json(REPORTS_FILE, []) | ||||
|     return priv, pub | ||||
| 
 | ||||
| RSA_PRIV, RSA_PUB = init_storage() | ||||
| PAI_PUB, PAI_PRIV = load_or_create_paillier() | ||||
| RSA_HOMO_G = load_or_create_config_rsa_homomorphic_base(RSA_PUB) | ||||
| 
 | ||||
| def get_public_info(): | ||||
|     return { | ||||
|         rsa_pub_pem_b64: b64e(RSA_PUB.export_key()), | ||||
|         rsa_n: str(RSA_PUB.n), | ||||
|         rsa_e: str(RSA_PUB.e), | ||||
|         paillier_n: str(PAI_PUB.n), | ||||
|         rsa_homo_g: str(RSA_HOMO_G), | ||||
|     } | ||||
| 
 | ||||
| def handle_register_doctor(body): | ||||
|     # body: {doctor_id, department_plain, dept_enc: {ciphertext, exponent}, elgamal_pub: {p,g,y}} | ||||
|     doc_id = body.get(doctor_id,).strip() | ||||
|     dept_plain = body.get(department_plain,).strip() | ||||
|     dept_enc = body.get(dept_enc) | ||||
|     elgamal_pub = body.get(elgamal_pub) | ||||
|     if not doc_id or not doc_id.isalnum(): | ||||
|         return {status:error,error:invalid doctor_id} | ||||
|     if not dept_plain: | ||||
|         return {status:error,error:invalid department} | ||||
|     if not dept_enc or ciphertext not in dept_enc or exponent not in dept_enc: | ||||
|         return {status:error,error:invalid dept_enc} | ||||
|     if not elgamal_pub or not all(k in elgamal_pub for k in [p,g,y]): | ||||
|         return {status:error,error:missing elgamal_pub} | ||||
| 
 | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|         doctors[doc_id] = { | ||||
|             department_plain: dept_plain, | ||||
|             dept_enc: { | ||||
|                 ciphertext: str(int(dept_enc[ciphertext])), | ||||
|                 exponent: int(dept_enc[exponent]) | ||||
|             }, | ||||
|             elgamal_pub: { | ||||
|                 p: str(int(elgamal_pub[p])), | ||||
|                 g: str(int(elgamal_pub[g])), | ||||
|                 y: str(int(elgamal_pub[y])) | ||||
|             } | ||||
|         } | ||||
|         write_json(DOCTORS_FILE, doctors) | ||||
|     print(f[server] registered doctor {doc_id} dept='{dept_plain}' (stored encrypted and plaintext)) | ||||
|     return {status:ok} | ||||
| 
 | ||||
| def handle_upload_report(body): | ||||
|     # body: {doctor_id, filename, timestamp, md5_hex, sig: {r,s}, aes: {key_rsa_oaep_b64, nonce_b64, tag_b64, ct_b64}} | ||||
|     doc_id = body.get(doctor_id,).strip() | ||||
|     filename = os.path.basename(body.get(filename,).strip()) | ||||
|     timestamp = body.get(timestamp,).strip() | ||||
|     md5_hex = body.get(md5_hex,).strip() | ||||
|     sig = body.get(sig) | ||||
|     aes = body.get(aes) | ||||
|     if not doc_id or not filename or not timestamp or not md5_hex or not sig or not aes: | ||||
|         return {status:error,error:missing fields} | ||||
| 
 | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|     if doc_id not in doctors: | ||||
|         return {status:error,error:unknown doctor_id} | ||||
| 
 | ||||
|     # decrypt AES key | ||||
|     try: | ||||
|         rsa_cipher = PKCS1_OAEP.new(RSA_PRIV) | ||||
|         aes_key = rsa_cipher.decrypt(b64d(aes[key_rsa_oaep_b64])) | ||||
|         nonce = b64d(aes[nonce_b64]) | ||||
|         tag = b64d(aes[tag_b64]) | ||||
|         ct = b64d(aes[ct_b64]) | ||||
|         aes_cipher = AES.new(aes_key, AES.MODE_EAX, nonce=nonce) | ||||
|         report_bytes = aes_cipher.decrypt_and_verify(ct, tag) | ||||
|     except Exception as e: | ||||
|         return {status:error,error:faes/rsa decrypt failed: {e}} | ||||
| 
 | ||||
|     # verify MD5 | ||||
|     import hashlib | ||||
|     md5_check = hashlib.md5(report_bytes).hexdigest() | ||||
|     if md5_check != md5_hex: | ||||
|         print([server] md5 mismatch) | ||||
|     # store file | ||||
|     outdir = os.path.join(DATA_DIR, reports) | ||||
|     os.makedirs(outdir, exist_ok=True) | ||||
|     savepath = os.path.join(outdir, f{doc_id}_{int(time.time())}_{filename}) | ||||
|     with open(savepath, wb) as f: | ||||
|         f.write(report_bytes) | ||||
| 
 | ||||
|     # store record | ||||
|     rec = { | ||||
|         doctor_id: doc_id, | ||||
|         filename: filename, | ||||
|         saved_path: savepath, | ||||
|         timestamp: timestamp, | ||||
|         md5_hex: md5_hex, | ||||
|         sig: {r: str(int(sig[r])), s: str(int(sig[s]))} | ||||
|     } | ||||
|     with lock: | ||||
|         records = read_json(REPORTS_FILE, []) | ||||
|         records.append(rec) | ||||
|         write_json(REPORTS_FILE, records) | ||||
|     print(f[server] report uploaded by {doc_id}, stored {savepath}) | ||||
|     return {status:ok} | ||||
| 
 | ||||
| def handle_submit_expense(body): | ||||
|     # body: {doctor_id, amount_ciphertext} | ||||
|     doc_id = body.get(doctor_id,).strip() | ||||
|     c = body.get(amount_ciphertext) | ||||
|     if not doc_id or not doc_id.isalnum(): | ||||
|         return {status:error,error:invalid doctor_id} | ||||
|     try: | ||||
|         c_int = int(c) | ||||
|     except: | ||||
|         return {status:error,error:invalid ciphertext} | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|     if doc_id not in doctors: | ||||
|         return {status:error,error:unknown doctor_id} | ||||
| 
 | ||||
|     with lock: | ||||
|         expenses = read_json(EXPENSES_FILE, []) | ||||
|         expenses.append({doctor_id: doc_id, ciphertext: str(c_int)}) | ||||
|         write_json(EXPENSES_FILE, expenses) | ||||
|     print(f[server] expense ciphertext stored for {doc_id}) | ||||
|     return {status:ok} | ||||
| 
 | ||||
| class RequestHandler(socketserver.StreamRequestHandler): | ||||
|     def handle(self): | ||||
|         try: | ||||
|             data = self.rfile.readline() | ||||
|             if not data: | ||||
|                 return | ||||
|             req = json.loads(data.decode()) | ||||
|             action = req.get(action) | ||||
|             role = req.get(role, ) | ||||
|             body = req.get(body, {}) | ||||
|             if action == get_public_info: | ||||
|                 resp = {status:ok,data: get_public_info()} | ||||
|             elif action == register_doctor: | ||||
|                 if role != doctor: | ||||
|                     resp = {status:error,error:unauthorized} | ||||
|                 else: | ||||
|                     resp = handle_register_doctor(body) | ||||
|             elif action == upload_report: | ||||
|                 if role != doctor: | ||||
|                     resp = {status:error,error:unauthorized} | ||||
|                 else: | ||||
|                     resp = handle_upload_report(body) | ||||
|             elif action == submit_expense: | ||||
|                 if role != doctor: | ||||
|                     resp = {status:error,error:unauthorized} | ||||
|                 else: | ||||
|                     resp = handle_submit_expense(body) | ||||
|             else: | ||||
|                 resp = {status:error,error:unknown action} | ||||
|         except Exception as e: | ||||
|             resp = {status:error,error:str(e)} | ||||
|         self.wfile.write((json.dumps(resp)+\n).encode()) | ||||
| 
 | ||||
| def start_server(): | ||||
|     server = socketserver.ThreadingTCPServer((127.0.0.1, PORT), RequestHandler) | ||||
|     t = threading.Thread(target=server.serve_forever, daemon=True) | ||||
|     t.start() | ||||
|     print(f[server] listening on 127.0.0.1:{PORT}) | ||||
|     return server | ||||
| 
 | ||||
| # Auditor utilities | ||||
| 
 | ||||
| def load_doctors(): | ||||
|     return read_json(DOCTORS_FILE, {}) | ||||
| 
 | ||||
| def load_expenses(): | ||||
|     return read_json(EXPENSES_FILE, []) | ||||
| 
 | ||||
| def load_reports(): | ||||
|     return read_json(REPORTS_FILE, []) | ||||
| 
 | ||||
| def audit_list_doctors(): | ||||
|     docs = load_doctors() | ||||
|     print(Doctors:) | ||||
|     for did, info in docs.items(): | ||||
|         enc = info[dept_enc] | ||||
|         print(f- {did} dept_plain='{info['department_plain']}' enc_ciphertext={enc['ciphertext']} exponent={enc['exponent']}) | ||||
| 
 | ||||
| def audit_keyword_search(): | ||||
|     docs = load_doctors() | ||||
|     if not docs: | ||||
|         print(no doctors) | ||||
|         return | ||||
|     q = input(Enter department keyword to search: ).strip() | ||||
|     if not q: | ||||
|         print(empty) | ||||
|         return | ||||
|     # hash to int | ||||
|     import hashlib | ||||
|     h = int.from_bytes(hashlib.sha256(q.encode()).digest(), big) | ||||
|     pub = PAI_PUB | ||||
|     priv = PAI_PRIV | ||||
|     enc_q = pub.encrypt(h) | ||||
|     print(Matching doctors (using Paillier equality on hashed dept):) | ||||
|     for did, info in docs.items(): | ||||
|         enc = info[dept_enc] | ||||
|         c = int(enc[ciphertext]) | ||||
|         exp = int(enc[exponent]) | ||||
|         enc_doc = paillier.EncryptedNumber(pub, c, exp) | ||||
|         diff = enc_doc - enc_q | ||||
|         dec = priv.decrypt(diff) | ||||
|         match = (dec == 0) | ||||
|         print(f  {did}: dept_plain='{info['department_plain']}' enc_ciphertext={c} match={match}) | ||||
| 
 | ||||
| def rsa_homo_decrypt_sum(c_prod_int): | ||||
|     n = RSA_PRIV.n | ||||
|     d = RSA_PRIV.d | ||||
|     g = RSA_HOMO_G | ||||
|     # decrypt to get g^sum mod n | ||||
|     m = pow(int(c_prod_int), d, n) | ||||
|     # brute force discrete log for small sums | ||||
|     max_iter = 500000 | ||||
|     acc = 1 | ||||
|     for k in range(0, max_iter+1): | ||||
|         if acc == m: | ||||
|             return k | ||||
|         acc = (acc * g) % n | ||||
|     return None | ||||
| 
 | ||||
| def audit_sum_expenses(): | ||||
|     exps = load_expenses() | ||||
|     if not exps: | ||||
|         print(no expenses) | ||||
|         return | ||||
|     # sum all | ||||
|     n = RSA_PUB.n | ||||
|     c_prod = 1 | ||||
|     for e in exps: | ||||
|         c_prod = (c_prod * int(e[ciphertext])) % n | ||||
|     print(fProduct ciphertext (represents sum under RSA-in-exponent): {c_prod}) | ||||
|     s = rsa_homo_decrypt_sum(c_prod) | ||||
|     if s is None: | ||||
|         print(sum decryption failed (exceeded search bound)) | ||||
|     else: | ||||
|         print(fDecrypted sum of expenses = {s}) | ||||
|     # by doctor | ||||
|     docs = load_doctors() | ||||
|     if docs: | ||||
|         print(Per-doctor sums:) | ||||
|         for did in docs.keys(): | ||||
|             c_prod_d = 1 | ||||
|             count = 0 | ||||
|             for e in exps: | ||||
|                 if e[doctor_id] == did: | ||||
|                     c_prod_d = (c_prod_d * int(e[ciphertext])) % n | ||||
|                     count += 1 | ||||
|             if count == 0: | ||||
|                 continue | ||||
|             s_d = rsa_homo_decrypt_sum(c_prod_d) | ||||
|             print(f  {did}: entries={count} product_ct={c_prod_d} sum={s_d}) | ||||
| 
 | ||||
| def elgamal_verify(p, g, y, H_int, r, s): | ||||
|     # verify: g^H ≡ y^r * r^s (mod p) | ||||
|     return pow(g, H_int, p) == (pow(y, r, p) * pow(r, s, p)) % p | ||||
| 
 | ||||
| def audit_verify_reports(): | ||||
|     records = load_reports() | ||||
|     if not records: | ||||
|         print(no reports) | ||||
|         return | ||||
|     doctors = load_doctors() | ||||
|     for rec in records: | ||||
|         did = rec[doctor_id] | ||||
|         docinfo = doctors.get(did) | ||||
|         ok_sig = False | ||||
|         ok_ts = False | ||||
|         if docinfo: | ||||
|             p = int(docinfo[elgamal_pub][p]) | ||||
|             g = int(docinfo[elgamal_pub][g]) | ||||
|             y = int(docinfo[elgamal_pub][y]) | ||||
|             r = int(rec[sig][r]) | ||||
|             s = int(rec[sig][s]) | ||||
|             try: | ||||
|                 with open(rec[saved_path], rb) as f: | ||||
|                     report_bytes = f.read() | ||||
|                 import hashlib | ||||
|                 H = int.from_bytes(hashlib.md5(report_bytes + rec[timestamp].encode()).digest(), big) % (p - 1) | ||||
|                 ok_sig = elgamal_verify(p, g, y, H, r, s) | ||||
|             except Exception as e: | ||||
|                 ok_sig = False | ||||
|         # timestamp check | ||||
|         try: | ||||
|             ts = datetime.fromisoformat(rec[timestamp]) | ||||
|         except: | ||||
|             try: | ||||
|                 ts = datetime.strptime(rec[timestamp], %Y-%m-%dT%H:%M:%S.%f) | ||||
|             except: | ||||
|                 ts = None | ||||
|         if ts: | ||||
|             now = datetime.utcnow().replace(tzinfo=None) | ||||
|             delta = (now - ts).total_seconds() | ||||
|             # simple rule: not in the future by more than 5 min | ||||
|             ok_ts = (delta >= -300) | ||||
|         print(f- report by {did} file={os.path.basename(rec['saved_path'])} sig_ok={ok_sig} ts_ok={ok_ts} ts={rec['timestamp']} md5={rec['md5_hex']}) | ||||
| 
 | ||||
| def auditor_menu(): while True: print(\n[Auditor Menu]) print(1) List doctors (show encrypted and plaintext dept)) print(2) Keyword search doctors by dept (Paillier)) print(3) Sum expenses (RSA-in-exponent demo)) print(4) Verify reports and timestamps) print(5) Show server public info) print(0) Exit) ch = input(Select: ).strip() if ch == 1: audit_list_doctors() elif ch == 2: audit_keyword_search() elif ch == 3: audit_sum_expenses() elif ch == 4: audit_verify_reports() elif ch == 5: info = get_public_info() print(json.dumps(info, indent=2)) elif ch == 0: print(bye) break else: print(invalid) if __name__ == __main__: start_server() auditor_menu() | ||||
| 
 | ||||
							
								
								
									
										436
									
								
								IS/Lab/Eval-Endsem/server.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								IS/Lab/Eval-Endsem/server.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,436 @@ | |||
| import os | ||||
| import json | ||||
| import threading | ||||
| import socketserver | ||||
| import base64 | ||||
| import time | ||||
| from datetime import datetime, timezone | ||||
| from Crypto.PublicKey import RSA | ||||
| from Crypto.Cipher import PKCS1_OAEP, AES | ||||
| from Crypto.Random import get_random_bytes | ||||
| from Crypto.Util.number import GCD | ||||
| from phe import paillier | ||||
| 
 | ||||
| DATA_DIR = "server_data" | ||||
| DOCTORS_FILE = os.path.join(DATA_DIR, "doctors.json") | ||||
| EXPENSES_FILE = os.path.join(DATA_DIR, "expenses.json") | ||||
| REPORTS_FILE = os.path.join(DATA_DIR, "reports.json") | ||||
| CONF_FILE = os.path.join(DATA_DIR, "config.json") | ||||
| RSA_PRIV_FILE = os.path.join(DATA_DIR, "server_rsa_priv.pem") | ||||
| RSA_PUB_FILE = os.path.join(DATA_DIR, "server_rsa_pub.pem") | ||||
| PORT = 5000 | ||||
| 
 | ||||
| lock = threading.Lock() | ||||
| 
 | ||||
| def ensure_dirs(): | ||||
|     os.makedirs(DATA_DIR, exist_ok=True) | ||||
| 
 | ||||
| def read_json(path, default): | ||||
|     if not os.path.exists(path): | ||||
|         return default | ||||
|     with open(path, "r") as f: | ||||
|         return json.load(f) | ||||
| 
 | ||||
| def write_json(path, obj): | ||||
|     tmp = path + ".tmp" | ||||
|     with open(tmp, "w") as f: | ||||
|         json.dump(obj, f, indent=2) | ||||
|     os.replace(tmp, path) | ||||
| 
 | ||||
| def load_or_create_rsa(): | ||||
|     if not os.path.exists(RSA_PRIV_FILE): | ||||
|         key = RSA.generate(2048) | ||||
|         with open(RSA_PRIV_FILE, "wb") as f: | ||||
|             f.write(key.export_key()) | ||||
|         with open(RSA_PUB_FILE, "wb") as f: | ||||
|             f.write(key.public_key().export_key()) | ||||
|     with open(RSA_PRIV_FILE, "rb") as f: | ||||
|         priv = RSA.import_key(f.read()) | ||||
|     with open(RSA_PUB_FILE, "rb") as f: | ||||
|         pub = RSA.import_key(f.read()) | ||||
|     return priv, pub | ||||
| 
 | ||||
| def load_or_create_paillier(): | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     if "paillier" not in conf: | ||||
|         pubkey, privkey = paillier.generate_paillier_keypair() | ||||
|         conf["paillier"] = { | ||||
|             "n": str(pubkey.n), | ||||
|             "p": str(privkey.p), | ||||
|             "q": str(privkey.q), | ||||
|         } | ||||
|         write_json(CONF_FILE, conf) | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     n = int(conf["paillier"]["n"]) | ||||
|     p = int(conf["paillier"]["p"]) | ||||
|     q = int(conf["paillier"]["q"]) | ||||
|     pubkey = paillier.PaillierPublicKey(n) | ||||
|     privkey = paillier.PaillierPrivateKey(pubkey, p, q) | ||||
|     return pubkey, privkey | ||||
| 
 | ||||
| def load_or_create_config_rsa_homomorphic_base(rsa_pub): | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     n = rsa_pub.n | ||||
|     if "rsa_homomorphic" not in conf: | ||||
|         # pick base g coprime to n | ||||
|         import random | ||||
|         while True: | ||||
|             g = random.randrange(2, n - 1) | ||||
|             if GCD(g, n) == 1: | ||||
|                 break | ||||
|         conf["rsa_homomorphic"] = { | ||||
|             "g": str(g) | ||||
|         } | ||||
|         write_json(CONF_FILE, conf) | ||||
|     conf = read_json(CONF_FILE, {}) | ||||
|     g = int(conf["rsa_homomorphic"]["g"]) | ||||
|     return g | ||||
| 
 | ||||
| def b64e(b: bytes) -> str: | ||||
|     return base64.b64encode(b).decode() | ||||
| 
 | ||||
| def b64d(s: str) -> bytes: | ||||
|     return base64.b64decode(s.encode()) | ||||
| 
 | ||||
| def init_storage(): | ||||
|     ensure_dirs() | ||||
|     priv, pub = load_or_create_rsa() | ||||
|     _ = load_or_create_paillier() | ||||
|     if not os.path.exists(DOCTORS_FILE): | ||||
|         write_json(DOCTORS_FILE, {}) | ||||
|     if not os.path.exists(EXPENSES_FILE): | ||||
|         write_json(EXPENSES_FILE, []) | ||||
|     if not os.path.exists(REPORTS_FILE): | ||||
|         write_json(REPORTS_FILE, []) | ||||
|     return priv, pub | ||||
| 
 | ||||
| RSA_PRIV, RSA_PUB = init_storage() | ||||
| PAI_PUB, PAI_PRIV = load_or_create_paillier() | ||||
| RSA_HOMO_G = load_or_create_config_rsa_homomorphic_base(RSA_PUB) | ||||
| 
 | ||||
| def get_public_info(): | ||||
|     return { | ||||
|         "rsa_pub_pem_b64": b64e(RSA_PUB.export_key()), | ||||
|         "rsa_n": str(RSA_PUB.n), | ||||
|         "rsa_e": str(RSA_PUB.e), | ||||
|         "paillier_n": str(PAI_PUB.n), | ||||
|         "rsa_homo_g": str(RSA_HOMO_G), | ||||
|     } | ||||
| 
 | ||||
| def handle_register_doctor(body): | ||||
|     # body: {doctor_id, department_plain, dept_enc: {ciphertext, exponent}, elgamal_pub: {p,g,y}} | ||||
|     doc_id = body.get("doctor_id","").strip() | ||||
|     dept_plain = body.get("department_plain","").strip() | ||||
|     dept_enc = body.get("dept_enc") | ||||
|     elgamal_pub = body.get("elgamal_pub") | ||||
|     if not doc_id or not doc_id.isalnum(): | ||||
|         return {"status":"error","error":"invalid doctor_id"} | ||||
|     if not dept_plain: | ||||
|         return {"status":"error","error":"invalid department"} | ||||
|     if not dept_enc or "ciphertext" not in dept_enc or "exponent" not in dept_enc: | ||||
|         return {"status":"error","error":"invalid dept_enc"} | ||||
|     if not elgamal_pub or not all(k in elgamal_pub for k in ["p","g","y"]): | ||||
|         return {"status":"error","error":"missing elgamal_pub"} | ||||
| 
 | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|         doctors[doc_id] = { | ||||
|             "department_plain": dept_plain, | ||||
|             "dept_enc": { | ||||
|                 "ciphertext": str(int(dept_enc["ciphertext"])), | ||||
|                 "exponent": int(dept_enc["exponent"]) | ||||
|             }, | ||||
|             "elgamal_pub": { | ||||
|                 "p": str(int(elgamal_pub["p"])), | ||||
|                 "g": str(int(elgamal_pub["g"])), | ||||
|                 "y": str(int(elgamal_pub["y"])) | ||||
|             } | ||||
|         } | ||||
|         write_json(DOCTORS_FILE, doctors) | ||||
|     print(f"[server] registered doctor {doc_id} dept='{dept_plain}' (stored encrypted and plaintext)") | ||||
|     return {"status":"ok"} | ||||
| 
 | ||||
| def handle_upload_report(body): | ||||
|     # body: {doctor_id, filename, timestamp, md5_hex, sig: {r,s}, aes: {key_rsa_oaep_b64, nonce_b64, tag_b64, ct_b64}} | ||||
|     doc_id = body.get("doctor_id","").strip() | ||||
|     filename = os.path.basename(body.get("filename","").strip()) | ||||
|     timestamp = body.get("timestamp","").strip() | ||||
|     md5_hex = body.get("md5_hex","").strip() | ||||
|     sig = body.get("sig") | ||||
|     aes = body.get("aes") | ||||
|     if not doc_id or not filename or not timestamp or not md5_hex or not sig or not aes: | ||||
|         return {"status":"error","error":"missing fields"} | ||||
| 
 | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|     if doc_id not in doctors: | ||||
|         return {"status":"error","error":"unknown doctor_id"} | ||||
| 
 | ||||
|     # decrypt AES key | ||||
|     try: | ||||
|         rsa_cipher = PKCS1_OAEP.new(RSA_PRIV) | ||||
|         aes_key = rsa_cipher.decrypt(b64d(aes["key_rsa_oaep_b64"])) | ||||
|         nonce = b64d(aes["nonce_b64"]) | ||||
|         tag = b64d(aes["tag_b64"]) | ||||
|         ct = b64d(aes["ct_b64"]) | ||||
|         aes_cipher = AES.new(aes_key, AES.MODE_EAX, nonce=nonce) | ||||
|         report_bytes = aes_cipher.decrypt_and_verify(ct, tag) | ||||
|     except Exception as e: | ||||
|         return {"status":"error","error":f"aes/rsa decrypt failed: {e}"} | ||||
| 
 | ||||
|     # verify MD5 | ||||
|     import hashlib | ||||
|     md5_check = hashlib.md5(report_bytes).hexdigest() | ||||
|     if md5_check != md5_hex: | ||||
|         print("[server] md5 mismatch") | ||||
|     # store file | ||||
|     outdir = os.path.join(DATA_DIR, "reports") | ||||
|     os.makedirs(outdir, exist_ok=True) | ||||
|     savepath = os.path.join(outdir, f"{doc_id}_{int(time.time())}_{filename}") | ||||
|     with open(savepath, "wb") as f: | ||||
|         f.write(report_bytes) | ||||
| 
 | ||||
|     # store record | ||||
|     rec = { | ||||
|         "doctor_id": doc_id, | ||||
|         "filename": filename, | ||||
|         "saved_path": savepath, | ||||
|         "timestamp": timestamp, | ||||
|         "md5_hex": md5_hex, | ||||
|         "sig": {"r": str(int(sig["r"])), "s": str(int(sig["s"]))} | ||||
|     } | ||||
|     with lock: | ||||
|         records = read_json(REPORTS_FILE, []) | ||||
|         records.append(rec) | ||||
|         write_json(REPORTS_FILE, records) | ||||
|     print(f"[server] report uploaded by {doc_id}, stored {savepath}") | ||||
|     return {"status":"ok"} | ||||
| 
 | ||||
| def handle_submit_expense(body): | ||||
|     # body: {doctor_id, amount_ciphertext} | ||||
|     doc_id = body.get("doctor_id","").strip() | ||||
|     c = body.get("amount_ciphertext") | ||||
|     if not doc_id or not doc_id.isalnum(): | ||||
|         return {"status":"error","error":"invalid doctor_id"} | ||||
|     try: | ||||
|         c_int = int(c) | ||||
|     except: | ||||
|         return {"status":"error","error":"invalid ciphertext"} | ||||
|     with lock: | ||||
|         doctors = read_json(DOCTORS_FILE, {}) | ||||
|     if doc_id not in doctors: | ||||
|         return {"status":"error","error":"unknown doctor_id"} | ||||
| 
 | ||||
|     with lock: | ||||
|         expenses = read_json(EXPENSES_FILE, []) | ||||
|         expenses.append({"doctor_id": doc_id, "ciphertext": str(c_int)}) | ||||
|         write_json(EXPENSES_FILE, expenses) | ||||
|     print(f"[server] expense ciphertext stored for {doc_id}") | ||||
|     return {"status":"ok"} | ||||
| 
 | ||||
| class RequestHandler(socketserver.StreamRequestHandler): | ||||
|     def handle(self): | ||||
|         try: | ||||
|             data = self.rfile.readline() | ||||
|             if not data: | ||||
|                 return | ||||
|             req = json.loads(data.decode()) | ||||
|             action = req.get("action") | ||||
|             role = req.get("role", "") | ||||
|             body = req.get("body", {}) | ||||
|             if action == "get_public_info": | ||||
|                 resp = {"status":"ok","data": get_public_info()} | ||||
|             elif action == "register_doctor": | ||||
|                 if role != "doctor": | ||||
|                     resp = {"status":"error","error":"unauthorized"} | ||||
|                 else: | ||||
|                     resp = handle_register_doctor(body) | ||||
|             elif action == "upload_report": | ||||
|                 if role != "doctor": | ||||
|                     resp = {"status":"error","error":"unauthorized"} | ||||
|                 else: | ||||
|                     resp = handle_upload_report(body) | ||||
|             elif action == "submit_expense": | ||||
|                 if role != "doctor": | ||||
|                     resp = {"status":"error","error":"unauthorized"} | ||||
|                 else: | ||||
|                     resp = handle_submit_expense(body) | ||||
|             else: | ||||
|                 resp = {"status":"error","error":"unknown action"} | ||||
|         except Exception as e: | ||||
|             resp = {"status":"error","error":str(e)} | ||||
|         self.wfile.write((json.dumps(resp)+"\n").encode()) | ||||
| 
 | ||||
| def start_server(): | ||||
|     server = socketserver.ThreadingTCPServer(("127.0.0.1", PORT), RequestHandler) | ||||
|     t = threading.Thread(target=server.serve_forever, daemon=True) | ||||
|     t.start() | ||||
|     print(f"[server] listening on 127.0.0.1:{PORT}") | ||||
|     return server | ||||
| 
 | ||||
| # Auditor utilities | ||||
| 
 | ||||
| def load_doctors(): | ||||
|     return read_json(DOCTORS_FILE, {}) | ||||
| 
 | ||||
| def load_expenses(): | ||||
|     return read_json(EXPENSES_FILE, []) | ||||
| 
 | ||||
| def load_reports(): | ||||
|     return read_json(REPORTS_FILE, []) | ||||
| 
 | ||||
| def audit_list_doctors(): | ||||
|     docs = load_doctors() | ||||
|     print("Doctors:") | ||||
|     for did, info in docs.items(): | ||||
|         enc = info["dept_enc"] | ||||
|         print(f"- {did} dept_plain='{info['department_plain']}' enc_ciphertext={enc['ciphertext']} exponent={enc['exponent']}") | ||||
| 
 | ||||
| def audit_keyword_search(): | ||||
|     docs = load_doctors() | ||||
|     if not docs: | ||||
|         print("no doctors") | ||||
|         return | ||||
|     q = input("Enter department keyword to search: ").strip() | ||||
|     if not q: | ||||
|         print("empty") | ||||
|         return | ||||
|     # hash to int | ||||
|     import hashlib | ||||
|     h = int.from_bytes(hashlib.sha256(q.encode()).digest(), "big") | ||||
|     pub = PAI_PUB | ||||
|     priv = PAI_PRIV | ||||
|     enc_q = pub.encrypt(h) | ||||
|     print("Matching doctors (using Paillier equality on hashed dept):") | ||||
|     for did, info in docs.items(): | ||||
|         enc = info["dept_enc"] | ||||
|         c = int(enc["ciphertext"]) | ||||
|         exp = int(enc["exponent"]) | ||||
|         enc_doc = paillier.EncryptedNumber(pub, c, exp) | ||||
|         diff = enc_doc - enc_q | ||||
|         dec = priv.decrypt(diff) | ||||
|         match = (dec == 0) | ||||
|         print(f"  {did}: dept_plain='{info['department_plain']}' enc_ciphertext={c} match={match}") | ||||
| 
 | ||||
| def rsa_homo_decrypt_sum(c_prod_int): | ||||
|     n = RSA_PRIV.n | ||||
|     d = RSA_PRIV.d | ||||
|     g = RSA_HOMO_G | ||||
|     # decrypt to get g^sum mod n | ||||
|     m = pow(int(c_prod_int), d, n) | ||||
|     # brute force discrete log for small sums | ||||
|     max_iter = 500000 | ||||
|     acc = 1 | ||||
|     for k in range(0, max_iter+1): | ||||
|         if acc == m: | ||||
|             return k | ||||
|         acc = (acc * g) % n | ||||
|     return None | ||||
| 
 | ||||
| def audit_sum_expenses(): | ||||
|     exps = load_expenses() | ||||
|     if not exps: | ||||
|         print("no expenses") | ||||
|         return | ||||
|     # sum all | ||||
|     n = RSA_PUB.n | ||||
|     c_prod = 1 | ||||
|     for e in exps: | ||||
|         c_prod = (c_prod * int(e["ciphertext"])) % n | ||||
|     print(f"Product ciphertext (represents sum under RSA-in-exponent): {c_prod}") | ||||
|     s = rsa_homo_decrypt_sum(c_prod) | ||||
|     if s is None: | ||||
|         print("sum decryption failed (exceeded search bound)") | ||||
|     else: | ||||
|         print(f"Decrypted sum of expenses = {s}") | ||||
|     # by doctor | ||||
|     docs = load_doctors() | ||||
|     if docs: | ||||
|         print("Per-doctor sums:") | ||||
|         for did in docs.keys(): | ||||
|             c_prod_d = 1 | ||||
|             count = 0 | ||||
|             for e in exps: | ||||
|                 if e["doctor_id"] == did: | ||||
|                     c_prod_d = (c_prod_d * int(e["ciphertext"])) % n | ||||
|                     count += 1 | ||||
|             if count == 0: | ||||
|                 continue | ||||
|             s_d = rsa_homo_decrypt_sum(c_prod_d) | ||||
|             print(f"  {did}: entries={count} product_ct={c_prod_d} sum={s_d}") | ||||
| 
 | ||||
| def elgamal_verify(p, g, y, H_int, r, s): | ||||
|     # verify: g^H ≡ y^r * r^s (mod p) | ||||
|     return pow(g, H_int, p) == (pow(y, r, p) * pow(r, s, p)) % p | ||||
| 
 | ||||
| def audit_verify_reports(): | ||||
|     records = load_reports() | ||||
|     if not records: | ||||
|         print("no reports") | ||||
|         return | ||||
|     doctors = load_doctors() | ||||
|     for rec in records: | ||||
|         did = rec["doctor_id"] | ||||
|         docinfo = doctors.get(did) | ||||
|         ok_sig = False | ||||
|         ok_ts = False | ||||
|         if docinfo: | ||||
|             p = int(docinfo["elgamal_pub"]["p"]) | ||||
|             g = int(docinfo["elgamal_pub"]["g"]) | ||||
|             y = int(docinfo["elgamal_pub"]["y"]) | ||||
|             r = int(rec["sig"]["r"]) | ||||
|             s = int(rec["sig"]["s"]) | ||||
|             try: | ||||
|                 with open(rec["saved_path"], "rb") as f: | ||||
|                     report_bytes = f.read() | ||||
|                 import hashlib | ||||
|                 H = int.from_bytes(hashlib.md5(report_bytes + rec["timestamp"].encode()).digest(), "big") % (p - 1) | ||||
|                 ok_sig = elgamal_verify(p, g, y, H, r, s) | ||||
|             except Exception as e: | ||||
|                 ok_sig = False | ||||
|         # timestamp check | ||||
|         try: | ||||
|             ts = datetime.fromisoformat(rec["timestamp"]) | ||||
|         except: | ||||
|             try: | ||||
|                 ts = datetime.strptime(rec["timestamp"], "%Y-%m-%dT%H:%M:%S.%f") | ||||
|             except: | ||||
|                 ts = None | ||||
|         if ts: | ||||
|             now = datetime.utcnow().replace(tzinfo=None) | ||||
|             delta = (now - ts).total_seconds() | ||||
|             # simple rule: not in the future by more than 5 min | ||||
|             ok_ts = (delta >= -300) | ||||
|         print(f"- report by {did} file={os.path.basename(rec['saved_path'])} sig_ok={ok_sig} ts_ok={ok_ts} ts={rec['timestamp']} md5={rec['md5_hex']}") | ||||
| 
 | ||||
| def auditor_menu(): | ||||
|     while True: | ||||
|         print("\n[Auditor Menu]") | ||||
|         print("1) List doctors (show encrypted and plaintext dept)") | ||||
|         print("2) Keyword search doctors by dept (Paillier)") | ||||
|         print("3) Sum expenses (RSA-in-exponent demo)") | ||||
|         print("4) Verify reports and timestamps") | ||||
|         print("5) Show server public info") | ||||
|         print("0) Exit") | ||||
|         ch = input("Select: ").strip() | ||||
|         if ch == "1": | ||||
|             audit_list_doctors() | ||||
|         elif ch == "2": | ||||
|             audit_keyword_search() | ||||
|         elif ch == "3": | ||||
|             audit_sum_expenses() | ||||
|         elif ch == "4": | ||||
|             audit_verify_reports() | ||||
|         elif ch == "5": | ||||
|             info = get_public_info() | ||||
|             print(json.dumps(info, indent=2)) | ||||
|         elif ch == "0": | ||||
|             print("bye") | ||||
|             break | ||||
|         else: | ||||
|             print("invalid") | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     start_server() | ||||
|     auditor_menu() | ||||
| 
 | ||||
|  | @ -46,4 +46,5 @@ def main() -> None: | |||
| if __name__ == "__main__": | ||||
|     main() | ||||
| 
 | ||||
| curl | ||||
| 
 | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 student
						student